commit 5b1c4f85b694a8892a453352915466128ab30fc9 Author: Hector Martin Date: Wed Nov 23 14:35:12 2016 +0900 Initial commit diff --git a/.gitignore b/.gitignore new file mode 100644 index 0000000..460a56d --- /dev/null +++ b/.gitignore @@ -0,0 +1,9 @@ +.*.swp +*.o +*.d +*~ +.*.swp +*.dSYM +*.pyc +.DS_Store + diff --git a/.kateconfig b/.kateconfig new file mode 100644 index 0000000..74ff610 --- /dev/null +++ b/.kateconfig @@ -0,0 +1 @@ +kate: space-indent off; tab-width 4; indent-width 4; replace-tabs off; eol unix; diff --git a/COPYING b/COPYING new file mode 100644 index 0000000..3912109 --- /dev/null +++ b/COPYING @@ -0,0 +1,340 @@ + GNU GENERAL PUBLIC LICENSE + Version 2, June 1991 + + Copyright (C) 1989, 1991 Free Software Foundation, Inc. + 51 Franklin St, 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 Library 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. + + + Copyright (C) + + 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 St, 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. + + , 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 Library General +Public License instead of this License. diff --git a/README.md b/README.md new file mode 100644 index 0000000..9e42a8f --- /dev/null +++ b/README.md @@ -0,0 +1,61 @@ +# The Homebrew Channel + +This repository contains the public release of the source code for +The Homebrew Channel. + +Included portions: + +* The Homebrew Channel +* Reload stub +* Banner +* PyWii (includes Alameda for banner creation) +* WiiPAX (LZMA executable packer) + +Not included: + +* Installer + +Note that the code in this repository differs from the source code used to build +the official version of The Homebrew Channel, which includes additional +protection features (i.e. we had to add reverse-DRM to stop scammers from +selling it). + +This code is released with no warranty, and hasn't even been tested on a real +Wii, only under Dolphin (yes, this release runs under Dolphin). + +## Build instructions + +You need DevkitPPC and libogc installed, and the DEVKITPRO/DEVKITPPC environment +variables correctly set. Use the latest available versions. Make sure you have +libogc/libfat, and also install the following 3rd party libraries: + +* zlib +* libpng +* mxml +* freetype + +You can download binaries of those +[here](https://sourceforge.net/projects/devkitpro/files/portlibs/ppc/). + +Additionally, you'll need the following packages on your host machine: + +* pycryptodome (for PyWii) +* libpng headers (libpng-dev) +* gettext + +The build process has only been tested on Linux. You're on your own if you +want to try building this on OSX or Windows. + +First run 'make' in wiipax, then 'make' in channel. You'll find a .wad file +that you can install or directly run with Dolphin under +channel/title/channel_retail.wad. You'll also find executable binaries under +channel/channelapp, but be advised that the NAND save file / theme storage +features won't work properly if HBC isn't launched as a channel with its +correct title identity/permissions. + +## License + +Unless otherwise noted in an individual file header, all source code in this +repository is released under the terms of the GNU General Public License, +version 2 or later. The full text of the license can be found in the COPYING +file. diff --git a/broadway.mk b/broadway.mk new file mode 100644 index 0000000..e2e3a8c --- /dev/null +++ b/broadway.mk @@ -0,0 +1,12 @@ +ifeq ($(strip $(DEVKITPPC)),) +$(error "Set DEVKITPPC in your environment.") +endif + +PREFIX = $(DEVKITPPC)/bin/powerpc-eabi- + +CFLAGS = -mcpu=750 -mpaired -m32 -mhard-float -mno-eabi -mno-sdata +CFLAGS += -ffreestanding -ffunction-sections -fdata-sections +CFLAGS += -Wall -Wextra -O2 +ASFLAGS = +LDFLAGS = -mcpu=750 -m32 -n -nostartfiles -nodefaultlibs -Wl,-gc-sections + diff --git a/channel/Makefile b/channel/Makefile new file mode 100644 index 0000000..f813e41 --- /dev/null +++ b/channel/Makefile @@ -0,0 +1,14 @@ +.PHONY: clean installer + +all: + @$(MAKE) -C channelapp channel + @$(MAKE) -C banner + @$(MAKE) -C title + +clean: + @$(MAKE) -C channelapp clean + @$(MAKE) -C banner clean + @$(MAKE) -C title clean + @$(MAKE) -C wiiload clean + @$(MAKE) -C wiiload clean WIN32=1 + diff --git a/channel/README b/channel/README new file mode 100644 index 0000000..7243e6b --- /dev/null +++ b/channel/README @@ -0,0 +1,69 @@ +The Homebrew Channel v1.1.2 +Copyright (C) 2007-2012 Team Twiizers, all rights reserved. +All rights reserved; do not redistribute + + + Update instructions: + + +If you have previously installed The Homebrew Channel, you can update it. If +this is the first time you are installing it, see README.txt for installation +instructions. The easiest way to update is using the built-in online update +functionality. Simply configure the WiFi network settings for your Wii for +proper Internet connectivity, and boot up the channel. If the connection is +established, you'll see an opaque white (not semitransparent) world icon +in the lower right corner, and an update prompt will automatically appear. +Accept it to begin downloading the update. If you cannot or do not want to +connect your Wii to the Internet, simply run the boot.elf file using any +homebrew booting method. For example, you can upload it using wiiload or +you can make a directory inside /apps (for example, /apps/Update) and copy +boot.elf there. Then, simply run it from the previous version of The Homebrew +Channel. + + + Adding and customizing apps: + + +All user applications should be stored in their own subdirectory inside of +apps/; some examples have been provided. Each subdirectory should have at +least three files; ScummVM will be used as an example. + +* apps/ScummVM/boot.elf main executable to be loaded +* apps/ScummVM/icon.png icon to be displayed in The Homebrew Channel + Menu; should be 128 x 48 pixels +* apps/ScummVM/meta.xml XML description of the channel. This format + might change for future releases of The + Homebrew channel, but we will try to remain + backwards-compatible. See + http://wiibrew.org/wiki/Homebrew_Channel + for information on what data should be included + in this file. + + + Staying current with new releases: + + +Relax, you will not need to do anything to keep up with new releases of the +Homebrew Channel. When a new version is available, a message will appear +giving you the option to download and install the new version, if your Wii +is configured to connect to the Internet. + + + Uninstallation: + + +You may uninstall the channel as you would any other channel, by using the Data +Management screen of the Wii Menu. Erasing every last trace of The Homebrew +Channel is not practical on a complex system such as the Wii. If a need +arises, we will develop a more thorough uninstaller application. + + + Reminder about elf files: + + +Old ELF files were incorrect and will not work. Please use DOL files or +recompile with the latest version of devkitPPC. You can use the following +command to convert a broken ELF file to a DOL file: + + powerpc-gekko-objcopy -O binary boot.elf boot.dol + diff --git a/channel/banner/.gitignore b/channel/banner/.gitignore new file mode 100644 index 0000000..a55550a --- /dev/null +++ b/channel/banner/.gitignore @@ -0,0 +1,6 @@ +build +channel.imet +tools/png2tpl +tools/mkbns +tools/lz77 +*.raw diff --git a/channel/banner/Makefile b/channel/banner/Makefile new file mode 100644 index 0000000..cbe4bc5 --- /dev/null +++ b/channel/banner/Makefile @@ -0,0 +1,64 @@ +ifeq ($(WIN32), 1) +export PREFIX = i586-mingw32msvc- +export EXE := .exe +export LFLAGS = -lws2_32 +else +export PREFIX = +export EXE = +export LFLAGS = +endif + +export PYWII := $(CURDIR)/../../pywii/pywii-tools +export ALAMEDA := $(CURDIR)/../../pywii/Alameda + +export PNG2TPL := $(CURDIR)/tools/png2tpl$(EXE) +export MKBNS := $(CURDIR)/tools/mkbns$(EXE) +export LZ77 := $(CURDIR)/tools/lz77$(EXE) +export ADDIMD5 := python $(CURDIR)/tools/addimd5.py +export ARCPACK := python $(PYWII)/arcpack.py +export SOX := sox + +all: channel.imet + +channel.imet: build/data.arc names.txt tools/join-imet.py + python tools/join-imet.py $@ build/data.arc build/icon.arc build/banner.arc build/sound.bns names.txt + +build/data.arc : build/data/meta/icon.bin build/data/meta/banner.bin build/data/meta/sound.bin + $(ARCPACK) $@ build/data + +build/data/meta/icon.bin : icon/* $(PNG2TPL) $(LZ77) + $(MAKE) -C icon -f ../Makefile.section + +build/data/meta/banner.bin : banner/* $(PNG2TPL) $(LZ77) + $(MAKE) -C banner -f ../Makefile.section + +build/data/meta/sound.bin : build/sound.bns + @[ ! -d build/data/meta ] && mkdir -p build/data/meta || true + $(ADDIMD5) $< $@ + +build/sound.bns : build/wiibrew-banner-intro-part.raw build/wiibrew-banner-loop-part.raw $(MKBNS) + @[ ! -d build ] && mkdir -p build || true + $(MKBNS) build/wiibrew-banner-intro-part.raw $@ 1 build/wiibrew-banner-loop-part.raw + +build/%.raw : sound/%.wav + @[ ! -d build ] && mkdir -p build || true + $(SOX) $< -r 32000 -c 2 -e signed-integer -b 16 -t raw $@ + +testi : channel.imet + python $(ALAMEDA)/Alameda.py channel.imet icon +testb : channel.imet + python $(ALAMEDA)/Alameda.py channel.imet banner + +$(PNG2TPL): tools/*.c + $(MAKE) -C tools png2tpl$(EXE) + +$(MKBNS): tools/*.c + $(MAKE) -C tools mkbns$(EXE) + +$(LZ77): tools/*.c + $(MAKE) -C tools lz77$(EXE) + +clean: + rm -f channel.imet + rm -rf build + $(MAKE) -C tools clean diff --git a/channel/banner/Makefile.section b/channel/banner/Makefile.section new file mode 100644 index 0000000..5c586c1 --- /dev/null +++ b/channel/banner/Makefile.section @@ -0,0 +1,30 @@ + +TYPE = $(notdir $(CURDIR)) + +PNGS = $(wildcard *.png) +TPLS = $(patsubst %.png,../build/$(TYPE)/arc/timg/%.tpl,$(PNGS)) + +../build/data/meta/$(TYPE).bin : ../build/$(TYPE).lz77 + @[ ! -d ../build/data/meta ] && mkdir -p ../build/data/meta || true + $(ADDIMD5) $< $@ + +../build/$(TYPE).lz77 : ../build/$(TYPE).arc + $(LZ77) $< $@ + +ifeq ($(TYPE),banner) +ANIMS = ../build/$(TYPE)/arc/anim/$(TYPE)_Start.brlan ../build/$(TYPE)/arc/anim/$(TYPE)_Loop.brlan +else +ANIMS = ../build/$(TYPE)/arc/anim/$(TYPE).brlan +endif +../build/$(TYPE)/arc/blyt/$(TYPE).brlyt $(ANIMS) : mk$(TYPE).py + @[ ! -d ../build/$(TYPE)/arc/anim ] && mkdir -p ../build/$(TYPE)/arc/anim || true + @[ ! -d ../build/$(TYPE)/arc/blyt ] && mkdir -p ../build/$(TYPE)/arc/blyt || true + python mk$(TYPE).py ../build/$(TYPE)/arc/blyt/$(TYPE).brlyt $(ANIMS) + +../build/$(TYPE).arc : $(TPLS) ../build/$(TYPE)/arc/blyt/$(TYPE).brlyt $(ANIMS) + $(ARCPACK) ../build/$(TYPE).arc ../build/$(TYPE) + +../build/$(TYPE)/arc/timg/%.tpl : %.png + @[ ! -d ../build/$(TYPE)/arc/timg ] && mkdir -p ../build/$(TYPE)/arc/timg || true + $(PNG2TPL) $< $@ `grep $< textures.txt | cut -f 2-` + diff --git a/channel/banner/banner/abubble1.png b/channel/banner/banner/abubble1.png new file mode 100644 index 0000000..3517adc Binary files /dev/null and b/channel/banner/banner/abubble1.png differ diff --git a/channel/banner/banner/abubble2.png b/channel/banner/banner/abubble2.png new file mode 100644 index 0000000..7546765 Binary files /dev/null and b/channel/banner/banner/abubble2.png differ diff --git a/channel/banner/banner/abubble3.png b/channel/banner/banner/abubble3.png new file mode 100644 index 0000000..9c65dd8 Binary files /dev/null and b/channel/banner/banner/abubble3.png differ diff --git a/channel/banner/banner/abubble4.png b/channel/banner/banner/abubble4.png new file mode 100644 index 0000000..6d693a6 Binary files /dev/null and b/channel/banner/banner/abubble4.png differ diff --git a/channel/banner/banner/abubble5.png b/channel/banner/banner/abubble5.png new file mode 100644 index 0000000..4b86e18 Binary files /dev/null and b/channel/banner/banner/abubble5.png differ diff --git a/channel/banner/banner/abubble6.png b/channel/banner/banner/abubble6.png new file mode 100644 index 0000000..0d48b20 Binary files /dev/null and b/channel/banner/banner/abubble6.png differ diff --git a/channel/banner/banner/banner_fade.png b/channel/banner/banner/banner_fade.png new file mode 100644 index 0000000..87c5510 Binary files /dev/null and b/channel/banner/banner/banner_fade.png differ diff --git a/channel/banner/banner/banner_shape2.png b/channel/banner/banner/banner_shape2.png new file mode 100644 index 0000000..8ffc2b8 Binary files /dev/null and b/channel/banner/banner/banner_shape2.png differ diff --git a/channel/banner/banner/banner_title.png b/channel/banner/banner/banner_title.png new file mode 100644 index 0000000..9dc8dcf Binary files /dev/null and b/channel/banner/banner/banner_title.png differ diff --git a/channel/banner/banner/banner_wave1a.png b/channel/banner/banner/banner_wave1a.png new file mode 100644 index 0000000..93362ed Binary files /dev/null and b/channel/banner/banner/banner_wave1a.png differ diff --git a/channel/banner/banner/banner_wave1b.png b/channel/banner/banner/banner_wave1b.png new file mode 100644 index 0000000..94f2490 Binary files /dev/null and b/channel/banner/banner/banner_wave1b.png differ diff --git a/channel/banner/banner/banner_wavea.png b/channel/banner/banner/banner_wavea.png new file mode 100644 index 0000000..895bf97 Binary files /dev/null and b/channel/banner/banner/banner_wavea.png differ diff --git a/channel/banner/banner/banner_waveb.png b/channel/banner/banner/banner_waveb.png new file mode 100644 index 0000000..66dc8ea Binary files /dev/null and b/channel/banner/banner/banner_waveb.png differ diff --git a/channel/banner/banner/bbubble1.png b/channel/banner/banner/bbubble1.png new file mode 100644 index 0000000..6761669 Binary files /dev/null and b/channel/banner/banner/bbubble1.png differ diff --git a/channel/banner/banner/cbubble1.png b/channel/banner/banner/cbubble1.png new file mode 100644 index 0000000..e01a6df Binary files /dev/null and b/channel/banner/banner/cbubble1.png differ diff --git a/channel/banner/banner/cbubble2.png b/channel/banner/banner/cbubble2.png new file mode 100644 index 0000000..b3398a9 Binary files /dev/null and b/channel/banner/banner/cbubble2.png differ diff --git a/channel/banner/banner/mkbanner.py b/channel/banner/banner/mkbanner.py new file mode 100644 index 0000000..bea4316 --- /dev/null +++ b/channel/banner/banner/mkbanner.py @@ -0,0 +1,383 @@ +import sys, os, random + +sys.path.append(os.environ['ALAMEDA']+"/../Common") +sys.path.append(os.environ['ALAMEDA']) + +from Alameda import * + +brlyt = Brlyt(None, None, None) +brlyt.Width = 810 +brlyt.Height = 456 + +brlyt.Textures.add(Brlyt.BrlytTexture("banner_title.tpl")) +brlyt.Textures.add(Brlyt.BrlytTexture("white.tpl")) +brlyt.Textures.add(Brlyt.BrlytTexture("banner_wavea.tpl")) +brlyt.Textures.add(Brlyt.BrlytTexture("banner_waveb.tpl")) +brlyt.Textures.add(Brlyt.BrlytTexture("banner_wave1a.tpl")) +brlyt.Textures.add(Brlyt.BrlytTexture("banner_wave1b.tpl")) +brlyt.Textures.add(Brlyt.BrlytTexture("banner_shape2.tpl")) +brlyt.Textures.add(Brlyt.BrlytTexture("banner_fade.tpl")) + +brlyt.Materials.add(Brlyt.BrlytMaterial("title")) +brlyt.Materials[0].Textures.append((0,0,0)) +brlyt.Materials[0].TextureCoords.append([0,0,0,1,1]) +brlyt.Materials[0].SthB.append(0x01041e00) + +brlyt.Materials.add(Brlyt.BrlytMaterial("white")) +brlyt.Materials[1].Textures.append((1,1,1)) +brlyt.Materials[1].TextureCoords.append([0,0,0,1,1]) +brlyt.Materials[1].SthB.append(0x01041e00) + +brlyt.Materials.add(Brlyt.BrlytMaterial("wavea")) +brlyt.Materials[2].Textures.append((2,1,0)) +brlyt.Materials[2].TextureCoords.append([0,2,0,3,6]) +brlyt.Materials[2].SthB.append(0x01041e00) + +brlyt.Materials.add(Brlyt.BrlytMaterial("waveb")) +brlyt.Materials[3].Textures.append((3,1,0)) +brlyt.Materials[3].TextureCoords.append([0,2,0,3,6]) +brlyt.Materials[3].SthB.append(0x01041e00) + +brlyt.Materials.add(Brlyt.BrlytMaterial("wave1a")) +brlyt.Materials[4].Textures.append((4,0,0)) +brlyt.Materials[4].TextureCoords.append([0,0,0,1,1]) +brlyt.Materials[4].SthB.append(0x01041e00) + +brlyt.Materials.add(Brlyt.BrlytMaterial("wave1b")) +brlyt.Materials[5].Textures.append((5,0,0)) +brlyt.Materials[5].TextureCoords.append([0,0,0,1,1]) +brlyt.Materials[5].SthB.append(0x01041e00) + +brlyt.Materials.add(Brlyt.BrlytMaterial("shape2")) +brlyt.Materials[6].Textures.append((6,0,0)) +brlyt.Materials[6].TextureCoords.append([0,0,0,1,1]) +brlyt.Materials[6].SthB.append(0x01041e00) + +brlyt.Materials.add(Brlyt.BrlytMaterial("fade")) +brlyt.Materials[7].Textures.append((7,0,0)) +brlyt.Materials[7].TextureCoords.append([0,0,0,1,1]) +brlyt.Materials[7].SthB.append(0x01041e00) + +brlyt.RootPane = Pane("RootPane") +brlyt.RootPane.Width = brlyt.Width +brlyt.RootPane.Height = brlyt.Height + +waterpane = Pane("water") +bubblepane = Pane("bubbles") + +bkg = Picture("background") +bkg.Material = 1 +bkg.X, bkg.Y, bkg.Width, bkg.Height = 0,0,brlyt.Width,brlyt.Height + +tit = Picture("title") +tit.Material = 0 +tit.X, tit.Y, tit.Width, tit.Height = 0,32,400,180 + +wavea = Picture("wavea") +wavea.Material = 2 +wavea.X, wavea.Y, wavea.Width, wavea.Height = -300,35,3072,384 + +waveb = Picture("waveb") +waveb.Material = 3 +waveb.X, waveb.Y, waveb.Width, waveb.Height = -300,32,3072,384 + +wave1a = Picture("wave1a") +wave1a.Material = 4 +wave1a.X, wave1a.Y, wave1a.Width, wave1a.Height = -200,160,382,32 + +wave1b1 = Picture("wave1b1") +wave1b1.Material = 5 +wave1b1.X, wave1b1.Y, wave1b1.Width, wave1b1.Height = 200,170,527,37 + +wave1b2 = Picture("wave1b2") +wave1b2.Material = 5 +wave1b2.X, wave1b2.Y, wave1b2.Width, wave1b2.Height = -380,170,527,37 + +shadow = Picture("shadow") +shadow.Material = 6 +shadow.X, shadow.Y, shadow.Width, shadow.Height = -180,150,644,28 + +fade = Picture("fade") +fade.Material = 7 +fade.X, fade.Y, fade.Width, fade.Height = 0,8,brlyt.Width,256 + +boom = Picture("boom") +boom.Material = 1 +boom.Alpha = 0 +boom.X, boom.Y, boom.Width, boom.Height = 0,0,brlyt.Width,brlyt.Height + +waterpane.Add(wavea) +waterpane.Add(waveb) +waterpane.Add(wave1a) +waterpane.Add(wave1b1) +waterpane.Add(wave1b2) +waterpane.Add(shadow) +waterpane.Add(fade) +waterpane.Add(bubblepane) + +brlyt.RootPane.Add(bkg) +brlyt.RootPane.Add(waterpane) +brlyt.RootPane.Add(tit) +brlyt.RootPane.Add(boom) + +fakeStart = -60 * 10 +loopStart = 60 * 6 +loopEnd = 60 * 22 + +brlan = Brlan() +brlan.Anim.add(Brlan.BrlanAnimSet("water")) +brlan.Anim.add(Brlan.BrlanAnimSet("wavea")) +brlan.Anim.add(Brlan.BrlanAnimSet("waveb")) +brlan.Anim.add(Brlan.BrlanAnimSet("wave1a")) +brlan.Anim.add(Brlan.BrlanAnimSet("wave1b1")) +brlan.Anim.add(Brlan.BrlanAnimSet("wave1b2")) +brlan.Anim.add(Brlan.BrlanAnimSet("shadow")) +brlan.Anim.add(Brlan.BrlanAnimSet("title")) +brlan.Anim.add(Brlan.BrlanAnimSet("boom")) + +brlan.Anim['title'].add(Brlan.BrlanAnimClass(Brlan.A_COORD)) +brlan.Anim['title'][Brlan.A_COORD].add(Brlan.BrlanAnim(Brlan.C_Y)) +brlan.Anim['title'][Brlan.A_COORD][Brlan.C_Y].repsimple(fakeStart, loopEnd, 12, 32, 0, 20, 0) + +brlan.Anim['title'].add(Brlan.BrlanAnimClass(Brlan.A_PARM)) +brlan.Anim['title'][Brlan.A_PARM].add(Brlan.BrlanAnim(Brlan.P_ALPHA)) +brlan.Anim['title'][Brlan.A_PARM][Brlan.P_ALPHA].Triplets.append((0, 0, 0)) +brlan.Anim['title'][Brlan.A_PARM][Brlan.P_ALPHA].Triplets.append((243, 0, 0)) +brlan.Anim['title'][Brlan.A_PARM][Brlan.P_ALPHA].Triplets.append((244, 255, 0)) + +brlan.Anim['boom'].add(Brlan.BrlanAnimClass(Brlan.A_PARM)) +brlan.Anim['boom'][Brlan.A_PARM].add(Brlan.BrlanAnim(Brlan.P_ALPHA)) +brlan.Anim['boom'][Brlan.A_PARM][Brlan.P_ALPHA].Triplets.append((0, 0, 0)) +brlan.Anim['boom'][Brlan.A_PARM][Brlan.P_ALPHA].Triplets.append((243, 0, 0)) +brlan.Anim['boom'][Brlan.A_PARM][Brlan.P_ALPHA].Triplets.append((244, 255, 0)) +brlan.Anim['boom'][Brlan.A_PARM][Brlan.P_ALPHA].Triplets.append((246, 255, 0)) +brlan.Anim['boom'][Brlan.A_PARM][Brlan.P_ALPHA].Triplets.append((256, 0, 0)) + + +for i in ['wavea', 'waveb', 'wave1a', 'wave1b1', 'wave1b2', 'shadow']: + brlan.Anim[i].add(Brlan.BrlanAnimClass(Brlan.A_COORD)) + brlan.Anim[i][Brlan.A_COORD].add(Brlan.BrlanAnim(Brlan.C_X)) + brlan.Anim[i][Brlan.A_COORD].add(Brlan.BrlanAnim(Brlan.C_Y)) + +brlan.Anim['wavea'][Brlan.A_COORD][Brlan.C_X].repsimple(fakeStart, loopEnd, 4, -300, 0, 300, 0) +brlan.Anim['waveb'][Brlan.A_COORD][Brlan.C_X].repsimple(fakeStart, loopEnd, 4, -300, 2.0, 300, 2.0) +brlan.Anim['wavea'][Brlan.A_COORD][Brlan.C_Y].repsimple(fakeStart, loopEnd, 6, 35, 0, 55, 0) +brlan.Anim['waveb'][Brlan.A_COORD][Brlan.C_Y].repsimple(fakeStart, loopEnd, 8, 45, 0, 55, 0) + +brlan.Anim['wave1a'][Brlan.A_COORD][Brlan.C_X].repsimple(fakeStart, loopEnd, 4, -200, 4.0, 200, 4.0) +brlan.Anim['wave1a'][Brlan.A_COORD][Brlan.C_Y].repsimple(fakeStart, loopEnd, 6, 160, 0.2, 180, 0.2) +brlan.Anim['wave1b1'][Brlan.A_COORD][Brlan.C_X].repsimple(fakeStart, loopEnd, 4, 200, 3.2, 400, 3.2) +brlan.Anim['wave1b1'][Brlan.A_COORD][Brlan.C_Y].repsimple(fakeStart, loopEnd, 6, 170, 0.2, 183, 0.2) +brlan.Anim['wave1b2'][Brlan.A_COORD][Brlan.C_X].repsimple(fakeStart, loopEnd, 4, -400, 3.7, -200, 3.7) +brlan.Anim['wave1b2'][Brlan.A_COORD][Brlan.C_Y].repsimple(fakeStart, loopEnd, 6, 165, 0.2, 185, 0.2) + +brlan.Anim['shadow'][Brlan.A_COORD][Brlan.C_X].repsimple(fakeStart, loopEnd, 4, -180, 1.4, 100, 1.4) +brlan.Anim['shadow'][Brlan.A_COORD][Brlan.C_Y].repsimple(fakeStart, loopEnd, 6, 150, 0.2, 155, 0.2) + +brlan.Anim.add(Brlan.BrlanAnimSet("water")) +brlan.Anim['water'].add(Brlan.BrlanAnimClass(Brlan.A_COORD)) +brlan.Anim['water'][Brlan.A_COORD].add(Brlan.BrlanAnim(Brlan.C_Y)) +brlan.Anim['water'][Brlan.A_COORD][Brlan.C_Y].Triplets.append((0, -320, 0)) +brlan.Anim['water'][Brlan.A_COORD][Brlan.C_Y].Triplets.append((160, 0, 0)) + +##### Bubble generation ##### + +random.seed(1) + +class BubbleType: + def __init__(self, name, width, height): + self.Name = name + self.TextureName = name + ".tpl" + self.Width = width + self.Height = height + self.PicCtr = 0 + def makemat(self, brlyt): + self.Brlyt = brlyt + self.Texture = len(brlyt.Textures) + brlyt.Textures.add(Brlyt.BrlytTexture(self.TextureName)) + self.Material = len(brlyt.Materials) + brlyt.Materials.add(Brlyt.BrlytMaterial(self.Name)) + brlyt.Materials[self.Material].Textures.append((self.Texture,0,0)) + brlyt.Materials[self.Material].TextureCoords.append([0,0,0,1,1]) + brlyt.Materials[self.Material].SthB.append(0x01041e00) + def makepic(self): + name = "%s_%d"%(self.Name,self.PicCtr) + pic = Picture(name) + pic.Material = self.Material + pic.X, pic.Y, pic.Width, pic.Height = 0,-600,self.Width,self.Height + self.PicCtr += 1 + return pic + +class BubbleInstance: + YSTART = -150 + YEND = 170 + FADESTART = 0.7 + def __init__(self, start, length=None, x=None, xp=None): + if x is None: + x = random.uniform(-brlyt.Width/2-64, brlyt.Width/2+64) + if xp is None: + xp = random.uniform(0, 32) + if length is None: + length = random.uniform(50, 170) + self.X = x + self.XP = xp + self.Start = start + self.Length = length + self.End = self.Start + self.Length + self.TypeID = None + def render(self, brlan): + if self.Picture.Name not in brlan.Anim: + #print "Adding animation set for %s"%self.Picture.Name + brlan.Anim.add(Brlan.BrlanAnimSet(self.Picture.Name)) + brlan.Anim[self.Picture.Name].add(Brlan.BrlanAnimClass(Brlan.A_COORD)) + brlan.Anim[self.Picture.Name].add(Brlan.BrlanAnimClass(Brlan.A_PARM)) + brlan.Anim[self.Picture.Name][Brlan.A_COORD].add(Brlan.BrlanAnim(Brlan.C_X)) + brlan.Anim[self.Picture.Name][Brlan.A_COORD].add(Brlan.BrlanAnim(Brlan.C_Y)) + brlan.Anim[self.Picture.Name][Brlan.A_PARM].add(Brlan.BrlanAnim(Brlan.P_ALPHA)) + + tps = [ + brlan.Anim[self.Picture.Name][Brlan.A_COORD][Brlan.C_X], + brlan.Anim[self.Picture.Name][Brlan.A_COORD][Brlan.C_Y], + brlan.Anim[self.Picture.Name][Brlan.A_PARM][Brlan.P_ALPHA] + ] + + for i in tps: + if len(i.Triplets) > 0: + if i.Triplets[-1][0] >= self.Start: + print "WTF at %s: %f >= %f"%(self.Picture.Name,i.Triplets[-1][0],self.Start) + raise RuntimeError("We Have A Problem") + + brlan.Anim[self.Picture.Name][Brlan.A_COORD][Brlan.C_X].Triplets.append((self.Start, self.X, 0)) + brlan.Anim[self.Picture.Name][Brlan.A_COORD][Brlan.C_Y].Triplets.append((self.Start, self.YSTART, 1)) + brlan.Anim[self.Picture.Name][Brlan.A_COORD][Brlan.C_X].Triplets.append((self.End, self.X, 0)) + brlan.Anim[self.Picture.Name][Brlan.A_COORD][Brlan.C_Y].Triplets.append((self.End, self.YEND, 1)) + + fadepoint = self.Start + self.Length * self.FADESTART + + brlan.Anim[self.Picture.Name][Brlan.A_PARM][Brlan.P_ALPHA].Triplets.append((self.Start, 0, 0)) + brlan.Anim[self.Picture.Name][Brlan.A_PARM][Brlan.P_ALPHA].Triplets.append((self.Start, 255, 0)) + brlan.Anim[self.Picture.Name][Brlan.A_PARM][Brlan.P_ALPHA].Triplets.append((fadepoint, 255, 0)) + brlan.Anim[self.Picture.Name][Brlan.A_PARM][Brlan.P_ALPHA].Triplets.append((self.End, 0, 0)) + def clone(self): + new = BubbleInstance(self.Start, self.Length, self.X, self.XP) + new.TypeID = self.TypeID + return new + +class BubbleCollection: + def __init__(self, brlyt, brlan, pane): + self.Brlyt = brlyt + self.Brlan = brlan + self.Pane = pane + self.BubbleTypes = [] + self.Instances = [] + def addtype(self, t, chance): + self.BubbleTypes.append((t, chance)) + def choosetype(self): + sumchances = sum([c for t,c in self.BubbleTypes]) + opt = random.uniform(0,sumchances) + for i,bt in enumerate(self.BubbleTypes): + t,c = bt + if c > opt: + return i + opt -= c + return i + def addinstance(self, i): + if i.TypeID is None: + i.TypeID = self.choosetype() + self.Instances.append(i) + def cleanInstances(self, time): + for tis in self.TypeInstances: + for i, ti in enumerate(tis): + pic, user = ti + if user is not None and user.End < time: + #print "Freeing instance: [%f-%f]"%(user.Start,user.End) + tis[i] = pic, None + def printinstances(self): + print "Type Instances:" + for tid, tis in enumerate(self.TypeInstances): + print " Type Instances for type %d (%s):"%(tid, self.BubbleTypes[tid][0].Name) + for i, ti in enumerate(tis): + pic, user = ti + if user is None: + print " %d: Picture %s, free"%(i,pic.Name) + else: + print " %d: Picture %s, user: %s [%f-%f]"%(i,pic.Name,repr(user),user.Start,user.End) + def render(self): + for t,c in self.BubbleTypes: + t.makemat(self.Brlyt) + self.TypeInstances = [] + for i in range(len(self.BubbleTypes)): + self.TypeInstances.append([]) + self.Instances.sort(key=lambda x: x.Start) + for n,i in enumerate(self.Instances): + #print "Processing instance %d of type %d (%f-%f)"%(n,i.TypeID,i.Start,i.End) + time = i.Start + self.cleanInstances(time) + tis = self.TypeInstances[i.TypeID] + for nti,ti in enumerate(tis): + pic, user = ti + if user is None: + tis[nti] = (pic,i) + i.Picture = pic + #print "Found space" + break + else: + #print "No space for type %d"%i.TypeID + pic = self.BubbleTypes[i.TypeID][0].makepic() + self.Pane.Add(pic) + tis.append((pic, i)) + i.Picture = pic + i.render(self.Brlan) + #self.printinstances() + +print "Fake Start",fakeStart +print "Loop Start",loopStart +print "Loop End",loopEnd + +col = BubbleCollection(brlyt, brlan, bubblepane) +col.addtype(BubbleType("abubble1", 48, 48),1) +col.addtype(BubbleType("abubble2", 32, 32),1) +col.addtype(BubbleType("abubble3", 16, 16),1) +col.addtype(BubbleType("abubble4", 24, 24),1) +col.addtype(BubbleType("abubble5", 32, 32),1) +col.addtype(BubbleType("abubble6", 16, 16),1) +col.addtype(BubbleType("bbubble1", 48, 48),1) +col.addtype(BubbleType("cbubble1", 64, 64),1) +col.addtype(BubbleType("cbubble2", 16, 16),1) + +bubbleBoom = 190 + +# bubble mania! +for i in range(100): + col.addinstance(BubbleInstance(bubbleBoom)) + +# now fill things +for i in range(280): + start = random.uniform(bubbleBoom, loopEnd) + col.addinstance(BubbleInstance(start)) + +# clear the ones that overrun the loop +for i in col.Instances: + if i.End > loopEnd: + col.Instances.remove(i) + +# and copy the loop start bubbles to the end +for i in col.Instances: + if i.Start < loopStart and i.End > loopStart: + new = i.clone() + new.Start -= loopStart + new.Start += loopEnd + new.End -= loopStart + new.End += loopEnd + col.Instances.append(new) + +col.render() + +brldata = brlyt.Pack() +open(sys.argv[1],"w").write(brldata) + +bradata = brlan.Pack(loopStart) +open(sys.argv[2],"w").write(bradata) + +bradata = brlan.Pack(loopStart, loopEnd) +open(sys.argv[3],"w").write(bradata) diff --git a/channel/banner/banner/textures.txt b/channel/banner/banner/textures.txt new file mode 100644 index 0000000..3608078 --- /dev/null +++ b/channel/banner/banner/textures.txt @@ -0,0 +1,20 @@ +#----------------------- +# texture x y +#----------------------- +abubble1.png 0 0 +abubble2.png 0 0 +abubble3.png 0 0 +abubble4.png 0 0 +abubble5.png 0 0 +abubble6.png 0 0 +bbubble1.png 0 0 +cbubble1.png 0 0 +cbubble2.png 0 0 +white.png 1 1 +banner_fade.png 1 0 +banner_title.png 0 0 +banner_wave1a.png 0 0 +banner_wave1b.png 0 0 +banner_wavea.png 1 0 +banner_waveb.png 1 0 +banner_shape2.png 0 0 diff --git a/channel/banner/banner/white.png b/channel/banner/banner/white.png new file mode 100644 index 0000000..62269a8 Binary files /dev/null and b/channel/banner/banner/white.png differ diff --git a/channel/banner/icon/icon_fade.png b/channel/banner/icon/icon_fade.png new file mode 100644 index 0000000..c127a61 Binary files /dev/null and b/channel/banner/icon/icon_fade.png differ diff --git a/channel/banner/icon/icon_shape2.png b/channel/banner/icon/icon_shape2.png new file mode 100644 index 0000000..b42d048 Binary files /dev/null and b/channel/banner/icon/icon_shape2.png differ diff --git a/channel/banner/icon/icon_title.png b/channel/banner/icon/icon_title.png new file mode 100644 index 0000000..396d471 Binary files /dev/null and b/channel/banner/icon/icon_title.png differ diff --git a/channel/banner/icon/icon_wave1a.png b/channel/banner/icon/icon_wave1a.png new file mode 100644 index 0000000..128157b Binary files /dev/null and b/channel/banner/icon/icon_wave1a.png differ diff --git a/channel/banner/icon/icon_wave1b.png b/channel/banner/icon/icon_wave1b.png new file mode 100644 index 0000000..677f4a1 Binary files /dev/null and b/channel/banner/icon/icon_wave1b.png differ diff --git a/channel/banner/icon/icon_wavea.png b/channel/banner/icon/icon_wavea.png new file mode 100644 index 0000000..c62fe12 Binary files /dev/null and b/channel/banner/icon/icon_wavea.png differ diff --git a/channel/banner/icon/icon_waveb.png b/channel/banner/icon/icon_waveb.png new file mode 100644 index 0000000..9dc8ffb Binary files /dev/null and b/channel/banner/icon/icon_waveb.png differ diff --git a/channel/banner/icon/mkicon.py b/channel/banner/icon/mkicon.py new file mode 100644 index 0000000..07ca2c0 --- /dev/null +++ b/channel/banner/icon/mkicon.py @@ -0,0 +1,182 @@ +import sys, os, struct, random + +sys.path.append(os.environ['ALAMEDA']+"/../Common") +sys.path.append(os.environ['ALAMEDA']) + +from Alameda import * + +brlyt = Brlyt(None, None, None) +brlyt.Width = 170 +brlyt.Height = 96 + +brlyt.Textures.add(Brlyt.BrlytTexture("icon_title.tpl")) +brlyt.Textures.add(Brlyt.BrlytTexture("white.tpl")) +brlyt.Textures.add(Brlyt.BrlytTexture("icon_wavea.tpl")) +brlyt.Textures.add(Brlyt.BrlytTexture("icon_waveb.tpl")) +brlyt.Textures.add(Brlyt.BrlytTexture("icon_wave1a.tpl")) +brlyt.Textures.add(Brlyt.BrlytTexture("icon_wave1b.tpl")) +brlyt.Textures.add(Brlyt.BrlytTexture("icon_shape2.tpl")) +brlyt.Textures.add(Brlyt.BrlytTexture("icon_fade.tpl")) + +brlyt.Materials.add(Brlyt.BrlytMaterial("title")) +brlyt.Materials[0].Textures.append((0,0,0)) +brlyt.Materials[0].TextureCoords.append([0,0,0,1,1]) +brlyt.Materials[0].SthB.append(0x01041e00) + +brlyt.Materials.add(Brlyt.BrlytMaterial("white")) +brlyt.Materials[1].Textures.append((1,1,1)) +brlyt.Materials[1].TextureCoords.append([0,0,0,1,1]) +brlyt.Materials[1].SthB.append(0x01041e00) + +brlyt.Materials.add(Brlyt.BrlytMaterial("wavea")) +brlyt.Materials[2].Textures.append((2,1,0)) +brlyt.Materials[2].TextureCoords.append([0,2,0,4,6]) +brlyt.Materials[2].SthB.append(0x01041e00) + +brlyt.Materials.add(Brlyt.BrlytMaterial("waveb")) +brlyt.Materials[3].Textures.append((3,1,0)) +brlyt.Materials[3].TextureCoords.append([0,2,0,4,6]) +brlyt.Materials[3].SthB.append(0x01041e00) + +brlyt.Materials.add(Brlyt.BrlytMaterial("wave1a")) +brlyt.Materials[4].Textures.append((4,0,0)) +brlyt.Materials[4].TextureCoords.append([0,0,0,1,1]) +brlyt.Materials[4].SthB.append(0x01041e00) + +brlyt.Materials.add(Brlyt.BrlytMaterial("wave1b")) +brlyt.Materials[5].Textures.append((5,0,0)) +brlyt.Materials[5].TextureCoords.append([0,0,0,1,1]) +brlyt.Materials[5].SthB.append(0x01041e00) + +brlyt.Materials.add(Brlyt.BrlytMaterial("shape2")) +brlyt.Materials[6].Textures.append((6,0,0)) +brlyt.Materials[6].TextureCoords.append([0,0,0,1,1]) +brlyt.Materials[6].SthB.append(0x01041e00) + +brlyt.Materials.add(Brlyt.BrlytMaterial("fade")) +brlyt.Materials[7].Textures.append((7,0,0)) +brlyt.Materials[7].TextureCoords.append([0,0,0,1,1]) +brlyt.Materials[7].SthB.append(0x01041e00) + + +brlyt.RootPane = Pane("RootPane") +brlyt.RootPane.Width = brlyt.Width +brlyt.RootPane.Height = brlyt.Height + +waterpane = Pane("water") + +bkg = Picture("background") +bkg.Material = 1 +bkg.X, bkg.Y, bkg.Width, bkg.Height = 0,0,170,96 + +tit = Picture("title") +tit.Material = 0 +tit.X, tit.Y, tit.Width, tit.Height = 0,-7,110,49 + +wavea = Picture("wavea") +wavea.Material = 2 +wavea.X, wavea.Y, wavea.Width, wavea.Height = -171,-5,1024,96 + +waveb = Picture("waveb") +waveb.Material = 3 +waveb.X, waveb.Y, waveb.Width, waveb.Height = -171,-3,1024,96 + +wave1a = Picture("wave1a") +wave1a.Material = 4 +wave1a.X, wave1a.Y, wave1a.Width, wave1a.Height = -75,27,94,8 + +wave1b = Picture("wave1b") +wave1b.Material = 5 +wave1b.X, wave1b.Y, wave1b.Width, wave1b.Height = 65,27,130,9 + +shadow = Picture("shadow") +shadow.Material = 6 +shadow.X, shadow.Y, shadow.Width, shadow.Height = -45,21,159,7 + +fade = Picture("fade") +fade.Material = 7 +fade.X, fade.Y, fade.Width, fade.Height = 0,-16,170,80 + + +waterpane.Add(wavea) +waterpane.Add(waveb) +waterpane.Add(wave1a) +waterpane.Add(wave1b) +waterpane.Add(shadow) +waterpane.Add(fade) + +brlyt.RootPane.Add(bkg) +brlyt.RootPane.Add(waterpane) +brlyt.RootPane.Add(tit) + +brldata = brlyt.Pack() + +open(sys.argv[1],"w").write(brldata) + + +brlan = Brlan() +brlan.Anim.add(Brlan.BrlanAnimSet("water")) +brlan.Anim.add(Brlan.BrlanAnimSet("wavea")) +brlan.Anim.add(Brlan.BrlanAnimSet("waveb")) +brlan.Anim.add(Brlan.BrlanAnimSet("wave1a")) +brlan.Anim.add(Brlan.BrlanAnimSet("wave1b")) +brlan.Anim.add(Brlan.BrlanAnimSet("shadow")) +brlan.Anim.add(Brlan.BrlanAnimSet("title")) + +brlan.Anim['title'].add(Brlan.BrlanAnimClass(Brlan.A_COORD)) +brlan.Anim['title'][Brlan.A_COORD].add(Brlan.BrlanAnim(Brlan.C_Y)) +brlan.Anim['title'][Brlan.A_COORD][Brlan.C_Y].repsimple(0, 960, 8, -7, 0, -11, 0) + +for i in ['wavea', 'waveb', 'wave1a', 'wave1b', 'shadow']: + brlan.Anim[i].add(Brlan.BrlanAnimClass(Brlan.A_COORD)) + brlan.Anim[i][Brlan.A_COORD].add(Brlan.BrlanAnim(Brlan.C_X)) + brlan.Anim[i][Brlan.A_COORD].add(Brlan.BrlanAnim(Brlan.C_Y)) + +brlan.Anim['wavea'][Brlan.A_COORD][Brlan.C_X].repsimple(0, 960, 2, -130, 0, 130, 0) +brlan.Anim['waveb'][Brlan.A_COORD][Brlan.C_X].repsimple(0, 960, 2, -130, 2.0, 130, 2.0) +brlan.Anim['wavea'][Brlan.A_COORD][Brlan.C_Y].repsimple(0, 960, 3, -5, 0, 1, 0) +brlan.Anim['waveb'][Brlan.A_COORD][Brlan.C_Y].repsimple(0, 960, 4, -3, 0, 3, 0) + +brlan.Anim['wave1a'][Brlan.A_COORD][Brlan.C_X].Triplets.append((0, -75, 0.2)) +brlan.Anim['wave1a'][Brlan.A_COORD][Brlan.C_Y].Triplets.append((0, 27, 0.2)) +brlan.Anim['wave1a'][Brlan.A_COORD][Brlan.C_X].Triplets.append((100, 0, 0.2)) +brlan.Anim['wave1a'][Brlan.A_COORD][Brlan.C_Y].Triplets.append((100, 29, 0.2)) +brlan.Anim['wave1a'][Brlan.A_COORD][Brlan.C_X].Triplets.append((200, 50, 0.2)) +brlan.Anim['wave1a'][Brlan.A_COORD][Brlan.C_Y].Triplets.append((200, 27, 0.2)) +brlan.Anim['wave1a'][Brlan.A_COORD][Brlan.C_X].Triplets.append((400, -75, 0.2)) +brlan.Anim['wave1a'][Brlan.A_COORD][Brlan.C_Y].Triplets.append((400, 27, 0.2)) +brlan.Anim['wave1a'][Brlan.A_COORD][Brlan.C_X].Triplets.append((500, -40, 0.2)) +brlan.Anim['wave1a'][Brlan.A_COORD][Brlan.C_Y].Triplets.append((500, 30, 0.2)) +brlan.Anim['wave1a'][Brlan.A_COORD][Brlan.C_X].Triplets.append((600, -40, 0.2)) +brlan.Anim['wave1a'][Brlan.A_COORD][Brlan.C_Y].Triplets.append((600, 30, 0.2)) +brlan.Anim['wave1a'][Brlan.A_COORD][Brlan.C_X].Triplets.append((750, 15, 0.2)) +brlan.Anim['wave1a'][Brlan.A_COORD][Brlan.C_Y].Triplets.append((750, 28, 0.2)) +brlan.Anim['wave1a'][Brlan.A_COORD][Brlan.C_X].Triplets.append((960, -75, 0.2)) +brlan.Anim['wave1a'][Brlan.A_COORD][Brlan.C_Y].Triplets.append((960, 27, 0.2)) + +brlan.Anim['wave1b'][Brlan.A_COORD][Brlan.C_X].Triplets.append((0, 65, 0.2)) +brlan.Anim['wave1b'][Brlan.A_COORD][Brlan.C_Y].Triplets.append((0, 27, 0.2)) +brlan.Anim['wave1b'][Brlan.A_COORD][Brlan.C_X].Triplets.append((120, 10, 0.2)) +brlan.Anim['wave1b'][Brlan.A_COORD][Brlan.C_Y].Triplets.append((120, 29, 0.2)) +brlan.Anim['wave1b'][Brlan.A_COORD][Brlan.C_X].Triplets.append((190, 65, 0.2)) +brlan.Anim['wave1b'][Brlan.A_COORD][Brlan.C_Y].Triplets.append((190, 27, 0.2)) +brlan.Anim['wave1b'][Brlan.A_COORD][Brlan.C_X].Triplets.append((430, 20, 0.2)) +brlan.Anim['wave1b'][Brlan.A_COORD][Brlan.C_Y].Triplets.append((430, 27, 0.2)) +brlan.Anim['wave1b'][Brlan.A_COORD][Brlan.C_X].Triplets.append((510, -20, 0.2)) +brlan.Anim['wave1b'][Brlan.A_COORD][Brlan.C_Y].Triplets.append((510, 30, 0.2)) +brlan.Anim['wave1b'][Brlan.A_COORD][Brlan.C_X].Triplets.append((670, -40, 0.2)) +brlan.Anim['wave1b'][Brlan.A_COORD][Brlan.C_Y].Triplets.append((670, 30, 0.2)) +brlan.Anim['wave1b'][Brlan.A_COORD][Brlan.C_X].Triplets.append((710, 0, 0.2)) +brlan.Anim['wave1b'][Brlan.A_COORD][Brlan.C_Y].Triplets.append((710, 28, 0.2)) +brlan.Anim['wave1b'][Brlan.A_COORD][Brlan.C_X].Triplets.append((960, 65, 0.2)) +brlan.Anim['wave1b'][Brlan.A_COORD][Brlan.C_Y].Triplets.append((960, 27, 0.2)) + +brlan.Anim['shadow'][Brlan.A_COORD][Brlan.C_Y].repsimple(0, 960, 4, 21, -0.1, 25, -0.1) +brlan.Anim['shadow'][Brlan.A_COORD][Brlan.C_X].repsimple(0, 960, 2, -45, -0.1, 45, -0.1) + + +bradata = brlan.Pack(60*16) +for a,b,c in brlan.Anim['waveb'][Brlan.A_COORD][Brlan.C_X].Triplets: + print a,b,c + +open(sys.argv[2],"w").write(bradata) diff --git a/channel/banner/icon/textures.txt b/channel/banner/icon/textures.txt new file mode 100644 index 0000000..f2ab860 --- /dev/null +++ b/channel/banner/icon/textures.txt @@ -0,0 +1,11 @@ +#----------------------- +# texture x y +#----------------------- +white.png 1 1 +icon_fade.png 1 0 +icon_title.png 0 0 +icon_wave1a.png 0 0 +icon_wave1b.png 0 0 +icon_wavea.png 1 0 +icon_waveb.png 1 0 +icon_shape2.png 0 0 diff --git a/channel/banner/icon/white.png b/channel/banner/icon/white.png new file mode 100644 index 0000000..62269a8 Binary files /dev/null and b/channel/banner/icon/white.png differ diff --git a/channel/banner/names.txt b/channel/banner/names.txt new file mode 100644 index 0000000..1c230d4 --- /dev/null +++ b/channel/banner/names.txt @@ -0,0 +1,9 @@ +jp=Homebrewチャンネル +en=Homebrew Channel +de=Homebrewkanal +fr=Chaîne Homebrew +sp=Canal Homebrew +it=Canale Homebrew +nl=Homebrewkanaal +cn=Homebrew频道 +ko=Homebrew 채널 diff --git a/channel/banner/sound/wiibrew-banner-intro-part.wav b/channel/banner/sound/wiibrew-banner-intro-part.wav new file mode 100644 index 0000000..4e4d0b5 Binary files /dev/null and b/channel/banner/sound/wiibrew-banner-intro-part.wav differ diff --git a/channel/banner/sound/wiibrew-banner-loop-part.wav b/channel/banner/sound/wiibrew-banner-loop-part.wav new file mode 100644 index 0000000..7b16620 Binary files /dev/null and b/channel/banner/sound/wiibrew-banner-loop-part.wav differ diff --git a/channel/banner/tools/Makefile b/channel/banner/tools/Makefile new file mode 100644 index 0000000..f42bd79 --- /dev/null +++ b/channel/banner/tools/Makefile @@ -0,0 +1,19 @@ + +CFLAGS = -O3 -Wall -I/usr/local/include -L/usr/local/lib + +CC = $(PREFIX)gcc +STRIP = $(PREFIX)strip + +all: mkbns$(EXE) png2tpl$(EXE) lz77$(EXE) + +mkbns$(EXE): mkbns.c + $(CC) $(CFLAGS) -o mkbns mkbns.c -lm + +png2tpl$(EXE): png2tpl.c + $(CC) $(CFLAGS) -o png2tpl png2tpl.c -lpng + +lz77$(EXE): lz77.c + $(CC) $(CFLAGS) -o lz77 lz77.c + +clean: + rm -f mkbns$(EXE) png2tpl$(EXE) lz77$(EXE) diff --git a/channel/banner/tools/addimd5.py b/channel/banner/tools/addimd5.py new file mode 100644 index 0000000..a33a271 --- /dev/null +++ b/channel/banner/tools/addimd5.py @@ -0,0 +1,13 @@ +import md5, sys, struct + +data= open(sys.argv[1]).read() + +digest = md5.new(data).digest() + +hdr = struct.pack(">4sI8x","IMD5",len(data)) + +f2 = open(sys.argv[2],"w") +f2.write(hdr) +f2.write(digest) +f2.write(data) +f2.close() diff --git a/channel/banner/tools/join-imet.py b/channel/banner/tools/join-imet.py new file mode 100644 index 0000000..31f19a5 --- /dev/null +++ b/channel/banner/tools/join-imet.py @@ -0,0 +1,48 @@ +import os, sys, struct, md5 + +output, datafile, iconarc, bannerarc, soundbns, namesfile = sys.argv[1:] + +data = open(datafile,"r").read() + +names={} + +for i in open(namesfile,"r"): + a,b = i.split("=") + while b[-1] == "\n": + b = b[:-1] + b = b.replace("\\n","\n") + names[a] = b.decode("utf-8") + +def getsize(x): + return os.stat(x).st_size + +def pad(x,l): + if len(x) > l: + raise ValueError("%d > %d",len(x),l) + n = l-len(x) + return x + "\x00"*n + +imet = "\x00"*0x40 +imet += struct.pack(">4sIIIIII","IMET",0x600,3,getsize(iconarc),getsize(bannerarc),getsize(soundbns),1) + +for i in ["jp", "en", "de", "fr", "sp", "it", "nl", "cn", None, "ko"]: + try: + imet += pad(names[i].encode("UTF-16BE"),0x54) + except KeyError: + imet += "\x00"*0x54 +imet += "\x00"*(0x600 - len(imet)) + +imet = imet[:-16] + md5.new(imet).digest() + +open(output,"w").write(imet) + +f = open(sys.argv[1],"w") +f.write(imet) +f.write(data) + +fsize = f.tell() + +if (fsize % 20) != 0: + f.write("\x00"*(20-(fsize%20))) + +f.close() diff --git a/channel/banner/tools/lz77.c b/channel/banner/tools/lz77.c new file mode 100644 index 0000000..0620b23 --- /dev/null +++ b/channel/banner/tools/lz77.c @@ -0,0 +1,273 @@ +#include +#include +#include +#include +#include +#include + +/*Altered by Kuwanger*/ +/************************************************************************* +* Name: lz.c +* Author: Marcus Geelnard +* Description: LZ77 coder/decoder implementation. +* Reentrant: Yes +* $Id: lz.c,v 1.4 2004/10/08 19:28:04 marcus256 Exp $ +* +* The LZ77 compression scheme is a substitutional compression scheme +* proposed by Abraham Lempel and Jakob Ziv in 1977. It is very simple in +* its design, and uses no fancy bit level compression. +* +* This is my first attempt at an implementation of a LZ77 code/decoder. +* +* The principle of the LZ77 compression algorithm is to store repeated +* occurrences of strings as references to previous occurrences of the same +* string. The point is that the reference consumes less space than the +* string itself, provided that the string is long enough (in this +* implementation, the string has to be at least 4 bytes long, since the +* minimum coded reference is 3 bytes long). Also note that the term +* "string" refers to any kind of byte sequence (it does not have to be +* an ASCII string, for instance). +* +* The coder uses a brute force approach to finding string matches in the +* history buffer (or "sliding window", if you wish), which is very, very +* slow. I recon the complexity is somewhere between O(n^2) and O(n^3), +* depending on the input data. +* +* There is also a faster implementation that uses a large working buffer +* in which a "jump table" is stored, which is used to quickly find +* possible string matches (see the source code for LZ_CompressFast() for +* more information). The faster method is an order of magnitude faster, +* and also does a full string search in the entire input buffer (it does +* not use a sliding window). +* +* The upside is that decompression is very fast, and the compression ratio +* is often very good. +* +* The reference to a string is coded as a (length,offset) pair, where the +* length indicates the length of the string, and the offset gives the +* offset from the current data position. To distinguish between string +* references and literal strings (uncompressed bytes), a string reference +* is preceded by a marker byte, which is chosen as the least common byte +* symbol in the input data stream (this marker byte is stored in the +* output stream as the first byte). +* +* Occurrences of the marker byte in the stream are encoded as the marker +* byte followed by a zero byte, which means that occurrences of the marker +* byte have to be coded with two bytes. +* +* The lengths and offsets are coded in a variable length fashion, allowing +* values of any magnitude (up to 4294967295 in this implementation). +* +* With this compression scheme, the worst case compression result is +* (257/256)*insize + 1. +* +*------------------------------------------------------------------------- +* Copyright (c) 2003-2004 Marcus Geelnard +* +* This software is provided 'as-is', without any express or implied +* warranty. In no event will the authors be held liable for any damages +* arising from the use of this software. +* +* Permission is granted to anyone to use this software for any purpose, +* including commercial applications, and to alter it and redistribute it +* freely, subject to the following restrictions: +* +* 1. The origin of this software must not be misrepresented; you must not +* claim that you wrote the original software. If you use this software +* in a product, an acknowledgment in the product documentation would +* be appreciated but is not required. +* +* 2. Altered source versions must be plainly marked as such, and must not +* be misrepresented as being the original software. +* +* 3. This notice may not be removed or altered from any source +* distribution. +* +* Marcus Geelnard +* marcus.geelnard at home.se +*************************************************************************/ + + +/************************************************************************* +* Constants used for LZ77 coding +*************************************************************************/ + +/* Maximum offset (can be any size < 2^32). Lower values gives faster + compression, while higher values gives better compression. + NOTE: LZ_CompressFast does not use this constant. */ +#define LZ_MAX_OFFSET 4096 + + + +/************************************************************************* +* INTERNAL FUNCTIONS * +*************************************************************************/ + + +/************************************************************************* +* _LZ_StringCompare() - Return maximum length string match. +*************************************************************************/ + +static unsigned int _LZ_StringCompare( unsigned char * str1, + unsigned char * str2, unsigned int minlen, unsigned int maxlen ) +{ + unsigned int len; + + for( len = minlen; (len < maxlen) && (str1[len] == str2[len]); ++ len ); +// for( len = minlen; (len < maxlen) && (str2+len+1 < str1-1) && (str1[len] == str2[len]); ++ len ); + + return len; +} + + +/************************************************************************* +* PUBLIC FUNCTIONS * +*************************************************************************/ + + +/************************************************************************* +* LZ_Compress() - Compress a block of data using an LZ77 coder. +* in - Input (uncompressed) buffer. +* out - Output (compressed) buffer. This buffer must be 0.4% larger +* than the input buffer, plus one byte. +* insize - Number of input bytes. +* The function returns the size of the compressed data. +*************************************************************************/ + +int LZ_Compress( unsigned char *in, unsigned char *out, + unsigned int insize ) +{ + unsigned char mask, bundle1, bundle2; + int inpos, outpos, bytesleft; + int maxoffset, offset, bestoffset; + int maxlength, length, bestlength; + unsigned char *ptr1, *ptr2, *flags; + + /* Do we have anything to compress? */ + if( insize < 1 ) + { + return 0; + } + + /* Remember the repetition marker for the decoder */ + + out[0] = 0x10; + out[1] = insize&0xFF; + out[2] = (insize>>8)&0xFF; + out[3] = (insize>>16)&0xFF; + flags = &out[4]; + *flags = 0; + mask = 128; + + /* Start of compression */ + inpos = 0; + outpos = 5; + + /* Main compression loop */ + bytesleft = insize; + do + { + /* Determine most distant position */ + if( inpos > LZ_MAX_OFFSET ) maxoffset = LZ_MAX_OFFSET; + else maxoffset = inpos; + + /* Get pointer to current position */ + ptr1 = &in[ inpos ]; + + /* Search history window for maximum length string match */ + bestlength = 2; + bestoffset = 0; + for( offset = 3; offset <= maxoffset; ++ offset ) + { + /* Get pointer to candidate string */ + ptr2 = &ptr1[ -offset ]; + + /* Quickly determine if this is a candidate (for speed) */ + if( (ptr1[ 0 ] == ptr2[ 0 ]) && + (ptr1[ bestlength ] == ptr2[ bestlength ]) ) + { + /* Determine maximum length for this offset */ + maxlength = ((inpos+1) > 18 ? 18 : inpos + 1); + + /* Count maximum length match at this offset */ + length = _LZ_StringCompare( ptr1, ptr2, 0, maxlength ); + + /* Better match than any previous match? */ + if( length > bestlength ) + { + bestlength = length; + bestoffset = offset; + } + } + } + + /* Was there a good enough match? */ + if( bestlength > 2) + { + *flags |= mask; + mask >>= 1; + bundle2 = ((bestlength-3)<<4) | (((bestoffset-1)&0xF00)>>8); + bundle1 = (bestoffset-1)&0xFF; + out [ outpos++ ] = bundle2; + out [ outpos++ ] = bundle1; + + inpos += bestlength; + bytesleft -= bestlength; + if (!mask) { + mask = 128; + flags = &out [ outpos++ ]; + *flags = 0; + } + } + else + { + mask >>= 1; + out[ outpos ++ ] = in[ inpos++ ]; + -- bytesleft; + if (!mask) { + mask = 128; + flags = &out [ outpos++ ]; + *flags = 0; + } + } + } + while( bytesleft > 3 ); + + /* Dump remaining bytes, if any */ + while( inpos < insize ) + { + out[ outpos ++ ] = in[ inpos++ ]; + } + + while(outpos&3) + out [ outpos ++] = 0; + + return outpos; +} + +int main(int argc, char *argv[]) +{ + int in, out, size; + struct stat buf; + unsigned char *uncompressed, *compressed; + + if (argc == 3) { + in = open(argv[1], O_RDONLY); + out = creat(argv[2], 0600); + fstat(in, &buf); + uncompressed = malloc(buf.st_size); + read(in, uncompressed, buf.st_size); + close(in); + compressed = malloc(buf.st_size*2); + size = LZ_Compress(uncompressed, compressed, buf.st_size); + write(out, "LZ77", 4); + write(out, compressed, size); + close(out); + free(compressed); + free(uncompressed); + return 0; + } + printf("Usage: %s file.bin file.lz77\n", argv[0]); + return 1; +} + diff --git a/channel/banner/tools/mkbns.c b/channel/banner/tools/mkbns.c new file mode 100644 index 0000000..ae15ccc --- /dev/null +++ b/channel/banner/tools/mkbns.c @@ -0,0 +1,353 @@ +#include +#include +#include +#include +#include + +#if 0 +uint16_t deftbl[16] = { + 2048, 0, + 4096, -2048, + 0, 0, + 1536, 512, + 1920, 0, + 2176, 0, + 3680, -1664, + 3136, -1856 +}; +#endif + +#if 0 +int16_t deftbl[16] = { + 2048, 0, + 0,0, + 0,0, + 0,0, + 0,0, + 0,0, + 0,0, + 0,0, +}; +#endif + +int16_t deftbl[16] = { +674,1040, +3598,-1738, +2270,-583, +3967,-1969, +1516,381, +3453, -1468, +2606, -617, +3795, -1759, +}; + + +typedef struct { + + uint32_t fourcc; + uint32_t unk; + uint32_t filesize; + uint16_t unk1,unk2; + uint32_t infooff; + uint32_t infosize; + uint32_t dataoff; + uint32_t datasize; +} bnshdr; + +typedef struct { + uint32_t fourcc; + uint32_t size; + uint16_t looped; + uint16_t unk2; + uint16_t srate; + uint16_t unk3; + uint32_t looppoint; + uint32_t samples; + uint32_t unknown1[6]; + uint32_t start1; + uint32_t start2; + uint32_t unknown2[2]; + int16_t tbl1[16]; + uint16_t unka1[8]; + int16_t tbl2[16]; + uint16_t unka2[8]; +} bnsinfo; + +float tables[2][16]; + +typedef struct { + uint32_t fourcc; + uint32_t size; +} bnsdatahdr; + +#define SWAB16(x) ((((x)>>8)&0xFF) | (((x)&0xFF)<<8)) +#define SWAB32(x) ((SWAB16((x)&0xFFFF)<<16)|(SWAB16(((x)>>16)&0xFFFF))) + +#define ISWAB16(x) x=SWAB16(x) +#define ISWAB32(x) x=SWAB32(x) + +typedef struct { + int16_t l; + int16_t r; +} sample; + +int16_t lsamps[2][2] = {{0,0},{0,0}}; +int16_t rlsamps[2][2] = {{0,0},{0,0}}; + +#define CLAMP(a,min,max) (((a)>(max))?(max):(((a)<(min))?(min):(a))) + +void unpack_adpcm(int idx, int16_t *table, uint8_t *data, int16_t *outbuf) +{ + + int32_t index = (data[0] >> 4) & 0x7; //highest bit of byte is ignored + uint32_t exponent = 28 - (data[0] & 0xf); + int32_t factor1 = table[2*index]; + int32_t factor2 = table[2*index + 1]; + int i; + int32_t sample; + for(i=0;i<14;i++) { + sample = data[1+(i/2)]; + if(!(i&1)) { + sample = (sample&0xf0)<<24; + } else { + sample = (sample)<<28; + } + sample = ((lsamps[idx][1]*factor1 + lsamps[idx][0]*factor2)>>11) + (sample>>exponent); + if(sample>32767) sample=32767; + if(sample<-32768) sample=-32768; + if(abs(sample)>20000) printf("dammit %d\n",sample); + outbuf[i] = sample; + lsamps[idx][0] = lsamps[idx][1]; + lsamps[idx][1] = outbuf[i]; + } +} + +uint8_t findexp(float residual, uint8_t *nybble) +{ + uint8_t exp = 0; + + while((residual > 7.5f) || (residual < -8.5f)) { + exp++; + residual /= 2; + } + if(nybble) + *nybble = CLAMP((int16_t)floor(residual),-8,7); + return exp; +} + +uint8_t determine_std_exponent(int idx, int16_t *table, int index, int16_t *inbuf) +{ + int32_t maxres = 0; + int32_t factor1 = table[2*index]; + int32_t factor2 = table[2*index + 1]; + int32_t predictor; + int32_t residual; + int i; + int16_t elsamps[2]; + memcpy(elsamps,rlsamps[idx],sizeof(int16_t)*2); + for(i=0;i<14;i++) { + predictor = (elsamps[1]*factor1 + elsamps[0]*factor2)/2048; + residual = inbuf[i] - predictor; + if(residual > maxres) maxres = residual; + elsamps[0] = elsamps[1]; + elsamps[1] = inbuf[i]; + } + return findexp(maxres,NULL); +} + +float compress_adpcm(int idx, int16_t *table, uint8_t tblidx, uint8_t *data, int16_t *inbuf, int16_t *lsamps) { + + int32_t factor1 = table[2*tblidx]; + int32_t factor2 = table[2*tblidx + 1]; + int32_t predictor; + int32_t residual; + uint8_t exp; + int8_t nybble; + int i; + float error = 0; + + exp = determine_std_exponent(idx, table, tblidx, inbuf); + + while(exp<=15) { + memcpy(lsamps,rlsamps[idx],sizeof(int16_t)*2); + data[0] = exp | tblidx<<4; + error = 0; + for(i=0;i<14;i++) { + predictor = (lsamps[1]*factor1 + lsamps[0]*factor2)>>11; + residual = inbuf[i] - predictor; + residual = residual>>exp; + if((residual > 7) || (residual < -8)) { + exp++; + break; + } + nybble = CLAMP(residual,-8,7); + if(i&1) { + data[i/2+1] |= nybble&0xf; + } else { + data[i/2+1] = nybble<<4; + } + predictor += nybble< 4 && (atoi(argv[3]) == 1)) + separated_loop = 1; + + f = fopen(argv[1],"r"); + fo = fopen(argv[2],"w"); + + fseek(f,0,SEEK_END); + + samples = ftell(f)/(sizeof(uint16_t)*2); + + if(separated_loop) { + f2 = fopen(argv[4],"r"); + fseek(f2,0,SEEK_END); + loop_pt = samples; + samples += ftell(f2)/(sizeof(uint16_t)*2); + } + + blocks = (samples+13)/14; + + memset(&hdr,0,sizeof(hdr)); + memset(&info,0,sizeof(info)); + memset(&datahdr,0,sizeof(datahdr)); + + hdr.fourcc = 0x20534e42; + hdr.unk = SWAB32(0xfeff0100); + hdr.filesize = SWAB32(blocks * 16 + sizeof(hdr) + sizeof(info) + sizeof(datahdr)); + hdr.unk1 = SWAB16(32); + hdr.unk2 = SWAB16(2); + hdr.infooff = SWAB32(sizeof(hdr)); + hdr.infosize = SWAB32(sizeof(info)); + hdr.dataoff = SWAB32(sizeof(hdr) + sizeof(info)); + hdr.datasize = SWAB32(sizeof(datahdr) + blocks * 16); + + info.fourcc = 0x4f464e49; + info.size = SWAB32(sizeof(info)); + info.srate = SWAB16(32000); + if(argc > 3 && (atoi(argv[3]) == 1)) + info.looped = SWAB16(1); + info.unk2 = SWAB16(0x200); + info.looppoint = SWAB32(loop_pt); + info.samples = SWAB32(samples); + info.unknown1[0] = SWAB32(0x18); + info.unknown1[1] = SWAB32(0x00); + info.unknown1[2] = SWAB32(0x20); + info.unknown1[3] = SWAB32(0x2c); + info.unknown1[4] = SWAB32(0x00); + info.unknown1[5] = SWAB32(0x38); + info.unknown2[0] = SWAB32(0x68); + info.unknown2[1] = SWAB32(0x00); + info.start1 = SWAB32(0); + info.start2 = SWAB32(blocks * 8); + for(i=0;i<16;i++) { + info.tbl1[i] = SWAB16((int16_t)(deftbl[i])); + info.tbl2[i] = SWAB16((int16_t)(deftbl[i])); + } + + datahdr.fourcc = 0x41544144; + datahdr.size = SWAB32(blocks * 16); + + fwrite(&hdr,sizeof(hdr),1,fo); + fwrite(&info,sizeof(info),1,fo); + fwrite(&datahdr,sizeof(datahdr),1,fo); + + + datain = malloc(sizeof(uint16_t)*2*blocks*14); + memset(datain,0,sizeof(uint16_t)*2*blocks*14); + + databuf = malloc(blocks * 16); + data1 = databuf; + data2 = databuf + blocks * 8; + + if(separated_loop) { + fseek(f,0,SEEK_SET); + fread(datain,sizeof(uint16_t)*2,loop_pt,f); + fseek(f2,0,SEEK_SET); + fread(&datain[loop_pt],sizeof(uint16_t)*2,samples-loop_pt,f2); + fclose(f); + fclose(f2); + } else { + fseek(f,0,SEEK_SET); + fread(datain,sizeof(uint16_t)*2,samples,f); + fclose(f); + } + + printf("Samples: 0x%x\n",samples); + printf("Blocks: 0x%x Size ADPCM: 0x%x Size PCM: 0x%x\n",blocks,blocks*8,blocks*14); + if(separated_loop) + printf("Loop point: 0x%x samples\n",loop_pt); + + for(i=0;i +#include +#include +#include +#include + +struct tpl_hdr { + uint32_t magic; + uint32_t ntextures; + uint32_t hdrsize; +}; + +struct tpl_tex { + uint32_t hdroff; + uint32_t pltoff; +}; + +struct tpl_texhdr { + uint16_t height; + uint16_t width; + uint32_t format; + uint32_t offset; + uint32_t wraps; + uint32_t wrapt; + uint32_t minfilter; + uint32_t maxfilter; + float lodbias; + uint8_t edgelod; + uint8_t minlod; + uint8_t maxlod; + uint8_t unpacked; +}; + +#define SWAB16(x) ((((x)>>8)&0xFF) | (((x)&0xFF)<<8)) +#define SWAB32(x) ((SWAB16(((uint32_t)(x))&0xFFFF)<<16)|(SWAB16((((uint32_t)(x))>>16)&0xFFFF))) + +#define ISWAB16(x) x=SWAB16(x) +#define ISWAB32(x) x=SWAB32(x) + +#define ALIGN(x) (((x)+31)&(~31)) + +void do_ia8(int width, int height, FILE *fp, uint8_t **row_pointers) { + + uint16_t **in = (uint16_t **)row_pointers; + int wo, ho, wt, i, j; + uint16_t *out; + wo = (width + 3) & ~3; + ho = (height + 3) & ~3; + wt = wo/4; + out = malloc(wo*ho*2); + memset(out,0,wo*ho*2); + for(i=0; i>3) & 0x001F); + } + } + + fwrite(out,2,wo*ho,fp); +} + +void do_i8(int width, int height, FILE *fp, uint8_t **in) { + + int wo, ho, wt, i, j; + uint8_t *out; + wo = (width + 7) & ~7; + ho = (height + 3) & ~3; + wt = wo/8; + out = malloc(wo*ho); + memset(out,0,wo*ho); + for(i=0; i>16); + } + } + + fwrite(out,2,wo*ho*2,fp); +} + + +int main(int argc, char **argv) +{ + png_structp png_ptr = png_create_read_struct + (PNG_LIBPNG_VER_STRING, NULL, NULL, NULL); + + png_infop info_ptr = png_create_info_struct(png_ptr); + + if (setjmp(png_jmpbuf(png_ptr))) + { + printf("PNG error\n"); + return 1; + } + + FILE *fp = fopen(argv[1],"rb"); + + png_init_io(png_ptr, fp); + png_read_png(png_ptr, info_ptr, PNG_TRANSFORM_PACKING|PNG_TRANSFORM_EXPAND|PNG_TRANSFORM_STRIP_16|PNG_TRANSFORM_SWAP_ALPHA, NULL); + + uint8_t **row_pointers = png_get_rows(png_ptr, info_ptr); + + png_uint_32 width, height; + int bit_depth, color_type, filter_method, compression_type, interlace_type; + png_get_IHDR(png_ptr, info_ptr, &width, &height, + &bit_depth, &color_type, &interlace_type, + &compression_type, &filter_method); + + printf("Texture: %d x %d\n",(int)width,(int)height); + + FILE *fo = fopen(argv[2],"wb"); + + struct tpl_hdr tplhdr; + tplhdr.magic = SWAB32(0x0020af30); + tplhdr.ntextures = SWAB32(1); + tplhdr.hdrsize = SWAB32(sizeof(struct tpl_hdr)); + + fwrite(&tplhdr,sizeof(struct tpl_hdr),1,fo); + + struct tpl_tex tpltex; + tpltex.hdroff = SWAB32(ftell(fo) + sizeof(struct tpl_tex)); + tpltex.pltoff = SWAB32(0); + + fwrite(&tpltex,sizeof(struct tpl_tex),1,fo); + + struct tpl_texhdr texhdr; + memset(&texhdr,0,sizeof(struct tpl_texhdr)); + + + + texhdr.width = SWAB16(width); + texhdr.height = SWAB16(height); + texhdr.offset = SWAB32(ALIGN(ftell(fo) + sizeof(struct tpl_texhdr))); + texhdr.minfilter = SWAB32(1); + texhdr.maxfilter = SWAB32(1); + texhdr.wraps = SWAB32(atoi(argv[3])); + texhdr.wrapt = SWAB32(atoi(argv[4])); + + switch(color_type) { + case PNG_COLOR_TYPE_GRAY_ALPHA: + texhdr.format = SWAB32(3); //IA8 + fwrite(&texhdr,sizeof(struct tpl_texhdr),1,fo); + fseek(fo,SWAB32(texhdr.offset),SEEK_SET); + do_ia8(width, height, fo,row_pointers); + break; + case PNG_COLOR_TYPE_GRAY: + texhdr.format = SWAB32(1); //I8 + fwrite(&texhdr,sizeof(struct tpl_texhdr),1,fo); + fseek(fo,SWAB32(texhdr.offset),SEEK_SET); + do_i8(width, height, fo,row_pointers); + break; + case PNG_COLOR_TYPE_RGB_ALPHA: + texhdr.format = SWAB32(6); //ARGB + fwrite(&texhdr,sizeof(struct tpl_texhdr),1,fo); + fseek(fo,SWAB32(texhdr.offset),SEEK_SET); + do_rgba8(width, height, fo,row_pointers); + break; + case PNG_COLOR_TYPE_RGB: + texhdr.format = SWAB32(4); //RGB565 + fwrite(&texhdr,sizeof(struct tpl_texhdr),1,fo); + fseek(fo,SWAB32(texhdr.offset),SEEK_SET); + do_rgb565(width, height, fo,row_pointers); + break; + } + return 0; +} diff --git a/channel/channelapp/.gitignore b/channel/channelapp/.gitignore new file mode 100644 index 0000000..e990be7 --- /dev/null +++ b/channel/channelapp/.gitignore @@ -0,0 +1,10 @@ +stub.bin +*.dol +*.elf +.*.swp +dist +build +i18n/merge +i18n/*.gen +*.map + diff --git a/channel/channelapp/Makefile b/channel/channelapp/Makefile new file mode 100644 index 0000000..bb80931 --- /dev/null +++ b/channel/channelapp/Makefile @@ -0,0 +1,230 @@ +#pngcrush -rem iTXt -rem tEXt -d out *.png + +PREFIX = $(DEVKITPPC)/bin/powerpc-eabi- + +CC = $(PREFIX)gcc +AS = $(PREFIX)as +OBJCOPY = $(PREFIX)objcopy +ELF2DOL =$(DEVKITPPC)/bin/elf2dol +GDB = $(PREFIX)gdb +BIN2S = $(DEVKITPPC)/bin/bin2s +TARGET_STUB = stub +TARGET_APP = channelapp +TARGET_CHAN = channelapp-channel + +WIIPAX = ../../wiipax/client/wiipax + +BASE_ADDR = 0x81330000 + +DIR_STUB = stub +DIR_SRC = source +DIR_DATA = data +DIR_DATA_CRYPT = $(DIR_DATA)/crypt +DIR_I18N = i18n +DIR_BUILD = build +DIR_BUILD_CHAN = $(DIR_BUILD)/channel + +DIR_INCLUDES = $(DIR_BUILD) \ + $(DEVKITPRO)/libogc/include \ + $(DEVKITPRO)/3rd/include \ + $(DEVKITPRO)/3rd/include/freetype2 + +DIR_LIBS = \ + $(DEVKITPRO)/libogc/lib/wii \ + $(DEVKITPRO)/3rd/lib + +LIBS = fat wiiuse bte mxml png15 z ogc m db freetype + +MACHDEP = -g -DGEKKO -mrvl -mcpu=750 -meabi -mhard-float +CFLAGS = $(MACHDEP) -Os -Wall -DBASE_ADDR=$(BASE_ADDR) $(DIR_INCLUDES:%=-I%) +# using -Os for the stub makes it dependent on libgcc +CFLAGS_STUB = $(MACHDEP) -O2 -Wall -DBASE_ADDR=$(BASE_ADDR) $(DIR_INCLUDES:%=-I%) +ASFLAGS = -D_LANGUAGE_ASSEMBLY -DHW_RVL +LDFLAGS_STUB = $(MACHDEP) -Wl,--section-start,.init=0x80001800 -nostartfiles -nodefaultlibs +LDFLAGS_APP = $(MACHDEP) -specs=newrvl.spec -Wl,--section-start,.init=$(BASE_ADDR) \ + $(DIR_LIBS:%=-L%) $(LIBS:%=-l%) -Wl,-Map,channelapp.map +LDFLAGS_CHAN = $(MACHDEP) -specs=newrvl.spec -Wl,--section-start,.init=$(BASE_ADDR) -Wl,-e,_stub_start $(DIR_LIBS:%=-L%) $(LIBS:%=-l%) -Wl,-Map,channelapp_chan.map + +FILES_STUB = $(wildcard $(DIR_STUB)/*.S) $(wildcard $(DIR_STUB)/*.c) +FILES_STUB_OBJx = $(FILES_STUB:$(DIR_STUB)/%.S=$(DIR_BUILD)/%.o) +FILES_STUB_OBJ = $(FILES_STUB_OBJx:$(DIR_STUB)/%.c=$(DIR_BUILD)/%.o) + +BANNER_BIN = banner/banner.bin +BANNER_OBJ = $(DIR_BUILD)/banner_bin.o + +FILES_TTF = $(wildcard $(DIR_DATA)/*.ttf) +FILES_TTF_OBJ = $(FILES_TTF:$(DIR_DATA)/%.ttf=$(DIR_BUILD)/%_ttf.o) + +FILES_PNG = $(wildcard $(DIR_DATA)/*.png) +FILES_PNG_OBJ = $(FILES_PNG:$(DIR_DATA)/%.png=$(DIR_BUILD)/%_png.o) + +POTPL = $(DIR_I18N)/template.pot +POTPL_MRG = $(DIR_I18N)/template.merge.pot +FILES_PO = $(wildcard $(DIR_I18N)/*.po) +FILES_PO_ENC = $(FILES_PO:$(DIR_I18N)/%.po=$(DIR_BUILD)/%.enc.po) +FILES_MO = $(FILES_PO_ENC:$(DIR_BUILD)/%.enc.po=$(DIR_BUILD)/%.mo) +FILES_MO_OBJ = $(FILES_MO:$(DIR_BUILD)/%.mo=$(DIR_BUILD)/%_mo.o) +ENCODING = utf-8 +FILES_PO_MRG = $(FILES_PO:$(DIR_I18N)/%.po=$(DIR_I18N)/merge/%.po) + +FILES_SRC = $(wildcard $(DIR_SRC)/*.c) +FILES_SRC_OBJ = $(FILES_SRC:$(DIR_SRC)/%.c=$(DIR_BUILD)/%.o) \ + $(DIR_BUILD)/$(TARGET_STUB)_bin.o $(BANNER_OBJ) \ + $(FILES_PNG_OBJ) $(FILES_MO_OBJ) $(FILES_TTF_OBJ) + +FILES_CHAN = $(DIR_CHAN)/nandloader.S +FILES_CHAN_OBJ = $(DIR_BUILD)/nandloader.o $(FILES_SRC_OBJ) + +.PHONY: all channel world clean upload gdb debug + +all: + @[ -d $(DIR_BUILD) ] || mkdir $(DIR_BUILD) + @$(MAKE) --no-print-directory data + @$(MAKE) --no-print-directory $(TARGET_APP).elf + +channel: + @$(MAKE) --no-print-directory all + @$(MAKE) --no-print-directory $(TARGET_CHAN).dol + +world: + @$(MAKE) --no-print-directory all + @$(MAKE) --no-print-directory $(TARGET_CHAN).dol + +data: $(BANNER_OBJ) $(FILES_PNG_OBJ) $(FILES_MO_OBJ) \ + $(FILES_PNG_INT_OBJ) $(FILES_INT_OBJ) \ + $(FILES_TTF_OBJ) + +%.bin: %.elf + @echo $(@F) + @$(OBJCOPY) -O binary $< $@ + +%.dol: %.elf $(ELF2DOL) + @echo $(@F) + @$(ELF2DOL) $< $@ + +$(TARGET_STUB).elf: $(FILES_STUB_OBJ) + @echo $(@F) + @$(CC) $(FILES_STUB_OBJ) $(LDFLAGS_STUB) -o $@ + +$(TARGET_APP)_nopax.elf: $(FILES_SRC_OBJ) newrvl.ld + @echo $(@F) + @$(CC) $(FILES_SRC_OBJ) $(LDFLAGS_APP) -o $@ + +$(TARGET_APP).elf: $(TARGET_APP)_nopax.elf + @$(WIIPAX) -s devkitfail $< $@ + +$(TARGET_CHAN).elf: $(TARGET_APP)_nopax.elf + @echo $(@F) + @$(WIIPAX) -s dkfailchannel $< $@ + +$(DIR_BUILD)/$(TARGET_STUB)_bin.o: $(TARGET_STUB).bin + @echo $(@F) + @$(BIN2S) -a 32 $< | $(AS) -o $@ + +$(BANNER_BIN): + @$(MAKE) -C banner + +$(BANNER_OBJ): $(BANNER_BIN) + @echo $(@F) + @$(BIN2S) -a 32 $< | $(AS) -o $@ + @echo "extern const u8 $( $(@:%.o=%.h) + @echo "extern const u8 $(> $(@:%.o=%.h) + @echo "extern const u32 $(> $(@:%.o=%.h) + +$(DIR_BUILD)/%_png.o: $(DIR_DATA)/%.png + @echo $(@F) + @$(BIN2S) -a 32 $< | $(AS) -o $@ + @echo "extern const u8 $( $(@:%.o=%.h) + @echo "extern const u8 $(> $(@:%.o=%.h) + @echo "extern const u32 $(> $(@:%.o=%.h) + +$(DIR_BUILD)/%_ttf.o: $(DIR_DATA)/%.ttf + @echo $(@F) + @$(BIN2S) -a 32 $< | $(AS) -o $@ + @echo "extern const u8 $( $(@:%.o=%.h) + @echo "extern const u8 $(> $(@:%.o=%.h) + @echo "extern const u32 $(> $(@:%.o=%.h) + +$(DIR_BUILD)/%_mo.o: $(DIR_BUILD)/%.mo + @echo $(@F) + @$(BIN2S) -a 32 $< | $(AS) -o $@ + @echo "extern const u8 $( $(@:%.o=%.h) + @echo "extern const u8 $(> $(@:%.o=%.h) + @echo "extern const u32 $(> $(@:%.o=%.h) + +$(DIR_BUILD)/%.o: $(DIR_INT)/% + @echo $(@F) + @$(BIN2S) -a 32 $< | $(AS) -o $@ + @echo "extern const u8 $(subst .,_,$( $(@:%.o=%.h) + @echo "extern const u8 $(subst .,_,$(> $(@:%.o=%.h) + @echo "extern const u32 $(subst .,_,$(> $(@:%.o=%.h) + +$(DIR_BUILD)/%.mo: $(DIR_BUILD)/%.enc.po + @echo $(@F) + @msgfmt --no-hash -o $@ $< + +$(DIR_BUILD)/%.enc.po: $(DIR_I18N)/%.po + @echo $(@F) + @msgconv -t $(ENCODING) -o $@ $< + +$(DIR_BUILD)/%.o: $(DIR_STUB)/%.c + @echo $(@F) + @$(CC) $(CFLAGS_STUB) -MMD -MP -MF $(@:%.o=%.d) -c $< -o $@ + +$(DIR_BUILD)/%.o: $(DIR_STUB)/%.S + @echo $(@F) + @$(CC) $(ASFLAGS) -c $< -o $@ + +$(DIR_BUILD)/%.o: $(DIR_SRC)/%.c + @echo $(@F) + @$(CC) $(CFLAGS) -MMD -MP -MF $(@:%.o=%.d) -c $< -o $@ + +-include $(FILES_SRC_OBJ:%.o=%.d) + +clean: + rm -rf $(DIR_BUILD) \ + *.elf *.dol *.bin *.map i18n/merge/* $(POTPL).gen + @$(MAKE) -C banner clean + +wiiload: + @$(MAKE) --no-print-directory all + @$(DEVKITPPC)/bin/wiiload $(TARGET_APP).elf + +upload: wiiload + +$(POTPL).gen: source/*.c + @echo "GETTEXT $(POTPL).gen" + @xgettext --from-code=UTF-8 -C -o $(POTPL).gen --keyword=_ source/*.c + @sed -i.bak s/CHARSET/UTF-8/g $(POTPL).gen + +$(POTPL): $(POTPL).gen + @echo "MERGE $(POTPL_MRG)" + @msgmerge $(POTPL) $(POTPL).gen > $(POTPL_MRG) + @mv -v $(POTPL_MRG) $(POTPL) + @rm $(POTPL).gen.bak + +genmsgs: $(POTPL) +msgmerge: $(POTPL) $(FILES_PO_MRG) +movemerge: + @mv -v i18n/merge/* i18n + +msgupdate: msgmerge movemerge msgstats + +%.postats: %.po + @echo -n "$<: " + @msgfmt --statistics -o /dev/null $< + +msgstats: $(FILES_PO:%.po=%.postats) +msgmergestats: msgmerge $(FILES_PO_MRG:%.po=%.postats) + +$(DIR_I18N)/merge/%.po : $(DIR_I18N)/%.po $(POTPL) + @[ ! -d $(DIR_I18N)/merge ] && mkdir -p $(DIR_I18N)/merge || true + @echo "MERGE $(@F)" + @msgmerge -o $@ $(@:$(DIR_I18N)/merge/%.po=$(DIR_I18N)/%.po) $(POTPL) + +gdb: + $(GDB) -n $(TARGET_APP)_nopax.elf + +debug: + $(GDB) -n $(TARGET_APP)_nopax.elf -x gdb.txt + diff --git a/channel/channelapp/banner/.gitignore b/channel/channelapp/banner/.gitignore new file mode 100644 index 0000000..a0b92ff --- /dev/null +++ b/channel/channelapp/banner/.gitignore @@ -0,0 +1,3 @@ +twintig +banner.bin + diff --git a/channel/channelapp/banner/Makefile b/channel/channelapp/banner/Makefile new file mode 100644 index 0000000..f292584 --- /dev/null +++ b/channel/channelapp/banner/Makefile @@ -0,0 +1,11 @@ +all: banner.bin + +banner.bin: twintig title banner.ppm icon.ppm + @$(CURDIR)/twintig + +twintig: twintig.c + @gcc -g -O2 -Wall $< -o $@ + +clean: + rm -f twintig banner.bin + diff --git a/channel/channelapp/banner/banner.ppm b/channel/channelapp/banner/banner.ppm new file mode 100644 index 0000000..4603f4a --- /dev/null +++ b/channel/channelapp/banner/banner.ppm @@ -0,0 +1,4 @@ +P6 +192 64 +255 +bbbbbbbcbbbbbcbbbbcbbbbbbbbbbbccbbbbZZ[\fgiijjjjjjjiifed\SW`bbbbcbbcbbbbbbbbcdeefgffedccbccbbcbcbbbccbbbbbbbbcbbbbbcbbcbbbbcbbcbbbcbbcbbcbbbbcbcbbcbbbbbcbcbbbcbbbbbbbcbbcbbccbbbbbbbccbbcbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbb\[_[eiijjjjjjjjjjigeZU[bbbbbbbbbbbbbbbbbdefijjjgfddccbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbb`b`bbbbbbbbbbbbbbbbbbbbbb^Zb^cijjjjjjjjjjjigdUW^bbbbbbbbbbbbbbbbcdgikkjifddddcbbbbbb`bbbbbbbb`bbbbbbbbbbbbbbbbbbbbbbbbbbbb`bbbbbb`bbb`b`bbb`bbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbb`bbbb`b`b``bbbb``bb``b```bbbbb``b```bb```X]dbgiijiijjjjiiig\UX`b``b``````bb`bbbdegjjgedb`_`bcbbbb```bb`b``````bb`bbbbb````b``b`bbbb`bbbbb`bbb`bbb`b`````bb`b`bbbbbb``b`bb```bb````bbbbb``b`bbb`bb`bb````````````b````````````````b``````b\Vbdegkkkjjiiiiji`XX\```b````````````bceffec`^\\\^`bbbb``````````````````b````b```__``__`````````````````````````````````b`````````````````````````````b`````````````````````````````````````````XWbdgjlmllkkjjjd\[X`````````````````bbbb`_^\[ZZZ[\_`b````````````````````````````m{{mf_``````````````````````````````````````````````````````````````````_``_`_```___`__``_``_`_______``__`````_WX`fmqrqpnmmnkgd\_``__``_````_]^_```_^]]]\\[ZZZZ[\_```````_`````_`_``__```_````uވsimj__``__``_`_``__``_`_```_````_```____```````_``__`_`_``_`````_````__`__`__``___`_______________________`___X\eltyzyyxwutqe__`_``_______`c`^___]\\\]]]]]\\\[[]^__`___`__`___`___`___```__kx{lTGGUmi^_`_____`________`___`_____`__`__`_```__``________`_`__`__```_____________________________________________\_gqy~~uf_________ZT\`VՍP[_^\\]]^^^_^^^]\[\]^_______________________^dgTPLGGFALjb________________________________________________________________^______^_________^__^^_________^__________^_fntyzyri_^___^___^]x٤nWQҔ1X^]\\]^__```___]\\\^^^^____^^_______________cTLQVXXVSGSd________^___________^_____^___^^_______^_^______^^___^________^_^^^^^^^^^^^_^^_^___^_^^^^^^^_^__^_^^^^^_^^^^^^___^]]^^___^^^^]\]IPϐ,V]\\^_```___``_^]\]^^^^^^^^_^__^^^^^^^_^^_^_ZO[_`bb`^XL]_^^^^_^^^^_^^^^__^^^_^^^^^^__^^^^^^_^^^^^_^^^_^^^_^^^^^^^^_^^_^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^_``_^^^^^^^^^^^^^^^^_XPG:Gɏ'zIZ\T[cdbW\`[T[`_]\\]^^^^^^^^^^^^^^^^^^^^^^^_ZU^defeec_P[^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^]^^^^^^^^]^^^^^^^^_djnpnjfc_]]^^^^^^^^^^^XzڇNn\c`ҦgT_^\\]^^^^^^^^^^^^^^^^^^^^^^^^[X_feeffe_T]^^^^]^^^^^^^^^^^^^^^^^^^^^^^^^^]^____^^]^^^^^^]^^^^^^^^^^^^^^^^^]]^]^]^]^^^]^]^^^]]]^]]]^^]^]^^^^^]^bkswxwungfe`]]]]]]^^^]]^SҍsTWbU]\\]^^]]^]]^]^]^^]^]^^^]^]^]]X_efeefeZX^^^]]]]]^]]^^]^^]^^]]^^]^^^]]]]]^`bccc`^^]]^]^^]]]]]^^^]^^^]^^]]^]]]]]]^]]]]]]]]]]]]]]]]]]]]]]]]]]]]`luwwtqkfccee`]]]]]]]]]]][L7nb#b Qxb)fOz] VCyC[]\]]]]]]]]]]]]]]]]]]]]]]]]]^[Xdmnlkg[\]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]^`dffeb`__^]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]\]]]]]]]]]]]]]]]\]]_fnsrnf^VQSX`fe_]]]]]]]]]]]P=G#,P d'HIO+7X^]\]]]]]]]]]]]]]]]]]]]]]]]]]][^ny{wi]]]]]]]]]\\]]]]]]]]]]\]]]]]]]]\]]_beec`]\^^_]]]]]]]]]]]]]]]]]]]\]]]\]\\]\]\\]\]]\\\]\\\\]]]]]]\]]\\]]ceffe^VOMJJLP\fd]\]\]\]]]]\ZVPBLʏ'yQS[L2U]\\]\]\]\\]\\]\\\]]\\\]\]\]]\]\^cd_\]]\]\\]]\]]]]]]\]]\\]]\\]]]\]\\]]__`_\XWWZ\^]]\]]\]\\]]]]]]]]]\\]]\\\\]\]\\\\\\]\\\]]\\\\]\\\\\\\]\\_d^VTUSOMMMLLLN[e_\\\\\\\\\\ZUOBMˎ){TT\L퐪#yW]\\\\\\\\\\\\\\\\]\\\\\]\\\\\\\\[[\\\]\\\]]\\]\\\\\\\\\\\\\\]\\\\\]\]][ZXXWVVVX[]\\\]\\\\\\\\\\\\\\\]\\\\\\\\\\\\\\\\\\\\\\\\\\\\\\\\\\\_]UOOQSSSTSQPOMN\b\\\\\\\\\\XUJ?Jˎ(zST\JN [ Z Z ZU:Z\\\\\\\\\\\\\\\\\\\\\\\\\\\\\\\\\\\\\\\\\\\\\\\\\\\\\\[\\\\\\\\\\\\\[ZXXXZZXXWWX[\\\\\\\\\\\\\\\\\\\\\\\\\\[[\[[[[\\[\[\[[[[\[[\[[[[[[\\]USSUVXXZZWPNMPNS]]\\[[\[\[[XUf?MŎ(zSU]HH6:>BJV[\[\[[\[\\\XSST[\\[\[[[\[[\\\[\[\\\[[[[[\\[\[[[\\[[[\\\[[\[[[[[[\[[[\ZXX[[\\[[ZXXZ\\\\\\[\[[[[\[\[[\[\[\[[[[[[[[[[\[\[[[[[[[[[[[\[[[[[[[\ZSTWZ[[\\[dՁ|\OOW\[[[[[[[[[XP(zQU`NPmP[[[[[[[[[[Zjցـy[X[[[[[[[[[[[[[[[[[[[[[[[[[[[[\[[[[[[[[[[[\[[[[[[[[[[[ZZZ\]]]]]\[XXZ[[[[\[[[[[[[[[[[[[[[[[[[[Z[[[[[[[[[[[[[[[[[[[[[[[[[[[[VTUZ\]]^^QћrCPU[[[[[[[[[[[Iœ'ySUdP,gIG[[[[[[[[[[OϱXO[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[ZZ[\]]]]]][XZ[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[Z[Z[Z[[Z[[[Z[[[[[Z[[[[ZZWUV[]]___N̜\9QVZ[ZZ[[[Z[[ZV8=tXI{ _`:rZ)W[S?|+cbM7#jGy^S4n SeP[[[[[[ZZZ[Jɱ>I[[ZZ[[[[[[[[[Z[[ZZ[[[[[[[ZZZ[[[[[ZZ[[[[[[[[Z[[[[[[[[ZZ[\]]]]\]ZZX[[[[[[ZZ[Z[ZZ[[[Z[[[[ZZZ[ZZ[ZZZZZZZZ[ZZZZZZZZZZ[Z[ZZZZ[WVWZ]^___Nʚ]8SXZ[ZZZ[ZZZZZZT8 ~"1A46MX[W>3GXXJ0yw)9NW[ZZZ[[ZZZ[Iư?GZ[ZZZZZZ[ZZ[ZZZZ[Z[ZZZZZZZZZZZ[ZZZZZ[ZZZ[Z[ZZZ[[[ZZZZX[\]]]]]\[XZZZZZZZZ[[ZZZZZZZ[ZZZ[ZZZZZZZZZZZZZZZZZZZZZZZZZZZZZZZZZZXVXZ]____Nʙ\4MNOTXZZZZXTPOPTWXVWUSSTZWSQT[^^\SNNSXZZZZWQOOQVZZHư9|=OPTXZZZVSTVZZVQXZZWSOOQVZZXTTTWZZVTTVZZZXUTTXZZZZZZZZZZ[]___^\[ZZZZZZZZZZZZZZZZZZZZZZZZZXXXZZZZZZZZZZZZZXXZZZZXXXZZZZXZZXVW[\____MəQ3sҐ܎pMUZ[SQqՌڒ܈nPOZWcqpfJZ~׎ڃ]UV[z֍ڇ`NZZXN]Ѐא܏~\LWHưVx͒܋nNSZXgpnePLf[VO\~֏ܐ܁_JU^qnn[VWfqpbTXZXnpn`VZXZZZXXZXZ\^__^\ZZXZXXZZXZXZZZXZXXZXXXZXZXXXXZXXXXZXXXZZXXXXXZXXXXXXXXXXXXXWUX[^```NʙLUQjmJMmʅIWP͕LEİ]NVO`?ŒGlBJʽHUOύyCXXXXXXXXXZZ[\\[ZZXXXXXXXZXZZXXXZXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXVUX^beeQʛ?_c=JFƗ2EN]x:>BÇUIIɼ^BXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXWXWXWXXXWXWXXXXXWXXXWWXWWXWWXXXXXXWXWW\bgkX̞'0TO˖Ed?АJZ>P7QiPXWWXXWXXWXXWXXXXXXXXXXWXXWWXXXXXXXWWXXWXXWWWWWWWWWXWXWWWWWWWWWWWWWWWWWWWWWWWWWWX]bfVʝ:r Wc0:tJ|:w3=t=tGz2lQrBx]_?xO X4r^Bx\rU1],jb$UXWWWWWWWWWWWWWXWWWWWWWWWWWWWWXWWWWXWWWWWWWWWWWWWWWWWWWWWWWWWWWWWWWWWWWWWWWWWWWWWWXZHȖLi1=;^>3> Xq{(i i{LғМٿx7z73t+FzSSXx׏͞ڸ)(:'i=VWWWWWWWWWWWWWWWWWWWWWWWWWWWWWWWWWWWWWWWWWVVVVVVVWWVVVWWWVWWVVVVVVWVVVWWWWVWWWVWWVWVEƕ]8OS>"|PC=,yIBABB’Ls>CIr( `6IAy:fdNWWWVVWWVWWWVVWWWVVWWWWWVWWWVWVWWWWVVVWVVWWVVVVVVVVVVWVVVVVVVVVVVVVVVVVVVVVVVVVVVVVWVEŕ\8SU>$SFľ8z,wJEŭA~CFŔLٵڵڵٷܧ_>EJt%)|OVGƜصڵٵٶܯ,g)ep{SWVVVVVVVVVVVVVVVVVWVVVVVVVVVVVVVVVVVVVVVVVVVVVVVVVVVVVVWWWWVVVVVVVVVVVVVVVVVVVVVVVVVEŕ\8QT4%EF*r,wJEĭ?~BFœLfIzVVUTSr3x9=Ĕ%'{OWGƉ{HzVVUTQuJB0m7TVVVVVVVVVVVVVVVVVVVVVVVVVVVVVVVVVVUVVVVVVVVUUVVUVUVVVWXZ[[ZXWWVVUVUUUVVVUUVUVVVVUVVVEŕ\7QT#wiȢg,wJEí?~BEœMdw^cnƃc:{pɌ`)'{OVLj{^bmŁfJS>3qcJUVUUVUVVVUUVVVVVUUVUUVVVVVVUVVUVUUUUVVVVVVVUUUUUUUUUVWZ\]]][ZWWVVUUUUUUUUUUUUUUUUUUUUCŔ\7PSpw i,wICĭ?~BEœT3("f9%{NVSE8THcXtPVUUUUUUUUUUUUUUUUUUUVUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUVX\^]\ZWVVWWVUUUUUUUUTUUUUUUUUUUUBŗ^6PS"x7Q0/xHCïBACĖZ(^b cI'{NUUCV0PQBrqX9r2SUUTUTTUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUTUUTTUTUVXZ[ZVTQQSUVVUUTTTTUUUUTTUUTUTTUUHu8q;QO±ㆢ uC)|rgI|)C⢶"mLGņ*lEIw8r;69x碹9m$~Bl?sQ?J㋦"wOUUS83w駾Br wQTB{^8A╯^HTTTUWXXWUTUTTUTUTTUUUTTTTTTTTTTTTUUUUTTUUTTTTTTTTTTTUVVVUTQPONOQTUUTTTTTTTTTTTTTTTTTTTT? ]WTlLTMtTWW7QH" ^\[ VP^6OQ,UXT+QS7WXT"uOT>\TQlLP:l^\[VSUpGG ~ _ \[[[ TPkBPO#zUXU4STTTQ=p]\[WSTkEPLJ4ZXT#yON%UXT1PTV\cfgfb]ZVTTUTTTTTTUTTTTTTTTTTTTTTTTTTTTTTTTTTTTTTTUTQQQPPPPOOOPSTTTTTTTTTTTTTTTTTTTTTPJFGNSTQLGGJOSSPH>8:CMPTSNHFHOSSOIGHNSTNOrmMOTQOE:8:?IOQSPME=98;FOQTSMGFIOSTSTTQOE:89?IOO[~sNHFHOSSNHFIOTZdjklkic]]\WTTTTTTTTTTTTTTTTTTTTTTTTTTTTTTTSTTTTSTTTSQPQQQQQQQPPPQSSTTSTSTTSTSSTTSSSTTSTSSTTTTSSSTTTTSTTSQSSSTTSTSSSTTTTSTTTSTLpJISTTSSQSSSTTTSTSSSQSSSTTTTSTTSSSSTSTTSSQSSSTIBMTTSTTTTSTSXcjkjfc]ZWZ\\WSSTTTSTTSSTSTTTSSSSSTSTTSTSTTTTSSSSSSSSQQPQQSSTTSSQPPQSSSSSSSSSSSSSSSSSSSSSSSSSSSSSSSSSSSSSSSSTSSSSSSSSSSSSSSSSSSJj3yFSSSSSTTSSSSSSSSTSSSSSSSSSSSSSSSSSSSSSSTSSSSGz*wISSSSSSSSSV]cfe`XPLHHNU\\VSSSSSSSSSSSSSSSSSSSSSSSSSSSSSSSSSSSSSSQQQSTTTUTTTSQPQQSSSSQSSSSSSSSSSSSSSSSSSSSSSSSSSSSSSSQSSSSSSSSSSSSSSSSSOMMOHi3{COMOQSSSPNMMNSSSQOQSQNMPSSQOQSPMMQSSSSQMMNSSGy+yHSSSSSSSSTZ[ZXWSLGEECCGP[ZTSSSSQSSSQSSSSSSQSSSSSSSSSSSSSSQQQQQSQPPQSTTTTTTTSQPPQSQQSQQSQQSQQQQQQSSQQQSQQQQQQQQQQQQQQQSQQSSQQSQQQQSQSMH\ig[Ae(t;^iWFQQJS_gg]HLQSVSEJ`iSGQTWNEPeeMJSSIQei]GMFx*xHQSQQQQQQVZSLJLIGGGGFFEFOZUQQQQQQQSSSSQQQQSQSQQSQSSQQQQQQQQQQQQQQPQSTTTTTTTSQPPQQQQQQQQQQQQQQQQQQQQQQQQQQQQQQQQQQQQQQQQQQQQQQQQQQQQM[˼yqBJƤ[>|z:׌^GGƃW:y*yGQQQQQQQQVSLGHJLLLMLJIHFGQVQQQQQQQQQQQQQQQQQQQQQQQQQQQQQQQQQQQQQQQQQSTTTTTTSQOQQQQQQQQPPQQPQQQPQPPQQQQQQQQQQQQPPQPQQQQQQQQQQQQQPQQOLb[9+9/x9t*xGQPQQQQPSSMIJLNOPPPPONMJGJSQQQPQQQQQPPQPQQPQPQQQQPQPQPQPPQQQPPPQPPPQSTVUUUTQPPPPPPPPPPPPPPPPPPQPPPPPPPPPPPQPPQPPQPPPQPPPPPPQQPPPPPHuwN V\ T>{4mOCzn21O{PSO%{0jS{?s_ W(~LPQ_i*xFQPPQPPPPOJLNPQQSSSSQQPNJHNPPQPPPPPPPPPPPQPPPPQPPPPPPPPQPPPPPPPPPPPPQTVVVVTSPPPPPPPPPPPPPPPPPPPPPPPPPPPPPPPPPPPPPPPPPPPPPPPPPPPPPPBŖ [+:8/b]m23n,;E]_T$uL~t+LAM~$%x3_u>7~c*wFPPPPPPPPMJMOQSSTTTTSSQPMHMPPPPPPPPPPPPPPPPPPPPPPPPPPPPPPOOPPOOOPOPPPSTUUTSQPPPOOPPPOPPPPPOPPPOOPPPOOPPOPPPPPPPPPPPOOOPOPPOPPPOOPP>kGNOFg3{ALFm'"q"uHFZA~$J?|:\)wFOPPOPPOONMMOQTTUTTTTTQPMJNPPPOOPOPOPOOOOPOPPPOOPOOOOPOPOOOOOOOOOOOOPPQQQQQPOOOOOPOOOOOOOPOOOOOOOOOOOOOOOPOOOOOOOOOOOOOOOOOOPOOOOP=#wHPPGf2zBMGebrp"q tJJ[?~$MB;࠷͢ϢϠ͞%cb)wFOOOOPOOONMNOSTTTTTTTTTQMJNOOOOOPOOOOOOOOOOOOOOOOOOOOOPOOOOOOONOOOOOOOOOOOOOOOOOOOOOOOOOOOOOOOOOOOOOOOOOOOOOOOOOOOOOOOOOOOONOOOOOP>*{ANMAf2zBMGcI~`XNp tII[?|$MB~2|WM~[[ZX js$sEOOOOOOOONMOPSTTTTTTTTTQLLOOOOOOOOOOOOOOOOOOOOOOOOOONOOOOONONOONNONNONONOONONONOOONOONOONNNOOOONOOOOOOOOONNONNONOOONNNOONOONNNNNONBpCNXVf2zALFct?_ľn tIH[?|#MAÀ sMAGUT:w=CNNNNNNNONMMPQTTTTSSTSQNJMNONONNONNNNNNONNOOOONNNNOONOONNNNNNNNNNNNNNNNNNNNNNNNNNNNNNNNNNNNNNNNNNNNNNNNNNNNNNNNNNNNNNNNNNNNNNNNNNNJ;|3zALFeuޛ tHG\B#L>‹qq7ku>NNNNNNNNNLNPSVVVUUTTSNMNNNNNNNNNNNNNNNNNNNNNNNNNNNNNNNNNNNMNNMNNNNNNNNNNNNNNNNNNNNNNNNNNNNNNNNNNNMNNNNNNNNNNNNNNNNNNNNNNNNNNNNNNN>9J|(qBLFX'$cwrIHU0qx$LAx"r+emAJ|3NNNNNNNMNMMOSWZ[ZXXWUPNNNNNNNNNNNNMNNNNNNNNMNNNNNNNNNMMMMMMMMNMMMMMNNNMMMMMNMMMMMMMMMMMNNNMMMMNNMMMNNNMMMMMMMMMMMMMNMNMNNNMNNMMMLWX*u]_^VL|$pdL{rINH'tO~ Z8t\^ TO~ws[Qc]M{+LNEgM~f, SS8MM;ZM{'{F'_]`^QO|0BtZ T ]CMNMMMMNNMMMMPV\^^^]XSNNMMMMMMMNMMMMMNMMMMMMNMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMScjcL8/*7ILEEJNJA9AHE968BHC89?9;GMMI>9CB:>HMMG;:FLI?6448BIJE87CJMMMMMMMMMMMMMPUWXXUPMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMLMLMLMMMMMMLMMMMLMMLLMMMMMLMMLMMMMMLMMMMMMMMMMMLLMMLMMMMMMMMMMMMMMLN[VSSF:8878H]QLLMLLLLLLJLLLLLLLLLMMMMLLLLLLMMMLLLMMMLLJJLLLMMLLLMLMMMMMMLLMLMMMMMMMMMMMLLMMLMMLMMMLMLMMMMMMLLMMMMMMLMMMLMLLLLLLLMMLMLLLLLLLMLLLLLLMLMMLLLLMLLLMLLLMLLMMLLLLLLLLLLMLLLLLLLLLLLMMLTSB;A??A?>;8HXLLLMMLLLMLLLLLLLMMMMLLLLLLLLLLLLMLLLLLMMMMMMLLLLMMLLLLLLMLLLMLLLLLLLLLLLLLLLLLLLLLLMLMLLLLLLLLMLLLMLLLMMLLLLLLLLLLLLLLLLLLLLLLLLLLLLLLLLLLLLLLLLLLLLLLLLLLLLLLLLLLLLLLLLLLLLLLLLLLQE?BFHIIHGE?:OOLLLLLLLLLLLLLLLLLLLLLLMOQQONLLLLLLLLLLLLLLLLLLLLLLLLLLLLLLLLLLLLLLLLLLLLLLLLLLLLLLLLLLLLLLLLLLLLLLLLLLLLJJJLLJLJLLJLLJJJJLLLLLJJJLLLLLLLLLLLJLLJJLJJJJLLLLLLLLLLLLLLLJLLJLLLLJJJMIBGILMMMMLIH?BNLLLLLLLJJJLLLLJJLJJJMTZ]_^ZUQNJJJJJLLLLJLLJJLLLLLLLLLJJJJLLJJLLJLLLJLLLLJLLJJJJJLLLJJLJLJLLJLLLLJJLLLLLLJJJJJJJJJJJJJJJJJJJJJJJJJJJJJJJJJJJJJJJJJJJJJJJJJJJJLMMLLJJJJJJJJJJJJJJJLGEIMNOPPOOMLEAJJJJJJJJJJJJJJJJJJJJNW_``_]WSSSNLJJJJJJJJJJJJJJJJJJJJJJJJJJJJJJJJJJJJJJJJJJJJJJJJJJJJJJJJJJJJLJJJJJJJJJJJJJJJJJJJJJJJJJJJJJJJJIJJJJJJJIJJJJJJJJJJJJJJJJJJJJJMOPPONLJJJJJJJJJJJJJJJHGHOPPQQQQOMFEJJJJJJJJJJJJJJJJJJJLU]_^ZTNJINSSNIJJJJJJJJJJJJJJJJJJIJJJJIJJJJJJJJJJJJJJJJJJJJJJJJJJJJJJJJJJJJJJJJJJJJJJJJIIJIIIIIJIIIIIIJIIJJIIIIIIIIIIJIIIIIIIIIIJIIIIIIJMPQQPNMLLJIIJJIIJJJIIIIHHJNPPQPPQPMEHJIIIJJIIIIIIIIIJIIJPUVWTLEA>>AHQSLIIIJIIIIIJJJIIIIIIJJJIIIIJIIIIJIIIJIIJJIIIJIIIIIIJIIIIIIIJIIIIIIJIIIIIIIIIIIIIIIIIIIIIIIIIIIIIIIIIIIIIIIIIIIIIIIIIIIIIIIJMONLIGHILJIIIIIIIIIIIIIIFLNPPQPPPOIEIIIIIIIIIIIIIIIIIIIMQMHHFC???>>>EPOIIIIIIIIIIIIIIIIIIIIIIIIIIIIIIIIIIIIIIIIIIIIIIIIIIIIIIIIIIIIIIIIIIIIIII \ No newline at end of file diff --git a/channel/channelapp/banner/icon.ppm b/channel/channelapp/banner/icon.ppm new file mode 100644 index 0000000..ba0d476 --- /dev/null +++ b/channel/channelapp/banner/icon.ppm @@ -0,0 +1,4 @@ +P6 +48 48 +255 +bbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbdbbbaabbabaaaabaaabbababbaaaadeaabaabaabaabbaabfhgdbabbbfaaaaaaaaaaaaaaaaaaalujebaaaaaaaaaaaaadgf`_baaaaef_````````````````bb[PTaa`a````````a`_^\ZY^a``aE`bba__````````````\X^`ZX````````````_\^``^\_```YXsha____________\_eebZ____________^^`aaa^^___xl|m_dla____^^_____^^gjb\_^________^_^^`bb`^^___h_S@<@Xk^^^^^^^^^^^^\bg`^^^^^^^^^^^^^^^_bda^^^^^RHIKIE=Xb\\\\\\\\\\\\[[\\\\\\\\\\\\\\\\\^_^\\\\\RX[\[YPF[\\[[\\\[[[[\\\\\\\\\\\\\\\[[\\\[[[\\\\\\`aba`\LV\[[[[[[[\\[[[[[[[[[[[[[[[[[[[[[[[[[[[[[\bbbbb_NXZZZZZZ[`ba^[ZZZZZZZZZZZZZZZZZZZZZZZZZZZ[bbbbbYRZZYZYZZ^a_YX[ZYYZZYYZZZZYY^\YZZYZZYZYZZYTakjhfXVYYUYTVZXVUSRUYYYYYYYYYYYYlr^\YYYYYYYYYYYUZlsrgYXXXfMҡyKSVTSVTUXXXXXXXXXXYUKANZXXXXXXXXXXZYXYYVVVSnTꁵaZTdrTUUVVVVVVVVVXRR[[NTXVVVXVVVVVmg^YUUVVMԖ˳pMTVVUVVVVVVVTYdbUUVVVVVVVVVVZKKZYUUUSX#~=լsITUUUUUUUUUUUUdbVUUUUUUUUUUUHE:EVTTTRb3yT׳^Y@UTTUMITTTTTTTSSTTTTTTTTTTTTRd֞_GTSSRUrUpޅHTSSMܒRSSSSSSSSSSSSTTSTSSSSP~r6KSRIBP^C +// Licensed under the terms of the GNU GPL, version 2 +// http://www.gnu.org/licenses/old-licenses/gpl-2.0.txt + +#include +#include +#include +#include +#include +#include +#include + +#define ERROR(s) do { fprintf(stderr, s "\n"); exit(1); } while (0) + +// basic data types +typedef unsigned char u8; +typedef unsigned short u16; +typedef unsigned int u32; + +void wbe16(u8 *p, u16 x) { + p[0] = x >> 8; + p[1] = x; +} + +void wbe32(u8 *p, u32 x) { + wbe16(p, x >> 16); + wbe16(p + 2, x); +} + +static int read_image(u8 *data, u32 w, u32 h, const char *name) { + FILE *fp; + u32 x, y; + u32 ww, hh; + + fp = fopen(name, "rb"); + if (!fp) + return -1; + + if (fscanf(fp, "P6 %d %d 255\n", &ww, &hh) != 2) + ERROR("bad ppm"); + if (ww != w || hh != h) + ERROR("wrong size ppm"); + + for (y = 0; y < h; y++) + for (x = 0; x < w; x++) { + u8 pix[3]; + u16 raw; + u32 x0, x1, y0, y1, off; + + x0 = x & 3; + x1 = x >> 2; + y0 = y & 3; + y1 = y >> 2; + off = x0 + 4 * y0 + 16 * x1 + 4 * w * y1; + + if (fread(pix, 3, 1, fp) != 1) + ERROR("read"); + + raw = (pix[0] & 0xf8) << 7; + raw |= (pix[1] & 0xf8) << 2; + raw |= (pix[2] & 0xf8) >> 3; + raw |= 0x8000; + + wbe16(data + 2*off, raw); + } + + fclose(fp); + return 0; +} + +int main(int argc, char **argv) { + FILE *in, *fp; + u8 header[0x72a0]; + + fp = fopen("banner.bin", "wb+"); + if (!fp) + ERROR("open banner.bin"); + + memset(header, 0, sizeof header); + + memcpy(header, "WIBN", 4); + header[9] = 2; // wtf + + in = fopen("title", "rb"); + if (!in) + ERROR("open title"); + if (fread(header + 0x20, 0x80, 1, in) != 1) + ERROR("read title"); + fclose(in); + + if(read_image(header + 0xa0, 192, 64, "banner.ppm")) + ERROR("open banner.ppm"); + + if(read_image(header + 0x60a0, 48, 48, "icon.ppm")) + ERROR("open icon.ppm"); + + if (fwrite(header, 0x72a0, 1, fp) != 1) + ERROR("write header"); + + fclose(fp); + + return 0; +} diff --git a/channel/channelapp/config.h b/channel/channelapp/config.h new file mode 100644 index 0000000..e47b7e0 --- /dev/null +++ b/channel/channelapp/config.h @@ -0,0 +1,143 @@ +#ifndef _CONFIG_H_ +#define _CONFIG_H_ + +#define CHANNEL_VERSION_DATE 201611230000llu +#define CHANNEL_VERSION_STR "1.1.3" + +//#define DEBUG_APP +//#define DEBUG_STUB + +#define ENABLE_WIDESCREEN +#define ENABLE_SCREENSHOTS +//#define ENABLE_UPDATES +//#define FORCE_LANG CONF_LANG_JAPANESE + +#ifdef DEBUG_APP +void gprintf_enable(int enable); +int gprintf(const char *format, ...) __attribute__ ((format (printf, 1, 2))); +void hexdump(const void *d, int len); +void memstats(int reset); +#define CHKBUFACC(access, ptr, len) \ + do { \ + if ((access < ptr) || (access >= ptr + len)) \ + gprintf("WARNING: buffer access out of range: %s:%d\n", __FILE__, __LINE__); \ + } while (0) +#else +#define gprintf(...) +#define hexdump(...) +#define memstats(...) +#define gprintf_enable(...) +#define CHKBUFACC(...) +#endif + +#define UPDATE_URL "http://example.com/update.sxml" +#define UPDATE_PUBLIC_KEY \ + "\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00" \ + "\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00" \ + "\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00" \ + "\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00" + +#define USBGECKO_CHANNEL 1 + +#define STUB_MAGIC 0x4c4f41444b544858ull +#define STUB_ADDR_MAGIC ((u64 *) 0x80002f00) +#define STUB_ADDR_TITLE ((u64 *) 0x80002f08) + +#define BOOTMII_IOS 254 +#define TITLEID_BOOTMII (0x0000000100000000LL | BOOTMII_IOS) + +#define PREFERRED IOS_GetPreferredVersion() +#define UNCHANGED IOS_GetVersion() + +#define MY_TITLEID 0x000100014f484243ull +#define STUB_LOAD_IOS_VERSION UNCHANGED +#define APPS_IOS_VERSION PREFERRED + +#define VIEW_Z_ORIGIN -420 + +#define GFX_ORIGIN_STACK_SIZE 16 + +// peak bubbles +#define MAX_BUBBLE_COUNT 20 +// minimum bubbles +#define MIN_BUBBLE_COUNT 4 +// cycle time in minutes +#define BUBBLE_TIME_CYCLE (60*24) +// time (in minutes, inside cycle) of minimum bubbles +#define BUBBLE_MIN_TIME (60*4) +// time (in minutes) offset from BUBBLE_MIN_TIME of maximum bubbles +#define BUBBLE_MAX_OFFSET (60*12) +// bubble size +#define BUBBLE_SIZE_MIN 0.4 +#define BUBBLE_SIZE_MAX 1.0 +// bubble pop radius modifier +#define BUBBLE_POP_RADIUS 0.8 +// bubble pop generates this many bubbles +#define BUBBLE_POP_MAX 10 +#define BUBBLE_POP_MIN 5 +// bubble pop sub-bubble size +#define BUBBLE_POP_SIZE_MIN 0.2 +#define BUBBLE_POP_SIZE_MAX 0.4 +// bubble pop spread out range +#define BUBBLE_POP_SPREAD_X 40 +#define BUBBLE_POP_SPREAD_Y 30 + +#define IRAND(max) ((int) ((float) (max) * (rand () / (RAND_MAX + 1.0)))) +#define FRAND(max) ((max) * (rand () / (RAND_MAX + 1.0))) + +#define WIDGET_DISABLED_COLOR 0xFFFFFF54 + +#define DIALOG_MASK_COLOR 0x101010a0 + +#define TEX_LAYER_WIDGETS 2 +#define TEX_LAYER_DIALOGS 30 + +#define TEX_LAYER_CURSOR 80 + +#define APP_ENTRY_ICON_X 8 +#define APP_ENTRY_ICON_Y 8 +#define GRID_APP_ENTRY_ICON_X 8 +#define GRID_APP_ENTRY_ICON_Y 8 +#define APP_ENTRY_ICON_WIDTH 128 +#define APP_ENTRY_ICON_HEIGHT 48 +#define APP_ENTRY_TEXT1_X 156 +#define APP_ENTRY_TEXT1_Y 8 +#define APP_ENTRY_TEXT2_X 156 +#define APP_ENTRY_TEXT2_Y 54 + +#define MAX_ENTRIES 1024 + +#define TCP_CONNECT_TIMEOUT 5000 +#define TCP_BLOCK_SIZE (16 * 1024) +#define TCP_BLOCK_RECV_TIMEOUT 10000 +#define TCP_BLOCK_SEND_TIMEOUT 4000 + +#define WIILOAD_MIN_VERSION 0x0005 +#define ARGS_MAX_LEN 1024 + +#define LD_TCP_PORT 4299 +#define LD_THREAD_STACKSIZE (1024 * 8) +#define LD_THREAD_PRIO 48 +#define LD_TIMEOUT 3000 +#define LD_MIN_ADDR 0x80003400 +#define LD_MAX_ADDR (BASE_ADDR - 1 - ARGS_MAX_LEN) +#define LD_MAX_SIZE (LD_MAX_ADDR - LD_MIN_ADDR) +#define LD_ARGS_ADDR (LD_MAX_ADDR + 1) + +#define HTTP_THREAD_STACKSIZE (1024 * 8) +#define HTTP_THREAD_PRIO 48 +#define HTTP_TIMEOUT 30000 + +#define MANAGE_THREAD_STACKSIZE (1024 * 16) +#define MANAGE_THREAD_PRIO 48 + +#define APPENTRY_THREAD_STACKSIZE (1024 * 16) +#define APPENTRY_THREAD_PRIO 62 + +#define UPDATE_THREAD_STACKSIZE (1024 * 8) +#define UPDATE_THREAD_PRIO 58 + +#define FORCE_INLINE __attribute__((always_inline)) + +#endif + diff --git a/channel/channelapp/data/about.png b/channel/channelapp/data/about.png new file mode 100644 index 0000000..86721a4 Binary files /dev/null and b/channel/channelapp/data/about.png differ diff --git a/channel/channelapp/data/apps_grid.png b/channel/channelapp/data/apps_grid.png new file mode 100644 index 0000000..38c791b Binary files /dev/null and b/channel/channelapp/data/apps_grid.png differ diff --git a/channel/channelapp/data/apps_grid_hover.png b/channel/channelapp/data/apps_grid_hover.png new file mode 100644 index 0000000..aafab5f Binary files /dev/null and b/channel/channelapp/data/apps_grid_hover.png differ diff --git a/channel/channelapp/data/apps_list.png b/channel/channelapp/data/apps_list.png new file mode 100644 index 0000000..dac8d8d Binary files /dev/null and b/channel/channelapp/data/apps_list.png differ diff --git a/channel/channelapp/data/apps_list_hover.png b/channel/channelapp/data/apps_list_hover.png new file mode 100644 index 0000000..2dfa840 Binary files /dev/null and b/channel/channelapp/data/apps_list_hover.png differ diff --git a/channel/channelapp/data/apps_next.png b/channel/channelapp/data/apps_next.png new file mode 100644 index 0000000..d792606 Binary files /dev/null and b/channel/channelapp/data/apps_next.png differ diff --git a/channel/channelapp/data/apps_next_hover.png b/channel/channelapp/data/apps_next_hover.png new file mode 100644 index 0000000..e14046d Binary files /dev/null and b/channel/channelapp/data/apps_next_hover.png differ diff --git a/channel/channelapp/data/apps_previous.png b/channel/channelapp/data/apps_previous.png new file mode 100644 index 0000000..c75c53f Binary files /dev/null and b/channel/channelapp/data/apps_previous.png differ diff --git a/channel/channelapp/data/apps_previous_hover.png b/channel/channelapp/data/apps_previous_hover.png new file mode 100644 index 0000000..855dd49 Binary files /dev/null and b/channel/channelapp/data/apps_previous_hover.png differ diff --git a/channel/channelapp/data/background.png b/channel/channelapp/data/background.png new file mode 100644 index 0000000..48c1c39 Binary files /dev/null and b/channel/channelapp/data/background.png differ diff --git a/channel/channelapp/data/background_wide.png b/channel/channelapp/data/background_wide.png new file mode 100644 index 0000000..9be9f4a Binary files /dev/null and b/channel/channelapp/data/background_wide.png differ diff --git a/channel/channelapp/data/bubble1.png b/channel/channelapp/data/bubble1.png new file mode 100644 index 0000000..1ddb5d2 Binary files /dev/null and b/channel/channelapp/data/bubble1.png differ diff --git a/channel/channelapp/data/bubble2.png b/channel/channelapp/data/bubble2.png new file mode 100644 index 0000000..e0c60da Binary files /dev/null and b/channel/channelapp/data/bubble2.png differ diff --git a/channel/channelapp/data/bubble3.png b/channel/channelapp/data/bubble3.png new file mode 100644 index 0000000..64e7141 Binary files /dev/null and b/channel/channelapp/data/bubble3.png differ diff --git a/channel/channelapp/data/button.png b/channel/channelapp/data/button.png new file mode 100644 index 0000000..1183c5a Binary files /dev/null and b/channel/channelapp/data/button.png differ diff --git a/channel/channelapp/data/button_focus.png b/channel/channelapp/data/button_focus.png new file mode 100644 index 0000000..99eb665 Binary files /dev/null and b/channel/channelapp/data/button_focus.png differ diff --git a/channel/channelapp/data/button_small.png b/channel/channelapp/data/button_small.png new file mode 100644 index 0000000..87e75be Binary files /dev/null and b/channel/channelapp/data/button_small.png differ diff --git a/channel/channelapp/data/button_small_focus.png b/channel/channelapp/data/button_small_focus.png new file mode 100644 index 0000000..8174838 Binary files /dev/null and b/channel/channelapp/data/button_small_focus.png differ diff --git a/channel/channelapp/data/button_tiny.png b/channel/channelapp/data/button_tiny.png new file mode 100644 index 0000000..17f189c Binary files /dev/null and b/channel/channelapp/data/button_tiny.png differ diff --git a/channel/channelapp/data/button_tiny_focus.png b/channel/channelapp/data/button_tiny_focus.png new file mode 100644 index 0000000..b0025fc Binary files /dev/null and b/channel/channelapp/data/button_tiny_focus.png differ diff --git a/channel/channelapp/data/content_arrow_down.png b/channel/channelapp/data/content_arrow_down.png new file mode 100644 index 0000000..af21ae1 Binary files /dev/null and b/channel/channelapp/data/content_arrow_down.png differ diff --git a/channel/channelapp/data/content_arrow_up.png b/channel/channelapp/data/content_arrow_up.png new file mode 100644 index 0000000..37b673d Binary files /dev/null and b/channel/channelapp/data/content_arrow_up.png differ diff --git a/channel/channelapp/data/cursor_drag.png b/channel/channelapp/data/cursor_drag.png new file mode 100644 index 0000000..57e8655 Binary files /dev/null and b/channel/channelapp/data/cursor_drag.png differ diff --git a/channel/channelapp/data/cursor_drag_shade.png b/channel/channelapp/data/cursor_drag_shade.png new file mode 100644 index 0000000..9c8af29 Binary files /dev/null and b/channel/channelapp/data/cursor_drag_shade.png differ diff --git a/channel/channelapp/data/cursor_pic.png b/channel/channelapp/data/cursor_pic.png new file mode 100644 index 0000000..cb90e75 Binary files /dev/null and b/channel/channelapp/data/cursor_pic.png differ diff --git a/channel/channelapp/data/cursor_shade.png b/channel/channelapp/data/cursor_shade.png new file mode 100644 index 0000000..6be9651 Binary files /dev/null and b/channel/channelapp/data/cursor_shade.png differ diff --git a/channel/channelapp/data/dialog_background.png b/channel/channelapp/data/dialog_background.png new file mode 100644 index 0000000..23a6cfb Binary files /dev/null and b/channel/channelapp/data/dialog_background.png differ diff --git a/channel/channelapp/data/dlg_confirm.png b/channel/channelapp/data/dlg_confirm.png new file mode 100644 index 0000000..a7c5160 Binary files /dev/null and b/channel/channelapp/data/dlg_confirm.png differ diff --git a/channel/channelapp/data/dlg_error.png b/channel/channelapp/data/dlg_error.png new file mode 100644 index 0000000..b421c49 Binary files /dev/null and b/channel/channelapp/data/dlg_error.png differ diff --git a/channel/channelapp/data/dlg_info.png b/channel/channelapp/data/dlg_info.png new file mode 100644 index 0000000..b6a1712 Binary files /dev/null and b/channel/channelapp/data/dlg_info.png differ diff --git a/channel/channelapp/data/dlg_warning.png b/channel/channelapp/data/dlg_warning.png new file mode 100644 index 0000000..40e4f1b Binary files /dev/null and b/channel/channelapp/data/dlg_warning.png differ diff --git a/channel/channelapp/data/droid.ttf b/channel/channelapp/data/droid.ttf new file mode 100644 index 0000000..ad1efca Binary files /dev/null and b/channel/channelapp/data/droid.ttf differ diff --git a/channel/channelapp/data/droidbold.ttf b/channel/channelapp/data/droidbold.ttf new file mode 100644 index 0000000..d065b64 Binary files /dev/null and b/channel/channelapp/data/droidbold.ttf differ diff --git a/channel/channelapp/data/icon_network.png b/channel/channelapp/data/icon_network.png new file mode 100644 index 0000000..943755d Binary files /dev/null and b/channel/channelapp/data/icon_network.png differ diff --git a/channel/channelapp/data/icon_network_active.png b/channel/channelapp/data/icon_network_active.png new file mode 100644 index 0000000..3a985ad Binary files /dev/null and b/channel/channelapp/data/icon_network_active.png differ diff --git a/channel/channelapp/data/icon_usbgecko.png b/channel/channelapp/data/icon_usbgecko.png new file mode 100644 index 0000000..533d389 Binary files /dev/null and b/channel/channelapp/data/icon_usbgecko.png differ diff --git a/channel/channelapp/data/icon_usbgecko_active.png b/channel/channelapp/data/icon_usbgecko_active.png new file mode 100644 index 0000000..e0ea0bf Binary files /dev/null and b/channel/channelapp/data/icon_usbgecko_active.png differ diff --git a/channel/channelapp/data/logo.png b/channel/channelapp/data/logo.png new file mode 100644 index 0000000..8c552bf Binary files /dev/null and b/channel/channelapp/data/logo.png differ diff --git a/channel/channelapp/data/progress.png b/channel/channelapp/data/progress.png new file mode 100644 index 0000000..cdce07a Binary files /dev/null and b/channel/channelapp/data/progress.png differ diff --git a/channel/channelapp/data/throbber.png b/channel/channelapp/data/throbber.png new file mode 100644 index 0000000..7fe286d Binary files /dev/null and b/channel/channelapp/data/throbber.png differ diff --git a/channel/channelapp/gdb.txt b/channel/channelapp/gdb.txt new file mode 100644 index 0000000..4e6c792 --- /dev/null +++ b/channel/channelapp/gdb.txt @@ -0,0 +1,4 @@ +target remote /dev/ttyUSB0 +info threads +bt + diff --git a/channel/channelapp/i18n/dutch.po b/channel/channelapp/i18n/dutch.po new file mode 100644 index 0000000..1a8f146 --- /dev/null +++ b/channel/channelapp/i18n/dutch.po @@ -0,0 +1,257 @@ +# translation of template.pot to Dutch +# Dutch translations for PACKAGE package. +# Copyright (C) 2008 THE PACKAGE'S COPYRIGHT HOLDER +# This file is distributed under the same license as the PACKAGE package. +# +# blasty +msgid "" +msgstr "" +"Project-Id-Version: nederlands\n" +"Report-Msgid-Bugs-To: \n" +"POT-Creation-Date: 2016-11-22 21:29+0900\n" +"PO-Revision-Date: 2008-05-21 01:17+0200\n" +"Last-Translator: ChronoX\n" +"Language-Team: Dutch\n" +"Language: \n" +"MIME-Version: 1.0\n" +"Content-Type: text/plain; charset=UTF-8\n" +"Content-Transfer-Encoding: 8bit\n" +"Plural-Forms: nplurals=2; plural=(n != 1);\n" +"X-Generator: KBabel 1.11.4\n" + +#: source/dialogs.c:53 +msgid "no description available" +msgstr "Geen omschrijving beschikbaar" + +#: source/dialogs.c:55 +msgid "Information" +msgstr "Informatie" + +#: source/dialogs.c:56 +msgid "Confirmation" +msgstr "Bevestiging" + +#: source/dialogs.c:57 +msgid "Warning" +msgstr "Waarschuwing" + +#: source/dialogs.c:58 +msgid "Error" +msgstr "Error" + +#: source/dialogs.c:59 +msgid "Ok" +msgstr "OK" + +#: source/dialogs.c:60 +msgid "Cancel" +msgstr "Annuleren" + +#: source/dialogs.c:61 +msgid "Yes" +msgstr "Ja" + +#: source/dialogs.c:62 +msgid "No" +msgstr "Nee" + +#: source/dialogs.c:63 +msgid "Delete" +msgstr "Verwijder" + +#: source/dialogs.c:64 +msgid "Load" +msgstr "Start" + +#: source/dialogs.c:65 source/m_main.c:95 +msgid "Back" +msgstr "Terug" + +#: source/dialogs.c:66 +msgid "Options" +msgstr "Opties" + +#: source/dialogs.c:67 +msgid "Device:" +msgstr "Apparaat:" + +#: source/dialogs.c:68 +msgid "Internal SD Slot" +msgstr "Intern SD Slot" + +#: source/dialogs.c:69 +msgid "USB device" +msgstr "USB apparaat" + +#: source/dialogs.c:70 +msgid "SDGecko Slot A" +msgstr "SDGecko Slot A" + +#: source/dialogs.c:71 +msgid "SDGecko Slot B" +msgstr "SDGecko Slot B" + +#: source/dialogs.c:72 +msgid "Sort applications by:" +msgstr "Sorteer applicaties op:" + +#: source/dialogs.c:73 +msgid "Name" +msgstr "Naam" + +#: source/dialogs.c:74 +msgid "Date" +msgstr "Datum" + +#: source/dialogs.c:110 +msgid " translation by " +msgstr "Nederlandse vertaling door bLAStY en ChronoX" + +#: source/dialogs.c:115 +msgid "Theme:" +msgstr "Thema:" + +#: source/dialogs.c:125 +#, c-format +msgid "Version: %s" +msgstr "Versie: %s" + +#: source/dialogs.c:126 +#, c-format +msgid "Author: %s" +msgstr "Auteur: %s" + +#: source/loader.c:716 +msgid "Error while reading the application from the SD card" +msgstr "" +"Er is een fout opgetreden tijdens het lezen van de applicatie van de SD kaart" + +#: source/loader.c:717 +msgid "Error while receiving the application" +msgstr "Er is een fout opgetreden tijdens het ontvangen van de applicatie" + +#: source/loader.c:718 +msgid "Error uncompressing the received data" +msgstr "Error tijdens het uitpakken van de ontvangen data" + +#: source/loader.c:719 +msgid "This is not a valid Wii application" +msgstr "Dit is geen geldige Wii applicatie" + +#: source/loader.c:720 +msgid "This is not a usable ZIP file" +msgstr "Dit is geen bruikbaar ZIP bestand" + +#: source/loader.c:721 +msgid "Not enough memory" +msgstr "Niet genoeg geheugen" + +#: source/loader.c:763 +#, c-format +msgid "Loading %s" +msgstr "Bezig met laden van %s" + +#: source/loader.c:772 +#, c-format +msgid "Receiving over USBGecko" +msgstr "Ontvangen via USBGecko" + +#: source/loader.c:782 +#, c-format +msgid "Receiving from %s" +msgstr "Laden via %s" + +#: source/loader.c:968 +#, c-format +msgid "" +"Extract the received ZIP file?\n" +"%s of free space are required." +msgstr "" +"Pak het ontvangen ZIP bestand uit?\n" +"%s aan vrije ruimte is vereist." + +#: source/loader.c:969 +#, c-format +msgid "WARNING: Files in '%s' will be overwritten" +msgstr "WAARSCHUWING: Bestanden in '%s' zullen worden overschreven" + +#: source/loader.c:970 +msgid "Error while extracting the ZIP file" +msgstr "Er is een fout opgetreden tijdens het uitpakken van de ZIP filee" + +#: source/main.c:250 +msgid "Do you really want to delete this application?" +msgstr "Wil je echt deze applicatie verwijderen?" + +#: source/main.c:251 +#, c-format +msgid "Error deleting '%s'" +msgstr "Kon '%s' niet verwijderen" + +#: source/manage.c:518 +msgid "Extracting" +msgstr "Uitpakken" + +#: source/m_main.c:80 +msgid "Network not initialized" +msgstr "Netwerk niet geïnitialiseerd" + +#: source/m_main.c:81 +#, c-format +msgid "Your Wii's IP is %u.%u.%u.%u" +msgstr "IP adres: %u.%u.%u.%u" + +#: source/m_main.c:97 +msgid "About" +msgstr "Info" + +#: source/m_main.c:102 +msgid "Launch BootMii" +msgstr "BootMii starten" + +#: source/m_main.c:106 +msgid "Exit to System Menu" +msgstr "Terug naar System Menu" + +#: source/m_main.c:109 +msgid "Shutdown" +msgstr "Afsluiten" + +#: source/update.c:232 +#, c-format +msgid "" +"An update to the Homebrew Channel (version %s, replacing the installed " +"version %s) is available for installation, do you want to update now?\n" +"\n" +"Release notes:\n" +"\n" +"%s" +msgstr "" +"Een update voor het Homebrew Kanaal (%s zal de huidige %s vervangen) is " +"beschikbaar, wil je nu updaten?\n" +"\n" +"Release notes:\n" +"\n" +"%s" + +#: source/update.c:262 +#, c-format +msgid "Downloading Update (%u kB)" +msgstr "Update wordt gedownload (%u kB)" + +#: source/update.c:304 +msgid "Download failed" +msgstr "Downloaden mislukt" + +#: source/widgets.c:16 +msgid "" +msgstr "" + +#~ msgid "Version: " +#~ msgstr "Versie: " + +#~ msgid "Coder: " +#~ msgstr "Auteur: " + +#~ msgid "Deleting" +#~ msgstr "Verijwderen" diff --git a/channel/channelapp/i18n/french.po b/channel/channelapp/i18n/french.po new file mode 100644 index 0000000..3f1ed46 --- /dev/null +++ b/channel/channelapp/i18n/french.po @@ -0,0 +1,261 @@ +# SOME DESCRIPTIVE TITLE. +# Copyright (C) YEAR THE PACKAGE'S COPYRIGHT HOLDER +# This file is distributed under the same license as the PACKAGE package. +# FIRST AUTHOR , YEAR. +# +msgid "" +msgstr "" +"Project-Id-Version: PACKAGE VERSION\n" +"Report-Msgid-Bugs-To: \n" +"POT-Creation-Date: 2016-11-22 21:29+0900\n" +"PO-Revision-Date: 2008-05-23 08:52+0100\n" +"Last-Translator: Bruno C. \n" +"Language-Team: LANGUAGE\n" +"Language: \n" +"MIME-Version: 1.0\n" +"Content-Type: text/plain; charset=UTF-8\n" +"Content-Transfer-Encoding: 8bit\n" + +#: source/dialogs.c:53 +msgid "no description available" +msgstr "aucune description disponible" + +# This and the following strings are dialog box "title bars" +#: source/dialogs.c:55 +msgid "Information" +msgstr "Information" + +#: source/dialogs.c:56 +msgid "Confirmation" +msgstr "Confirmation" + +#: source/dialogs.c:57 +msgid "Warning" +msgstr "Avertissement" + +#: source/dialogs.c:58 +msgid "Error" +msgstr "Erreur" + +# button text +#: source/dialogs.c:59 +msgid "Ok" +msgstr "Ok" + +# button text +#: source/dialogs.c:60 +msgid "Cancel" +msgstr "Annuler" + +# button text +#: source/dialogs.c:61 +msgid "Yes" +msgstr "Oui" + +# button text +#: source/dialogs.c:62 +msgid "No" +msgstr "Non" + +#: source/dialogs.c:63 +msgid "Delete" +msgstr "Supprimer" + +# button text +#: source/dialogs.c:64 +msgid "Load" +msgstr "Charger" + +# button text +#: source/dialogs.c:65 source/m_main.c:95 +msgid "Back" +msgstr "Retour" + +#: source/dialogs.c:66 +msgid "Options" +msgstr "Options" + +#: source/dialogs.c:67 +msgid "Device:" +msgstr "Dispositif:" + +#: source/dialogs.c:68 +msgid "Internal SD Slot" +msgstr "Lecteur de carte SD" + +#: source/dialogs.c:69 +msgid "USB device" +msgstr "Dispositif USB" + +#: source/dialogs.c:70 +msgid "SDGecko Slot A" +msgstr "Lecteur SDGecko A" + +#: source/dialogs.c:71 +msgid "SDGecko Slot B" +msgstr "Lecteur SDGecko B" + +#: source/dialogs.c:72 +msgid "Sort applications by:" +msgstr "Trier les applications par:" + +#: source/dialogs.c:73 +msgid "Name" +msgstr "Nom" + +#: source/dialogs.c:74 +msgid "Date" +msgstr "Date" + +#: source/dialogs.c:110 +msgid " translation by " +msgstr "Traduction française par Chandler et Strangerke" + +#: source/dialogs.c:115 +msgid "Theme:" +msgstr "Thème:" + +#: source/dialogs.c:125 +#, c-format +msgid "Version: %s" +msgstr "Version : %s" + +#: source/dialogs.c:126 +#, c-format +msgid "Author: %s" +msgstr "Auteur : %s" + +#: source/loader.c:716 +msgid "Error while reading the application from the SD card" +msgstr "Erreur de lecture de l'application à partir de la carte SD" + +#: source/loader.c:717 +msgid "Error while receiving the application" +msgstr "Erreur lors de la réception de l'application" + +#: source/loader.c:718 +msgid "Error uncompressing the received data" +msgstr "Erreur de décompression des données reçues" + +#: source/loader.c:719 +msgid "This is not a valid Wii application" +msgstr "Application Wii non valide" + +#: source/loader.c:720 +msgid "This is not a usable ZIP file" +msgstr "Ce fichier ZIP est inutilisable" + +#: source/loader.c:721 +msgid "Not enough memory" +msgstr "Mémoire insuffisante" + +#: source/loader.c:763 +#, c-format +msgid "Loading %s" +msgstr "Chargement de %s" + +#: source/loader.c:772 +#, c-format +msgid "Receiving over USBGecko" +msgstr "Réception sur USBGecko" + +#: source/loader.c:782 +#, c-format +msgid "Receiving from %s" +msgstr "Réception à partir de %s" + +#: source/loader.c:968 +#, c-format +msgid "" +"Extract the received ZIP file?\n" +"%s of free space are required." +msgstr "" +"Decompression du fichier ZIP reçu?\n" +"Un espace libre de %s est nécessaire." + +#: source/loader.c:969 +#, c-format +msgid "WARNING: Files in '%s' will be overwritten" +msgstr "ATTENTION: Les fichiers dans '%s' seront écrasés" + +#: source/loader.c:970 +msgid "Error while extracting the ZIP file" +msgstr "Erreur de décompression du fichier ZIP" + +#: source/main.c:250 +msgid "Do you really want to delete this application?" +msgstr "Voulez-vous vraiment supprimer cette application?" + +#: source/main.c:251 +#, c-format +msgid "Error deleting '%s'" +msgstr "Errur lors de la suppression de '%s'" + +#: source/manage.c:518 +msgid "Extracting" +msgstr "Extraction en cours" + +#: source/m_main.c:80 +msgid "Network not initialized" +msgstr "Réseau non initialisé" + +#: source/m_main.c:81 +#, c-format +msgid "Your Wii's IP is %u.%u.%u.%u" +msgstr "L'IP de votre Wii est %u.%u.%u.%u" + +# button text +#: source/m_main.c:97 +msgid "About" +msgstr "À propos de" + +#: source/m_main.c:102 +msgid "Launch BootMii" +msgstr "Lancer BootMii" + +#: source/m_main.c:106 +msgid "Exit to System Menu" +msgstr "Retour au menu système" + +#: source/m_main.c:109 +msgid "Shutdown" +msgstr "Arrêt" + +#: source/update.c:232 +#, c-format +msgid "" +"An update to the Homebrew Channel (version %s, replacing the installed " +"version %s) is available for installation, do you want to update now?\n" +"\n" +"Release notes:\n" +"\n" +"%s" +msgstr "" +"Une mise à jour de la chaine Homebrew (version %s, remplaçant la version " +"installée %s) est disponible. Voulez-vous faire la mise à jour maintenant ?\n" +"\n" +"Notes de version :\n" +"\n" +"%s" + +#: source/update.c:262 +#, c-format +msgid "Downloading Update (%u kB)" +msgstr "Téléchargement... (%u Ko)" + +#: source/update.c:304 +msgid "Download failed" +msgstr "Échec du téléchargement" + +#: source/widgets.c:16 +msgid "" +msgstr "" + +#~ msgid "Version: " +#~ msgstr "Version : " + +#~ msgid "Coder: " +#~ msgstr "Auteur : " + +#~ msgid "Deleting" +#~ msgstr "Suppression en cours" diff --git a/channel/channelapp/i18n/german.po b/channel/channelapp/i18n/german.po new file mode 100644 index 0000000..df46377 --- /dev/null +++ b/channel/channelapp/i18n/german.po @@ -0,0 +1,256 @@ +# SOME DESCRIPTIVE TITLE. +# Copyright (C) YEAR THE PACKAGE'S COPYRIGHT HOLDER +# This file is distributed under the same license as the PACKAGE package. +# FIRST AUTHOR , YEAR. +# +msgid "" +msgstr "" +"Project-Id-Version: PACKAGE VERSION\n" +"Report-Msgid-Bugs-To: \n" +"POT-Creation-Date: 2016-11-22 21:29+0900\n" +"PO-Revision-Date: YEAR-MO-DA HO:MI+ZONE\n" +"Last-Translator: NICKNAME \n" +"Language-Team: LANGUAGE\n" +"Language: \n" +"MIME-Version: 1.0\n" +"Content-Type: text/plain; charset=UTF-8\n" +"Content-Transfer-Encoding: 8bit\n" + +#: source/dialogs.c:53 +msgid "no description available" +msgstr "keine Beschreibung" + +#: source/dialogs.c:55 +msgid "Information" +msgstr "Information" + +#: source/dialogs.c:56 +msgid "Confirmation" +msgstr "Bestätigen" + +#: source/dialogs.c:57 +msgid "Warning" +msgstr "Achtung" + +#: source/dialogs.c:58 +msgid "Error" +msgstr "Fehler" + +#: source/dialogs.c:59 +msgid "Ok" +msgstr "OK" + +#: source/dialogs.c:60 +msgid "Cancel" +msgstr "Abbrechen" + +#: source/dialogs.c:61 +msgid "Yes" +msgstr "Ja" + +#: source/dialogs.c:62 +msgid "No" +msgstr "Nein" + +#: source/dialogs.c:63 +msgid "Delete" +msgstr "Löschen" + +# button text +#: source/dialogs.c:64 +msgid "Load" +msgstr "Laden" + +# button text +#: source/dialogs.c:65 source/m_main.c:95 +msgid "Back" +msgstr "Zurück" + +#: source/dialogs.c:66 +msgid "Options" +msgstr "Einstellungen" + +#: source/dialogs.c:67 +msgid "Device:" +msgstr "Quelle:" + +#: source/dialogs.c:68 +msgid "Internal SD Slot" +msgstr "Interner SD Slot" + +#: source/dialogs.c:69 +msgid "USB device" +msgstr "USB-Laufwerk" + +#: source/dialogs.c:70 +msgid "SDGecko Slot A" +msgstr "SDGecko Slot A" + +#: source/dialogs.c:71 +msgid "SDGecko Slot B" +msgstr "SDGecko Slot B" + +#: source/dialogs.c:72 +msgid "Sort applications by:" +msgstr "Anwendungen sortieren nach:" + +#: source/dialogs.c:73 +msgid "Name" +msgstr "Name" + +#: source/dialogs.c:74 +msgid "Date" +msgstr "Datum" + +#: source/dialogs.c:110 +msgid " translation by " +msgstr "Deutsche Übersetzung: Elena Vectoradvenberg" + +#: source/dialogs.c:115 +msgid "Theme:" +msgstr "Thema:" + +#: source/dialogs.c:125 +#, c-format +msgid "Version: %s" +msgstr "Version: %s" + +#: source/dialogs.c:126 +#, c-format +msgid "Author: %s" +msgstr "Autor: %s" + +#: source/loader.c:716 +msgid "Error while reading the application from the SD card" +msgstr "Fehler beim Lesen der Anwendung von der SD-Karte" + +#: source/loader.c:717 +msgid "Error while receiving the application" +msgstr "Fehler beim Empfang der Anwendung" + +#: source/loader.c:718 +msgid "Error uncompressing the received data" +msgstr "Fehler beim Entpacken der empfangenen Daten" + +#: source/loader.c:719 +msgid "This is not a valid Wii application" +msgstr "Das ist keine gültige Wii-Anwendung" + +#: source/loader.c:720 +msgid "This is not a usable ZIP file" +msgstr "Dies ist keine gültige ZIP-Datei" + +#: source/loader.c:721 +msgid "Not enough memory" +msgstr "Nicht genug Speicher" + +#: source/loader.c:763 +#, c-format +msgid "Loading %s" +msgstr "Lade %s" + +#: source/loader.c:772 +#, c-format +msgid "Receiving over USBGecko" +msgstr "Empfange über USBGecko" + +#: source/loader.c:782 +#, c-format +msgid "Receiving from %s" +msgstr "Empfange von %s" + +#: source/loader.c:968 +#, c-format +msgid "" +"Extract the received ZIP file?\n" +"%s of free space are required." +msgstr "" +"Entpacken der empfangenen ZIP-Datei?\n" +"%s bytes freier Speicherplatz werden benötigt." + +#: source/loader.c:969 +#, c-format +msgid "WARNING: Files in '%s' will be overwritten" +msgstr "ACHTUNG: Dateien in '%s' werden überschrieben" + +#: source/loader.c:970 +msgid "Error while extracting the ZIP file" +msgstr "Fehler beim Empfang der Anwendung" + +#: source/main.c:250 +msgid "Do you really want to delete this application?" +msgstr "Soll die Anwendung wirklich gelöscht werden?" + +#: source/main.c:251 +#, c-format +msgid "Error deleting '%s'" +msgstr "Fehler beim Löschen von '%s'" + +#: source/manage.c:518 +msgid "Extracting" +msgstr "Entpacke" + +#: source/m_main.c:80 +msgid "Network not initialized" +msgstr "Netzwerk nicht initialisiert" + +#: source/m_main.c:81 +#, c-format +msgid "Your Wii's IP is %u.%u.%u.%u" +msgstr "Die IP deiner Wii ist %u.%u.%u.%u" + +# button text +#: source/m_main.c:97 +msgid "About" +msgstr "Info" + +#: source/m_main.c:102 +msgid "Launch BootMii" +msgstr "BootMii starten" + +#: source/m_main.c:106 +msgid "Exit to System Menu" +msgstr "Beenden, zum Systemmenü" + +#: source/m_main.c:109 +msgid "Shutdown" +msgstr "Herunterfahren" + +#: source/update.c:232 +#, c-format +msgid "" +"An update to the Homebrew Channel (version %s, replacing the installed " +"version %s) is available for installation, do you want to update now?\n" +"\n" +"Release notes:\n" +"\n" +"%s" +msgstr "" +"Ein Update für den Homebrew Channel ist verfügbar (Version %s, ersetzt die " +"aktuelle Version %s). Soll jetzt aktualisiert werden?\n" +"\n" +"Versionsinfo:\n" +"\n" +"%s" + +#: source/update.c:262 +#, c-format +msgid "Downloading Update (%u kB)" +msgstr "Lade Update (%u KB)" + +#: source/update.c:304 +msgid "Download failed" +msgstr "Download fehlgeschlagen" + +#: source/widgets.c:16 +msgid "" +msgstr "" + +#~ msgid "Version: " +#~ msgstr "Version: " + +#~ msgid "Coder: " +#~ msgstr "Coder: " + +#~ msgid "Deleting" +#~ msgstr "Lösche" diff --git a/channel/channelapp/i18n/italian.po b/channel/channelapp/i18n/italian.po new file mode 100644 index 0000000..1427195 --- /dev/null +++ b/channel/channelapp/i18n/italian.po @@ -0,0 +1,267 @@ +# SOME DESCRIPTIVE TITLE. +# Copyright (C) YEAR THE PACKAGE'S COPYRIGHT HOLDER +# This file is distributed under the same license as the PACKAGE package. +# FIRST AUTHOR , YEAR. +# +msgid "" +msgstr "" +"Project-Id-Version: PACKAGE VERSION\n" +"Report-Msgid-Bugs-To: \n" +"POT-Creation-Date: 2016-11-22 21:29+0900\n" +"PO-Revision-Date: YEAR-MO-DA HO:MI+ZONE\n" +"Last-Translator: NICKNAME \n" +"Language-Team: LANGUAGE\n" +"Language: \n" +"MIME-Version: 1.0\n" +"Content-Type: text/plain; charset=UTF-8\n" +"Content-Transfer-Encoding: 8bit\n" + +#: source/dialogs.c:53 +msgid "no description available" +msgstr "nessuna descrizione disponibile" + +# This and the following strings are dialog box "title bars" +#: source/dialogs.c:55 +msgid "Information" +msgstr "Informazioni" + +#: source/dialogs.c:56 +msgid "Confirmation" +msgstr "Conferma" + +#: source/dialogs.c:57 +msgid "Warning" +msgstr "Avvertenza" + +#: source/dialogs.c:58 +msgid "Error" +msgstr "Errore" + +# button text +#: source/dialogs.c:59 +msgid "Ok" +msgstr "Ok" + +# button text +#: source/dialogs.c:60 +msgid "Cancel" +msgstr "Annulla" + +# button text +#: source/dialogs.c:61 +msgid "Yes" +msgstr "Sì" + +# button text +#: source/dialogs.c:62 +msgid "No" +msgstr "No" + +#: source/dialogs.c:63 +msgid "Delete" +msgstr "Elimina" + +# button text +#: source/dialogs.c:64 +msgid "Load" +msgstr "Carica" + +# button text +#: source/dialogs.c:65 source/m_main.c:95 +msgid "Back" +msgstr "Indietro" + +#: source/dialogs.c:66 +msgid "Options" +msgstr "Opzioni" + +#: source/dialogs.c:67 +msgid "Device:" +msgstr "Periferica:" + +#: source/dialogs.c:68 +msgid "Internal SD Slot" +msgstr "Slot SD Interno" + +#: source/dialogs.c:69 +msgid "USB device" +msgstr "Pendrive USB" + +#: source/dialogs.c:70 +msgid "SDGecko Slot A" +msgstr "SDGecko Slot A" + +#: source/dialogs.c:71 +msgid "SDGecko Slot B" +msgstr "SDGecko Slot B" + +#: source/dialogs.c:72 +msgid "Sort applications by:" +msgstr "Ordina applicazioni per:" + +#: source/dialogs.c:73 +msgid "Name" +msgstr "Nome" + +#: source/dialogs.c:74 +msgid "Date" +msgstr "Data" + +#: source/dialogs.c:110 +msgid " translation by " +msgstr "" +"Traduzione italiana di EnricoBr\n" +"Aggiornata da The Lemon Man" + +#: source/dialogs.c:115 +msgid "Theme:" +msgstr "Tema:" + +#: source/dialogs.c:125 +#, c-format +msgid "Version: %s" +msgstr "Versione: %s" + +#: source/dialogs.c:126 +#, c-format +msgid "Author: %s" +msgstr "Autore: %s" + +#: source/loader.c:716 +msgid "Error while reading the application from the SD card" +msgstr "Errore durante la lettura dell'applicazione dalla scheda SD" + +#: source/loader.c:717 +msgid "Error while receiving the application" +msgstr "Errore durante la ricezione dell'applicazione" + +#: source/loader.c:718 +msgid "Error uncompressing the received data" +msgstr "Errore durante la decompressione dei dati ricevuti" + +#: source/loader.c:719 +msgid "This is not a valid Wii application" +msgstr "Non è un'applicazione Wii valida" + +#: source/loader.c:720 +msgid "This is not a usable ZIP file" +msgstr "Questo non è un file ZIP utilizzabile" + +#: source/loader.c:721 +msgid "Not enough memory" +msgstr "Memoria insufficiente" + +#: source/loader.c:763 +#, c-format +msgid "Loading %s" +msgstr "Caricamento di %s" + +#: source/loader.c:772 +#, c-format +msgid "Receiving over USBGecko" +msgstr "Ricezione su USBGecko" + +#: source/loader.c:782 +#, c-format +msgid "Receiving from %s" +msgstr "Ricezione da %s" + +#: source/loader.c:968 +#, c-format +msgid "" +"Extract the received ZIP file?\n" +"%s of free space are required." +msgstr "" +"Estrarre il file ZIP ricevuto?\n" +"Sono richiesti %s di spazio libero." + +#: source/loader.c:969 +#, c-format +msgid "WARNING: Files in '%s' will be overwritten" +msgstr "ATTENZIONE: I file in '%s' verranno sovrascritti" + +#: source/loader.c:970 +msgid "Error while extracting the ZIP file" +msgstr "Errore durante l'estrazione del file ZIP" + +#: source/main.c:250 +msgid "Do you really want to delete this application?" +msgstr "Sei sicuro di voler cancellare questa domanda?" + +#: source/main.c:251 +#, c-format +msgid "Error deleting '%s'" +msgstr "Errore durante l' eliminazione di '%s'" + +#: source/manage.c:518 +msgid "Extracting" +msgstr "Estraendo" + +#: source/m_main.c:80 +msgid "Network not initialized" +msgstr "Rete non inizializzata" + +#: source/m_main.c:81 +#, c-format +msgid "Your Wii's IP is %u.%u.%u.%u" +msgstr "L'indirizzo IP del Wii è %u.%u.%u.%u" + +# button text +#: source/m_main.c:97 +msgid "About" +msgstr "Informazioni" + +#: source/m_main.c:102 +msgid "Launch BootMii" +msgstr "Lanciare BootMii" + +#: source/m_main.c:106 +msgid "Exit to System Menu" +msgstr "Ritorna al Menu di Sistema" + +#: source/m_main.c:109 +msgid "Shutdown" +msgstr "Spegni" + +#: source/update.c:232 +#, c-format +msgid "" +"An update to the Homebrew Channel (version %s, replacing the installed " +"version %s) is available for installation, do you want to update now?\n" +"\n" +"Release notes:\n" +"\n" +"%s" +msgstr "" +"È disponibile per l'installazione un aggiornamento di Homebrew Channel " +"(versione %s, in sostituzione della versione %s installata); vuoi eseguire " +"adesso l'aggiornamento?\n" +"\n" +"Note sulla versione:\n" +"\n" +"%s" + +#: source/update.c:262 +#, c-format +msgid "Downloading Update (%u kB)" +msgstr "Download (%u kB)" + +#: source/update.c:304 +msgid "Download failed" +msgstr "Download non riuscito" + +#: source/widgets.c:16 +msgid "" +msgstr "" + +#~ msgid "Version: " +#~ msgstr "Versione: " + +#~ msgid "Coder: " +#~ msgstr "Sviluppatore: " + +#~ msgid "Deleting" +#~ msgstr "Eliminando" + +#~ msgid "Do you really want to exit to the boring, boring System Menu?\n" +#~ msgstr "Confermi di voler uscire al noiosissimo menu di sistema?\n" diff --git a/channel/channelapp/i18n/japanese.po b/channel/channelapp/i18n/japanese.po new file mode 100644 index 0000000..4737fbb --- /dev/null +++ b/channel/channelapp/i18n/japanese.po @@ -0,0 +1,253 @@ +# SOME DESCRIPTIVE TITLE. +# Copyright (C) YEAR THE PACKAGE'S COPYRIGHT HOLDER +# This file is distributed under the same license as the PACKAGE package. +# FIRST AUTHOR , YEAR. +# +msgid "" +msgstr "" +"Project-Id-Version: homebrew_channel\n" +"Report-Msgid-Bugs-To: \n" +"POT-Creation-Date: 2016-11-22 21:29+0900\n" +"PO-Revision-Date: 2011-01-10 05:53+0200\n" +"Last-Translator: Jan Ekstrom \n" +"Language-Team: \n" +"Language: \n" +"MIME-Version: 1.0\n" +"Content-Type: text/plain; charset=UTF-8\n" +"Content-Transfer-Encoding: 8bit\n" + +#: source/dialogs.c:53 +msgid "no description available" +msgstr "詳細情報はありません " + +# This and the following strings are dialog box "title bars" +#: source/dialogs.c:55 +msgid "Information" +msgstr "報告" + +#: source/dialogs.c:56 +msgid "Confirmation" +msgstr "確認" + +#: source/dialogs.c:57 +msgid "Warning" +msgstr "警告" + +#: source/dialogs.c:58 +msgid "Error" +msgstr "エラー" + +# button text +#: source/dialogs.c:59 +msgid "Ok" +msgstr "Ok" + +# button text +#: source/dialogs.c:60 +msgid "Cancel" +msgstr "キャンセル" + +# button text +#: source/dialogs.c:61 +msgid "Yes" +msgstr "はい" + +# button text +#: source/dialogs.c:62 +msgid "No" +msgstr "いいえ" + +#: source/dialogs.c:63 +msgid "Delete" +msgstr "削除" + +# button text +#: source/dialogs.c:64 +msgid "Load" +msgstr "起動" + +# button text +#: source/dialogs.c:65 source/m_main.c:95 +msgid "Back" +msgstr "戻る" + +#: source/dialogs.c:66 +msgid "Options" +msgstr "オプション" + +#: source/dialogs.c:67 +msgid "Device:" +msgstr "デバイス:" + +#: source/dialogs.c:68 +msgid "Internal SD Slot" +msgstr "内部SDスロット" + +#: source/dialogs.c:69 +msgid "USB device" +msgstr "USBデバイス" + +#: source/dialogs.c:70 +msgid "SDGecko Slot A" +msgstr "SDGeckoのスロットA" + +#: source/dialogs.c:71 +msgid "SDGecko Slot B" +msgstr "SDGeckoのスロットB" + +#: source/dialogs.c:72 +msgid "Sort applications by:" +msgstr "アプリケーションの並べ替え順序 " + +#: source/dialogs.c:73 +msgid "Name" +msgstr "名前" + +#: source/dialogs.c:74 +msgid "Date" +msgstr "日付" + +#: source/dialogs.c:110 +msgid " translation by " +msgstr "日本語、JEEB氏による翻訳" + +#: source/dialogs.c:115 +msgid "Theme:" +msgstr "テーマ:" + +#: source/dialogs.c:125 +#, c-format +msgid "Version: %s" +msgstr "バージョン: %s" + +#: source/dialogs.c:126 +#, c-format +msgid "Author: %s" +msgstr "作者: %s" + +#: source/loader.c:716 +msgid "Error while reading the application from the SD card" +msgstr "アプリケーションをSDカードから読み込んでいる最中エラーが発生しました。" + +#: source/loader.c:717 +msgid "Error while receiving the application" +msgstr "アプリケーションを受信している最中エラーが発生しました。" + +#: source/loader.c:718 +msgid "Error uncompressing the received data" +msgstr "受信されたデータを解凍中エラーが発生しました。" + +#: source/loader.c:719 +msgid "This is not a valid Wii application" +msgstr "これは妥当なWiiアプリケーションではありません。" + +#: source/loader.c:720 +msgid "This is not a usable ZIP file" +msgstr "これは妥当なZIPファイルではありません。" + +#: source/loader.c:721 +msgid "Not enough memory" +msgstr "メモリーが足りません。" + +#: source/loader.c:763 +#, c-format +msgid "Loading %s" +msgstr "%sをロード中。" + +#: source/loader.c:772 +#, c-format +msgid "Receiving over USBGecko" +msgstr "USBGecko経由で受信中。" + +#: source/loader.c:782 +#, c-format +msgid "Receiving from %s" +msgstr "%sから受信中。" + +#: source/loader.c:968 +#, c-format +msgid "" +"Extract the received ZIP file?\n" +"%s of free space are required." +msgstr "" +"受信されたZIPファイルを解凍しますか。\n" +"%sの空き領域が必要になります。" + +#: source/loader.c:969 +#, c-format +msgid "WARNING: Files in '%s' will be overwritten" +msgstr "警告: 「%s」の中のファイルはすべて上書きされます。" + +#: source/loader.c:970 +msgid "Error while extracting the ZIP file" +msgstr "ZIPファイルを解凍中エラーが発生しました。" + +#: source/main.c:250 +msgid "Do you really want to delete this application?" +msgstr "本当にこのアプリケーションを削除しますか。" + +#: source/main.c:251 +#, c-format +msgid "Error deleting '%s'" +msgstr "「%s」を削除中にエラーが発生しました。" + +#: source/manage.c:518 +msgid "Extracting" +msgstr "解凍中" + +#: source/m_main.c:80 +msgid "Network not initialized" +msgstr "ネットワークが初期化されていません。" + +#: source/m_main.c:81 +#, c-format +msgid "Your Wii's IP is %u.%u.%u.%u" +msgstr "このWiiのIPアドレスは %u.%u.%u.%u" + +# button text +#: source/m_main.c:97 +msgid "About" +msgstr "このアプリケーションについて" + +#: source/m_main.c:102 +msgid "Launch BootMii" +msgstr "BootMiiを起動" + +#: source/m_main.c:106 +msgid "Exit to System Menu" +msgstr "システムメニューへ戻る" + +#: source/m_main.c:109 +msgid "Shutdown" +msgstr "シャットダウン" + +# note: a non-breaking space is used in "Homebrew Channel" here. +#: source/update.c:232 +#, c-format +msgid "" +"An update to the Homebrew Channel (version %s, replacing the installed " +"version %s) is available for installation, do you want to update now?\n" +"\n" +"Release notes:\n" +"\n" +"%s" +msgstr "" +"Homebrew Channelに利用可能な更新があります。 (バージョン%s、現在のバージョ" +"ン%sを置換) 今すぐ更新しますか。\n" +"\n" +"リリース ノート :\n" +"\n" +"%s" + +#: source/update.c:262 +#, c-format +msgid "Downloading Update (%u kB)" +msgstr "更新をダウンロード中 (%u kB)" + +#: source/update.c:304 +msgid "Download failed" +msgstr "ダウンロードが失敗しました。" + +#: source/widgets.c:16 +msgid "" +msgstr "<詳細情報はありません >" diff --git a/channel/channelapp/i18n/spanish.po b/channel/channelapp/i18n/spanish.po new file mode 100644 index 0000000..dac8dd0 --- /dev/null +++ b/channel/channelapp/i18n/spanish.po @@ -0,0 +1,258 @@ +# translation of template.pot to Spanish +# Spanish translations for PACKAGE package. +# Copyright (C) 2008 THE PACKAGE'S COPYRIGHT HOLDER +# This file is distributed under the same license as the PACKAGE package. +# +# marcan , 2008. +msgid "" +msgstr "" +"Project-Id-Version: spanish\n" +"Report-Msgid-Bugs-To: \n" +"POT-Creation-Date: 2016-11-22 21:29+0900\n" +"PO-Revision-Date: 2008-05-21 01:17+0200\n" +"Last-Translator: marcan \n" +"Language-Team: Spanish\n" +"Language: \n" +"MIME-Version: 1.0\n" +"Content-Type: text/plain; charset=UTF-8\n" +"Content-Transfer-Encoding: 8bit\n" +"Plural-Forms: nplurals=2; plural=(n != 1);\n" +"X-Generator: KBabel 1.11.4\n" + +#: source/dialogs.c:53 +msgid "no description available" +msgstr "descripción no disponible" + +#: source/dialogs.c:55 +msgid "Information" +msgstr "Información" + +#: source/dialogs.c:56 +msgid "Confirmation" +msgstr "Confirmación" + +#: source/dialogs.c:57 +msgid "Warning" +msgstr "Advertencia" + +#: source/dialogs.c:58 +msgid "Error" +msgstr "Error" + +#: source/dialogs.c:59 +msgid "Ok" +msgstr "Aceptar" + +#: source/dialogs.c:60 +msgid "Cancel" +msgstr "Cancelar" + +#: source/dialogs.c:61 +msgid "Yes" +msgstr "Sí" + +#: source/dialogs.c:62 +msgid "No" +msgstr "No" + +#: source/dialogs.c:63 +msgid "Delete" +msgstr "Eliminar" + +# button text +#: source/dialogs.c:64 +msgid "Load" +msgstr "Cargar" + +# button text +#: source/dialogs.c:65 source/m_main.c:95 +msgid "Back" +msgstr "Atrás" + +#: source/dialogs.c:66 +msgid "Options" +msgstr "Opciones" + +#: source/dialogs.c:67 +msgid "Device:" +msgstr "Dispositivo:" + +#: source/dialogs.c:68 +msgid "Internal SD Slot" +msgstr "Ranura SD Interna" + +#: source/dialogs.c:69 +msgid "USB device" +msgstr "Dispositivo USB" + +#: source/dialogs.c:70 +msgid "SDGecko Slot A" +msgstr "SDGecko en Slot A" + +#: source/dialogs.c:71 +msgid "SDGecko Slot B" +msgstr "SDGecko en Slot B" + +#: source/dialogs.c:72 +msgid "Sort applications by:" +msgstr "Ordenar aplicaciones por:" + +#: source/dialogs.c:73 +msgid "Name" +msgstr "Nombre" + +#: source/dialogs.c:74 +msgid "Date" +msgstr "Fecha" + +#: source/dialogs.c:110 +msgid " translation by " +msgstr "Traducción al español - marcan" + +#: source/dialogs.c:115 +msgid "Theme:" +msgstr "Tema:" + +#: source/dialogs.c:125 +#, c-format +msgid "Version: %s" +msgstr "Versión: %s" + +#: source/dialogs.c:126 +#, c-format +msgid "Author: %s" +msgstr "Autor: %s" + +#: source/loader.c:716 +msgid "Error while reading the application from the SD card" +msgstr "Error al leer la aplicación desde la tarjeta SD" + +#: source/loader.c:717 +msgid "Error while receiving the application" +msgstr "Error al recibir la aplicación" + +#: source/loader.c:718 +msgid "Error uncompressing the received data" +msgstr "Error al descomprimir los datos recibidos" + +#: source/loader.c:719 +msgid "This is not a valid Wii application" +msgstr "Esta no es una aplicación de Wii válida" + +#: source/loader.c:720 +msgid "This is not a usable ZIP file" +msgstr "Este archivo ZIP no se puede utilizar" + +#: source/loader.c:721 +msgid "Not enough memory" +msgstr "Memoria insuficiente" + +#: source/loader.c:763 +#, c-format +msgid "Loading %s" +msgstr "Cargando %s" + +#: source/loader.c:772 +#, c-format +msgid "Receiving over USBGecko" +msgstr "Recibiendo por USBGecko" + +#: source/loader.c:782 +#, c-format +msgid "Receiving from %s" +msgstr "Recibiendo desde %s" + +#: source/loader.c:968 +#, c-format +msgid "" +"Extract the received ZIP file?\n" +"%s of free space are required." +msgstr "" +"¿Desea extraer el archivo ZIP recibido?\n" +"Se requiren %s de espacio libre." + +#: source/loader.c:969 +#, c-format +msgid "WARNING: Files in '%s' will be overwritten" +msgstr "AVISO: Se reemplazarán archivos en '%s'" + +#: source/loader.c:970 +msgid "Error while extracting the ZIP file" +msgstr "Error al extraer el archivo ZIP" + +#: source/main.c:250 +msgid "Do you really want to delete this application?" +msgstr "¿Estás seguro de que quieres eliminar esta aplicación?" + +#: source/main.c:251 +#, c-format +msgid "Error deleting '%s'" +msgstr "Error al eliminar '%s'" + +#: source/manage.c:518 +msgid "Extracting" +msgstr "Extrayendo" + +#: source/m_main.c:80 +msgid "Network not initialized" +msgstr "La red no se ha inicializado" + +#: source/m_main.c:81 +#, c-format +msgid "Your Wii's IP is %u.%u.%u.%u" +msgstr "La IP de tu Wii es %u.%u.%u.%u" + +#: source/m_main.c:97 +msgid "About" +msgstr "Acerca de" + +#: source/m_main.c:102 +msgid "Launch BootMii" +msgstr "Lanzar BootMii" + +#: source/m_main.c:106 +msgid "Exit to System Menu" +msgstr "Salir al menú del sistema" + +#: source/m_main.c:109 +msgid "Shutdown" +msgstr "Apagar" + +#: source/update.c:232 +#, c-format +msgid "" +"An update to the Homebrew Channel (version %s, replacing the installed " +"version %s) is available for installation, do you want to update now?\n" +"\n" +"Release notes:\n" +"\n" +"%s" +msgstr "" +"Está disponible la versión %s del Canal Homebrew, que sustituye a la versión " +"instalada (%s). ¿Quieres actualizar ahora?\n" +"\n" +"Notas sobre la versión:\n" +"\n" +"%s" + +#: source/update.c:262 +#, c-format +msgid "Downloading Update (%u kB)" +msgstr "Descargando (%u kB)" + +#: source/update.c:304 +msgid "Download failed" +msgstr "Error al descargar" + +#: source/widgets.c:16 +msgid "" +msgstr "" + +#~ msgid "Version: " +#~ msgstr "Versión: " + +#~ msgid "Coder: " +#~ msgstr "Autor: " + +#~ msgid "Deleting" +#~ msgstr "Eliminando" diff --git a/channel/channelapp/i18n/template.pot b/channel/channelapp/i18n/template.pot new file mode 100644 index 0000000..586afa9 --- /dev/null +++ b/channel/channelapp/i18n/template.pot @@ -0,0 +1,244 @@ +# SOME DESCRIPTIVE TITLE. +# Copyright (C) YEAR THE PACKAGE'S COPYRIGHT HOLDER +# This file is distributed under the same license as the PACKAGE package. +# FIRST AUTHOR , YEAR. +# +msgid "" +msgstr "" +"Project-Id-Version: PACKAGE VERSION\n" +"Report-Msgid-Bugs-To: \n" +"POT-Creation-Date: 2016-11-22 21:29+0900\n" +"PO-Revision-Date: YEAR-MO-DA HO:MI+ZONE\n" +"Last-Translator: NICKNAME \n" +"Language-Team: LANGUAGE\n" +"Language: \n" +"MIME-Version: 1.0\n" +"Content-Type: text/plain; charset=UTF-8\n" +"Content-Transfer-Encoding: 8bit\n" + +#: source/dialogs.c:53 +msgid "no description available" +msgstr "" + +# This and the following strings are dialog box "title bars" +#: source/dialogs.c:55 +msgid "Information" +msgstr "" + +#: source/dialogs.c:56 +msgid "Confirmation" +msgstr "" + +#: source/dialogs.c:57 +msgid "Warning" +msgstr "" + +#: source/dialogs.c:58 +msgid "Error" +msgstr "" + +# button text +#: source/dialogs.c:59 +msgid "Ok" +msgstr "" + +# button text +#: source/dialogs.c:60 +msgid "Cancel" +msgstr "" + +# button text +#: source/dialogs.c:61 +msgid "Yes" +msgstr "" + +# button text +#: source/dialogs.c:62 +msgid "No" +msgstr "" + +#: source/dialogs.c:63 +msgid "Delete" +msgstr "" + +# button text +#: source/dialogs.c:64 +msgid "Load" +msgstr "" + +# button text +#: source/dialogs.c:65 source/m_main.c:95 +msgid "Back" +msgstr "" + +#: source/dialogs.c:66 +msgid "Options" +msgstr "" + +#: source/dialogs.c:67 +msgid "Device:" +msgstr "" + +#: source/dialogs.c:68 +msgid "Internal SD Slot" +msgstr "" + +#: source/dialogs.c:69 +msgid "USB device" +msgstr "" + +#: source/dialogs.c:70 +msgid "SDGecko Slot A" +msgstr "" + +#: source/dialogs.c:71 +msgid "SDGecko Slot B" +msgstr "" + +#: source/dialogs.c:72 +msgid "Sort applications by:" +msgstr "" + +#: source/dialogs.c:73 +msgid "Name" +msgstr "" + +#: source/dialogs.c:74 +msgid "Date" +msgstr "" + +#: source/dialogs.c:110 +msgid " translation by " +msgstr "" + +#: source/dialogs.c:115 +msgid "Theme:" +msgstr "" + +#: source/dialogs.c:125 +#, c-format +msgid "Version: %s" +msgstr "" + +#: source/dialogs.c:126 +#, c-format +msgid "Author: %s" +msgstr "" + +#: source/loader.c:716 +msgid "Error while reading the application from the SD card" +msgstr "" + +#: source/loader.c:717 +msgid "Error while receiving the application" +msgstr "" + +#: source/loader.c:718 +msgid "Error uncompressing the received data" +msgstr "" + +#: source/loader.c:719 +msgid "This is not a valid Wii application" +msgstr "" + +#: source/loader.c:720 +msgid "This is not a usable ZIP file" +msgstr "" + +#: source/loader.c:721 +msgid "Not enough memory" +msgstr "" + +#: source/loader.c:763 +#, c-format +msgid "Loading %s" +msgstr "" + +#: source/loader.c:772 +#, c-format +msgid "Receiving over USBGecko" +msgstr "" + +#: source/loader.c:782 +#, c-format +msgid "Receiving from %s" +msgstr "" + +#: source/loader.c:968 +#, c-format +msgid "" +"Extract the received ZIP file?\n" +"%s of free space are required." +msgstr "" + +#: source/loader.c:969 +#, c-format +msgid "WARNING: Files in '%s' will be overwritten" +msgstr "" + +#: source/loader.c:970 +msgid "Error while extracting the ZIP file" +msgstr "" + +#: source/main.c:250 +msgid "Do you really want to delete this application?" +msgstr "" + +#: source/main.c:251 +#, c-format +msgid "Error deleting '%s'" +msgstr "" + +#: source/manage.c:518 +msgid "Extracting" +msgstr "" + +#: source/m_main.c:80 +msgid "Network not initialized" +msgstr "" + +#: source/m_main.c:81 +#, c-format +msgid "Your Wii's IP is %u.%u.%u.%u" +msgstr "" + +# button text +#: source/m_main.c:97 +msgid "About" +msgstr "" + +#: source/m_main.c:102 +msgid "Launch BootMii" +msgstr "" + +#: source/m_main.c:106 +msgid "Exit to System Menu" +msgstr "" + +#: source/m_main.c:109 +msgid "Shutdown" +msgstr "" + +#: source/update.c:232 +#, c-format +msgid "" +"An update to the Homebrew Channel (version %s, replacing the installed " +"version %s) is available for installation, do you want to update now?\n" +"\n" +"Release notes:\n" +"\n" +"%s" +msgstr "" + +#: source/update.c:262 +#, c-format +msgid "Downloading Update (%u kB)" +msgstr "" + +#: source/update.c:304 +msgid "Download failed" +msgstr "" + +#: source/widgets.c:16 +msgid "" +msgstr "" diff --git a/channel/channelapp/newrvl.ld b/channel/channelapp/newrvl.ld new file mode 100644 index 0000000..efd7ddb --- /dev/null +++ b/channel/channelapp/newrvl.ld @@ -0,0 +1,306 @@ +/* + * Linkscript for GC, automatically sets up DOL structures, + * generate ELF file and use objdump, or generate binary + * directly. + * + * PSUL doesn't seem to handle separate text and data sections correctly, + * that is why the text and data sections are merged in the header. + * + * If you want to experiment, it should be quite obvious how to + * change the header to list the data separately. + */ + +OUTPUT_FORMAT("elf32-powerpc", "elf32-powerpc", "elf32-powerpc"); +OUTPUT_ARCH(powerpc:common); +EXTERN(_start); +ENTRY(_start); + +PHDRS +{ + stub PT_LOAD FLAGS(5); + text PT_LOAD FLAGS(5); + data PT_LOAD FLAGS(6); +} + +SECTIONS +{ + /* stub is loaded at physical address 0x00003400 (though both 0x80003400 and 0x00003400 are equivalent for IOS) */ + /* This can also be used to load an arbitrary standalone stub at an arbitrary address in memory, for any purpose */ + /* Use -Wl,--section-start,.stub=0xADDRESS to change */ + . = 0x00003400; + + .stub : + { + KEEP(*(.stub)) + } :stub = 0 + + /* default base address */ + /* use -Wl,--section-start,.init=0xADDRESS to change */ + . = 0x80004000; + + /* Program */ + .init : + { + KEEP (*crt0.o(*.init)) + KEEP (*(.init)) + } :text = 0 + .plt : { *(.plt) } + .interp : { *(.interp) } + .hash : { *(.hash) } + .dynsym : { *(.dynsym) } + .dynstr : { *(.dynstr) } + .gnu.version : { *(.gnu.version) } + .gnu.version_d : { *(.gnu.version_d) } + .gnu.version_r : { *(.gnu.version_r) } + .rel.init : { *(.rel.init) } + .rela.init : { *(.rela.init) } + .rel.text : { *(.rel.text .rel.text.* .rel.gnu.linkonce.t.*) } + .rela.text : { *(.rela.text .rela.text.* .rela.gnu.linkonce.t.*) } + .rel.fini : { *(.rel.fini) } + .rela.fini : { *(.rela.fini) } + .rel.rodata : { *(.rel.rodata .rel.rodata.* .rel.gnu.linkonce.r.*) } + .rela.rodata : { *(.rela.rodata .rela.rodata.* .rela.gnu.linkonce.r.*) } + .rel.data : { *(.rel.data .rel.data.* .rel.gnu.linkonce.d.*) } + .rela.data : { *(.rela.data .rela.data.* .rela.gnu.linkonce.d.*) } + .rel.tdata : { *(.rel.tdata .rel.tdata.* .rel.gnu.linkonce.td.*) } + .rela.tdata : { *(.rela.tdata .rela.tdata.* .rela.gnu.linkonce.td.*) } + .rel.tbss : { *(.rel.tbss .rel.tbss.* .rel.gnu.linkonce.tb.*) } + .rela.tbss : { *(.rela.tbss .rela.tbss.* .rela.gnu.linkonce.tb.*) } + .rel.ctors : { *(.rel.ctors) } + .rela.ctors : { *(.rela.ctors) } + .rel.dtors : { *(.rel.dtors) } + .rela.dtors : { *(.rela.dtors) } + .rel.got : { *(.rel.got) } + .rela.got : { *(.rela.got) } + .rela.got1 : { *(.rela.got1) } + .rela.got2 : { *(.rela.got2) } + .rel.sdata : { *(.rel.sdata .rel.sdata.* .rel.gnu.linkonce.s.*) } + .rela.sdata : { *(.rela.sdata .rela.sdata.* .rela.gnu.linkonce.s.*) } + .rel.sbss : { *(.rel.sbss .rel.sbss.* .rel.gnu.linkonce.sb.*) } + .rela.sbss : { *(.rela.sbss .rela.sbss.* .rel.gnu.linkonce.sb.*) } + .rel.sdata2 : { *(.rel.sdata2 .rel.sdata2.* .rel.gnu.linkonce.s2.*) } + .rela.sdata2 : { *(.rela.sdata2 .rela.sdata2.* .rela.gnu.linkonce.s2.*) } + .rel.sbss2 : { *(.rel.sbss2 .rel.sbss2.* .rel.gnu.linkonce.sb2.*) } + .rela.sbss2 : { *(.rela.sbss2 .rela.sbss2.* .rela.gnu.linkonce.sb2.*) } + .rel.bss : { *(.rel.bss .rel.bss.* .rel.gnu.linkonce.b.*) } + .rela.bss : { *(.rela.bss .rela.bss.* .rela.gnu.linkonce.b.*) } + .rel.plt : { *(.rel.plt) } + .rela.plt : { *(.rela.plt) } + +.text : + { + *(.text) + *(.text.*) + /* .gnu.warning sections are handled specially by elf32.em. */ + *(.gnu.warning) + *(.gnu.linkonce.t.*) + . = ALIGN(32); /* REQUIRED. LD is flaky without it. */ + } = 0 + + .fini : + { + KEEP (*(.fini)) + . = ALIGN(32); /* REQUIRED. LD is flaky without it. */ + } = 0 + + PROVIDE (__etext = .); + PROVIDE (_etext = .); + PROVIDE (etext = .); + + .rodata : { *(.rodata) *(.rodata.*) *(.gnu.linkonce.r.*) } :data + .rodata1 : { + *(.rodata1) + . = ALIGN(32); + } :data + .sdata2 : { *(.sdata2) *(.sdata2.*) *(.gnu.linkonce.s2.*) } + .sbss2 : { *(.sbss2) *(.sbss2.*) *(.gnu.linkonce.sb2.*) } + /* Adjust the address for the data segment. We want to adjust up to + the same address within the page on the next page up. */ + /* Ensure the __preinit_array_start label is properly aligned. We + could instead move the label definition inside the section, but + the linker would then create the section even if it turns out to + be empty, which isn't pretty. */ + . = ALIGN(32 / 8); + PROVIDE (__preinit_array_start = .); + .preinit_array : { *(.preinit_array) } + PROVIDE (__preinit_array_end = .); + PROVIDE (__init_array_start = .); + .init_array : { *(.init_array) } + PROVIDE (__init_array_end = .); + PROVIDE (__fini_array_start = .); + .fini_array : { *(.fini_array) } + PROVIDE (__fini_array_end = .); + .data : + { + *(.data) + *(.data.*) + *(.gnu.linkonce.d.*) + SORT(CONSTRUCTORS) + . = ALIGN(32); /* REQUIRED. LD is flaky without it. */ + } + + .data1 : { *(.data1) } + .tdata : { *(.tdata .tdata.* .gnu.linkonce.td.*) } + .tbss : { *(.tbss .tbss.* .gnu.linkonce.tb.*) *(.tcommon) } + .eh_frame : { KEEP (*(.eh_frame)) } + .gcc_except_table : { *(.gcc_except_table) } + .fixup : { *(.fixup) } + .got1 : { *(.got1) } + .got2 : { *(.got2) } + .dynamic : { *(.dynamic) } + + .ctors : + { + /* gcc uses crtbegin.o to find the start of + the constructors, so we make sure it is + first. Because this is a wildcard, it + doesn't matter if the user does not + actually link against crtbegin.o; the + linker won't look for a file to match a + wildcard. The wildcard also means that it + doesn't matter which directory crtbegin.o + is in. */ + + KEEP (*crtbegin.o(.ctors)) + + /* We don't want to include the .ctor section from + from the crtend.o file until after the sorted ctors. + The .ctor section from the crtend file contains the + end of ctors marker and it must be last */ + + KEEP (*(EXCLUDE_FILE (*crtend.o ) .ctors)) + KEEP (*(SORT(.ctors.*))) + KEEP (*(.ctors)) + . = ALIGN(32); /* REQUIRED. LD is flaky without it. */ + } + + .dtors : + { + KEEP (*crtbegin.o(.dtors)) + KEEP (*(EXCLUDE_FILE (*crtend.o ) .dtors)) + KEEP (*(SORT(.dtors.*))) + KEEP (*(.dtors)) + . = ALIGN(32); /* REQUIRED. LD is flaky without it. */ + } + + .jcr : { KEEP (*(.jcr)) } + .got : { *(.got.plt) *(.got) } + + + /* We want the small data sections together, so single-instruction offsets + can access them all, and initialized data all before uninitialized, so + we can shorten the on-disk segment size. */ + + .sdata : + { + *(.sdata) + *(.sdata.*) + *(.gnu.linkonce.s.*) + . = ALIGN(32); /* REQUIRED. LD is flaky without it. */ + } + + _edata = .; + PROVIDE (edata = .); + + .sbss : + { + __sbss_start = .; + PROVIDE (__sbss_start = .); + PROVIDE (___sbss_start = .); + *(.dynsbss) + *(.sbss) + *(.sbss.*) + *(.gnu.linkonce.sb.*) + *(.scommon) + PROVIDE (__sbss_end = .); + PROVIDE (___sbss_end = .); + . = ALIGN(32); /* REQUIRED. LD is flaky without it. */ + __sbss_end = .; + } + + .bss : + { + __bss_start = .; + PROVIDE (__bss_start = .); + *(.dynbss) + *(.bss) + *(.bss.*) + *(.gnu.linkonce.b.*) + *(COMMON) + /* Align here to ensure that the .bss section occupies space up to + _end. Align after .bss to ensure correct alignment even if the + .bss section disappears because there are no input sections. */ + + . = ALIGN(32); + + PROVIDE (__bss_end = .); + __bss_end = .; + } + + _end = .; + PROVIDE(end = .); + /* Stabs debugging sections. */ + .stab 0 : { *(.stab) } + .stabstr 0 : { *(.stabstr) } + .stab.excl 0 : { *(.stab.excl) } + .stab.exclstr 0 : { *(.stab.exclstr) } + .stab.index 0 : { *(.stab.index) } + .stab.indexstr 0 : { *(.stab.indexstr) } + .comment 0 : { *(.comment) } + /* DWARF debug sections. + Symbols in the DWARF debugging sections are relative to the beginning + of the section so we begin them at 0. */ + /* DWARF 1 */ + .debug 0 : { *(.debug) } + .line 0 : { *(.line) } + /* GNU DWARF 1 extensions */ + .debug_srcinfo 0 : { *(.debug_srcinfo) } + .debug_sfnames 0 : { *(.debug_sfnames) } + /* DWARF 1.1 and DWARF 2 */ + .debug_aranges 0 : { *(.debug_aranges) } + .debug_pubnames 0 : { *(.debug_pubnames) } + /* DWARF 2 */ + .debug_info 0 : { *(.debug_info) } + .debug_abbrev 0 : { *(.debug_abbrev) } + .debug_line 0 : { *(.debug_line) } + .debug_frame 0 : { *(.debug_frame) } + .debug_str 0 : { *(.debug_str) } + .debug_loc 0 : { *(.debug_loc) } + .debug_macinfo 0 : { *(.debug_macinfo) } + /* SGI/MIPS DWARF 2 extensions */ + .debug_weaknames 0 : { *(.debug_weaknames) } + .debug_funcnames 0 : { *(.debug_funcnames) } + .debug_typenames 0 : { *(.debug_typenames) } + .debug_varnames 0 : { *(.debug_varnames) } + /* These must appear regardless of . */ +} + +__isIPL = 0; +__stack_addr = (__bss_start + SIZEOF(.bss) + 0x20000 + 7) & (-8); +__stack_end = (__bss_start + SIZEOF(.bss)); +__intrstack_addr = (__stack_addr + 0x4000); +__intrstack_end = (__stack_addr); +__Arena1Lo = (__intrstack_addr + 31) & (-32); +__Arena1Hi = (0x817FEFF0); +__Arena2Lo = (0x90002000); +__Arena2Hi = (0x933E0000); + +__gxregs = (__Arena1Hi + 31) & (-32); +__ipcbufferLo = (0x933e0000); +__ipcbufferHi = (0x93400000); + +/* for backward compatibility with old crt0 */ +PROVIDE (__stack = (0x817FEFF0)); + +PROVIDE(__isIPL = __isIPL); +PROVIDE(__stack_addr = __stack_addr); +PROVIDE(__stack_end = __stack_end); +PROVIDE(__intrstack_addr = __intrstack_addr); +PROVIDE(__intrstack_end = __intrstack_end); +PROVIDE(__Arena1Lo = __Arena1Lo); +PROVIDE(__Arena1Hi = __Arena1Hi); +PROVIDE(__Arena2Lo = __Arena2Lo); +PROVIDE(__Arena2Hi = __Arena2Hi); +PROVIDE(__ipcbufferLo = __ipcbufferLo); +PROVIDE(__ipcbufferHi = __ipcbufferHi); +PROVIDE(__gxregs = __gxregs); diff --git a/channel/channelapp/newrvl.spec b/channel/channelapp/newrvl.spec new file mode 100644 index 0000000..0f2b207 --- /dev/null +++ b/channel/channelapp/newrvl.spec @@ -0,0 +1,2 @@ +*link_start_rvl: + -T newrvl.ld%s diff --git a/channel/channelapp/source/appentry.c b/channel/channelapp/source/appentry.c new file mode 100644 index 0000000..d488ff4 --- /dev/null +++ b/channel/channelapp/source/appentry.c @@ -0,0 +1,713 @@ +#include +#include +#include +#include +#include +#include +#include + +#include +#include +#include +#include +#include +#include + +#include "../config.h" +#include "tex.h" +#include "panic.h" + +#include "appentry.h" + +typedef struct { + const char *name; + const DISC_INTERFACE *device; + bool last_status; +} device_t; + +static app_entry *entries_all[MAX_ENTRIES]; +static u32 entry_count_all = 0; + +app_entry *entries[MAX_ENTRIES]; +u32 entry_count = 0; + +const char *app_path = "/apps"; +const char *app_fn_boot_elf = "boot.elf"; +const char *app_fn_boot_dol = "boot.dol"; +const char *app_fn_theme = "theme.zip"; +const char *app_fn_meta = "meta.xml"; +const char *app_fn_icon = "icon.png"; + +static device_t devices[DEVICE_COUNT] = { + { "sd", &__io_wiisd, false }, + { "usb", &__io_usbstorage, false }, + { "carda", &__io_gcsda, false }, + { "cardb", &__io_gcsdb, false } +}; + +static int device_active = -1; +static int device_prefered = -1; +static app_filter current_filter = APP_FILTER_ALL; +static app_sort current_sort = APP_SORT_NAME; +static bool cmp_descending = false; +static bool cmp_release_date = false; + +static int cmp_app_entry (const void *p1, const void *p2) { + const app_entry *a1; + const app_entry *a2; + + if (cmp_descending) { + a1 = *((const app_entry **) p2); + a2 = *((const app_entry **) p1); + } else { + a1 = *((const app_entry **) p1); + a2 = *((const app_entry **) p2); + } + + if (!a1->meta && !a2->meta) + return stricmp (a1->dirname, a2->dirname); + + if (!a1->meta && a2->meta) + return 1; + + if (a1->meta && !a2->meta) + return -1; + + if (cmp_release_date) { + if (!a1->meta->release_date && !a2->meta->release_date) + return 0; + + if (!a1->meta->release_date && a2->meta->release_date) + return -1; + + if (a1->meta->release_date && !a2->meta->release_date) + return 1; + + if (a1->meta->release_date > a2->meta->release_date) + return 1; + + return -1; + } + + if (!a1->meta->name && !a2->meta->name) + return stricmp (a1->dirname, a2->dirname); + + if (!a1->meta->name && a2->meta->name) + return 1; + + if (a1->meta->name && !a2->meta->name) + return -1; + + return stricmp (a1->meta->name, a2->meta->name); +} + +static void app_entry_filter(app_filter filter) { + u32 i; + + for (i = 0; i < MAX_ENTRIES; ++i) + entries[i] = NULL; + + entry_count = 0; + + for (i = 0; i < MAX_ENTRIES; ++i) { + if (!entries_all[i]) + continue; + + switch (filter) { + case APP_FILTER_ICONSONLY: + if (!entries_all[i]->icon) + continue; + break; + + case APP_FILTER_DATEONLY: + if (!entries_all[i]->meta) //|| !entries_all[i]->meta->release_date) + continue; + break; + + default: + break; + } + + entries[entry_count] = entries_all[i]; + entry_count++; + } + + current_filter = filter; + + if (entry_count) + qsort(entries, entry_count, sizeof(app_entry *), cmp_app_entry); +} + +app_sort app_entry_get_sort(void) { + return current_sort; +} + +void app_entry_set_sort(app_sort sort) { + switch (sort) { + case APP_SORT_DATE: + cmp_descending = true; + cmp_release_date = true; + current_filter = APP_FILTER_DATEONLY; + current_sort = APP_SORT_DATE; + break; + + default: + cmp_descending = false; + cmp_release_date = false; + current_filter = APP_FILTER_ALL; + current_sort = APP_SORT_NAME; + break; + } + + if (settings.sort_order != current_sort) + settings.sort_order = current_sort; +} + +static app_entry *app_entry_load_single (const char *dirname) { + app_entry *entry; + app_entry_type type; + char tmp[MAXPATHLEN + 32]; + struct stat st; + + type = AET_BOOT_ELF; + sprintf(tmp, "%s/%s/%s", app_path, dirname, app_fn_boot_elf); + + if (stat(tmp, &st)) { + type = AET_BOOT_DOL; + sprintf(tmp, "%s/%s/%s", app_path, dirname, app_fn_boot_dol); + + if (stat(tmp, &st)) { + type = AET_THEME; + sprintf(tmp, "%s/%s/%s", app_path, dirname, app_fn_theme); + + if (stat(tmp, &st)) + return NULL; + + if (st.st_size > MAX_THEME_ZIP_SIZE) + return NULL; + } + } + + if (!st.st_size || st.st_size > LD_MAX_SIZE) + return NULL; + + entry = (app_entry *) pmalloc(sizeof(app_entry)); + + entry->type = type; + entry->size = st.st_size; + entry->dirname = pstrdup(dirname); + entry->icon = NULL; + entry->meta = NULL; + + sprintf(tmp, "%s/%s/%s", app_path, dirname, app_fn_meta); + if (stat(tmp, &st) == 0) + entry->meta = meta_parse(tmp); + + sprintf(tmp, "%s/%s/%s", app_path, dirname, app_fn_icon); + if (stat(tmp, &st) == 0) + entry->icon = tex_from_png_file(tmp, &st, APP_ENTRY_ICON_WIDTH, + APP_ENTRY_ICON_HEIGHT); + + return entry; +} + +void app_entries_free(void) { + u32 i; + + for (i = 0; i < MAX_ENTRIES; ++i) { + entries[i] = NULL; + + if (!entries_all[i]) + continue; + + free (entries_all[i]->dirname); + tex_free (entries_all[i]->icon); + meta_free (entries_all[i]->meta); + + free(entries_all[i]); + entries_all[i] = NULL; + } + + entry_count_all = 0; + entry_count = 0; +} + +static void app_entry_load_all (void) { + app_entry *entry; + DIR *d; + struct dirent *de; + + app_entries_free (); + + d = opendir(app_path); + if (!d) + return; + + while ((de = readdir(d))) { + // ignore dotfiles / dotdirs + if (de->d_name[0] == '\0' || de->d_name[0] == '.') + continue; + + // All apps have their own dir + if (de->d_type != DT_DIR) + continue; + + entry = app_entry_load_single(de->d_name); + + if (!entry) + continue; + + entries_all[entry_count_all] = entry; + + entry_count_all++; + + if (entry_count_all >= MAX_ENTRIES) + break; + } + + closedir(d); + + app_entry_filter(current_filter); +} + +bool app_entry_remove(app_entry *app) { + u32 i; + bool res = false; + + for (i = 0; i < MAX_ENTRIES; ++i) + if (entries_all[i] == app) { + free(entries_all[i]->dirname); + tex_free(entries_all[i]->icon); + meta_free(entries_all[i]->meta); + + free(entries_all[i]); + entries_all[i] = NULL; + entry_count_all--; + + res = true; + + break; + } + + if (res) + app_entry_filter(current_filter); + + return res; +} + +app_entry *app_entry_add(const char *dirname) { + app_entry *entry = NULL; + u32 i; + bool filter = false; + + gprintf("adding entry '%s'\n", dirname); + + for (i = 0; i < MAX_ENTRIES; ++i) { + if (entries_all[i]) { + if (!strcasecmp(entries_all[i]->dirname, dirname)) { + gprintf("removing old entry '%s'\n", entries_all[i]->dirname); + + free(entries_all[i]->dirname); + tex_free(entries_all[i]->icon); + meta_free(entries_all[i]->meta); + + free(entries_all[i]); + entries_all[i] = NULL; + entry_count_all--; + + filter = true; + + break; + } + } + } + + if (entry_count_all >= MAX_ENTRIES) + goto exit; + + entry = app_entry_load_single(dirname); + + if (!entry) + goto exit; + + for (i = 0; i < MAX_ENTRIES; ++i) + if (!entries_all[i]) { + entries_all[i] = entry; + entry_count_all++; + + filter = true; + + break; + } + +exit: + if (filter) + app_entry_filter(current_filter); + + return entry; +} + +static bool _mount(int index) { + devices[index].last_status = fatMountSimple(devices[index].name, + devices[index].device); + + if (!devices[index].last_status) { + devices[index].device->shutdown(); + + return false; + } + + return true; +} + +typedef enum { + AE_CMD_IDLE = 0, + AE_CMD_EXIT, + AE_CMD_SCAN, + AE_CMD_POLLSTATUS +} ae_cmd; + +typedef struct { + bool running; + ae_cmd cmd; + ae_action action; + bool status[DEVICE_COUNT]; + bool umount; + int mount; + bool loading; + mutex_t cmutex; + cond_t cond; +} ae_args; + +static lwp_t ae_thread; +static u8 ae_stack[APPENTRY_THREAD_STACKSIZE] ATTRIBUTE_ALIGN (32); +static ae_args ta_ae; + +static void *ae_func (void *arg) { + ae_args *ta = (ae_args *) arg; + int i; + char cwd[16]; + + ta->running = true; + + for (i = 0; i < DEVICE_COUNT; ++i) + ta_ae.status[i] = false; + + LWP_MutexLock (ta->cmutex); + + while (true) { + + ta->cmd = AE_CMD_IDLE; + LWP_CondWait(ta->cond, ta->cmutex); + + if (ta->cmd == AE_CMD_EXIT) + break; + + switch (ta->cmd) { + case AE_CMD_SCAN: + + if (device_active >= 0) { + if (!ta->umount && devices[device_active].device->isInserted()) + continue; + + gprintf("device lost: %s\n", devices[device_active].name); + + ta->umount = false; + + fatUnmount(devices[device_active].name); + devices[device_active].device->shutdown(); + devices[device_active].last_status = false; + device_active = -1; + + ta->action = AE_ACT_REMOVE; + + continue; + } + + if (ta->mount >= 0) { + if (_mount(ta->mount)) + device_active = ta->mount; + ta->mount = -1; + } + + if ((device_active < 0) && (device_prefered >= 0)) { + if (_mount(device_prefered)) + device_active = device_prefered; + } + + if (device_active < 0) { + for (i = 0; i < DEVICE_COUNT; ++i) + if (_mount(i)) { + device_active = i; + break; + } + } + + if (device_active >= 0) { + gprintf("device mounted: %s\n", devices[device_active].name); + + ta->loading = true; + + strcpy(cwd, devices[device_active].name); + strcat(cwd, ":/"); + + gprintf("chdir to '%s'\n", cwd); + if (chdir(cwd)) + gprintf("chdir failed: %d\n", errno); + + app_entry_load_all(); + + ta->loading = false; + + ta->action = AE_ACT_ADD; + } + + continue; + + case AE_CMD_POLLSTATUS: + for (i = 0; i < DEVICE_COUNT; ++i) { + if (i == device_active) { + ta->status[i] = devices[i].last_status = + devices[i].device->isInserted(); + + if (!ta->umount && !devices[i].last_status) + ta->umount = true; + + continue; + } + + if (devices[i].last_status) { + if (!devices[i].device->isInserted()) { + ta->status[i] = devices[i].last_status = false; + devices[i].device->shutdown(); + } + + continue; + } + + if (!devices[i].device->startup()) { + ta->status[i] = devices[i].last_status = false; + continue; + } + + ta->status[i] = devices[i].last_status = + devices[i].device->isInserted(); + + if (!devices[i].last_status) + devices[i].device->shutdown(); + } + + continue; + + default: + break; + } + } + + LWP_MutexUnlock (ta->cmutex); + + gprintf("app entry thread done\n"); + + ta->running = false; + + return NULL; +} + +void app_entry_init(void) { + s32 res; + + memset(&entries_all, 0, sizeof(app_entry *) * MAX_ENTRIES); + memset(&entries, 0, sizeof(app_entry *) * MAX_ENTRIES); + + gprintf("starting app entry thread\n"); + + ta_ae.cmd = AE_CMD_IDLE; + ta_ae.action = AE_ACT_NONE; + ta_ae.umount = false; + ta_ae.mount = -1; + ta_ae.loading = false; + + res = LWP_MutexInit (&ta_ae.cmutex, false); + + if (res) { + gprintf ("error creating cmutex: %ld\n", res); + return; + } + + res = LWP_CondInit (&ta_ae.cond); + + if (res) { + gprintf ("error creating cond: %ld\n", res); + return; + } + + memset(&ae_stack, 0, APPENTRY_THREAD_STACKSIZE); + res = LWP_CreateThread(&ae_thread, ae_func, &ta_ae, ae_stack, + APPENTRY_THREAD_STACKSIZE, APPENTRY_THREAD_PRIO); + + if (res) { + gprintf("error creating thread: %ld\n", res); + } +} + +void app_entry_deinit (void) { + u8 i; + + if (ta_ae.running) { + gprintf ("stopping app entry thread\n"); + + for (i = 0; i < 25; ++i) { + if (LWP_MutexTryLock (ta_ae.cmutex) == 0) { + break; + } + usleep (20 * 1000); + } + if (i < 25) { + gprintf ("sending app entry thread the exit cmd\n"); + ta_ae.cmd = AE_CMD_EXIT; + LWP_SetThreadPriority (ae_thread, LWP_PRIO_HIGHEST); + LWP_CondBroadcast (ta_ae.cond); + LWP_MutexUnlock (ta_ae.cmutex); + LWP_JoinThread(ae_thread, NULL); + LWP_CondDestroy (ta_ae.cond); + LWP_MutexDestroy (ta_ae.cmutex); + app_entries_free (); + } else { + gprintf("app entry thread didn't shutdown gracefully!\n"); + } + + if (device_active >= 0) { + fatUnmount(devices[device_active].name); + devices[device_active].device->shutdown(); + } + + USB_Deinitialize(); + } + +} + +void app_entry_scan(void) { + if (!ta_ae.running) + return; + + if (LWP_MutexTryLock(ta_ae.cmutex) != 0) + return; + + ta_ae.cmd = AE_CMD_SCAN; + LWP_CondBroadcast(ta_ae.cond); + LWP_MutexUnlock(ta_ae.cmutex); +} + +ae_action app_entry_action(void) { + ae_action res; + + if (!ta_ae.running) + return AE_ACT_NONE; + + if (LWP_MutexTryLock(ta_ae.cmutex) != 0) + return AE_ACT_NONE; + + res = ta_ae.action; + ta_ae.action = AE_ACT_NONE; + LWP_MutexUnlock(ta_ae.cmutex); + + return res; +} + +void app_entry_poll_status(bool reset) { + if (!ta_ae.running) + return; + + if (LWP_MutexTryLock(ta_ae.cmutex) != 0) + return; + + ta_ae.cmd = AE_CMD_POLLSTATUS; + LWP_CondBroadcast(ta_ae.cond); + LWP_MutexUnlock(ta_ae.cmutex); +} + +int app_entry_get_status(bool *status) { + u8 i; + + if (status) + for (i = 0; i < DEVICE_COUNT; ++i) + status[i] = ta_ae.status[i]; + + return device_active; +} + +void app_entry_set_prefered(int device) { + if (device < 0) { + device_prefered = -1; + return; + } + + if (device > DEVICE_COUNT - 1) { + device_prefered = -1; + return; + } + + device_prefered = device; + + if (settings.device != device_prefered) + settings.device = device_prefered; +} + +void app_entry_set_device(int device) { + if (device < 0) + return; + + if (device > DEVICE_COUNT - 1) + return; + + ta_ae.umount = true; + ta_ae.mount = device; +} + +bool app_entry_get_path(char *buf) { + if (device_active < 0) + return false; + + strcpy(buf, devices[device_active].name); + return true; +} + +bool app_entry_get_filename(char *buf, app_entry *app) { + if (device_active < 0) + return false; + + sprintf(buf, "%s:%s/%s/", devices[device_active].name, + app_path, app->dirname); + + switch(app->type) { + case AET_BOOT_ELF: + strcat(buf, app_fn_boot_elf); + return true; + case AET_BOOT_DOL: + strcat(buf, app_fn_boot_dol); + return true; + case AET_THEME: + strcat(buf, app_fn_theme); + return true; + } + + return false; +} + +app_entry *app_entry_find(char *dirname) { + u32 i; + + for (i = 0; i < entry_count; ++i) + if (!strcasecmp(entries[i]->dirname, dirname)) + return entries[i]; + + return NULL; +} + +bool app_entry_is_loading(void) { + if (!ta_ae.running) + return false; + + return ta_ae.loading; +} + diff --git a/channel/channelapp/source/appentry.h b/channel/channelapp/source/appentry.h new file mode 100644 index 0000000..9adc970 --- /dev/null +++ b/channel/channelapp/source/appentry.h @@ -0,0 +1,80 @@ +#ifndef _APPENTRY_H_ +#define _APPENTRY_H_ + +#include + +#include "gfx.h" +#include "xml.h" + +#define DEVICE_COUNT 4 +#define MAX_THEME_ZIP_SIZE (20 * 1024 * 1024) + +typedef enum { + AET_BOOT_ELF = 0, + AET_BOOT_DOL, + AET_THEME +} app_entry_type; + +typedef struct { + app_entry_type type; + u32 size; + char *dirname; + gfx_entity *icon; + meta_info *meta; +} app_entry; + +typedef enum { + AE_ACT_NONE = 0, + AE_ACT_REMOVE, + AE_ACT_ADD +} ae_action; + +typedef enum { + APP_FILTER_ALL = 0, + APP_FILTER_ICONSONLY, + APP_FILTER_DATEONLY +} app_filter; + +typedef enum { + APP_SORT_NAME = 0, + APP_SORT_DATE +} app_sort; + +extern const char *app_path; +extern const char *app_fn_boot_elf; +extern const char *app_fn_boot_dol; +extern const char *app_fn_theme; +extern const char *app_fn_meta; +extern const char *app_fn_icon; + +extern app_entry *entries[MAX_ENTRIES]; +extern u32 entry_count; + +void app_entry_init (void); +void app_entry_deinit (void); + +void app_entries_free(void); + +void app_entry_scan(void); +ae_action app_entry_action(void); + +void app_entry_poll_status(bool reset); +int app_entry_get_status(bool *status); + +void app_entry_set_prefered(int device); +void app_entry_set_device(int device); + +bool app_entry_get_path(char *buf); +bool app_entry_get_filename(char *buf, app_entry *app); +app_entry *app_entry_find(char *dirname); + +app_sort app_entry_get_sort(void); +void app_entry_set_sort(app_sort sort); + +app_entry *app_entry_add(const char *dirname); +bool app_entry_remove(app_entry *app); + +bool app_entry_is_loading(void); + +#endif + diff --git a/channel/channelapp/source/asm.h b/channel/channelapp/source/asm.h new file mode 100644 index 0000000..8e335b4 --- /dev/null +++ b/channel/channelapp/source/asm.h @@ -0,0 +1,340 @@ +// this file was taken from libogc, see http://www.devkitpro.org/ + +#ifndef __ASM_H__ +#define __ASM_H__ + +#ifdef _LANGUAGE_ASSEMBLY +/* Condition Register Bit Fields */ + +#define cr0 0 +#define cr1 1 +#define cr2 2 +#define cr3 3 +#define cr4 4 +#define cr5 5 +#define cr6 6 +#define cr7 7 + + +/* General Purpose Registers (GPRs) */ + +#define r0 0 +#define r1 1 +#define sp 1 +#define r2 2 +#define toc 2 +#define r3 3 +#define r4 4 +#define r5 5 +#define r6 6 +#define r7 7 +#define r8 8 +#define r9 9 +#define r10 10 +#define r11 11 +#define r12 12 +#define r13 13 +#define r14 14 +#define r15 15 +#define r16 16 +#define r17 17 +#define r18 18 +#define r19 19 +#define r20 20 +#define r21 21 +#define r22 22 +#define r23 23 +#define r24 24 +#define r25 25 +#define r26 26 +#define r27 27 +#define r28 28 +#define r29 29 +#define r30 30 +#define r31 31 + + +/* Floating Point Registers (FPRs) */ + +#define fr0 0 +#define fr1 1 +#define fr2 2 +#define fr3 3 +#define fr4 4 +#define fr5 5 +#define fr6 6 +#define fr7 7 +#define fr8 8 +#define fr9 9 +#define fr10 10 +#define fr11 11 +#define fr12 12 +#define fr13 13 +#define fr14 14 +#define fr15 15 +#define fr16 16 +#define fr17 17 +#define fr18 18 +#define fr19 19 +#define fr20 20 +#define fr21 21 +#define fr22 22 +#define fr23 23 +#define fr24 24 +#define fr25 25 +#define fr26 26 +#define fr27 27 +#define fr28 28 +#define fr29 29 +#define fr30 30 +#define fr31 31 + +#define vr0 0 +#define vr1 1 +#define vr2 2 +#define vr3 3 +#define vr4 4 +#define vr5 5 +#define vr6 6 +#define vr7 7 +#define vr8 8 +#define vr9 9 +#define vr10 10 +#define vr11 11 +#define vr12 12 +#define vr13 13 +#define vr14 14 +#define vr15 15 +#define vr16 16 +#define vr17 17 +#define vr18 18 +#define vr19 19 +#define vr20 20 +#define vr21 21 +#define vr22 22 +#define vr23 23 +#define vr24 24 +#define vr25 25 +#define vr26 26 +#define vr27 27 +#define vr28 28 +#define vr29 29 +#define vr30 30 +#define vr31 31 + +#endif //_LANGUAGE_ASSEMBLY + +#define SPRG0 272 +#define SPRG1 273 +#define SPRG2 274 +#define SPRG3 275 + +#define PMC1 953 +#define PMC2 954 +#define PMC3 957 +#define PMC4 958 + +#define MMCR0 952 +#define MMCR1 956 + + +#define LINK_REGISTER_CALLEE_UPDATE_ROOM 4 +#define EXCEPTION_NUMBER 8 +#define SRR0_OFFSET 12 +#define SRR1_OFFSET 16 +#define GPR0_OFFSET 20 +#define GPR1_OFFSET 24 +#define GPR2_OFFSET 28 +#define GPR3_OFFSET 32 +#define GPR4_OFFSET 36 +#define GPR5_OFFSET 40 +#define GPR6_OFFSET 44 +#define GPR7_OFFSET 48 +#define GPR8_OFFSET 52 +#define GPR9_OFFSET 56 +#define GPR10_OFFSET 60 +#define GPR11_OFFSET 64 +#define GPR12_OFFSET 68 +#define GPR13_OFFSET 72 +#define GPR14_OFFSET 76 +#define GPR15_OFFSET 80 +#define GPR16_OFFSET 84 +#define GPR17_OFFSET 88 +#define GPR18_OFFSET 92 +#define GPR19_OFFSET 96 +#define GPR20_OFFSET 100 +#define GPR21_OFFSET 104 +#define GPR22_OFFSET 108 +#define GPR23_OFFSET 112 +#define GPR24_OFFSET 116 +#define GPR25_OFFSET 120 +#define GPR26_OFFSET 124 +#define GPR27_OFFSET 128 +#define GPR28_OFFSET 132 +#define GPR29_OFFSET 136 +#define GPR30_OFFSET 140 +#define GPR31_OFFSET 144 + +#define GQR0_OFFSET 148 +#define GQR1_OFFSET 152 +#define GQR2_OFFSET 156 +#define GQR3_OFFSET 160 +#define GQR4_OFFSET 164 +#define GQR5_OFFSET 168 +#define GQR6_OFFSET 172 +#define GQR7_OFFSET 176 + +#define CR_OFFSET 180 +#define LR_OFFSET 184 +#define CTR_OFFSET 188 +#define XER_OFFSET 192 +#define MSR_OFFSET 196 +#define DAR_OFFSET 200 + +#define STATE_OFFSET 204 +#define MODE_OFFSET 206 + +#define FPR0_OFFSET 208 +#define FPR1_OFFSET 216 +#define FPR2_OFFSET 224 +#define FPR3_OFFSET 232 +#define FPR4_OFFSET 240 +#define FPR5_OFFSET 248 +#define FPR6_OFFSET 256 +#define FPR7_OFFSET 264 +#define FPR8_OFFSET 272 +#define FPR9_OFFSET 280 +#define FPR10_OFFSET 288 +#define FPR11_OFFSET 296 +#define FPR12_OFFSET 304 +#define FPR13_OFFSET 312 +#define FPR14_OFFSET 320 +#define FPR15_OFFSET 328 +#define FPR16_OFFSET 336 +#define FPR17_OFFSET 344 +#define FPR18_OFFSET 352 +#define FPR19_OFFSET 360 +#define FPR20_OFFSET 368 +#define FPR21_OFFSET 376 +#define FPR22_OFFSET 384 +#define FPR23_OFFSET 392 +#define FPR24_OFFSET 400 +#define FPR25_OFFSET 408 +#define FPR26_OFFSET 416 +#define FPR27_OFFSET 424 +#define FPR28_OFFSET 432 +#define FPR29_OFFSET 440 +#define FPR30_OFFSET 448 +#define FPR31_OFFSET 456 + +#define FPSCR_OFFSET 464 + +#define PSR0_OFFSET 472 +#define PSR1_OFFSET 480 +#define PSR2_OFFSET 488 +#define PSR3_OFFSET 496 +#define PSR4_OFFSET 504 +#define PSR5_OFFSET 512 +#define PSR6_OFFSET 520 +#define PSR7_OFFSET 528 +#define PSR8_OFFSET 536 +#define PSR9_OFFSET 544 +#define PSR10_OFFSET 552 +#define PSR11_OFFSET 560 +#define PSR12_OFFSET 568 +#define PSR13_OFFSET 576 +#define PSR14_OFFSET 584 +#define PSR15_OFFSET 592 +#define PSR16_OFFSET 600 +#define PSR17_OFFSET 608 +#define PSR18_OFFSET 616 +#define PSR19_OFFSET 624 +#define PSR20_OFFSET 632 +#define PSR21_OFFSET 640 +#define PSR22_OFFSET 648 +#define PSR23_OFFSET 656 +#define PSR24_OFFSET 664 +#define PSR25_OFFSET 672 +#define PSR26_OFFSET 680 +#define PSR27_OFFSET 688 +#define PSR28_OFFSET 696 +#define PSR29_OFFSET 704 +#define PSR30_OFFSET 712 +#define PSR31_OFFSET 720 +/* + * maintain the EABI requested 8 bytes aligment + * As SVR4 ABI requires 16, make it 16 (as some + * exception may need more registers to be processed...) + */ +#define EXCEPTION_FRAME_END 728 + +#define IBAT0U 528 +#define IBAT0L 529 +#define IBAT1U 530 +#define IBAT1L 531 +#define IBAT2U 532 +#define IBAT2L 533 +#define IBAT3U 534 +#define IBAT3L 535 +#define IBAT4U 560 +#define IBAT4L 561 +#define IBAT5U 562 +#define IBAT5L 563 +#define IBAT6U 564 +#define IBAT6L 565 +#define IBAT7U 566 +#define IBAT7L 567 + +#define DBAT0U 536 +#define DBAT0L 537 +#define DBAT1U 538 +#define DBAT1L 538 +#define DBAT2U 540 +#define DBAT2L 541 +#define DBAT3U 542 +#define DBAT3L 543 +#define DBAT4U 568 +#define DBAT4L 569 +#define DBAT5U 570 +#define DBAT5L 571 +#define DBAT6U 572 +#define DBAT6L 573 +#define DBAT7U 574 +#define DBAT7L 575 + +#define HID0 1008 +#define HID1 1009 +#define HID2 920 +#define HID4 1011 + +#define GQR0 912 +#define GQR1 913 +#define GQR2 914 +#define GQR3 915 +#define GQR4 916 +#define GQR5 917 +#define GQR6 918 +#define GQR7 919 + +#define L2CR 1017 + +#define WPAR 921 + +#define DMAU 922 +#define DMAL 923 + +#define MSR_RI 0x00000002 +#define MSR_DR 0x00000010 +#define MSR_IR 0x00000020 +#define MSR_IP 0x00000040 +#define MSR_SE 0x00000400 +#define MSR_ME 0x00001000 +#define MSR_FP 0x00002000 +#define MSR_POW 0x00004000 +#define MSR_EE 0x00008000 + +#define PPC_ALIGNMENT 8 + +#define PPC_CACHE_ALIGNMENT 32 + +#endif //__ASM_H__ diff --git a/channel/channelapp/source/blob.c b/channel/channelapp/source/blob.c new file mode 100644 index 0000000..8d2fbdf --- /dev/null +++ b/channel/channelapp/source/blob.c @@ -0,0 +1,84 @@ +#include +#include + +#include "../config.h" +#include "debug.h" +#include "panic.h" +#include "blob.h" + +#define MAX_BLOBS 8 +#define BLOB_MINSLACK (512*1024) + +typedef struct { + void *the_blob; + size_t blob_size; + void *old_arena2hi; +} blob_t; + +blob_t blobs[MAX_BLOBS]; +int num_blobs = 0; + +// supports only stack-type allocs (free last alloced) +void *blob_alloc(size_t size) +{ + u32 level; + u32 addr; + void *old_arena2hi; + + _CPU_ISR_Disable(level); + if (num_blobs >= MAX_BLOBS) { + _CPU_ISR_Restore(level); + gprintf("too many blobs\n"); + panic(); + } + + old_arena2hi = SYS_GetArena2Hi(); + addr = (((u32)old_arena2hi) - size) & (~0x1f); + + if (addr < (BLOB_MINSLACK + (u32)SYS_GetArena2Lo())) { + _CPU_ISR_Restore(level); + return NULL; + } + + blobs[num_blobs].old_arena2hi = old_arena2hi; + blobs[num_blobs].the_blob = (void*)addr; + blobs[num_blobs].blob_size = size; + num_blobs++; + + SYS_SetArena2Hi((void*)addr); + _CPU_ISR_Restore(level); + gprintf("allocated blob size %d at 0x%08lx\n", size, addr); + return (void*)addr; +} + +void blob_free(void *p) +{ + u32 level; + if (!p) + return; + + _CPU_ISR_Disable(level); + + if (num_blobs == 0) { + _CPU_ISR_Restore(level); + gprintf("blob_free with no blobs\n"); + panic(); + } + + num_blobs--; + if (p != blobs[num_blobs].the_blob) { + _CPU_ISR_Restore(level); + gprintf("mismatched blob_free (%p != %p)\n", p, blobs[num_blobs].the_blob); + panic(); + } + if (SYS_GetArena2Hi() != p) { + _CPU_ISR_Restore(level); + gprintf("someone else used MEM2 (%p != %p)\n", p, SYS_GetArena2Hi()); + panic(); + } + + SYS_SetArena2Hi(blobs[num_blobs].old_arena2hi); + _CPU_ISR_Restore(level); + gprintf("freed blob size %d at %p\n", blobs[num_blobs].blob_size, p); +} + diff --git a/channel/channelapp/source/blob.h b/channel/channelapp/source/blob.h new file mode 100644 index 0000000..f355a40 --- /dev/null +++ b/channel/channelapp/source/blob.h @@ -0,0 +1,9 @@ +#ifndef _BLOB_H_ +#define _BLOB_H_ + +#include + +void *blob_alloc(size_t size); +void blob_free(void *p); + +#endif diff --git a/channel/channelapp/source/browser.c b/channel/channelapp/source/browser.c new file mode 100644 index 0000000..abdaad2 --- /dev/null +++ b/channel/channelapp/source/browser.c @@ -0,0 +1,330 @@ +#include +#include +#include +#include + +#include +#include + +#include "../config.h" +#include "theme.h" +#include "view.h" +#include "controls.h" +#include "i18n.h" + +#include "browser.h" + +#define AE_OFFSET 4 +#define TRANS_STEPS 20 +#define MAX_COLUMNS 4 +#define ROWS 5 + +static bool first_set = true; +static u16 top_offset = 0; + +static view *v_browser = NULL; + +static int columns_current = 1; +static int columns_new = 1; + +static bool inited_widgets = false; + +view * browser_init(void) { + v_browser = view_new (AE_OFFSET + (MAX_COLUMNS * ROWS * 2), NULL, + 0, 0, 0, 0); + browser_theme_reinit(); + + return v_browser; +} + +void browser_deinit(void) { + view_free(v_browser); + inited_widgets = false; + v_browser = NULL; +} + +void browser_theme_reinit(void) { + int i; + if (inited_widgets) + for (i = 0; i < v_browser->widget_count; ++i) + widget_free(&v_browser->widgets[i]); + + widget_image (&v_browser->widgets[0], 24, 192, 0, + theme_gfx[THEME_ARROW_LEFT], NULL, true, + theme_gfx[THEME_ARROW_LEFT_FOCUS]); + widget_image (&v_browser->widgets[1], + view_width - 24 - theme_gfx[THEME_ARROW_RIGHT]->w, 192, 0, + theme_gfx[THEME_ARROW_RIGHT], NULL, true, + theme_gfx[THEME_ARROW_RIGHT_FOCUS]); + widget_image (&v_browser->widgets[2], + view_width - 32 - theme_gfx[THEME_GECKO_ACTIVE]->w - + theme_gfx[THEME_LAN_ACTIVE]->w, 412, 0, + theme_gfx[THEME_GECKO_ACTIVE], theme_gfx[THEME_GECKO], + false, NULL); + widget_image (&v_browser->widgets[3], + view_width - 32 - theme_gfx[THEME_GECKO_ACTIVE]->w, 412, 0, + theme_gfx[THEME_LAN_ACTIVE], theme_gfx[THEME_LAN], + false, NULL); + widget_set_flag (&v_browser->widgets[2], WF_ENABLED, false); + widget_set_flag (&v_browser->widgets[3], WF_ENABLED, false); + + widget_set_flag (&v_browser->widgets[0], WF_VISIBLE, false); + widget_set_flag (&v_browser->widgets[1], WF_VISIBLE, false); + + inited_widgets = true; +} + +static void browser_set_top_offset(const app_entry *app) { + u32 i; + + if (!app || (entry_count < 1)) { + top_offset = 0; + return; + } + + for (i = 0; i < entry_count; ++i) + if (entries[i] == app) { + top_offset = i; + break; + } + + top_offset /= columns_new * ROWS; + top_offset *= columns_new * ROWS; + + if (top_offset > entry_count - 1) + top_offset -= columns_new * ROWS; +} +void browser_gen_view(browser_action action, const app_entry *app) { + bool less, more; + app_entry *entry; + s8 focus; + + u32 i, j; + u16 y; + + u8 o1, o2; + s16 xal, xar, x1, x2; + float xm; + float vala = 0; + float val1[MAX_COLUMNS * ROWS]; + float val2[MAX_COLUMNS * ROWS]; + float stepa = M_TWOPI / TRANS_STEPS; + float step = M_PI / (TRANS_STEPS - 6); + s16 s; + float f1, f2; + + switch (action) { + case BA_REMOVE: + break; + + case BA_ADD: + case BA_REFRESH: + browser_set_top_offset(app); + break; + + case BA_NEXT: + if (entry_count <= top_offset + (columns_new * ROWS)) + return; + top_offset += columns_current * ROWS; + break; + + case BA_PREV: + if (top_offset < 1) + return; + if (top_offset < columns_current * ROWS) + return; + top_offset -= columns_current * ROWS; + break; + } + + if (action == BA_REMOVE) { + less = false; + more = false; + } else { + less = top_offset > 0; + more = entry_count > top_offset + (columns_new * ROWS); + } + + memset(val1, 0, sizeof(float) * MAX_COLUMNS * ROWS); + memset(val2, 0, sizeof(float) * MAX_COLUMNS * ROWS); + + if (first_set) { + o1 = AE_OFFSET; + o2 = AE_OFFSET + (MAX_COLUMNS * ROWS); + } else { + o1 = AE_OFFSET + (MAX_COLUMNS * ROWS); + o2 = AE_OFFSET; + } + + first_set = !first_set; + focus = o2; + + xal = v_browser->widgets[0].coords.x; + xar = v_browser->widgets[1].coords.x; + + if (columns_current == 1) + x1 = (view_width - theme_gfx[THEME_APP_ENTRY]->w) / 2; + else + x1 = (view_width - (theme_gfx[THEME_GRID_APP_ENTRY]->w * + columns_current)) / 2; + + if (columns_new == 1) + x2 = (view_width - theme_gfx[THEME_APP_ENTRY]->w) / 2; + else + x2 = (view_width - (theme_gfx[THEME_GRID_APP_ENTRY]->w * + columns_new)) / 2; + + if (action == BA_PREV) { + xm = view_width / 2; + x2 = -view_width + x2; + } else { + xm = -view_width / 2; + x2 = view_width + x2; + } + + y = 64; + + for (i = 0; i < (MAX_COLUMNS * ROWS); ++i) + widget_free(&v_browser->widgets[o2 + i]); + + if (action != BA_REMOVE) + for (i = 0; i < (columns_new * ROWS); ++i) { + if (entry_count > top_offset + i) { + entry = entries[top_offset + i]; + + if (entry && (entry == app)) + focus += i; + + if (columns_new == 1) + widget_app_entry(&v_browser->widgets[o2 + i], + x2, y, 0, entry); + else + widget_grid_app_entry(&v_browser->widgets[o2 + i], + x2 + ((i % columns_new) * + theme_gfx[THEME_GRID_APP_ENTRY]->w), + y, 0, entry); + } + + if (((i+1) % columns_new) == 0) + y += theme_gfx[THEME_APP_ENTRY]->h; + } + + for (i = 0; i < TRANS_STEPS; ++i) { + vala += stepa; + s = roundf (156.0 * (cosf (vala) - 1)); + + // adjust L/R button positions + v_browser->widgets[0].coords.x = xal + s; + v_browser->widgets[1].coords.x = xar - s; + + for (j = 0; j < MAX_COLUMNS * ROWS; ++j) { + if ((i > j / columns_current) && + (i < TRANS_STEPS - (ROWS - j / columns_current))) + val1[j] += step; + if ((i > j / columns_new) && + (i < TRANS_STEPS - (ROWS - j / columns_new))) + val2[j] += step; + + f1 = roundf (xm * (cosf (val1[j]) - 1)); + f2 = roundf (xm * (cosf (val2[j]) - 1)); + + v_browser->widgets[o1 + j].coords.x = x1 - f1 + + ((j % columns_current) * theme_gfx[THEME_GRID_APP_ENTRY]->w); + v_browser->widgets[o2 + j].coords.x = x2 - f2 + + ((j % columns_new) * theme_gfx[THEME_GRID_APP_ENTRY]->w); + } + + view_plot (v_browser, 0, NULL, NULL, NULL); + + if (i == TRANS_STEPS / 2) { + widget_set_flag (&v_browser->widgets[0], WF_VISIBLE, true); + widget_set_flag (&v_browser->widgets[1], WF_VISIBLE, true); + widget_set_flag (&v_browser->widgets[0], WF_VISIBLE, less); + widget_set_flag (&v_browser->widgets[1], WF_VISIBLE, more); + + view_set_focus (v_browser, focus); + } + } + + for (i = 0; i < (MAX_COLUMNS * ROWS); ++i) + widget_free(&v_browser->widgets[o1 + i]); + + columns_current = columns_new; + + if (action == BA_REMOVE) + top_offset = 0; +} + +void browser_set_focus(u32 bd) { + if (columns_current == 1) { + if (bd & PADS_UP) { + view_set_focus_prev(v_browser); + return; + } + + if (bd & PADS_DOWN) { + view_set_focus_next(v_browser); + return; + } + + return; + } else { + if (bd & PADS_LEFT) { + view_set_focus_prev(v_browser); + return; + } + + if (bd & PADS_RIGHT) { + view_set_focus_next(v_browser); + return; + } + + if (bd & PADS_UP) { + view_move_focus(v_browser, -columns_current); + return; + } + + if (bd & PADS_DOWN) { + view_move_focus(v_browser, columns_current); + return; + } + + return; + } +} + +app_entry *browser_sel(void) { + if ((entry_count < 1) || (v_browser->focus < AE_OFFSET)) + return NULL; + + u32 i; + if (first_set) + i = top_offset + v_browser->focus - AE_OFFSET; + else + i = top_offset + v_browser->focus - (MAX_COLUMNS * ROWS) - AE_OFFSET; + + return entries[i]; +} + +void browser_switch_mode(void) { + const app_entry *app = browser_sel(); + int mode = 0; + + if (columns_current == 1) { + if (widescreen) + columns_new = 4; + else + columns_new = 3; + + mode = 1; + } else { + columns_new = 1; + } + + if (v_browser) + browser_gen_view(BA_REFRESH, app); + + if (settings.browse_mode != mode) + settings.browse_mode = mode; +} + diff --git a/channel/channelapp/source/browser.h b/channel/channelapp/source/browser.h new file mode 100644 index 0000000..07b23b1 --- /dev/null +++ b/channel/channelapp/source/browser.h @@ -0,0 +1,26 @@ +#ifndef _BROWSER_H_ +#define _BROWSER_H_ + +#include + +#include "view.h" + +typedef enum { + BA_ADD = 0, + BA_REMOVE, + BA_REFRESH, + BA_NEXT, + BA_PREV +} browser_action; + +view * browser_init(void); +void browser_deinit(void); +void browser_theme_reinit(void); + +void browser_gen_view(browser_action action, const app_entry *app); +void browser_set_focus(u32 bd); +app_entry *browser_sel(void); +void browser_switch_mode(void); + +#endif + diff --git a/channel/channelapp/source/bubbles.c b/channel/channelapp/source/bubbles.c new file mode 100644 index 0000000..19ead4e --- /dev/null +++ b/channel/channelapp/source/bubbles.c @@ -0,0 +1,233 @@ +#include +#include + +#include "../config.h" +#include "gfx.h" +#include "theme.h" + +#include "bubbles.h" + +#include + +#define BUBBLE_DELTA (MAX_BUBBLE_COUNT-MIN_BUBBLE_COUNT) + +typedef struct { + float x; + float py; + float speed; + float xm; + float val; + float step; + int popped; + int popcnt; + int tex; +} bubble; + +static gfx_entity *tex_bubbles[3]; + +static gfx_queue_entry entries_bubbles[MAX_BUBBLE_COUNT]; +static gfx_queue_entry entries_sub_bubbles[MAX_BUBBLE_COUNT][BUBBLE_POP_MAX]; + +static bubble bubbles[MAX_BUBBLE_COUNT]; +static bubble sub_bubbles[MAX_BUBBLE_COUNT][BUBBLE_POP_MAX]; + +static int bubble_count = -1; + +static void bubble_rand(int i) { + int tex; + + tex = IRAND (3); + + bubbles[i].x = IRAND (view_width); + bubbles[i].py = view_height + IRAND (200); + bubbles[i].speed = 1.2 + FRAND (4 - tex); + bubbles[i].xm = 3.0 + (IRAND ((tex + 1)) * bubbles[i].speed); + bubbles[i].val = 0; + bubbles[i].step = M_TWOPI / (64 + FRAND (64.0)); + bubbles[i].popped = 0; + bubbles[i].popcnt = 0; + bubbles[i].tex = tex; + + gfx_qe_entity(&entries_bubbles[i], tex_bubbles[tex], bubbles[i].x, + bubbles[i].py, -2, COL_DEFAULT); + + entries_bubbles[i].entity.scale = BUBBLE_SIZE_MIN + + FRAND (BUBBLE_SIZE_MAX - BUBBLE_SIZE_MIN); + entries_bubbles[i].entity.rad = FRAND (M_PI_4); +} + +static void bubble_update_count(void) { + static int div = 0; + s32 minute; + static int new_count; + int t; + + // time() might be expensive due to RTC reading + // so slow it down a bit + if ((div++ >= 600) || (bubble_count < 0)) { + div = 0; + t = time(NULL); + minute = (t / 60 - BUBBLE_MIN_TIME) % BUBBLE_TIME_CYCLE; + + if (minute <= BUBBLE_MAX_OFFSET) + new_count = (BUBBLE_DELTA * minute / BUBBLE_MAX_OFFSET) + + MIN_BUBBLE_COUNT; + else + new_count = (BUBBLE_DELTA * (BUBBLE_TIME_CYCLE - minute) / + (BUBBLE_TIME_CYCLE - BUBBLE_MAX_OFFSET)) + + MIN_BUBBLE_COUNT; + + if (new_count < MIN_BUBBLE_COUNT) // should never happen + new_count = MIN_BUBBLE_COUNT; + + if (new_count > MAX_BUBBLE_COUNT) // should never happen + new_count = MAX_BUBBLE_COUNT; + } + + if (((div % 6) == 0) || (bubble_count < 0)) { + if (bubble_count < 0) + bubble_count = 0; + if (bubble_count < new_count) { + while (bubble_count < new_count) + bubble_rand(bubble_count++); + } else if (bubble_count > new_count) + bubble_count--; + } +} + +static void bubble_pop(int i) { + int j; + bubbles[i].popped = 1; + bubbles[i].popcnt = IRAND(BUBBLE_POP_MAX - BUBBLE_POP_MIN) + BUBBLE_POP_MIN; + entries_bubbles[i].entity.color = 0x00000000; + + for (j = 0; j < bubbles[i].popcnt; j++) { + int tex; + float sa; + float dx,dy; + + sa = FRAND(M_TWOPI); + dx = sin(sa) * FRAND(BUBBLE_POP_SPREAD_X); + dy = (cos(sa) - 1.5) * FRAND(BUBBLE_POP_SPREAD_Y); + + tex = bubbles[i].tex; + + sub_bubbles[i][j].x = bubbles[i].x + dx; + sub_bubbles[i][j].py = entries_bubbles[i].entity.coords.y + dy; + sub_bubbles[i][j].speed = bubbles[i].speed - 0.5 + FRAND (4 - tex); + sub_bubbles[i][j].xm = bubbles[i].xm * (0.8 + FRAND(1.0)); + sub_bubbles[i][j].val = bubbles[i].val; + sub_bubbles[i][j].step = bubbles[i].step * (0.8 + FRAND(0.4)); + sub_bubbles[i][j].popped = 0; + sub_bubbles[i][j].popcnt = 0; + + gfx_qe_entity (&entries_sub_bubbles[i][j], tex_bubbles[tex], + sub_bubbles[i][j].x, sub_bubbles[i][j].py, + -2, COL_DEFAULT); + + entries_sub_bubbles[i][j].entity.scale = (BUBBLE_POP_SIZE_MIN + + FRAND (BUBBLE_POP_SIZE_MAX - BUBBLE_POP_SIZE_MIN)) * + entries_bubbles[i].entity.scale; + entries_sub_bubbles[i][j].entity.rad = entries_bubbles[i].entity.rad; + } +} + +void bubbles_init(void) { + srand (gettime ()); + bubbles_theme_reinit(); + bubble_update_count(); +} + +void bubbles_deinit(void) { +} + +void bubbles_theme_reinit(void) { + tex_bubbles[0] = theme_gfx[THEME_BUBBLE1]; + tex_bubbles[1] = theme_gfx[THEME_BUBBLE2]; + tex_bubbles[2] = theme_gfx[THEME_BUBBLE3]; + + bubble_count = -1; + bubble_update_count(); +} + +void bubble_update(bool wm, s32 x, s32 y) { + int i, j, deadcnt; + gfx_coordinates *coords; + f32 radius; + + bubble_update_count(); + + for (i = 0; i < bubble_count; ++i) { + coords = &entries_bubbles[i].entity.coords; + radius = entries_bubbles[i].entity.scale * + entries_bubbles[i].entity.entity->w / 2 * BUBBLE_POP_RADIUS; + if (!bubbles[i].popped && wm) { + float cx = coords->x + entries_bubbles[i].entity.entity->w/2; + float cy = coords->y - entries_bubbles[i].entity.entity->h/2; + if ((abs(x - cx) < radius) && (abs(y - cy) < radius)) + bubble_pop(i); + } + + if (bubbles[i].popped) { + deadcnt = 0; + + for (j = 0; j < bubbles[i].popcnt; j++) { + coords = &entries_sub_bubbles[i][j].entity.coords; + radius = entries_sub_bubbles[i][j].entity.scale * + entries_sub_bubbles[i][j].entity.entity->w / 2 * + BUBBLE_POP_RADIUS; + + if (!sub_bubbles[i][j].popped && wm) { + float cx = coords->x + entries_bubbles[i].entity.entity->w/2; + float cy = coords->y - entries_bubbles[i].entity.entity->h/2; + if ((abs(x - cx) < radius) && (abs(y - cy) < radius)) { + entries_sub_bubbles[i][j].entity.color = 0x00000000; + sub_bubbles[i][j].popped = 1; + } + } + + sub_bubbles[i][j].py -= sub_bubbles[i][j].speed; + coords->y = sub_bubbles[i][j].py; + + if ((coords->y < -100) || sub_bubbles[i][j].popped) { + deadcnt++; + continue; + } + + coords->x = sub_bubbles[i][j].x + roundf (sub_bubbles[i][j].xm * + sinf (sub_bubbles[i][j].val)); + sub_bubbles[i][j].val += sub_bubbles[i][j].step; + } + + if(deadcnt >= bubbles[i].popcnt) { + bubble_rand(i); + continue; + } + + gfx_frame_push (entries_sub_bubbles[i], bubbles[i].popcnt); + } else { + bubbles[i].py -= bubbles[i].speed; + coords->y = bubbles[i].py; + + if (coords->y < -100) { + bubble_rand(i); + continue; + } + + coords->x = bubbles[i].x + roundf (bubbles[i].xm * + sinf (bubbles[i].val)); + bubbles[i].val += bubbles[i].step; + } + } + + gfx_frame_push(entries_bubbles, bubble_count); +} + +void bubble_popall(void) { + int i; + + for (i = 0; i < bubble_count; ++i) + if (!bubbles[i].popped) + bubble_pop(i); +} + diff --git a/channel/channelapp/source/bubbles.h b/channel/channelapp/source/bubbles.h new file mode 100644 index 0000000..d13048d --- /dev/null +++ b/channel/channelapp/source/bubbles.h @@ -0,0 +1,15 @@ +#ifndef _BUBBLES_H_ +#define _BUBBLES_H_ + +#include + +void bubbles_init(void); +void bubbles_deinit(void); +void bubbles_theme_reinit(void); + +void bubble_update(bool wm, s32 x, s32 y); + +void bubble_popall(void); + +#endif + diff --git a/channel/channelapp/source/controls.c b/channel/channelapp/source/controls.c new file mode 100644 index 0000000..e3f18eb --- /dev/null +++ b/channel/channelapp/source/controls.c @@ -0,0 +1,240 @@ +#include +#include +#include + +#include +#include + +#include "../config.h" + +#include "controls.h" +#include "gfx.h" + +static s32 pointer_owner = -1; +WPADData *wpads[WPAD_MAX_WIIMOTES]; + +static int rumbling = 0; + +void controls_init (void) { + int i; + + i = WPAD_Init (); + + if(i < 0) { + gprintf("WPAD_Init failed: %d\n",i); + return; + } + + for(i=0;iexp.type) { + case WPAD_EXP_NUNCHUK: + if(btns & WPAD_NUNCHUK_BUTTON_Z) + btns |= PADW_BUTTON_NET_INIT; + if(btns & WPAD_NUNCHUK_BUTTON_C) + btns |= PADW_BUTTON_SCREENSHOT; + break; + case WPAD_EXP_CLASSIC: + if(btns & WPAD_CLASSIC_BUTTON_LEFT) + btns |= WPAD_BUTTON_LEFT; + if(btns & WPAD_CLASSIC_BUTTON_RIGHT) + btns |= WPAD_BUTTON_RIGHT; + if(btns & WPAD_CLASSIC_BUTTON_UP) + btns |= WPAD_BUTTON_UP; + if(btns & WPAD_CLASSIC_BUTTON_DOWN) + btns |= WPAD_BUTTON_DOWN; + if(btns & WPAD_CLASSIC_BUTTON_A) + btns |= WPAD_BUTTON_A; + if(btns & WPAD_CLASSIC_BUTTON_B) + btns |= WPAD_BUTTON_B; + if(btns & WPAD_CLASSIC_BUTTON_X) + btns |= WPAD_BUTTON_1; + if(btns & WPAD_CLASSIC_BUTTON_Y) + btns |= WPAD_BUTTON_2; + if((btns & WPAD_CLASSIC_BUTTON_FULL_L) || (btns & WPAD_CLASSIC_BUTTON_MINUS)) + btns |= WPAD_BUTTON_MINUS; + if((btns & WPAD_CLASSIC_BUTTON_FULL_R) || (btns & WPAD_CLASSIC_BUTTON_PLUS)) + btns |= WPAD_BUTTON_PLUS; + if(btns & WPAD_CLASSIC_BUTTON_HOME) + btns |= WPAD_BUTTON_HOME; + if(btns & WPAD_CLASSIC_BUTTON_ZR) + btns |= PADW_BUTTON_NET_INIT; + if(btns & WPAD_CLASSIC_BUTTON_ZL) + btns |= PADW_BUTTON_SCREENSHOT; + break; + case WPAD_EXP_GUITARHERO3: + if(btns & WPAD_GUITAR_HERO_3_BUTTON_STRUM_UP) + btns |= WPAD_BUTTON_UP; + if(btns & WPAD_GUITAR_HERO_3_BUTTON_STRUM_DOWN) + btns |= WPAD_BUTTON_DOWN; + if(btns & WPAD_GUITAR_HERO_3_BUTTON_GREEN) + btns |= WPAD_BUTTON_A; + if(btns & WPAD_GUITAR_HERO_3_BUTTON_RED) + btns |= WPAD_BUTTON_B; + if(btns & WPAD_GUITAR_HERO_3_BUTTON_PLUS) + btns |= WPAD_BUTTON_PLUS; + if(btns & WPAD_GUITAR_HERO_3_BUTTON_MINUS) + btns |= WPAD_BUTTON_MINUS; + if(btns & WPAD_GUITAR_HERO_3_BUTTON_YELLOW) + btns |= WPAD_BUTTON_LEFT; + if(btns & WPAD_GUITAR_HERO_3_BUTTON_BLUE) + btns |= WPAD_BUTTON_RIGHT; + if(btns & WPAD_GUITAR_HERO_3_BUTTON_ORANGE) + btns |= WPAD_BUTTON_1; + break; + } + return (btns&0xffff) << 16; +} + +void controls_scan (u32 *down, u32 *held, u32 *up) { + u32 bd, bh, bu; + int i; + s32 last_owner; + + PAD_ScanPads (); + + bd = PAD_ButtonsDown (0); + bh = PAD_ButtonsHeld (0); + bu = PAD_ButtonsUp (0); + + WPAD_ScanPads (); + + for(i=0;i= 0) && (!wpads[pointer_owner] || !wpads[pointer_owner]->ir.valid)) { + pointer_owner = -1; + } + + // find a new pointer owner if necessary + if(pointer_owner < 0) { + for(i=0;iir.valid) { + pointer_owner = i; + break; + } + } + } + + // pointer owner owns buttons + if(pointer_owner >= 0) { + bd |= wpad_button_transform(wpads[pointer_owner], wpads[pointer_owner]->btns_d); + bh |= wpad_button_transform(wpads[pointer_owner], wpads[pointer_owner]->btns_h); + bu |= wpad_button_transform(wpads[pointer_owner], wpads[pointer_owner]->btns_u); + } else { + // otherwise just mix all buttons together + for(i=0;ibtns_d); + bh |= wpad_button_transform(wpads[i], wpads[i]->btns_h); + bu |= wpad_button_transform(wpads[i], wpads[i]->btns_u); + } + } + } + + if(last_owner >= 0 && last_owner != pointer_owner && rumbling) { + WPAD_Rumble (last_owner, 0); + if(pointer_owner >= 0) + WPAD_Rumble(pointer_owner, 1); + } + + if (down) + *down = bd; + + if (held) + *held = bh; + + if (up) + *up = bu; +} + +bool controls_ir (s32 *x, s32 *y, f32 *roll) { + if (pointer_owner >= 0 && wpads[pointer_owner] && wpads[pointer_owner]->ir.valid) { + *x = wpads[pointer_owner]->ir.x - 64; + *y = wpads[pointer_owner]->ir.y - 64; + + if(roll) + *roll = -wpads[pointer_owner]->ir.angle / 180.0 * M_PI; + return true; + } else { + return false; + } +} + +void controls_rumble(int rumble) { + if(pointer_owner >= 0) + WPAD_Rumble(pointer_owner, rumble); + rumbling = rumble; +} + +static s32 wpad_sticky(int chan) { + s32 sy = 0; + if(wpads[chan]) { + switch(wpads[chan]->exp.type) { + case WPAD_EXP_NUNCHUK: + sy = wpads[chan]->exp.nunchuk.js.pos.y - wpads[chan]->exp.nunchuk.js.center.y; + break; + case WPAD_EXP_CLASSIC: + sy = (wpads[chan]->exp.classic.ljs.pos.y - wpads[chan]->exp.classic.ljs.center.y) * 4; + break; + case WPAD_EXP_GUITARHERO3: + sy = (wpads[chan]->exp.gh3.js.pos.y - wpads[chan]->exp.gh3.js.center.y) * 4; + break; + } + } + return sy; +} + +#define DEADZONE 10 + +s32 deadzone(s32 v) { + if(v > DEADZONE) + return v - DEADZONE; + if(v < -DEADZONE) + return v + DEADZONE; + return 0; +} + +s32 controls_sticky(void) { + s32 sy; + int i; + sy = deadzone(PAD_StickY(0)); + if(pointer_owner >= 0) { + sy += deadzone(wpad_sticky(pointer_owner)); + } else { + for(i=0;i +#include + +// fake wiimote buttons for mapping extension buttons +#define PADW_BUTTON_NET_INIT 0x2000 +#define PADW_BUTTON_SCREENSHOT 0x4000 + +#define PADS_A (PAD_BUTTON_A | (WPAD_BUTTON_A << 16)) +#define PADS_B (PAD_BUTTON_B | (WPAD_BUTTON_B << 16)) +#define PADS_MINUS (PAD_TRIGGER_L | (WPAD_BUTTON_MINUS << 16)) +#define PADS_PLUS (PAD_TRIGGER_R | (WPAD_BUTTON_PLUS << 16)) +#define PADS_1 (PAD_BUTTON_X | (WPAD_BUTTON_1 << 16)) +#define PADS_2 (PAD_BUTTON_Y | (WPAD_BUTTON_2 << 16)) +#define PADS_HOME (PAD_BUTTON_START | (WPAD_BUTTON_HOME << 16)) +#define PADS_UP (PAD_BUTTON_UP | (WPAD_BUTTON_UP << 16)) +#define PADS_DOWN (PAD_BUTTON_DOWN | (WPAD_BUTTON_DOWN << 16)) +#define PADS_LEFT (PAD_BUTTON_LEFT | (WPAD_BUTTON_LEFT << 16)) +#define PADS_RIGHT (PAD_BUTTON_RIGHT | (WPAD_BUTTON_RIGHT << 16)) +#define PADS_DPAD (PADS_UP | PADS_DOWN | PADS_LEFT | PADS_RIGHT) +#define PADS_NET_INIT (PAD_TRIGGER_Z | (PADW_BUTTON_NET_INIT << 16)) +#define PADS_SCREENSHOT (PADW_BUTTON_SCREENSHOT << 16) + +void controls_init (void); +void controls_deinit (void); + +void controls_scan (u32 *down, u32 *held, u32 *up); +bool controls_ir (s32 *x, s32 *y, f32 *roll); +void controls_rumble(int rumble); +s32 controls_sticky(void); +void controls_set_ir_threshold(int on); + +#endif + diff --git a/channel/channelapp/source/cursors.c b/channel/channelapp/source/cursors.c new file mode 100644 index 0000000..babe56c --- /dev/null +++ b/channel/channelapp/source/cursors.c @@ -0,0 +1,55 @@ +#include "../config.h" + +#include + +#include "cursor_drag_png.h" +#include "cursor_drag_shade_png.h" +#include "cursor_pic_png.h" +#include "cursor_shade_png.h" + +#include "cursors.h" + +static cursor cursors[CUR_MAX]; + +void cursors_init (void) { + cursors[CUR_STD].tex[0] = tex_from_png (cursor_shade_png, + cursor_shade_png_size, 96,96); + cursors[CUR_STD].tex[1] = tex_from_png (cursor_pic_png, cursor_pic_png_size, + 96, 96); + + cursors[CUR_STD].hotspot_x = cursors[CUR_STD].tex[1]->w / 2; + cursors[CUR_STD].hotspot_y = cursors[CUR_STD].tex[1]->h / 2; + + cursors[CUR_DRAG].tex[0] = tex_from_png (cursor_drag_shade_png, + cursor_drag_shade_png_size, + 96, 96); + cursors[CUR_DRAG].tex[1] = tex_from_png (cursor_drag_png, + cursor_drag_png_size, 96, 96); + cursors[CUR_DRAG].hotspot_x = cursors[CUR_DRAG].tex[1]->w / 2; + cursors[CUR_DRAG].hotspot_y = cursors[CUR_DRAG].tex[1]->h / 2; +} + +void cursors_deinit (void) { + u8 i; + + for (i = 0; i < CUR_MAX; ++i) { + tex_free (cursors[i].tex[0]); + tex_free (cursors[i].tex[1]); + } +} + +void cursors_queue (gfx_queue_entry *queue, cursor_type type, s16 x, s16 y, + f32 roll) { + gfx_qe_entity (&queue[0], cursors[type].tex[0], + x - cursors[type].hotspot_x + 2, + y - cursors[type].hotspot_y + 4, TEX_LAYER_CURSOR, + COL_DEFAULT); + gfx_qe_entity (&queue[1], cursors[type].tex[1], + x - cursors[type].hotspot_x, + y - cursors[type].hotspot_y, TEX_LAYER_CURSOR + 1, + COL_DEFAULT); + + queue[0].entity.rad = roll; + queue[1].entity.rad = roll; +} + diff --git a/channel/channelapp/source/cursors.h b/channel/channelapp/source/cursors.h new file mode 100644 index 0000000..e02d709 --- /dev/null +++ b/channel/channelapp/source/cursors.h @@ -0,0 +1,28 @@ +#ifndef _CURSORS_H_ +#define _CURSORS_H_ + +#include + +#include "gfx.h" +#include "tex.h" + +typedef enum { + CUR_STD = 0, + CUR_DRAG, + + CUR_MAX +} cursor_type; + +typedef struct { + gfx_entity *tex[2]; + + s16 hotspot_x, hotspot_y; +} cursor; + +void cursors_init (void); +void cursors_deinit (void); +void cursors_queue (gfx_queue_entry *queue, cursor_type type, s16 x, s16 y, + f32 roll); + +#endif + diff --git a/channel/channelapp/source/debug.c b/channel/channelapp/source/debug.c new file mode 100644 index 0000000..014ff31 --- /dev/null +++ b/channel/channelapp/source/debug.c @@ -0,0 +1,88 @@ +#include +#include + +#include +#include + +#include "../config.h" + +#ifdef DEBUG_APP + +//#define DEBUG_MEMSTATS + +static int gprintf_enabled = 1; + +void gprintf_enable(int enable) { + gprintf_enabled = enable; +} + +int gprintf(const char *format, ...) +{ + va_list ap; + u32 level; + int ret; + + if (!gprintf_enabled) + return 0; + + level = IRQ_Disable(); + va_start(ap, format); + ret = vprintf(format, ap); + va_end(ap); + IRQ_Restore(level); + + return ret; +} + +/********* you know you love it **********/ +static char ascii(char s) { + if(s < 0x20) return '.'; + if(s > 0x7E) return '.'; + return s; +} + +void hexdump(const void *d, int len) { + u8 *data; + int i, off; + data = (u8*)d; + for (off=0; off=len) gprintf(" "); + else gprintf("%02x ",data[off+i]); + + gprintf(" "); + for(i=0; i<16; i++) + if((i+off)>=len) gprintf(" "); + else gprintf("%c",ascii(data[off+i])); + gprintf("\n"); + } +} +/********* you know you love it **********/ + +#ifndef UINT_MAX +#define UINT_MAX ((u32)((s32)-1)) +#endif +void memstats(int reset) { +#ifdef DEBUG_MEMSTATS + static u32 min_free = UINT_MAX; + static u32 temp_free; + static u32 level; + + if (reset) + min_free = UINT_MAX; + + _CPU_ISR_Disable(level); + + temp_free = (u32) SYS_GetArena2Hi() - (u32) SYS_GetArena2Lo(); + + _CPU_ISR_Restore(level); + + if (temp_free < min_free) { + min_free = temp_free; + gprintf("MEM2 free: %8u\n", min_free); + } +#endif +} + +#endif diff --git a/channel/channelapp/source/dialogs.c b/channel/channelapp/source/dialogs.c new file mode 100644 index 0000000..f0aa48d --- /dev/null +++ b/channel/channelapp/source/dialogs.c @@ -0,0 +1,623 @@ +#include +#include +#include +#include + +#include + +#include "../config.h" +#include "controls.h" +#include "theme.h" +#include "font.h" +#include "widgets.h" +#include "view.h" +#include "xml.h" +#include "panic.h" + +#include "dialogs.h" + +#include "i18n.h" + +#define TRANS_STEPS 15 + +static const char *app_entry_desc_default; + +static const char *caption_info; +static const char *caption_confirm; +static const char *caption_warning; +static const char *caption_error; +static const char *caption_ok; +static const char *caption_cancel; +static const char *caption_yes; +static const char *caption_no; +static const char *caption_delete; +static const char *caption_load; +static const char *caption_back; +static const char *caption_options; +static const char *caption_device; +static const char *caption_device_names[DEVICE_COUNT]; +static const char *caption_sort_by; +static const char *caption_sort_name; +static const char *caption_sort_date; + +static const char *l_version; +static const char *l_coder; + +static const char *string_about_pre; +static const char *string_about_post; +static const char *string_about_trans; +static const char *string_about_theme; +static char *string_about_gen; + +void dialogs_theme_reinit (void) { + app_entry_desc_default = _("no description available"); + + caption_info = _("Information"); + caption_confirm = _("Confirmation"); + caption_warning = _("Warning"); + caption_error = _("Error"); + caption_ok = _("Ok"); + caption_cancel = _("Cancel"); + caption_yes = _("Yes"); + caption_no = _("No"); + caption_delete = _("Delete"); + caption_load = _("Load"); + caption_back = _("Back"); + caption_options = _("Options"); + caption_device = _("Device:"); + caption_device_names[0] = _("Internal SD Slot"); + caption_device_names[1] = _("USB device"); + caption_device_names[2] = _("SDGecko Slot A"); + caption_device_names[3] = _("SDGecko Slot B"); + caption_sort_by = _("Sort applications by:"); + caption_sort_name = _("Name"); + caption_sort_date = _("Date"); + + string_about_pre = + "Credits\n\n" + + "The Homebrew Channel was made possible by the following people:\n\n" + + "dhewg (EWRONGCHAN) - application code, geckoloader code\n" + "blasty (ESTONED) - application code\n" + "marcan (EFAILURE) - reload stub, banner, installer, packaging\n" + "bushing (EWANTPONY) - socket code, loader code\n" + "segher (EBUGFOUND) - nandloader stub code\n" + "souLLy (ENOTHERE) - banner graphics\n" + "drmr (EMORECOWBELL) - banner audio, channel graphics\n" + "mha (E404) - update server and hosting"; + + string_about_post = + "Powered by devkitPPC and libogc, by shagkur, WinterMute, " + "and everyone else who contributed\n\n" + + "Thanks to all the beta testers\n\n" + + "Kind regards to the following people too:\n\n" + + "sepp256 - dropped some good GX hints\n" + "chishm, svpe, rodries, hermes - libfat port\n" + "alien - some graphics\n" + "jodi - the lulz\n\n" + + "And last but not least, thanks to the authors of the following libraries:\n\n" + + "wiiuse - para's Wiimote library, now integrated with libogc\n" + "libpng - the official PNG library\n" + "Mini-XML - small and efficient XML parsing library\n" + "FreeType - the free TrueType/OpenType font renderer\n"; + + string_about_trans = _(" translation by "); + + if (!i18n_have_mo()) + string_about_trans = ""; + + string_about_theme = _("Theme:"); + + if (string_about_gen) + free(string_about_gen); + + string_about_gen = pmalloc(strlen(string_about_pre) + + strlen(string_about_post) + + strlen(string_about_trans) + + strlen(string_about_theme) + 128); + + l_version = _("Version: %s"); + l_coder = _("Author: %s"); +} + +void dialogs_init (void) { + string_about_gen = NULL; + dialogs_theme_reinit(); +} + +void dialogs_deinit (void) { + free(string_about_gen); +} + +void dialog_fade (view *v, bool fade_in) { + float val; + float step; + s16 y; + float yf; + u32 c; + u8 stepa; + u8 i; + + if (fade_in) { + val = 0; + step = M_PI / (2 * TRANS_STEPS); + y = v->coords.y + view_height; + c = DIALOG_MASK_COLOR & 0xffffff00; + stepa = (DIALOG_MASK_COLOR & 0xff) / TRANS_STEPS; + } else { + val = M_PI; + step = M_PI / (2 * TRANS_STEPS); + y = v->coords.y; + c = DIALOG_MASK_COLOR; + stepa = -(DIALOG_MASK_COLOR & 0xff) / TRANS_STEPS; + } + + yf = view_height; + + for (i = 0; i < TRANS_STEPS; ++i) { + v->coords.y = y - roundf (yf * sinf (val)); + val += step; + c += stepa; + + view_plot (v, c, NULL, NULL, NULL); + } +} + +view * dialog_app (const app_entry *entry, const view *sub_view) { + view *v; + u16 x, gap; + char *name; + char coder[64]; + char version[64]; + const char *desc; + u16 ym, hm, yb; + + if (entry->meta && entry->meta->name) + name = entry->meta->name; + else + name = entry->dirname; + + if (entry->meta && entry->meta->coder) + snprintf (coder, sizeof (coder), l_coder, entry->meta->coder); + else + *coder = 0; + + if (entry->meta && entry->meta->version) + snprintf (version, sizeof (version), l_version, entry->meta->version); + else + *version = 0; + + if (entry->meta && entry->meta->long_description) + desc = entry->meta->long_description; + else + desc = app_entry_desc_default; + + v = view_new (11, sub_view, (view_width - theme_gfx[THEME_DIALOG]->w) / 2, + 44, TEX_LAYER_DIALOGS, PADS_B); + + widget_image(&v->widgets[0], 0, 0, 0, theme_gfx[THEME_DIALOG], + NULL, false, NULL); + + widget_label (&v->widgets[1], 32, 16, 1, name, + theme_gfx[THEME_DIALOG]->w - 64, FA_CENTERED, FA_ASCENDER, + FONT_DLGTITLE); + + if (entry->icon) + widget_image(&v->widgets[2], 32, 48, 1, entry->icon, NULL, false, NULL); + + widget_label (&v->widgets[3], 48 + APP_ENTRY_ICON_WIDTH, 72, 1, version, + theme_gfx[THEME_DIALOG]->w - 72 - APP_ENTRY_ICON_WIDTH, + FA_LEFT, FA_DESCENDER, FONT_LABEL); + + widget_label (&v->widgets[4], 48 + APP_ENTRY_ICON_WIDTH, 72, 1, coder, + theme_gfx[THEME_DIALOG]->w - 72 - APP_ENTRY_ICON_WIDTH, + FA_LEFT, FA_ASCENDER, FONT_LABEL); + + yb = theme_gfx[THEME_DIALOG]->h - theme_gfx[THEME_BUTTON_TINY]->h - 16; + ym = 48 + APP_ENTRY_ICON_HEIGHT + 8; + hm = yb - ym - 8; + + widget_memo_deco (&v->widgets[5], 32, ym, 1, + theme_gfx[THEME_DIALOG]->w - 64, hm, desc, FA_LEFT); + + gap = (theme_gfx[THEME_DIALOG]->w - + theme_gfx[THEME_BUTTON_TINY]->w * 3) / 4; + + x = gap; + widget_button (&v->widgets[8], x, yb, 1, BTN_TINY, caption_delete); + + x += gap + theme_gfx[THEME_BUTTON_TINY]->w; + widget_button (&v->widgets[9], x, yb, 1, BTN_TINY, caption_load); + + x += gap + theme_gfx[THEME_BUTTON_TINY]->w; + widget_button (&v->widgets[10], x, yb, 1, BTN_TINY, caption_back); + + view_set_focus (v, 10); + + return v; +} + +view * dialog_progress (const view *sub_view, const char *caption, u32 max) { + view *v; + + v = view_new (1, sub_view, (view_width - theme_gfx[THEME_PROGRESS]->w) / 2, + (view_height - theme_gfx[THEME_PROGRESS]->h) / 2, + TEX_LAYER_DIALOGS, 0); + + widget_progress (&v->widgets[0], 0, 0, 0, caption, max); + widget_set_progress (&v->widgets[0], 0); + + return v; +} + +void dialog_set_progress (const view *v, u32 progress) { + widget_set_progress (&v->widgets[0], progress); +} + +view * dialog_about (const view *sub_view) { + view *v; + u16 ym, hm, yb, hmn; + u8 l, hf; + + strcpy(string_about_gen, string_about_pre); + if (string_about_trans && strlen(string_about_trans)) { + strcat(string_about_gen, "\n\n"); + strcat(string_about_gen, string_about_trans); + } + + if (theme.description && strlen(theme.description)) { + strcat(string_about_gen, "\n\n"); + strcat(string_about_gen, string_about_theme); + strcat(string_about_gen, " "); + strcat(string_about_gen, theme.description); + } + + strcat(string_about_gen, "\n\n"); + strcat(string_about_gen, string_about_post); + + v = view_new (3, sub_view, (view_width - theme_gfx[THEME_DIALOG]->w) / 2, + 44, TEX_LAYER_DIALOGS, 0); + + widget_image (&v->widgets[0], 0, 0, 0, theme_gfx[THEME_DIALOG], + NULL, false, NULL); + + widget_image (&v->widgets[1], (theme_gfx[THEME_DIALOG]->w - + theme_gfx[THEME_ABOUT]->w) / 2, 16, 1, + theme_gfx[THEME_ABOUT], NULL, false, NULL); + + yb = theme_gfx[THEME_DIALOG]->h - 16; + ym = 16 + theme_gfx[THEME_ABOUT]->h + 8; + hm = yb - ym - 8; + hf = font_get_y_spacing(FONT_MEMO); + l = hm / hf; + hmn = l * hf; + + if (hmn < hm) { + ym += (hm - hmn) / 2; + hm = hmn; + } + + widget_memo (&v->widgets[2], 32, ym, 1, theme_gfx[THEME_DIALOG]->w - 64, + hm, string_about_gen, FA_CENTERED); + + v->widgets[2].cur = CUR_STD; + + return v; +} + +static view *dialog_message(const view *sub_view, dialog_message_type type, + dialog_message_buttons buttons, const char *text, + u8 focus) { + view *v; + u8 c; + u16 x, gap; + const char *caption = NULL, *b1 = NULL, *b2 = NULL; + u16 ym, hm, yb; + u8 hf; + gfx_entity *icon = NULL; + + c = 6; + switch (buttons) { + case DLGB_OK: + c++; + break; + + case DLGB_OKCANCEL: + case DLGB_YESNO: + c += 2; + break; + } + + v = view_new (c, sub_view, (view_width - theme_gfx[THEME_DIALOG]->w) / 2, + 44, TEX_LAYER_DIALOGS, PADS_B); + + widget_image (&v->widgets[0], 0, 0, 0, theme_gfx[THEME_DIALOG], + NULL, false, NULL); + + switch (type) { + case DLGMT_INFO: + caption = caption_info; + icon = theme_gfx[THEME_DLG_INFO]; + break; + + case DLGMT_CONFIRM: + caption = caption_confirm; + icon = theme_gfx[THEME_DLG_CONFIRM]; + break; + + case DLGMT_WARNING: + caption = caption_warning; + icon = theme_gfx[THEME_DLG_WARNING]; + break; + + case DLGMT_ERROR: + caption = caption_error; + icon = theme_gfx[THEME_DLG_ERROR]; + break; + } + + widget_image (&v->widgets[1], 128, 24, 0, icon, NULL, false, NULL); + widget_label (&v->widgets[2], 32, 32, 1, caption, + theme_gfx[THEME_DIALOG]->w - 64, FA_CENTERED, FA_ASCENDER, + FONT_DLGTITLE); + + hf = font_get_height (FONT_DLGTITLE); + yb = theme_gfx[THEME_DIALOG]->h - theme_gfx[THEME_BUTTON_SMALL]->h - 32; + ym = 32 + hf + 8; + hm = yb - ym - 8; + + widget_memo_deco (&v->widgets[3], 32, ym, 1, + theme_gfx[THEME_DIALOG]->w - 64, hm, text, FA_CENTERED); + + switch (buttons) { + case DLGB_OK: + b1 = caption_ok; + b2 = NULL; + break; + + case DLGB_OKCANCEL: + b1 = caption_ok; + b2 = caption_cancel; + break; + + case DLGB_YESNO: + b1 = caption_yes; + b2 = caption_no; + break; + } + + if (b2) { + gap = (theme_gfx[THEME_DIALOG]->w - + theme_gfx[THEME_BUTTON_SMALL]->w * 2) / 3; + + x = gap; + widget_button (&v->widgets[6], x, yb, 1, BTN_SMALL, b1); + + x += gap + theme_gfx[THEME_BUTTON_SMALL]->w; + widget_button (&v->widgets[7], x, yb, 1, BTN_SMALL, b2); + } else { + gap = (theme_gfx[THEME_DIALOG]->w - + theme_gfx[THEME_BUTTON_SMALL]->w) / 2; + + x = gap; + widget_button (&v->widgets[6], x, yb, 1, BTN_SMALL, b1); + } + + view_set_focus (v, 6 + focus); + + return v; +} + +s8 show_message (const view *sub_view, dialog_message_type type, + dialog_message_buttons buttons, const char *text, u8 focus) { + view *v; + u8 fhw; + u32 bd; + s8 res; + s16 mm; + + v = dialog_message (sub_view, type, buttons, text, focus); + + dialog_fade (v, true); + + fhw = font_get_y_spacing(FONT_MEMO); + + while (true) { + view_plot (v, DIALOG_MASK_COLOR, &bd, NULL, NULL); + + if (bd & PADS_LEFT) + view_set_focus_prev (v); + + if (bd & PADS_RIGHT) + view_set_focus_next (v); + + mm = 0; + if (bd & PADS_UP) + mm += fhw; + + if (bd & PADS_DOWN) + mm -= fhw; + + mm += controls_sticky() / 8; + + if (v->drag && (v->drag_widget == 4)) + mm += -v->drag_y / 32; + + widget_scroll_memo_deco (&v->widgets[3], mm); + + if ((bd & PADS_A) && (v->focus != -1)) + break; + } + + res = v->focus - 6; + + dialog_fade (v, false); + + view_free (v); + + return res; +} + +#define DLG_DEV_FIRST 4 + +dialog_options_result show_options_dialog(const view *sub_view) { + u32 frame = 0; + view *v; + dialog_options_result ret; + int device; + app_sort sort; + bool status[DEVICE_COUNT]; + u32 i, bd; + + app_entry_poll_status(true); + + v = view_new (12, sub_view, (view_width - theme_gfx[THEME_DIALOG]->w) / 2, + 44, TEX_LAYER_DIALOGS, PADS_B); + + widget_image (&v->widgets[0], 0, 0, 0, theme_gfx[THEME_DIALOG], + NULL, false, NULL); + widget_label (&v->widgets[1], 32, 16, 1, caption_options, + theme_gfx[THEME_DIALOG]->w - 64, FA_CENTERED, FA_ASCENDER, FONT_DLGTITLE); + + widget_label (&v->widgets[2], 32, 60, 1, caption_device, + theme_gfx[THEME_DIALOG]->w - 64, FA_LEFT, FA_DESCENDER, FONT_LABEL); + widget_label (&v->widgets[3], 32, 212, 1, caption_sort_by, + theme_gfx[THEME_DIALOG]->w - 64, FA_LEFT, FA_DESCENDER, FONT_LABEL); + + widget_button (&v->widgets[4], 52, 64, 1, BTN_SMALL, NULL); + widget_button (&v->widgets[5], 268, 64, 1, BTN_SMALL, NULL); + widget_button (&v->widgets[6], 52, 128, 1, BTN_SMALL, NULL); + widget_button (&v->widgets[7], 268, 128, 1, BTN_SMALL, NULL); + + widget_button (&v->widgets[8], 52, 216, 1, BTN_SMALL, NULL); + widget_button (&v->widgets[9], 268, 216, 1, BTN_SMALL, NULL); + + widget_button (&v->widgets[10], 32, + theme_gfx[THEME_DIALOG]->h - + theme_gfx[THEME_BUTTON_SMALL]->h - 16 , 1, BTN_SMALL, + caption_ok); + widget_button (&v->widgets[11], theme_gfx[THEME_DIALOG]->w - + theme_gfx[THEME_BUTTON_SMALL]->w - 32, + theme_gfx[THEME_DIALOG]->h - + theme_gfx[THEME_BUTTON_SMALL]->h - 16 , 1, BTN_SMALL, + caption_back); + + device = app_entry_get_status(status); + sort = app_entry_get_sort(); + + ret.confirmed = false; + ret.device = device; + ret.sort = sort; + + for (i = 0; i < DEVICE_COUNT; ++i) { + if (i == device) + widget_button_set_caption(&v->widgets[DLG_DEV_FIRST + i], + FONT_BUTTON, + caption_device_names[i]); + else + widget_button_set_caption(&v->widgets[DLG_DEV_FIRST + i], + FONT_BUTTON_DESEL, + caption_device_names[i]); + + widget_set_flag (&v->widgets[DLG_DEV_FIRST + i], WF_ENABLED, status[i]); + } + + if (ret.sort == APP_SORT_DATE) { + widget_button_set_caption(&v->widgets[8], + FONT_BUTTON_DESEL, + caption_sort_name); + widget_button_set_caption(&v->widgets[9], + FONT_BUTTON, + caption_sort_date); + } else { + widget_button_set_caption(&v->widgets[8], + FONT_BUTTON, + caption_sort_name); + widget_button_set_caption(&v->widgets[9], + FONT_BUTTON_DESEL, + caption_sort_date); + } + + view_set_focus (v, 11); + + dialog_fade (v, true); + + while (true) { + app_entry_get_status(status); + + for (i = 0; i < DEVICE_COUNT; ++i) + widget_set_flag (&v->widgets[DLG_DEV_FIRST + i], WF_ENABLED, + status[i]); + + view_plot (v, DIALOG_MASK_COLOR, &bd, NULL, NULL); + frame++; + + if (bd & PADS_LEFT) + view_set_focus_prev (v); + + if (bd & PADS_RIGHT) + view_set_focus_next (v); + + if (bd & PADS_UP) + if (v->focus == view_move_focus(v, -2)) + view_move_focus(v, -4); + + if (bd & PADS_DOWN) + if (v->focus == view_move_focus(v, 2)) + view_move_focus(v, 4); + + if (bd & (PADS_B | PADS_1)) + break; + + if ((bd & PADS_A) && (v->focus != -1)) { + if ((v->focus >= DLG_DEV_FIRST) && + (v->focus < DLG_DEV_FIRST + DEVICE_COUNT)) { + widget_button_set_caption(&v->widgets[DLG_DEV_FIRST + ret.device], + FONT_BUTTON_DESEL, + caption_device_names[ret.device]); + ret.device = v->focus - DLG_DEV_FIRST; + widget_button_set_caption(&v->widgets[DLG_DEV_FIRST + ret.device], + FONT_BUTTON, + caption_device_names[ret.device]); + } else if (v->focus == 8) { + ret.sort = APP_SORT_NAME; + widget_button_set_caption(&v->widgets[8], + FONT_BUTTON, + caption_sort_name); + widget_button_set_caption(&v->widgets[9], + FONT_BUTTON_DESEL, + caption_sort_date); + } else if (v->focus == 9) { + ret.sort = APP_SORT_DATE; + widget_button_set_caption(&v->widgets[8], + FONT_BUTTON_DESEL, + caption_sort_name); + widget_button_set_caption(&v->widgets[9], + FONT_BUTTON, + caption_sort_date); + } else if ((v->focus == 10) || (v->focus == 11)) { + break; + } + } + + if ((frame % 30) == 0) + app_entry_poll_status(false); + } + + if ((bd & PADS_A) && (v->focus == 10)) + ret.confirmed = true; + + dialog_fade (v, false); + + view_free (v); + + return ret; +} + + diff --git a/channel/channelapp/source/dialogs.h b/channel/channelapp/source/dialogs.h new file mode 100644 index 0000000..d29fddb --- /dev/null +++ b/channel/channelapp/source/dialogs.h @@ -0,0 +1,46 @@ +#ifndef _DIALOGS_H_ +#define _DIALOGS_H_ + +#include + +#include "gfx.h" +#include "view.h" + +typedef enum { + DLGMT_INFO = 0, + DLGMT_CONFIRM, + DLGMT_WARNING, + DLGMT_ERROR +} dialog_message_type; + +typedef enum { + DLGB_OK, + DLGB_OKCANCEL, + DLGB_YESNO +} dialog_message_buttons; + +typedef struct { + bool confirmed; + int device; + app_sort sort; +} dialog_options_result; + +extern u16 width_dialog, height_dialog; + +void dialogs_init (void); +void dialogs_theme_reinit (void); +void dialogs_deinit (void); + +void dialog_fade (view *v, bool fade_in); + +view * dialog_app (const app_entry *entry, const view *sub_view); +view * dialog_progress (const view *sub_view, const char *caption, u32 max); +void dialog_set_progress (const view *v, u32 progress); +view * dialog_about (const view *sub_view); + +s8 show_message (const view *sub_view, dialog_message_type type, + dialog_message_buttons buttons, const char *text, u8 focus); +dialog_options_result show_options_dialog(const view *sub_view); + +#endif + diff --git a/channel/channelapp/source/dvd.c b/channel/channelapp/source/dvd.c new file mode 100644 index 0000000..3d25632 --- /dev/null +++ b/channel/channelapp/source/dvd.c @@ -0,0 +1,79 @@ +#include +#include +#include +#include + +#include "../config.h" + +static u32 inbuf[8] __attribute__((aligned(0x20))); +static u32 outbuf[8] __attribute__((aligned(0x20))); + +static s32 _dvd_fd = -1; + +static const char _dvd_path[] __attribute__((aligned(0x20))) = "/dev/di"; + +typedef enum { + WDVD_OPEN = 0, + WDVD_STOP, + WDVD_CLOSE, + WDVD_DONE +} wiidvd_state; + +static wiidvd_state _state; + +static s32 __WiiDVD_Callback(s32 result, void *userdata) +{ + s32 res = -1; + + switch (_state) { + case WDVD_OPEN: + _dvd_fd = result; + if (_dvd_fd < 0) + return 0; + + memset(inbuf, 0, 0x20); + inbuf[0x00] = 0xe3000000; + inbuf[0x01] = 0; + inbuf[0x02] = 0; + + _state = WDVD_STOP; + res = IOS_IoctlAsync( _dvd_fd, 0xe3, inbuf, 0x20, outbuf, 0x20, + __WiiDVD_Callback, NULL); + break; + + case WDVD_STOP: + _state = WDVD_CLOSE; + res = IOS_CloseAsync(_dvd_fd, __WiiDVD_Callback, NULL); + break; + + case WDVD_CLOSE: + _dvd_fd = -1; + _state = WDVD_DONE; + res = 0; + break; + + default: + break; + } + + return res; +} + +s32 WiiDVD_StopMotorAsync(void) { + _state = WDVD_OPEN; + + gprintf("starting DVD motor stop callback chain\n"); + return IOS_OpenAsync(_dvd_path, 0, __WiiDVD_Callback, NULL); +} + +void WiiDVD_ShutDown(void) { + s32 fd = _dvd_fd; + + if (fd > 0) { + _dvd_fd = -1; + _state = WDVD_DONE; + IOS_Close(fd); + gprintf("killed DVD motor stop callback chain\n"); + } +} + diff --git a/channel/channelapp/source/dvd.h b/channel/channelapp/source/dvd.h new file mode 100644 index 0000000..53cb727 --- /dev/null +++ b/channel/channelapp/source/dvd.h @@ -0,0 +1,10 @@ +#ifndef _DVD_H_ +#define _DVD_H_ + +#include + +s32 WiiDVD_StopMotorAsync(void); +void WiiDVD_ShutDown(void); + +#endif + diff --git a/channel/channelapp/source/ecdsa.c b/channel/channelapp/source/ecdsa.c new file mode 100644 index 0000000..46eee59 --- /dev/null +++ b/channel/channelapp/source/ecdsa.c @@ -0,0 +1,413 @@ +// Copyright 2007-2009 Segher Boessenkool +// Licensed under the terms of the GNU GPL, version 2 +// http://www.gnu.org/licenses/old-licenses/gpl-2.0.txt + +#include + +#include + +#include "sha1.h" +#include "ecdsa.h" + +#include "../config.h" + +#ifdef ENABLE_UPDATES + +// order of the addition group of points +static u8 ec_N[30] = + "\x01\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00" + "\x13\xe9\x74\xe7\x2f\x8a\x69\x22\x03\x1d\x26\x03\xcf\xe0\xd7"; + +// base point +static u8 ec_G[60] = + "\x00\xfa\xc9\xdf\xcb\xac\x83\x13\xbb\x21\x39\xf1\xbb\x75\x5f" + "\xef\x65\xbc\x39\x1f\x8b\x36\xf8\xf8\xeb\x73\x71\xfd\x55\x8b" + "\x01\x00\x6a\x08\xa4\x19\x03\x35\x06\x78\xe5\x85\x28\xbe\xbf" + "\x8a\x0b\xef\xf8\x67\xa7\xca\x36\x71\x6f\x7e\x01\xf8\x10\x52"; + +static const u8 square[16] = + "\x00\x01\x04\x05\x10\x11\x14\x15\x40\x41\x44\x45\x50\x51\x54\x55"; + +static void bn_zero(u8 *d, u32 n) +{ + memset(d, 0, n); +} + +static void bn_copy(u8 *d, const u8 *a, u32 n) +{ + memcpy(d, a, n); +} + +static int bn_compare(const u8 *a, const u8 *b, u32 n) +{ + u32 i; + + for (i = 0; i < n; i++) { + if (a[i] < b[i]) + return -1; + if (a[i] > b[i]) + return 1; + } + + return 0; +} + +static void bn_sub_modulus(u8 *a, const u8 *N, u32 n) +{ + u32 i; + u32 dig; + u8 c; + + c = 0; + for (i = n - 1; i < n; i--) { + dig = N[i] + c; + c = (a[i] < dig); + a[i] -= dig; + } +} + +static void bn_add(u8 *d, const u8 *a, const u8 *b, const u8 *N, u32 n) +{ + u32 i; + u32 dig; + u8 c; + + c = 0; + for (i = n - 1; i < n; i--) { + dig = a[i] + b[i] + c; + c = (dig >= 0x100); + d[i] = dig; + } + + if (c) + bn_sub_modulus(d, N, n); + + if (bn_compare(d, N, n) >= 0) + bn_sub_modulus(d, N, n); +} + +static void bn_mul(u8 *d, const u8 *a, const u8 *b, const u8 *N, u32 n) +{ + u32 i; + u8 mask; + + bn_zero(d, n); + + for (i = 0; i < n; i++) + for (mask = 0x80; mask != 0; mask >>= 1) { + bn_add(d, d, d, N, n); + if ((a[i] & mask) != 0) + bn_add(d, d, b, N, n); + } +} + +static void bn_exp(u8 *d, const u8 *a, const u8 *N, u32 n, const u8 *e, u32 en) +{ + u8 t[512]; + u32 i; + u8 mask; + + bn_zero(d, n); + d[n-1] = 1; + for (i = 0; i < en; i++) + for (mask = 0x80; mask != 0; mask >>= 1) { + bn_mul(t, d, d, N, n); + if ((e[i] & mask) != 0) + bn_mul(d, t, a, N, n); + else + bn_copy(d, t, n); + } +} + +// only for prime N -- stupid but lazy, see if I care +static void bn_inv(u8 *d, const u8 *a, const u8 *N, u32 n) +{ + u8 t[512], s[512]; + + bn_copy(t, N, n); + bn_zero(s, n); + s[n-1] = 2; + bn_sub_modulus(t, s, n); + bn_exp(d, a, N, n, t, n); +} + +static void elt_copy(u8 *d, const u8 *a) +{ + memcpy(d, a, 30); +} + +static void elt_zero(u8 *d) +{ + memset(d, 0, 30); +} + +static int elt_is_zero(const u8 *d) +{ + u32 i; + + for (i = 0; i < 30; i++) + if (d[i] != 0) + return 0; + + return 1; +} + +static void elt_add(u8 *d, u8 *a, const u8 *b) +{ + u32 i; + + for (i = 0; i < 30; i++) + d[i] = a[i] ^ b[i]; +} + +static void elt_mul_x(u8 *d, u8 *a) +{ + u8 carry, x, y; + u32 i; + + carry = a[0] & 1; + + x = 0; + for (i = 0; i < 29; i++) { + y = a[i + 1]; + d[i] = x ^ (y >> 7); + x = y << 1; + } + d[29] = x ^ carry; + + d[20] ^= carry << 2; +} + +static void elt_mul(u8 *d, u8 *a, u8 *b) +{ + u32 i, n; + u8 mask; + + elt_zero(d); + + i = 0; + mask = 1; + for (n = 0; n < 233; n++) { + elt_mul_x(d, d); + + if ((a[i] & mask) != 0) + elt_add(d, d, b); + + mask >>= 1; + if (mask == 0) { + mask = 0x80; + i++; + } + } +} + +static void elt_square_to_wide(u8 *d, u8 *a) +{ + u32 i; + + for (i = 0; i < 30; i++) { + d[2*i] = square[a[i] >> 4]; + d[2*i + 1] = square[a[i] & 15]; + } +} + +static void wide_reduce(u8 *d) +{ + u32 i; + u8 x; + + for (i = 0; i < 30; i++) { + x = d[i]; + + d[i + 19] ^= x >> 7; + d[i + 20] ^= x << 1; + + d[i + 29] ^= x >> 1; + d[i + 30] ^= x << 7; + } + + x = d[30] & ~1; + + d[49] ^= x >> 7; + d[50] ^= x << 1; + + d[59] ^= x >> 1; + + d[30] &= 1; +} + +static void elt_square(u8 *d, u8 *a) +{ + u8 wide[60]; + + elt_square_to_wide(wide, a); + wide_reduce(wide); + + elt_copy(d, wide + 30); +} + +static void itoh_tsujii(u8 *d, u8 *a, u8 *b, u32 j) +{ + u8 t[30]; + + elt_copy(t, a); + while (j--) { + elt_square(d, t); + elt_copy(t, d); + } + + elt_mul(d, t, b); +} + +static void elt_inv(u8 *d, u8 *a) +{ + u8 t[30]; + u8 s[30]; + + itoh_tsujii(t, a, a, 1); + itoh_tsujii(s, t, a, 1); + itoh_tsujii(t, s, s, 3); + itoh_tsujii(s, t, a, 1); + itoh_tsujii(t, s, s, 7); + itoh_tsujii(s, t, t, 14); + itoh_tsujii(t, s, a, 1); + itoh_tsujii(s, t, t, 29); + itoh_tsujii(t, s, s, 58); + itoh_tsujii(s, t, t, 116); + elt_square(d, s); +} + +static int point_is_zero(const u8 *p) +{ + return elt_is_zero(p) && elt_is_zero(p + 30); +} + +static void point_double(u8 *r, u8 *p) +{ + u8 s[30], t[30]; + u8 *px, *py, *rx, *ry; + + px = p; + py = p + 30; + rx = r; + ry = r + 30; + + if (elt_is_zero(px)) { + elt_zero(rx); + elt_zero(ry); + + return; + } + + elt_inv(t, px); + elt_mul(s, py, t); + elt_add(s, s, px); + + elt_square(t, px); + + elt_square(rx, s); + elt_add(rx, rx, s); + rx[29] ^= 1; + + elt_mul(ry, s, rx); + elt_add(ry, ry, rx); + elt_add(ry, ry, t); +} + +static void point_add(u8 *r, u8 *p, const u8 *q) +{ + u8 s[30], t[30], u[30]; + u8 *px, *py, *rx, *ry; + const u8 *qx, *qy; + + px = p; + py = p + 30; + qx = q; + qy = q + 30; + rx = r; + ry = r + 30; + + if (point_is_zero(p)) { + elt_copy(rx, qx); + elt_copy(ry, qy); + return; + } + + if (point_is_zero(q)) { + elt_copy(rx, px); + elt_copy(ry, py); + return; + } + + elt_add(u, px, qx); + + if (elt_is_zero(u)) { + elt_add(u, py, qy); + if (elt_is_zero(u)) + point_double(r, p); + else { + elt_zero(rx); + elt_zero(ry); + } + + return; + } + + elt_inv(t, u); + elt_add(u, py, qy); + elt_mul(s, t, u); + + elt_square(t, s); + elt_add(t, t, s); + elt_add(t, t, qx); + t[29] ^= 1; + + elt_mul(u, s, t); + elt_add(s, u, py); + elt_add(rx, t, px); + elt_add(ry, s, rx); +} + +static void point_mul(u8 *d, u8 *a, const u8 *b) // a is bignum +{ + u32 i; + u8 mask; + + elt_zero(d); + elt_zero(d + 30); + + for (i = 0; i < 30; i++) + for (mask = 0x80; mask != 0; mask >>= 1) { + point_double(d, d); + if ((a[i] & mask) != 0) + point_add(d, d, b); + } +} + +int check_ecdsa(const u8 *Q, u8 *R, u8 *S, u8 *hash) +{ + u8 Sinv[30]; + u8 e[30]; + u8 w1[30], w2[30]; + u8 r1[60], r2[60]; + + bn_inv(Sinv, S, ec_N, 30); + + elt_zero(e); + memcpy(e + 10, hash, 20); + + bn_mul(w1, e, Sinv, ec_N, 30); + bn_mul(w2, R, Sinv, ec_N, 30); + + point_mul(r1, w1, ec_G); + point_mul(r2, w2, Q); + + point_add(r1, r1, r2); + + if (bn_compare(r1, ec_N, 30) >= 0) + bn_sub_modulus(r1, ec_N, 30); + + return (bn_compare(r1, R, 30) == 0); +} + +#endif diff --git a/channel/channelapp/source/ecdsa.h b/channel/channelapp/source/ecdsa.h new file mode 100644 index 0000000..0b3a5f6 --- /dev/null +++ b/channel/channelapp/source/ecdsa.h @@ -0,0 +1,8 @@ +#ifndef _ECDSA_H_ +#define _ECDSA_H_ + +#include + +int check_ecdsa(const u8 *Q, u8 *R, u8 *S, u8 *hash); + +#endif diff --git a/channel/channelapp/source/elf_abi.h b/channel/channelapp/source/elf_abi.h new file mode 100644 index 0000000..c9e705e --- /dev/null +++ b/channel/channelapp/source/elf_abi.h @@ -0,0 +1,594 @@ +/* + * Copyright (c) 1995, 1996, 2001, 2002 + * Erik Theisen. All rights reserved. + * + * Redistribution and use in source and binary forms, with or without + * modification, are permitted provided that the following conditions + * are met: + * 1. Redistributions of source code must retain the above copyright + * notice, this list of conditions and the following disclaimer. + * 2. Redistributions in binary form must reproduce the above copyright + * notice, this list of conditions and the following disclaimer in the + * documentation and/or other materials provided with the distribution. + * 3. The name of the author may not be used to endorse or promote products + * derived from this software without specific prior written permission + * + * THIS SOFTWARE IS PROVIDED BY THE AUTHOR ``AS IS'' AND ANY EXPRESS OR + * IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE IMPLIED WARRANTIES + * OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE ARE DISCLAIMED. + * IN NO EVENT SHALL THE AUTHOR BE LIABLE FOR ANY DIRECT, INDIRECT, + * INCIDENTAL, SPECIAL, EXEMPLARY, OR CONSEQUENTIAL DAMAGES (INCLUDING, BUT + * NOT LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS OR SERVICES; LOSS OF USE, + * DATA, OR PROFITS; OR BUSINESS INTERRUPTION) HOWEVER CAUSED AND ON ANY + * THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT LIABILITY, OR TORT + * (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY OUT OF THE USE OF + * THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF SUCH DAMAGE. + */ + +/* + * This is the ELF ABI header file + * formerly known as "elf_abi.h". + */ + +#ifndef _ELF_ABI_H +#define _ELF_ABI_H + +#include + +/* + * This version doesn't work for 64-bit ABIs - Erik. + */ + +/* + * These typedefs need to be handled better. + */ +typedef u32 Elf32_Addr; /* Unsigned program address */ +typedef u32 Elf32_Off; /* Unsigned file offset */ +typedef s32 Elf32_Sword; /* Signed large integer */ +typedef u32 Elf32_Word; /* Unsigned large integer */ +typedef u16 Elf32_Half; /* Unsigned medium integer */ + +/* e_ident[] identification indexes */ +#define EI_MAG0 0 /* file ID */ +#define EI_MAG1 1 /* file ID */ +#define EI_MAG2 2 /* file ID */ +#define EI_MAG3 3 /* file ID */ +#define EI_CLASS 4 /* file class */ +#define EI_DATA 5 /* data encoding */ +#define EI_VERSION 6 /* ELF header version */ +#define EI_OSABI 7 /* OS/ABI specific ELF extensions */ +#define EI_ABIVERSION 8 /* ABI target version */ +#define EI_PAD 9 /* start of pad bytes */ +#define EI_NIDENT 16 /* Size of e_ident[] */ + +/* e_ident[] magic number */ +#define ELFMAG0 0x7f /* e_ident[EI_MAG0] */ +#define ELFMAG1 'E' /* e_ident[EI_MAG1] */ +#define ELFMAG2 'L' /* e_ident[EI_MAG2] */ +#define ELFMAG3 'F' /* e_ident[EI_MAG3] */ +#define ELFMAG "\177ELF" /* magic */ +#define SELFMAG 4 /* size of magic */ + +/* e_ident[] file class */ +#define ELFCLASSNONE 0 /* invalid */ +#define ELFCLASS32 1 /* 32-bit objs */ +#define ELFCLASS64 2 /* 64-bit objs */ +#define ELFCLASSNUM 3 /* number of classes */ + +/* e_ident[] data encoding */ +#define ELFDATANONE 0 /* invalid */ +#define ELFDATA2LSB 1 /* Little-Endian */ +#define ELFDATA2MSB 2 /* Big-Endian */ +#define ELFDATANUM 3 /* number of data encode defines */ + +/* e_ident[] OS/ABI specific ELF extensions */ +#define ELFOSABI_NONE 0 /* No extension specified */ +#define ELFOSABI_HPUX 1 /* Hewlett-Packard HP-UX */ +#define ELFOSABI_NETBSD 2 /* NetBSD */ +#define ELFOSABI_LINUX 3 /* Linux */ +#define ELFOSABI_SOLARIS 6 /* Sun Solaris */ +#define ELFOSABI_AIX 7 /* AIX */ +#define ELFOSABI_IRIX 8 /* IRIX */ +#define ELFOSABI_FREEBSD 9 /* FreeBSD */ +#define ELFOSABI_TRU64 10 /* Compaq TRU64 UNIX */ +#define ELFOSABI_MODESTO 11 /* Novell Modesto */ +#define ELFOSABI_OPENBSD 12 /* OpenBSD */ +/* 64-255 Architecture-specific value range */ + +/* e_ident[] ABI Version */ +#define ELFABIVERSION 0 + +/* e_ident */ +#define IS_ELF(ehdr) ((ehdr).e_ident[EI_MAG0] == ELFMAG0 && \ + (ehdr).e_ident[EI_MAG1] == ELFMAG1 && \ + (ehdr).e_ident[EI_MAG2] == ELFMAG2 && \ + (ehdr).e_ident[EI_MAG3] == ELFMAG3) + +/* ELF Header */ +typedef struct elfhdr{ + unsigned char e_ident[EI_NIDENT]; /* ELF Identification */ + Elf32_Half e_type; /* object file type */ + Elf32_Half e_machine; /* machine */ + Elf32_Word e_version; /* object file version */ + Elf32_Addr e_entry; /* virtual entry point */ + Elf32_Off e_phoff; /* program header table offset */ + Elf32_Off e_shoff; /* section header table offset */ + Elf32_Word e_flags; /* processor-specific flags */ + Elf32_Half e_ehsize; /* ELF header size */ + Elf32_Half e_phentsize; /* program header entry size */ + Elf32_Half e_phnum; /* number of program header entries */ + Elf32_Half e_shentsize; /* section header entry size */ + Elf32_Half e_shnum; /* number of section header entries */ + Elf32_Half e_shstrndx; /* section header table's "section + header string table" entry offset */ +} Elf32_Ehdr; + +/* e_type */ +#define ET_NONE 0 /* No file type */ +#define ET_REL 1 /* relocatable file */ +#define ET_EXEC 2 /* executable file */ +#define ET_DYN 3 /* shared object file */ +#define ET_CORE 4 /* core file */ +#define ET_NUM 5 /* number of types */ +#define ET_LOOS 0xfe00 /* reserved range for operating */ +#define ET_HIOS 0xfeff /* system specific e_type */ +#define ET_LOPROC 0xff00 /* reserved range for processor */ +#define ET_HIPROC 0xffff /* specific e_type */ + +/* e_machine */ +#define EM_NONE 0 /* No Machine */ +#define EM_M32 1 /* AT&T WE 32100 */ +#define EM_SPARC 2 /* SPARC */ +#define EM_386 3 /* Intel 80386 */ +#define EM_68K 4 /* Motorola 68000 */ +#define EM_88K 5 /* Motorola 88000 */ +#if 0 +#define EM_486 6 /* RESERVED - was Intel 80486 */ +#endif +#define EM_860 7 /* Intel 80860 */ +#define EM_MIPS 8 /* MIPS R3000 Big-Endian only */ +#define EM_S370 9 /* IBM System/370 Processor */ +#define EM_MIPS_RS4_BE 10 /* MIPS R4000 Big-Endian */ +#if 0 +#define EM_SPARC64 11 /* RESERVED - was SPARC v9 + 64-bit unoffical */ +#endif +/* RESERVED 11-14 for future use */ +#define EM_PARISC 15 /* HPPA */ +/* RESERVED 16 for future use */ +#define EM_VPP500 17 /* Fujitsu VPP500 */ +#define EM_SPARC32PLUS 18 /* Enhanced instruction set SPARC */ +#define EM_960 19 /* Intel 80960 */ +#define EM_PPC 20 /* PowerPC */ +#define EM_PPC64 21 /* 64-bit PowerPC */ +#define EM_S390 22 /* IBM System/390 Processor */ +/* RESERVED 23-35 for future use */ +#define EM_V800 36 /* NEC V800 */ +#define EM_FR20 37 /* Fujitsu FR20 */ +#define EM_RH32 38 /* TRW RH-32 */ +#define EM_RCE 39 /* Motorola RCE */ +#define EM_ARM 40 /* Advanced Risc Machines ARM */ +#define EM_ALPHA 41 /* Digital Alpha */ +#define EM_SH 42 /* Hitachi SH */ +#define EM_SPARCV9 43 /* SPARC Version 9 */ +#define EM_TRICORE 44 /* Siemens TriCore embedded processor */ +#define EM_ARC 45 /* Argonaut RISC Core */ +#define EM_H8_300 46 /* Hitachi H8/300 */ +#define EM_H8_300H 47 /* Hitachi H8/300H */ +#define EM_H8S 48 /* Hitachi H8S */ +#define EM_H8_500 49 /* Hitachi H8/500 */ +#define EM_IA_64 50 /* Intel Merced */ +#define EM_MIPS_X 51 /* Stanford MIPS-X */ +#define EM_COLDFIRE 52 /* Motorola Coldfire */ +#define EM_68HC12 53 /* Motorola M68HC12 */ +#define EM_MMA 54 /* Fujitsu MMA Multimedia Accelerator*/ +#define EM_PCP 55 /* Siemens PCP */ +#define EM_NCPU 56 /* Sony nCPU embeeded RISC */ +#define EM_NDR1 57 /* Denso NDR1 microprocessor */ +#define EM_STARCORE 58 /* Motorola Start*Core processor */ +#define EM_ME16 59 /* Toyota ME16 processor */ +#define EM_ST100 60 /* STMicroelectronic ST100 processor */ +#define EM_TINYJ 61 /* Advanced Logic Corp. Tinyj emb.fam*/ +#define EM_X86_64 62 /* AMD x86-64 */ +#define EM_PDSP 63 /* Sony DSP Processor */ +/* RESERVED 64,65 for future use */ +#define EM_FX66 66 /* Siemens FX66 microcontroller */ +#define EM_ST9PLUS 67 /* STMicroelectronics ST9+ 8/16 mc */ +#define EM_ST7 68 /* STmicroelectronics ST7 8 bit mc */ +#define EM_68HC16 69 /* Motorola MC68HC16 microcontroller */ +#define EM_68HC11 70 /* Motorola MC68HC11 microcontroller */ +#define EM_68HC08 71 /* Motorola MC68HC08 microcontroller */ +#define EM_68HC05 72 /* Motorola MC68HC05 microcontroller */ +#define EM_SVX 73 /* Silicon Graphics SVx */ +#define EM_ST19 74 /* STMicroelectronics ST19 8 bit mc */ +#define EM_VAX 75 /* Digital VAX */ +#define EM_CHRIS 76 /* Axis Communications embedded proc. */ +#define EM_JAVELIN 77 /* Infineon Technologies emb. proc. */ +#define EM_FIREPATH 78 /* Element 14 64-bit DSP Processor */ +#define EM_ZSP 79 /* LSI Logic 16-bit DSP Processor */ +#define EM_MMIX 80 /* Donald Knuth's edu 64-bit proc. */ +#define EM_HUANY 81 /* Harvard University mach-indep objs */ +#define EM_PRISM 82 /* SiTera Prism */ +#define EM_AVR 83 /* Atmel AVR 8-bit microcontroller */ +#define EM_FR30 84 /* Fujitsu FR30 */ +#define EM_D10V 85 /* Mitsubishi DV10V */ +#define EM_D30V 86 /* Mitsubishi DV30V */ +#define EM_V850 87 /* NEC v850 */ +#define EM_M32R 88 /* Mitsubishi M32R */ +#define EM_MN10300 89 /* Matsushita MN10200 */ +#define EM_MN10200 90 /* Matsushita MN10200 */ +#define EM_PJ 91 /* picoJava */ +#define EM_NUM 92 /* number of machine types */ + +/* Version */ +#define EV_NONE 0 /* Invalid */ +#define EV_CURRENT 1 /* Current */ +#define EV_NUM 2 /* number of versions */ + +/* Section Header */ +typedef struct { + Elf32_Word sh_name; /* name - index into section header + string table section */ + Elf32_Word sh_type; /* type */ + Elf32_Word sh_flags; /* flags */ + Elf32_Addr sh_addr; /* address */ + Elf32_Off sh_offset; /* file offset */ + Elf32_Word sh_size; /* section size */ + Elf32_Word sh_link; /* section header table index link */ + Elf32_Word sh_info; /* extra information */ + Elf32_Word sh_addralign; /* address alignment */ + Elf32_Word sh_entsize; /* section entry size */ +} Elf32_Shdr; + +/* Special Section Indexes */ +#define SHN_UNDEF 0 /* undefined */ +#define SHN_LORESERVE 0xff00 /* lower bounds of reserved indexes */ +#define SHN_LOPROC 0xff00 /* reserved range for processor */ +#define SHN_HIPROC 0xff1f /* specific section indexes */ +#define SHN_LOOS 0xff20 /* reserved range for operating */ +#define SHN_HIOS 0xff3f /* specific semantics */ +#define SHN_ABS 0xfff1 /* absolute value */ +#define SHN_COMMON 0xfff2 /* common symbol */ +#define SHN_XINDEX 0xffff /* Index is an extra table */ +#define SHN_HIRESERVE 0xffff /* upper bounds of reserved indexes */ + +/* sh_type */ +#define SHT_NULL 0 /* inactive */ +#define SHT_PROGBITS 1 /* program defined information */ +#define SHT_SYMTAB 2 /* symbol table section */ +#define SHT_STRTAB 3 /* string table section */ +#define SHT_RELA 4 /* relocation section with addends*/ +#define SHT_HASH 5 /* symbol hash table section */ +#define SHT_DYNAMIC 6 /* dynamic section */ +#define SHT_NOTE 7 /* note section */ +#define SHT_NOBITS 8 /* no space section */ +#define SHT_REL 9 /* relation section without addends */ +#define SHT_SHLIB 10 /* reserved - purpose unknown */ +#define SHT_DYNSYM 11 /* dynamic symbol table section */ +#define SHT_INIT_ARRAY 14 /* Array of constructors */ +#define SHT_FINI_ARRAY 15 /* Array of destructors */ +#define SHT_PREINIT_ARRAY 16 /* Array of pre-constructors */ +#define SHT_GROUP 17 /* Section group */ +#define SHT_SYMTAB_SHNDX 18 /* Extended section indeces */ +#define SHT_NUM 19 /* number of section types */ +#define SHT_LOOS 0x60000000 /* Start OS-specific */ +#define SHT_HIOS 0x6fffffff /* End OS-specific */ +#define SHT_LOPROC 0x70000000 /* reserved range for processor */ +#define SHT_HIPROC 0x7fffffff /* specific section header types */ +#define SHT_LOUSER 0x80000000 /* reserved range for application */ +#define SHT_HIUSER 0xffffffff /* specific indexes */ + +/* Section names */ +#define ELF_BSS ".bss" /* uninitialized data */ +#define ELF_COMMENT ".comment" /* version control information */ +#define ELF_DATA ".data" /* initialized data */ +#define ELF_DATA1 ".data1" /* initialized data */ +#define ELF_DEBUG ".debug" /* debug */ +#define ELF_DYNAMIC ".dynamic" /* dynamic linking information */ +#define ELF_DYNSTR ".dynstr" /* dynamic string table */ +#define ELF_DYNSYM ".dynsym" /* dynamic symbol table */ +#define ELF_FINI ".fini" /* termination code */ +#define ELF_FINI_ARRAY ".fini_array" /* Array of destructors */ +#define ELF_GOT ".got" /* global offset table */ +#define ELF_HASH ".hash" /* symbol hash table */ +#define ELF_INIT ".init" /* initialization code */ +#define ELF_INIT_ARRAY ".init_array" /* Array of constuctors */ +#define ELF_INTERP ".interp" /* Pathname of program interpreter */ +#define ELF_LINE ".line" /* Symbolic line numnber information */ +#define ELF_NOTE ".note" /* Contains note section */ +#define ELF_PLT ".plt" /* Procedure linkage table */ +#define ELF_PREINIT_ARRAY ".preinit_array" /* Array of pre-constructors */ +#define ELF_REL_DATA ".rel.data" /* relocation data */ +#define ELF_REL_FINI ".rel.fini" /* relocation termination code */ +#define ELF_REL_INIT ".rel.init" /* relocation initialization code */ +#define ELF_REL_DYN ".rel.dyn" /* relocaltion dynamic link info */ +#define ELF_REL_RODATA ".rel.rodata" /* relocation read-only data */ +#define ELF_REL_TEXT ".rel.text" /* relocation code */ +#define ELF_RODATA ".rodata" /* read-only data */ +#define ELF_RODATA1 ".rodata1" /* read-only data */ +#define ELF_SHSTRTAB ".shstrtab" /* section header string table */ +#define ELF_STRTAB ".strtab" /* string table */ +#define ELF_SYMTAB ".symtab" /* symbol table */ +#define ELF_SYMTAB_SHNDX ".symtab_shndx"/* symbol table section index */ +#define ELF_TBSS ".tbss" /* thread local uninit data */ +#define ELF_TDATA ".tdata" /* thread local init data */ +#define ELF_TDATA1 ".tdata1" /* thread local init data */ +#define ELF_TEXT ".text" /* code */ + +/* Section Attribute Flags - sh_flags */ +#define SHF_WRITE 0x1 /* Writable */ +#define SHF_ALLOC 0x2 /* occupies memory */ +#define SHF_EXECINSTR 0x4 /* executable */ +#define SHF_MERGE 0x10 /* Might be merged */ +#define SHF_STRINGS 0x20 /* Contains NULL terminated strings */ +#define SHF_INFO_LINK 0x40 /* sh_info contains SHT index */ +#define SHF_LINK_ORDER 0x80 /* Preserve order after combining*/ +#define SHF_OS_NONCONFORMING 0x100 /* Non-standard OS specific handling */ +#define SHF_GROUP 0x200 /* Member of section group */ +#define SHF_TLS 0x400 /* Thread local storage */ +#define SHF_MASKOS 0x0ff00000 /* OS specific */ +#define SHF_MASKPROC 0xf0000000 /* reserved bits for processor */ + /* specific section attributes */ + +/* Section Group Flags */ +#define GRP_COMDAT 0x1 /* COMDAT group */ +#define GRP_MASKOS 0x0ff00000 /* Mask OS specific flags */ +#define GRP_MASKPROC 0xf0000000 /* Mask processor specific flags */ + +/* Symbol Table Entry */ +typedef struct elf32_sym { + Elf32_Word st_name; /* name - index into string table */ + Elf32_Addr st_value; /* symbol value */ + Elf32_Word st_size; /* symbol size */ + unsigned char st_info; /* type and binding */ + unsigned char st_other; /* 0 - no defined meaning */ + Elf32_Half st_shndx; /* section header index */ +} Elf32_Sym; + +/* Symbol table index */ +#define STN_UNDEF 0 /* undefined */ + +/* Extract symbol info - st_info */ +#define ELF32_ST_BIND(x) ((x) >> 4) +#define ELF32_ST_TYPE(x) (((unsigned int) x) & 0xf) +#define ELF32_ST_INFO(b,t) (((b) << 4) + ((t) & 0xf)) +#define ELF32_ST_VISIBILITY(x) ((x) & 0x3) + +/* Symbol Binding - ELF32_ST_BIND - st_info */ +#define STB_LOCAL 0 /* Local symbol */ +#define STB_GLOBAL 1 /* Global symbol */ +#define STB_WEAK 2 /* like global - lower precedence */ +#define STB_NUM 3 /* number of symbol bindings */ +#define STB_LOOS 10 /* reserved range for operating */ +#define STB_HIOS 12 /* system specific symbol bindings */ +#define STB_LOPROC 13 /* reserved range for processor */ +#define STB_HIPROC 15 /* specific symbol bindings */ + +/* Symbol type - ELF32_ST_TYPE - st_info */ +#define STT_NOTYPE 0 /* not specified */ +#define STT_OBJECT 1 /* data object */ +#define STT_FUNC 2 /* function */ +#define STT_SECTION 3 /* section */ +#define STT_FILE 4 /* file */ +#define STT_NUM 5 /* number of symbol types */ +#define STT_TLS 6 /* Thread local storage symbol */ +#define STT_LOOS 10 /* reserved range for operating */ +#define STT_HIOS 12 /* system specific symbol types */ +#define STT_LOPROC 13 /* reserved range for processor */ +#define STT_HIPROC 15 /* specific symbol types */ + +/* Symbol visibility - ELF32_ST_VISIBILITY - st_other */ +#define STV_DEFAULT 0 /* Normal visibility rules */ +#define STV_INTERNAL 1 /* Processor specific hidden class */ +#define STV_HIDDEN 2 /* Symbol unavailable in other mods */ +#define STV_PROTECTED 3 /* Not preemptible, not exported */ + + +/* Relocation entry with implicit addend */ +typedef struct +{ + Elf32_Addr r_offset; /* offset of relocation */ + Elf32_Word r_info; /* symbol table index and type */ +} Elf32_Rel; + +/* Relocation entry with explicit addend */ +typedef struct +{ + Elf32_Addr r_offset; /* offset of relocation */ + Elf32_Word r_info; /* symbol table index and type */ + Elf32_Sword r_addend; +} Elf32_Rela; + +/* Extract relocation info - r_info */ +#define ELF32_R_SYM(i) ((i) >> 8) +#define ELF32_R_TYPE(i) ((unsigned char) (i)) +#define ELF32_R_INFO(s,t) (((s) << 8) + (unsigned char)(t)) + +/* Program Header */ +typedef struct { + Elf32_Word p_type; /* segment type */ + Elf32_Off p_offset; /* segment offset */ + Elf32_Addr p_vaddr; /* virtual address of segment */ + Elf32_Addr p_paddr; /* physical address - ignored? */ + Elf32_Word p_filesz; /* number of bytes in file for seg. */ + Elf32_Word p_memsz; /* number of bytes in mem. for seg. */ + Elf32_Word p_flags; /* flags */ + Elf32_Word p_align; /* memory alignment */ +} Elf32_Phdr; + +/* Segment types - p_type */ +#define PT_NULL 0 /* unused */ +#define PT_LOAD 1 /* loadable segment */ +#define PT_DYNAMIC 2 /* dynamic linking section */ +#define PT_INTERP 3 /* the RTLD */ +#define PT_NOTE 4 /* auxiliary information */ +#define PT_SHLIB 5 /* reserved - purpose undefined */ +#define PT_PHDR 6 /* program header */ +#define PT_TLS 7 /* Thread local storage template */ +#define PT_NUM 8 /* Number of segment types */ +#define PT_LOOS 0x60000000 /* reserved range for operating */ +#define PT_HIOS 0x6fffffff /* system specific segment types */ +#define PT_LOPROC 0x70000000 /* reserved range for processor */ +#define PT_HIPROC 0x7fffffff /* specific segment types */ + +/* Segment flags - p_flags */ +#define PF_X 0x1 /* Executable */ +#define PF_W 0x2 /* Writable */ +#define PF_R 0x4 /* Readable */ +#define PF_MASKOS 0x0ff00000 /* OS specific segment flags */ +#define PF_MASKPROC 0xf0000000 /* reserved bits for processor */ + /* specific segment flags */ +/* Dynamic structure */ +typedef struct +{ + Elf32_Sword d_tag; /* controls meaning of d_val */ + union + { + Elf32_Word d_val; /* Multiple meanings - see d_tag */ + Elf32_Addr d_ptr; /* program virtual address */ + } d_un; +} Elf32_Dyn; + +extern Elf32_Dyn _DYNAMIC[]; + +/* Dynamic Array Tags - d_tag */ +#define DT_NULL 0 /* marks end of _DYNAMIC array */ +#define DT_NEEDED 1 /* string table offset of needed lib */ +#define DT_PLTRELSZ 2 /* size of relocation entries in PLT */ +#define DT_PLTGOT 3 /* address PLT/GOT */ +#define DT_HASH 4 /* address of symbol hash table */ +#define DT_STRTAB 5 /* address of string table */ +#define DT_SYMTAB 6 /* address of symbol table */ +#define DT_RELA 7 /* address of relocation table */ +#define DT_RELASZ 8 /* size of relocation table */ +#define DT_RELAENT 9 /* size of relocation entry */ +#define DT_STRSZ 10 /* size of string table */ +#define DT_SYMENT 11 /* size of symbol table entry */ +#define DT_INIT 12 /* address of initialization func. */ +#define DT_FINI 13 /* address of termination function */ +#define DT_SONAME 14 /* string table offset of shared obj */ +#define DT_RPATH 15 /* string table offset of library + search path */ +#define DT_SYMBOLIC 16 /* start sym search in shared obj. */ +#define DT_REL 17 /* address of rel. tbl. w addends */ +#define DT_RELSZ 18 /* size of DT_REL relocation table */ +#define DT_RELENT 19 /* size of DT_REL relocation entry */ +#define DT_PLTREL 20 /* PLT referenced relocation entry */ +#define DT_DEBUG 21 /* bugger */ +#define DT_TEXTREL 22 /* Allow rel. mod. to unwritable seg */ +#define DT_JMPREL 23 /* add. of PLT's relocation entries */ +#define DT_BIND_NOW 24 /* Process relocations of object */ +#define DT_INIT_ARRAY 25 /* Array with addresses of init fct */ +#define DT_FINI_ARRAY 26 /* Array with addresses of fini fct */ +#define DT_INIT_ARRAYSZ 27 /* Size in bytes of DT_INIT_ARRAY */ +#define DT_FINI_ARRAYSZ 28 /* Size in bytes of DT_FINI_ARRAY */ +#define DT_RUNPATH 29 /* Library search path */ +#define DT_FLAGS 30 /* Flags for the object being loaded */ +#define DT_ENCODING 32 /* Start of encoded range */ +#define DT_PREINIT_ARRAY 32 /* Array with addresses of preinit fct*/ +#define DT_PREINIT_ARRAYSZ 33 /* size in bytes of DT_PREINIT_ARRAY */ +#define DT_NUM 34 /* Number used. */ +#define DT_LOOS 0x60000000 /* reserved range for OS */ +#define DT_HIOS 0x6fffffff /* specific dynamic array tags */ +#define DT_LOPROC 0x70000000 /* reserved range for processor */ +#define DT_HIPROC 0x7fffffff /* specific dynamic array tags */ + +/* Dynamic Tag Flags - d_un.d_val */ +#define DF_ORIGIN 0x01 /* Object may use DF_ORIGIN */ +#define DF_SYMBOLIC 0x02 /* Symbol resolutions starts here */ +#define DF_TEXTREL 0x04 /* Object contains text relocations */ +#define DF_BIND_NOW 0x08 /* No lazy binding for this object */ +#define DF_STATIC_TLS 0x10 /* Static thread local storage */ + +/* Standard ELF hashing function */ +unsigned long elf_hash(const unsigned char *name); + +#define ELF_TARG_VER 1 /* The ver for which this code is intended */ + +/* + * XXX - PowerPC defines really don't belong in here, + * but we'll put them in for simplicity. + */ + +/* Values for Elf32/64_Ehdr.e_flags. */ +#define EF_PPC_EMB 0x80000000 /* PowerPC embedded flag */ + +/* Cygnus local bits below */ +#define EF_PPC_RELOCATABLE 0x00010000 /* PowerPC -mrelocatable flag*/ +#define EF_PPC_RELOCATABLE_LIB 0x00008000 /* PowerPC -mrelocatable-lib + flag */ + +/* PowerPC relocations defined by the ABIs */ +#define R_PPC_NONE 0 +#define R_PPC_ADDR32 1 /* 32bit absolute address */ +#define R_PPC_ADDR24 2 /* 26bit address, 2 bits ignored. */ +#define R_PPC_ADDR16 3 /* 16bit absolute address */ +#define R_PPC_ADDR16_LO 4 /* lower 16bit of absolute address */ +#define R_PPC_ADDR16_HI 5 /* high 16bit of absolute address */ +#define R_PPC_ADDR16_HA 6 /* adjusted high 16bit */ +#define R_PPC_ADDR14 7 /* 16bit address, 2 bits ignored */ +#define R_PPC_ADDR14_BRTAKEN 8 +#define R_PPC_ADDR14_BRNTAKEN 9 +#define R_PPC_REL24 10 /* PC relative 26 bit */ +#define R_PPC_REL14 11 /* PC relative 16 bit */ +#define R_PPC_REL14_BRTAKEN 12 +#define R_PPC_REL14_BRNTAKEN 13 +#define R_PPC_GOT16 14 +#define R_PPC_GOT16_LO 15 +#define R_PPC_GOT16_HI 16 +#define R_PPC_GOT16_HA 17 +#define R_PPC_PLTREL24 18 +#define R_PPC_COPY 19 +#define R_PPC_GLOB_DAT 20 +#define R_PPC_JMP_SLOT 21 +#define R_PPC_RELATIVE 22 +#define R_PPC_LOCAL24PC 23 +#define R_PPC_UADDR32 24 +#define R_PPC_UADDR16 25 +#define R_PPC_REL32 26 +#define R_PPC_PLT32 27 +#define R_PPC_PLTREL32 28 +#define R_PPC_PLT16_LO 29 +#define R_PPC_PLT16_HI 30 +#define R_PPC_PLT16_HA 31 +#define R_PPC_SDAREL16 32 +#define R_PPC_SECTOFF 33 +#define R_PPC_SECTOFF_LO 34 +#define R_PPC_SECTOFF_HI 35 +#define R_PPC_SECTOFF_HA 36 +/* Keep this the last entry. */ +#define R_PPC_NUM 37 + +/* The remaining relocs are from the Embedded ELF ABI, and are not + in the SVR4 ELF ABI. */ +#define R_PPC_EMB_NADDR32 101 +#define R_PPC_EMB_NADDR16 102 +#define R_PPC_EMB_NADDR16_LO 103 +#define R_PPC_EMB_NADDR16_HI 104 +#define R_PPC_EMB_NADDR16_HA 105 +#define R_PPC_EMB_SDAI16 106 +#define R_PPC_EMB_SDA2I16 107 +#define R_PPC_EMB_SDA2REL 108 +#define R_PPC_EMB_SDA21 109 /* 16 bit offset in SDA */ +#define R_PPC_EMB_MRKREF 110 +#define R_PPC_EMB_RELSEC16 111 +#define R_PPC_EMB_RELST_LO 112 +#define R_PPC_EMB_RELST_HI 113 +#define R_PPC_EMB_RELST_HA 114 +#define R_PPC_EMB_BIT_FLD 115 +#define R_PPC_EMB_RELSDA 116 /* 16 bit relative offset in SDA */ + +/* Diab tool relocations. */ +#define R_PPC_DIAB_SDA21_LO 180 /* like EMB_SDA21, but lower 16 bit */ +#define R_PPC_DIAB_SDA21_HI 181 /* like EMB_SDA21, but high 16 bit */ +#define R_PPC_DIAB_SDA21_HA 182 /* like EMB_SDA21, adjusted high 16 */ +#define R_PPC_DIAB_RELSDA_LO 183 /* like EMB_RELSDA, but lower 16 bit */ +#define R_PPC_DIAB_RELSDA_HI 184 /* like EMB_RELSDA, but high 16 bit */ +#define R_PPC_DIAB_RELSDA_HA 185 /* like EMB_RELSDA, adjusted high 16 */ + +/* This is a phony reloc to handle any old fashioned TOC16 references + that may still be in object files. */ +#define R_PPC_TOC16 255 + +#endif /* _ELF_H */ + diff --git a/channel/channelapp/source/font.c b/channel/channelapp/source/font.c new file mode 100644 index 0000000..c425d06 --- /dev/null +++ b/channel/channelapp/source/font.c @@ -0,0 +1,774 @@ +#include +#include + +#include +#include FT_FREETYPE_H + +#include "../config.h" +#include "panic.h" +#include "theme.h" +#include "font.h" + +#include "droid_ttf.h" +#include "droidbold_ttf.h" + +#define ROUNDUP4B(x) ((x + 4 - 1) & ~(4 - 1)) + +#define X_RATIO (widescreen?WIDESCREEN_RATIO:1.0) + +//#define FONT_CHECKER + +FT_Library library; + +typedef struct { + int codepoint; + int next_idx; + int glyph_index; + int valid; + gfx_entity *texture; + int x, y; + int w, h; + FT_Pos dx, dy; +} font_glyph; + +typedef struct { + const void *data; + u32 data_len; + FT_Face face; + int max_glyphs; + int num_glyphs; + int em_w, em_h; + int em_height; + int ascender; + int descender; + int height; + font_glyph *glyphs; +} font_face; + +static font_face *fonts[FONT_MAX]; + +typedef struct { + int size; + const void *data; + const u32 *data_len; // bin2s stupidity +} font_default; + +const font_default default_fonts[FONT_MAX] = +{ + [FONT_LABEL] = { 16, droidbold_ttf, &droidbold_ttf_size}, + [FONT_DLGTITLE] = { 20, droidbold_ttf, &droidbold_ttf_size}, + [FONT_MEMO] = { 16, droid_ttf, &droid_ttf_size}, + [FONT_APPNAME] = { 20, droidbold_ttf, &droidbold_ttf_size}, + [FONT_APPDESC] = { 16, droidbold_ttf, &droidbold_ttf_size}, + [FONT_BUTTON] = { 20, droidbold_ttf, &droidbold_ttf_size}, + [FONT_BUTTON_DESEL] = { 20, droid_ttf, &droid_ttf_size}, +}; + +#define GLYPH_CACHE_ROOT 64 +#define GLYPH_CACHE_INIT 32 +#define GLYPH_CACHE_GROW 32 + +font_glyph *font_get_char(font_id id, int codepoint); + +#if 0 +static const char *utf8(u32 codepoint) +{ + static char buf[5]; + if (codepoint < 0x80) { + buf[0] = codepoint; + buf[1] = 0; + } else if (codepoint < 0x800) { + buf[0] = 0xC0 | (codepoint>>6); + buf[1] = 0x80 | (codepoint&0x3F); + buf[2] = 0; + } else if (codepoint < 0x10000) { + buf[0] = 0xE0 | (codepoint>>12); + buf[1] = 0x80 | ((codepoint>>6)&0x3F); + buf[2] = 0x80 | (codepoint&0x3F); + buf[3] = 0; + } else if (codepoint < 0x110000) { + buf[0] = 0xF0 | (codepoint>>18); + buf[1] = 0x80 | ((codepoint>>12)&0x3F); + buf[2] = 0x80 | ((codepoint>>6)&0x3F); + buf[3] = 0x80 | (codepoint&0x3F); + buf[4] = 0; + } else { + buf[0] = '?'; + buf[1] = 0; + } + return buf; +} +#endif + +int font_load(font_id id, bool use_theme) +{ + int ret; + int i; + + const void *data; + u32 data_len; + int em; + + font_face *font; + + if (fonts[id]) + return 0; + + gprintf("Loading font ID %d\n", id); + + data = default_fonts[id].data; + data_len = *default_fonts[id].data_len; + em = default_fonts[id].size; + + if (use_theme && theme_fonts[id].data) { + data = theme_fonts[id].data; + data_len = theme_fonts[id].data_len; + } + + if (use_theme && theme_fonts[id].size) + em = theme_fonts[id].size; + + // maybe we can reuse the entire font, look for other fonts + for (i=0; idata == data && fonts[i]->em_h == em) { + gprintf("Cloned font object from font ID %d\n", i); + fonts[id] = fonts[i]; + return 0; + } + } + + font = pmalloc(sizeof(*font)); + memset(font, 0, sizeof(*font)); + + font->data = data; + font->data_len = data_len; + font->em_h = font->em_w = em; + + font->face = NULL; + + // maybe we can reuse the FT_Font object, look for other fonts + for (i=0; idata == font->data) { + gprintf("Reused FreeType font subobject from font ID %d\n", i); + font->face = fonts[i]->face; + break; + } + } + + if (!font->face) { + ret = FT_New_Memory_Face(library, font->data, font->data_len, 0, &font->face); + if (ret != 0) { + gprintf("Failed to load font\n"); + // try to fall back + if (use_theme && theme_fonts[id].data) { + free(font); + theme_fonts[id].data = NULL; + return font_load(id, false); + } + return ret; + } else { + gprintf("Loaded font at %p\n", font->data); + } + } + gprintf("Font contains %ld faces\n", font->face->num_faces); + gprintf("Face contains %ld glyphs\n", font->face->num_glyphs); + + if (widescreen) + font->em_w = (int)(font->em_w / WIDESCREEN_RATIO + 0.5); + + ret = FT_Set_Pixel_Sizes(font->face, font->em_w, font->em_h); + if (ret) { + gprintf("FT_Set_Pixel_Sizes failed: %d\n", ret); + free(font); + return ret; + } + + if (!FT_HAS_KERNING(font->face)) + gprintf("Font has no usable kerning data\n"); + else + gprintf("Font has kerning data\n"); + + font->max_glyphs = GLYPH_CACHE_ROOT+GLYPH_CACHE_INIT; + font->num_glyphs = GLYPH_CACHE_ROOT; //base "hashtable" set + font->glyphs = pmalloc(sizeof(font_glyph) * font->max_glyphs); + memset(font->glyphs, 0, sizeof(font_glyph) * font->max_glyphs); + for(i=0; imax_glyphs; i++) { + font->glyphs[i].codepoint = -1; + font->glyphs[i].next_idx = -1; + } + + fonts[id] = font; + + font_glyph *glyph = font_get_char(id, 'M'); + + font->em_height = glyph->h; // height of capital M + font->ascender = (font->face->size->metrics.ascender+32)>>6; + font->descender = (font->face->size->metrics.descender+32)>>6; + font->height = (font->face->size->metrics.height+32)>>6; + + gprintf("Ascender is %d\n", font->ascender); + gprintf("Descender is %d\n", font->descender); + gprintf("Height is %d\n", font->height); + + gprintf("Font ID %d loaded\n", id); + return 0; +} + +font_glyph *font_get_char(font_id id, int codepoint) +{ + int ret; + font_glyph *glyph; + + font_load(id,1); + font_face *font = fonts[id]; + + glyph = &font->glyphs[codepoint&(GLYPH_CACHE_ROOT-1)]; + + if (glyph->codepoint != -1) { + while(1) { + if (glyph->codepoint == codepoint) { + //gprintf("FONT: Glyph %d (%s) is cached\n", codepoint, utf8(codepoint)); + return glyph; + } + if (glyph->next_idx == -1) { + glyph->next_idx = font->num_glyphs; + if (font->num_glyphs == font->max_glyphs) { + font->glyphs = prealloc(font->glyphs, sizeof(font_glyph) * (font->max_glyphs + GLYPH_CACHE_GROW)); + memset(&font->glyphs[font->max_glyphs], 0, GLYPH_CACHE_GROW*sizeof(font_glyph)); + font->max_glyphs += GLYPH_CACHE_GROW; + gprintf("FONT: expanded glyph cache size to %d\n", font->max_glyphs); + } + glyph = &font->glyphs[font->num_glyphs]; + font->num_glyphs++; + break; + } + glyph = &font->glyphs[glyph->next_idx]; + } + } + + memset(glyph, 0, sizeof(*glyph)); + glyph->codepoint = codepoint; + glyph->next_idx = -1; + glyph->valid = 0; + glyph->texture = pmalloc(sizeof(*glyph->texture)); + glyph->dx = 0; + glyph->dy = 0; + + FT_GlyphSlot slot = font->face->glyph; + FT_UInt glyph_index; + + ret = FT_Set_Pixel_Sizes(font->face, font->em_w, font->em_h); + if (ret) + return glyph; + + glyph_index = FT_Get_Char_Index(font->face, codepoint); + ret = FT_Load_Glyph(font->face, glyph_index, FT_LOAD_DEFAULT); + if (ret) + return glyph; + + ret = FT_Render_Glyph(slot, FT_RENDER_MODE_NORMAL); + if (ret) + return glyph; + + glyph->dx = slot->advance.x; + glyph->dy = slot->advance.y; + glyph->glyph_index = glyph_index; + + int cw = slot->bitmap.width; + int ch = slot->bitmap.rows; + + glyph->w = cw; + glyph->h = ch; + + if (!cw || !ch) + return glyph; + + int tw = (cw+7)/8; + int th = (ch+3)/4; + + int tpitch = tw * 32; + + u8 *pix = pmemalign(32, tw*th*32); + memset(pix, 0, tw*th*32); + + int x,y; + u8 *p = slot->bitmap.buffer; + for(y=0; ybitmap.pitch; + } + + gfx_gen_tex (glyph->texture, 8*tw, 4*th, pix, GFXT_A8); + + glyph->x = slot->bitmap_left; + glyph->y = slot->bitmap_top; + glyph->valid = 1; + + //gprintf("FONT: Rendered and cached glyph %d (%s) at pos %d size %dx%d\n", codepoint, utf8(codepoint), glyph-font->glyphs, cw, ch); + //gprintf("Free MEM1: %ld MEM2: %ld\n", SYS_GetArena1Size(), SYS_GetArena2Size()); + return glyph; +} + +void font_kern(font_id id, int left, int right, FT_Pos *dx, FT_Pos *dy) +{ + FT_Vector delta; + if (!FT_HAS_KERNING(fonts[id]->face)) { + return; + } + if (FT_Get_Kerning(fonts[id]->face, left, right, FT_KERNING_UNFITTED, &delta)) + return; + //gprintf("Kern font %d for glyphs %d,%d is %ld,%ld\n", id, left, right, delta.x, delta.y); + *dx += delta.x; + *dy += delta.y; +} + + +void font_init(void) +{ + int ret; + + memset(fonts, 0, sizeof(fonts)); + + ret = FT_Init_FreeType(&library); + if (ret != 0) { + gprintf("FreeType init failed (%d)\n", ret); + return; + } + gprintf("FreeType initialized\n"); +} + +void font_clear (void) { + int id,id2; + for (id=0; idnum_glyphs; g++) { + if (fonts[id]->glyphs[g].texture) + free(fonts[id]->glyphs[g].texture); + fonts[id]->glyphs[g].texture = NULL; + } + free(fonts[id]->glyphs); + fonts[id]->glyphs = NULL; + + if (fonts[id]->face) { + FT_Face face = fonts[id]->face; + FT_Done_Face(face); + // other fonts can share this face object, so nuke all + for (id2=0; id2face == face) + fonts[id2]->face = NULL; + } + } + + free(fonts[id]); + fonts[id] = NULL; + } +} + +void font_deinit (void) { + font_clear(); + FT_Done_FreeType(library); +} + +static u32 utf8_get_char(const char **s) +{ + const char *c = *s; + u32 mbc = '?'; + + if (!c[0]) + return 0; + + if (c[0] <= 0x7f) { + mbc = c[0]; + c++; + } else if (c[0] >= 0xc0 && c[0] <= 0xdf) { + if ((c[1]&0xc0) != 0x80) { + c++; + if (c[0]) + c++; + } else { + mbc = (c[1]&0x3f) | ((c[0]&0x1f)<<6); + c+=2; + } + } else if (c[0] >= 0xe0 && c[0] <= 0xef) { + if (((c[1]&0xc0) != 0x80) || ((c[2]&0xc0) != 0x80)) { + c++; + if (c[0]) + c++; + if (c[0]) + c++; + } else { + mbc = (c[2]&0x3f) | ((c[1]&0x3f)<<6) | ((c[0]&0x1f)<<12); + c+=3; + } + } else if (c[0] >= 0xf0 && c[0] <= 0xf7) { + if (((c[1]&0xc0) != 0x80) || ((c[2]&0xc0) != 0x80) || ((c[3]&0xc0) != 0x80)) { + c++; + if (c[0]) + c++; + if (c[0]) + c++; + if (c[0]) + c++; + } else { + mbc = (c[3]&0x3f) | ((c[2]&0x3f)<<6) | ((c[1]&0x3f)<<12) | ((c[0]&0x1f)<<18); + c+=4; + } + } else { + c++; + } + *s = c; + return mbc; +} + +int font_get_ascender(font_id id) { + font_load(id,1); + + return fonts[id]->ascender; +} + +int font_get_height(font_id id) { + font_load(id,1); + + return fonts[id]->ascender - fonts[id]->descender; +} + +int font_get_em_height(font_id id) { + font_load(id,1); + + return fonts[id]->em_height; +} + +int font_get_y_spacing(font_id id) { + font_load(id,1); + + return fonts[id]->height; +} + +int font_get_min_y(font_id id) { + font_load(id,1); + + return -fonts[id]->descender; +} + +u16 font_get_string_width (font_id id, const char *s, int count) { + int i = 0; + u32 mbc; + int cx = 0; + int cy = 0; + + FT_Pos cdx = 0, cdy = 0; + u32 previous = 0; + + font_load(id,1); + + while((mbc = utf8_get_char(&s))) { + if (mbc == '\n') { + if (*s) + mbc = ' '; + else + continue; + } + if (mbc == '\r') + continue; + font_glyph *glyph = font_get_char(id, mbc); + + if (previous) + font_kern(id, previous, glyph->glyph_index, &cdx, &cdy); + + cx += (cdx+32) >> 6; + cy += (cdy+32) >> 6; + + cdx = glyph->dx; + cdy = glyph->dy; + previous = glyph->glyph_index; + i++; + if (i == count) + break; + } + + cx += (cdx+32) >> 6; + cy += (cdy+32) >> 6; + + return cx; +} + +int font_get_char_count (font_id id, const char *s, u16 max_width) { + //u16 res = 0; + int i = 0; + u32 mbc; + int cx = 0; + int cy = 0; + + FT_Pos cdx = 0, cdy = 0; + u32 previous = 0; + + max_width /= X_RATIO; + + font_load(id,1); + + while((mbc = utf8_get_char(&s))) { + if (mbc == '\n') { + if (*s) + mbc = ' '; + else + continue; + } + if (mbc == '\r') + continue; + font_glyph *glyph = font_get_char(id, mbc); + + if (previous) + font_kern(id, previous, glyph->glyph_index, &cdx, &cdy); + + cx += (cdx+32) >> 6; + cy += (cdy+32) >> 6; + + if (max_width && (cx >= max_width)) + return i; + + cdx = glyph->dx; + cdy = glyph->dy; + previous = glyph->glyph_index; + i++; + } + + return i; +} + +int font_wrap_string (char ***lines, font_id id, const char *s, + u16 max_width) { + const char *p = s; + char **res = NULL; + int line = 0; + int start = 0; + int end = 0; + int ls = -1; + bool lb; + + int cx = 0; + int cy = 0; + + int i = 0; + u32 mbc; + + FT_Pos cdx = 0, cdy = 0; + u32 previous = 0; + + max_width /= X_RATIO; + + font_load(id,1); + + while (true) { + i = p - s; + mbc = utf8_get_char(&p); + lb = false; + + if (mbc == ' ') + ls = p - s; + + if ((mbc == '\n') || mbc == 0) { + lb = true; + end = i; + i = p - s; + } else if (mbc == '\r') { + continue; + } else { + font_glyph *glyph = font_get_char(id, mbc); + + if (previous) + font_kern(id, previous, glyph->glyph_index, &cdx, &cdy); + + cx += (cdx+32) >> 6; + cy += (cdy+32) >> 6; + + int w = (glyph->dx+32) >> 6; + if ((glyph->w + glyph->x) > w) + w = glyph->w + glyph->x; + + if ((cx + w) >= max_width) { + lb = true; + if (ls <= start) { + if (i == start) + i++; + end = i; + p = &s[i]; + } else { + end = ls; + i = ls; + p = &s[i]; + } + } + + cdx = glyph->dx; + cdy = glyph->dy; + previous = glyph->glyph_index; + } + + if (lb) { + res = prealloc (res, (line + 1) * sizeof (char **)); + if (end <= start) + res[line] = NULL; + else { + res[line] = strndup (&s[start], end - start); + } + + line++; + start = i; + cx = 0; + cy = 0; + cdx = 0; + cdy = 0; + previous = 0; + } + + if (mbc == 0) + break; + } + + *lines = res; + return line; +} + +void font_free_lines (char **lines, u32 count) { + int i; + + for (i = 0; i < count; ++i) + free (lines[i]); + + free (lines); +} + +void font_plot_string (gfx_queue_entry *entries, int count, font_id id, + const char *s, u16 x, u16 y, u16 layer, u16 width, + font_xalign xalign, font_yalign yalign) { + int cx; + int cy; + + + if (!count) + return; + + cx = x; + cy = y; + + cx /= X_RATIO; + + switch (xalign) { + case FA_LEFT: + break; + + case FA_CENTERED: + cx += (width/X_RATIO - font_get_string_width (id, s, count)) / 2; + break; + + case FA_RIGHT: + cx += width/X_RATIO - font_get_string_width (id, s, count); + break; + } + + switch (yalign) { + case FA_ASCENDER: + cy += fonts[id]->ascender; + break; + case FA_EM_TOP: + cy += fonts[id]->em_height; + break; + case FA_EM_CENTERED: + cy += fonts[id]->em_height/2; + break; + case FA_BASELINE: + break; + case FA_DESCENDER: + cy += fonts[id]->descender; + break; + } + + FT_Pos cdx = 0, cdy = 0; + u32 previous = 0; + int first = 1; + u32 mbc; + + while ((mbc = utf8_get_char(&s))) { + if (mbc == '\n') { + if (*s) + mbc = ' '; + else + break; + } + if (mbc == '\r') + continue; + font_glyph *glyph = font_get_char(id, mbc); + + if (previous) + font_kern(id, previous, glyph->glyph_index, &cdx, &cdy); + + cx += (cdx+32) >> 6; + cy += (cdy+32) >> 6; + + if (!glyph->valid) { + entries->type = GFXQ_NULL; + goto next_glyph; + } + + // fudgity fudge, helps make text left-align slightly better/nicer, + // especially with differing fonts. + // do this only for chars with glyph->x > 0. Those with glyph->x < 0 + // are usually kerned together and look better a bit to the left anyway. + if(xalign == FA_LEFT && first && glyph->x > 0) + cx -= glyph->x; + + gfx_qe_entity (entries, glyph->texture, + ((float)(cx + glyph->x)) * X_RATIO, + cy - glyph->y, + layer, theme_fonts[id].color); + + //gprintf("Render %d (%s) at %d %d -> %d %d size %d %d\n", mbc, utf8(mbc), cx, cy, cx + glyph->x, cy - glyph->y + fonts[id]->height, glyph->texture->w, glyph->texture->h); + + first = 0; + +next_glyph: + count--; + entries++; + + if (!count) + break; + + cdx = glyph->dx; + cdy = glyph->dy; + previous = glyph->glyph_index; + } + + if (count) { + gprintf("BUG: %d queue entries empty, padding with NULLs\n", count); + while (count--) { + entries->type = GFXQ_NULL; + // this superfluous statement carefully chosen for optimal crashiness + entries++->entity.coords.z = 0; + } + } +} + diff --git a/channel/channelapp/source/font.h b/channel/channelapp/source/font.h new file mode 100644 index 0000000..b6ea154 --- /dev/null +++ b/channel/channelapp/source/font.h @@ -0,0 +1,54 @@ +#ifndef _FONT_H_ +#define _FONT_H_ + +#include + +#include "gfx.h" + +typedef enum { + FONT_LABEL = 0, + FONT_DLGTITLE, + FONT_MEMO, + FONT_APPNAME, + FONT_APPDESC, + FONT_BUTTON, + FONT_BUTTON_DESEL, + + FONT_MAX +} font_id; + +typedef enum { + FA_LEFT = 0, + FA_CENTERED, + FA_RIGHT +} font_xalign; + +typedef enum { + FA_ASCENDER = 0, + FA_EM_TOP, + FA_EM_CENTERED, + FA_BASELINE, + FA_DESCENDER, +} font_yalign; + + +void font_init (void); +void font_clear (void); +void font_deinit (void); + +int font_get_height (font_id id); +int font_get_em_height (font_id id); +int font_get_ascender (font_id id); +int font_get_y_spacing (font_id id); +int font_get_min_y(font_id id); + +int font_get_char_count (font_id id, const char *s, u16 max_width); +int font_wrap_string (char ***lines, font_id id, const char *s, u16 max_width); +void font_free_lines (char **lines, u32 count); + +void font_plot_string (gfx_queue_entry *entries, int count, font_id id, + const char *s, u16 x, u16 y, u16 layer, u16 width, + font_xalign xalign, font_yalign yalign); + +#endif + diff --git a/channel/channelapp/source/gfx.c b/channel/channelapp/source/gfx.c new file mode 100644 index 0000000..d630f5d --- /dev/null +++ b/channel/channelapp/source/gfx.c @@ -0,0 +1,539 @@ +#include +#include +#include +#include +#include + +#include +#include +#include +#include + +#include "../config.h" +#include "gfx.h" +#include "panic.h" +#include "title.h" + +#define DEFAULT_FIFO_SIZE (256 * 1024) +#define CLIPPING_X (view_width / 2 + 64) +#define CLIPPING_Y (view_height / 2 + 64) + +#define FONT_NEAREST_NEIGHBOR + +static u32 *xfb; +static GXRModeObj *vmode; + +static float px_width, px_height; +static u8 *gp_fifo; + +static Mtx view; + +static u8 origin_stack_size; +static const gfx_coordinates *origin_stack[GFX_ORIGIN_STACK_SIZE]; +static gfx_coordinates origin_current; + +static u32 *efb_buffer = NULL; + +u16 view_width = 0; +u16 view_height = 0; +bool widescreen = false; + +void gfx_init_video (void) { + u8 i; + + VIDEO_Init (); + + vmode = VIDEO_GetPreferredMode (NULL); + +#ifdef ENABLE_WIDESCREEN + if (CONF_GetAspectRatio() == CONF_ASPECT_16_9) { + gprintf("aspect: 16:9\n"); + view_width = 852; // 16:9 + view_height = 480; + + vmode->viWidth = 672; + + widescreen = true; + + if (is_vwii()) { + // poke DMCU to turn off pillarboxing + write32(0xd8006a0, 0x30000004); + mask32(0xd8006a8, 0, 2); + } + } else { +#endif + gprintf("aspect: 4:3\n"); + view_width = 640; + view_height = 480; + + vmode->viWidth = 672; + + if (is_vwii()) { + // poke DMCU to turn on pillarboxing + write32(0xd8006a0, 0x10000002); + mask32(0xd8006a8, 0, 2); + } +#ifdef ENABLE_WIDESCREEN + } +#endif + + if (vmode == &TVPal576IntDfScale || vmode == &TVPal576ProgScale) { + vmode->viXOrigin = (VI_MAX_WIDTH_PAL - vmode->viWidth) / 2; + vmode->viYOrigin = (VI_MAX_HEIGHT_PAL - vmode->viHeight) / 2; + } else { + vmode->viXOrigin = (VI_MAX_WIDTH_NTSC - vmode->viWidth) / 2; + vmode->viYOrigin = (VI_MAX_HEIGHT_NTSC - vmode->viHeight) / 2; + } + + px_width = vmode->fbWidth / (float) view_width; + px_height = vmode->efbHeight / (float) view_height; + + VIDEO_SetBlack (TRUE); + VIDEO_Configure (vmode); + VIDEO_Flush (); + VIDEO_WaitVSync(); + + xfb = (u32 *) SYS_AllocateFramebuffer (vmode); + DCInvalidateRange(xfb, VIDEO_GetFrameBufferSize(vmode)); + xfb = MEM_K0_TO_K1 (xfb); + + VIDEO_ClearFrameBuffer (vmode, xfb, COLOR_BLACK); + + VIDEO_SetNextFramebuffer (xfb); + VIDEO_SetBlack (TRUE); + VIDEO_Flush (); + + gp_fifo = (u8 *) pmemalign (32, DEFAULT_FIFO_SIZE); + + for (i = 0; i < 4; ++i) + VIDEO_WaitVSync(); +} + +void gfx_init () { + Mtx44 p; + + GX_AbortFrame (); + + GXColor gxbackground = { 0, 0, 0, 0xff }; + + memset (gp_fifo, 0, DEFAULT_FIFO_SIZE); + + GX_Init (gp_fifo, DEFAULT_FIFO_SIZE); + GX_SetCopyClear (gxbackground, 0x00ffffff); + + // GX voodoo - properly comment me some day + // the 0.05 fudge factors for some reason help align textures on the pixel grid + // it's still not perfect though + GX_SetViewport (0, 0, vmode->fbWidth+0.05, vmode->efbHeight+0.05, 0, 1); + GX_SetDispCopyYScale ((f32) vmode->xfbHeight / (f32) vmode->efbHeight); + GX_SetScissor (0, 0, vmode->fbWidth, vmode->efbHeight); + GX_SetDispCopySrc (0, 0, vmode->fbWidth, vmode->efbHeight); + GX_SetDispCopyDst (vmode->fbWidth, vmode->xfbHeight); + GX_SetCopyFilter (vmode->aa, vmode->sample_pattern, GX_TRUE, + vmode->vfilter); + GX_SetFieldMode (vmode->field_rendering, + ((vmode->viHeight == 2 * vmode->xfbHeight) ? + GX_ENABLE : GX_DISABLE)); + + GX_SetPixelFmt (GX_PF_RGB8_Z24, GX_ZC_LINEAR); + GX_SetCullMode (GX_CULL_NONE); + + GX_SetDispCopyGamma (GX_GM_1_0); + + guOrtho(p, view_height / 2, -(view_height / 2), + -(view_width / 2), view_width / 2, 100, 1000); + + + GX_LoadProjectionMtx (p, GX_ORTHOGRAPHIC); + + GX_ClearVtxDesc (); + GX_SetVtxDesc (GX_VA_POS, GX_DIRECT); + GX_SetVtxDesc (GX_VA_CLR0, GX_DIRECT); + GX_SetVtxDesc (GX_VA_TEX0, GX_DIRECT); + + GX_SetVtxAttrFmt (GX_VTXFMT0, GX_VA_POS, GX_POS_XYZ, GX_F32, 0); + GX_SetVtxAttrFmt (GX_VTXFMT0, GX_VA_CLR0, GX_CLR_RGBA, GX_RGBA8, 0); + GX_SetVtxAttrFmt (GX_VTXFMT0, GX_VA_TEX0, GX_TEX_ST, GX_F32, 0); + + memset (&view, 0, sizeof (Mtx)); + guMtxIdentity(view); + + GX_Flush (); + GX_DrawDone (); + + GX_SetColorUpdate (GX_TRUE); + GX_CopyDisp (xfb, GX_TRUE); + GX_Flush (); + GX_DrawDone (); + GX_CopyDisp (xfb, GX_TRUE); + GX_Flush (); + GX_DrawDone (); + + VIDEO_SetBlack (FALSE); + VIDEO_Flush (); +} + +void gfx_deinit (void) { + u8 i; + + GX_AbortFrame (); + VIDEO_SetBlack (TRUE); + VIDEO_Flush(); + + for (i = 0; i < 4; ++i) + VIDEO_WaitVSync(); +} + +void gfx_get_efb_size(u16 *x, u16*y) { + *x = vmode->fbWidth; + *y = vmode->efbHeight; +} + +void gfx_set_efb_buffer(u32 *buffer) { + efb_buffer = buffer; +} + +// entity generators + +void gfx_gen_gradient (gfx_entity *entity, u16 w, u16 h, + u32 c1, u32 c2, u32 c3, u32 c4) { + entity->type = GFXE_GRADIENT; + entity->w = w; + entity->h = h; + + entity->gradient.c1 = c1; + entity->gradient.c2 = c2; + entity->gradient.c3 = c3; + entity->gradient.c4 = c4; +} + +void gfx_gen_tex (gfx_entity *entity, u16 w, u16 h, u8 *pixels, gfx_tex_type type) { + entity->type = GFXE_TEXTURE; + entity->w = w; + entity->h = h; + entity->texture.pixels = pixels; + entity->texture.type = type; + + switch(type) { + case GFXT_RGBA8: + DCFlushRange (pixels, w * h * 4); + GX_InitTexObj (&entity->texture.obj, pixels, w, h, GX_TF_RGBA8, GX_CLAMP, + GX_CLAMP, GX_FALSE); + break; + case GFXT_A8: + DCFlushRange (pixels, w * h); + GX_InitTexObj (&entity->texture.obj, pixels, w, h, GX_TF_I8, GX_CLAMP, + GX_CLAMP, GX_FALSE); +#ifdef FONT_NEAREST_NEIGHBOR + GX_InitTexObjFilterMode(&entity->texture.obj, GX_NEAR, GX_NEAR); +#endif + break; + } +} + +// queue entry generators + +void gfx_qe_origin_push (gfx_queue_entry *entry, gfx_coordinates *coords) { + entry->type = GFXQ_ORIGIN; + entry->origin.coords = coords; +} + +void gfx_qe_origin_pop (gfx_queue_entry *entry) { + entry->type = GFXQ_ORIGIN; + entry->origin.coords = NULL; +} + +void gfx_qe_scissor_reset (gfx_queue_entry *entry) { + entry->type = GFXQ_SCISSOR; + entry->scissor.x = 0; + entry->scissor.y = 0; + entry->scissor.z = 0; + entry->scissor.w = 0; + entry->scissor.h = 0; +} + +void gfx_qe_scissor (gfx_queue_entry *entry, u16 x, u16 y, u16 z, u16 w, u16 h) { + entry->type = GFXQ_SCISSOR; + entry->scissor.x = x; + entry->scissor.y = y; + entry->scissor.z = z; + entry->scissor.w = w; + entry->scissor.h = h; +} + +void gfx_qe_entity (gfx_queue_entry *entry, gfx_entity *entity, f32 x, f32 y, + s16 layer, u32 color) { + entry->type = GFXQ_ENTITY; + entry->entity.coords.x = x; + entry->entity.coords.y = y + entity->h; + entry->entity.coords.z = layer; + entry->entity.scale = 1.0f; + entry->entity.rad = 0.0f; + entry->entity.entity = entity; + entry->entity.color = color; +} + +// helper functions + +static void get_origin (s16 *x, s16 *y, s16 *z, u8 m) { + u8 i; + + *x = -view_width / 2; + *y = view_height / 2; + *z = VIEW_Z_ORIGIN; + + for (i = 0; i < origin_stack_size - m; ++i) { + *x += origin_stack[i]->x; + *y -= origin_stack[i]->y; + *z += origin_stack[i]->z; + } +} + +static void set_scissor (const gfx_queue_entry *entry) { + s16 x, y, z, w, h; + + if (entry->scissor.w != 0 && entry->scissor.h != 0) { + // TODO 1ast parm hardcoded for memo + get_origin (&x, &y, &z, 1); + x = roundf ((float) (x + view_width / 2 + entry->scissor.x) * px_width); + y = roundf ((float) (-y + view_height / 2 + entry->scissor.y) * + px_height); + w = roundf ((float) entry->scissor.w * px_width); + h = roundf ((float) entry->scissor.h * px_height); + + GX_SetScissor (x, y, w, h); + } else { + GX_SetScissor (0, 0, vmode->fbWidth, vmode->efbHeight); + } +} + +static void plot_vert_c (f32 x, f32 y, f32 z, u32 c) { + GX_Position3f32 (x, y, z); + GX_Color1u32 (c); + GX_TexCoord2f32 (0.0f, 0.0f); +} + +static void plot_gradient (const gfx_queue_entry *entry, Mtx v) { + Mtx m, m2; + Mtx mv; + f32 xf, yf, zf, wf, hf; + + xf = origin_current.x + entry->entity.coords.x; + yf = origin_current.y - entry->entity.coords.y; + zf = origin_current.z + entry->entity.coords.z; + + wf = entry->entity.entity->w; + hf = entry->entity.entity->h; + + guMtxIdentity(m); + guMtxTransApply(m, m, -wf/2, -hf/2, 0); + + if (entry->entity.scale != 1.0f) { + guMtxScale(m2, entry->entity.scale, entry->entity.scale, 0.0); + guMtxConcat(m2, m, m); + } + + if (entry->entity.rad != 0.0f) { + guMtxRotRad(m2, 'z', entry->entity.rad); + guMtxConcat(m2, m, m); + } + + guMtxTransApply(m, m, wf/2+xf, hf/2+yf, zf); + guMtxConcat(v, m, mv); + + GX_LoadPosMtxImm (mv, GX_PNMTX0); + + GX_Begin (GX_QUADS, GX_VTXFMT0, 4); + plot_vert_c (0, hf, 0, entry->entity.entity->gradient.c1); + plot_vert_c (wf, hf, 0, entry->entity.entity->gradient.c2); + plot_vert_c (wf, 0, 0, entry->entity.entity->gradient.c3); + plot_vert_c (0, 0, 0, entry->entity.entity->gradient.c4); + GX_End (); +} + +static void plot_vert (f32 x, f32 y, f32 z, f32 s, f32 t, u32 c) { + GX_Position3f32 (x, y, z); + GX_Color1u32 (c); + GX_TexCoord2f32 (s, t); +} + +static void plot_texture (const gfx_queue_entry *entry, Mtx v) { + Mtx m, m2; + Mtx mv; + f32 xf, yf, zf, wf, hf; + + yf = origin_current.y - entry->entity.coords.y; + zf = origin_current.z + entry->entity.coords.z; + hf = entry->entity.entity->h; + + if (widescreen && entry->entity.entity->texture.type == GFXT_A8) { + // align origin to pixel grid + xf = (int)(origin_current.x / WIDESCREEN_RATIO); + xf = xf*WIDESCREEN_RATIO + entry->entity.coords.x; + // width is specified in physical pixels + wf = entry->entity.entity->w * WIDESCREEN_RATIO; + } else { + xf = origin_current.x + entry->entity.coords.x; + + wf = entry->entity.entity->w; + } + + if (yf > CLIPPING_Y) + return; + + if ((yf + hf) < -CLIPPING_Y) + return; + + if (xf > CLIPPING_X) + return; + + if ((xf + wf) < -CLIPPING_X) + return; + + GX_LoadTexObj (&entry->entity.entity->texture.obj, GX_TEXMAP0); + + guMtxIdentity(m); + guMtxTransApply(m, m, -wf/2, -hf/2, 0); + + if (entry->entity.scale != 1.0f) { + guMtxScale(m2, entry->entity.scale, entry->entity.scale, 0.0); + guMtxConcat(m2, m, m); + } + + if (entry->entity.rad != 0.0f) { + guMtxRotRad(m2, 'z', entry->entity.rad); + guMtxConcat(m2, m, m); + } + + guMtxTransApply(m, m, wf/2+xf, hf/2+yf, zf); + guMtxConcat(v, m, mv); + + switch (entry->entity.entity->texture.type) { + case GFXT_RGBA8: + GX_SetTevOp (GX_TEVSTAGE0, GX_MODULATE); + break; + case GFXT_A8: + GX_SetNumTevStages(1); + GX_SetTevColorIn(GX_TEVSTAGE0,GX_CC_ZERO,GX_CC_ZERO,GX_CC_ZERO,GX_CC_RASC); + GX_SetTevAlphaIn(GX_TEVSTAGE0,GX_CA_ZERO,GX_CA_TEXA,GX_CA_RASA,GX_CA_ZERO); + GX_SetTevColorOp(GX_TEVSTAGE0,GX_TEV_ADD,GX_TB_ZERO,GX_CS_SCALE_1,GX_TRUE,GX_TEVPREV); + GX_SetTevAlphaOp(GX_TEVSTAGE0,GX_TEV_ADD,GX_TB_ZERO,GX_CS_SCALE_1,GX_TRUE,GX_TEVPREV); + break; + } + + GX_LoadPosMtxImm (mv, GX_PNMTX0); + + GX_Begin (GX_QUADS, GX_VTXFMT0, 4); + plot_vert (0, hf, 0, 0.0, 0.0, entry->entity.color); + plot_vert (wf, hf, 0, 1.0, 0.0, entry->entity.color); + plot_vert (wf, 0, 0, 1.0, 1.0, entry->entity.color); + plot_vert (0, 0, 0, 0.0, 1.0, entry->entity.color); + GX_End (); +} + +// higher level frame functions + +void gfx_frame_start (void) { + origin_stack_size = 0; + origin_current.x = -view_width / 2; + origin_current.y = view_height / 2; + origin_current.z = VIEW_Z_ORIGIN; + + GX_InvVtxCache (); + GX_InvalidateTexAll (); + + GX_SetNumChans (1); + GX_SetNumTexGens (1); + GX_SetTexCoordGen (GX_TEXCOORD0, GX_TG_MTX2x4, GX_TG_TEX0, GX_IDENTITY); + + GX_SetTevOp (GX_TEVSTAGE0, GX_MODULATE); + GX_SetTevOrder (GX_TEVSTAGE0, GX_TEXCOORD0, GX_TEXMAP0, GX_COLOR0A0); + + GX_SetBlendMode (GX_BM_BLEND, GX_BL_SRCALPHA, GX_BL_INVSRCALPHA, + GX_LO_NOOP); +} + +void gfx_frame_push (const gfx_queue_entry *entries, int count) { + while (count--) { + switch (entries->type) { + case GFXQ_ORIGIN: + if (entries->origin.coords) { + origin_current.x += entries->origin.coords->x; + origin_current.y -= entries->origin.coords->y; + origin_current.z += entries->origin.coords->z; + origin_stack[origin_stack_size] = entries->origin.coords; + origin_stack_size++; + } else { + if (origin_stack_size > 0) { + origin_current.x -= origin_stack[origin_stack_size - 1]->x; + origin_current.y += origin_stack[origin_stack_size - 1]->y; + origin_current.z -= origin_stack[origin_stack_size - 1]->z; + origin_stack_size--; + } else { + origin_current.x += -view_width / 2; + origin_current.y -= view_height / 2; + origin_current.z += view_height / 2; + } + } + break; + + case GFXQ_SCISSOR: + set_scissor (entries); + break; + + case GFXQ_ENTITY: + switch (entries->entity.entity->type) { + case GFXE_GRADIENT: + GX_SetTevOp (GX_TEVSTAGE0, GX_PASSCLR); + plot_gradient (entries, view); + + break; + + case GFXE_TEXTURE: + plot_texture (entries, view); + break; + } + case GFXQ_NULL: + break; + default: + gprintf("Unknown queue entry type 0x%x\n", entries->type); + break; + } + entries++; + } +} + +void gfx_frame_end (void) { + GX_DrawDone (); + +#ifdef ENABLE_SCREENSHOTS + if (efb_buffer) { + u16 x, y; + GXColor c; + u32 val; + u32 *p = efb_buffer; + + for (y = 0; y < vmode->efbHeight; ++y) { + for (x = 0; x < vmode->fbWidth; ++x) { + GX_PeekARGB(x, y, &c); + val = ((u32) c.a) << 24; + val |= ((u32) c.r) << 16; + val |= ((u32) c.g) << 8; + val |= c.b; + + *p++ = val; + } + } + + efb_buffer = NULL; + } +#endif + + GX_SetZMode (GX_TRUE, GX_LEQUAL, GX_TRUE); + GX_SetColorUpdate (GX_TRUE); + + VIDEO_WaitVSync (); + GX_CopyDisp (xfb, GX_TRUE); + GX_Flush (); +} + diff --git a/channel/channelapp/source/gfx.h b/channel/channelapp/source/gfx.h new file mode 100644 index 0000000..4869d66 --- /dev/null +++ b/channel/channelapp/source/gfx.h @@ -0,0 +1,96 @@ +#ifndef _GFX_H_ +#define _GFX_H_ + +#include + +#define COL_DEFAULT 0xFFFFFFFF + +#define WIDESCREEN_RATIO (852.0/640.0) + +typedef struct { + f32 x, y, z; +} gfx_coordinates; + +typedef enum { + GFXT_RGBA8, + GFXT_A8 +} gfx_tex_type; + +typedef enum { + GFXE_GRADIENT, + GFXE_TEXTURE +} gfx_entity_type; + +typedef struct { + gfx_entity_type type; + u16 w, h; + + union { + struct { + u32 c1, c2, c3, c4; + } gradient; + + struct { + u8 *pixels; + gfx_tex_type type; + GXTexObj obj; + } texture; + }; +} gfx_entity; + +typedef enum { + GFXQ_NULL = -1, + GFXQ_ORIGIN, + GFXQ_SCISSOR, + GFXQ_ENTITY +} gfx_queue_type; + +typedef struct { + gfx_queue_type type; + + union { + struct { + gfx_coordinates *coords; + } origin; + + struct { + u16 x, y, z, w, h; + } scissor; + + struct { + gfx_coordinates coords; + f32 scale; + f32 rad; + gfx_entity *entity; + u32 color; + } entity; + }; +} gfx_queue_entry; + +extern bool widescreen; +extern u16 view_height, view_width; + +void gfx_init_video (void); +void gfx_init (void); +void gfx_deinit (void); + +void gfx_get_efb_size(u16 *x, u16 *y); +void gfx_set_efb_buffer(u32 *buffer); + +void gfx_gen_gradient (gfx_entity *entity, u16 w, u16 h, + u32 c1, u32 c2, u32 c3, u32 c4); +void gfx_gen_tex (gfx_entity *entity, u16 w, u16 h, u8 *pixels, gfx_tex_type type); + +void gfx_qe_origin_push (gfx_queue_entry *entry, gfx_coordinates *coords); +void gfx_qe_origin_pop (gfx_queue_entry *entry); +void gfx_qe_scissor_reset (gfx_queue_entry *entry); +void gfx_qe_scissor (gfx_queue_entry *entry, u16 x, u16 y, u16 z, u16 w, u16 h); +void gfx_qe_entity (gfx_queue_entry *entry, gfx_entity *entity, f32 x, f32 y, + s16 layer, u32 color); + +void gfx_frame_start (void); +void gfx_frame_push (const gfx_queue_entry *entries, int count); +void gfx_frame_end (void); + +#endif + diff --git a/channel/channelapp/source/ggets.c b/channel/channelapp/source/ggets.c new file mode 100644 index 0000000..8db22cd --- /dev/null +++ b/channel/channelapp/source/ggets.c @@ -0,0 +1,67 @@ +/* File ggets.c - goodgets is a safe alternative to gets */ +/* By C.B. Falconer. Public domain 2002-06-22 */ +/* attribution appreciated. */ + +/* Revised 2002-06-26 New prototype. + 2002-06-27 Incomplete final lines + 2006-06-14 Simplified, using getc, not fgets + 2006-06-15 Fixed memory leak at EOF + */ + +/* fggets and ggets [which is fggets(ln, stdin)] set *ln to + a buffer filled with the next complete line from the text + stream f. The storage has been allocated within fggets, + and is normally reduced to be an exact fit. The trailing + \n has been removed, so the resultant line is ready for + dumping with puts. The buffer will be as large as is + required to hold the complete line. + + Note: this means a final file line without a \n terminator + has an effective \n appended, as EOF occurs within the read. + + If no error occurs fggets returns 0. If an EOF occurs on + the input file, EOF is returned. For memory allocation + errors some positive value is returned. In this case *ln + may point to a partial line. For other errors memory is + freed and *ln is set to NULL. + + Freeing of assigned storage is the callers responsibility + */ + +#include +#include +#include "panic.h" +#include "ggets.h" + +#define INITSIZE 112 /* power of 2 minus 16, helps malloc */ +#define DELTASIZE (INITSIZE + 16) + +enum {OK = 0, NOMEM}; + +int fggets(char* *ln, FILE *f) +{ + int cursize, ch, ix; + char *buffer; + + *ln = NULL; /* default */ + buffer = pmalloc(INITSIZE); + cursize = INITSIZE; + + ix = 0; + while ((EOF != (ch = getc(f))) && ('\n' != ch)) { + if (ix >= (cursize - 1)) { /* extend buffer */ + cursize += DELTASIZE; + buffer = prealloc(buffer, (size_t)cursize); + } + buffer[ix++] = ch; + } + if ((EOF == ch) && (0 == ix)) { + free(buffer); + return EOF; + } + + buffer[ix] = '\0'; + *ln = prealloc(buffer, (size_t)ix + 1); + return OK; +} /* fggets */ +/* End of ggets.c */ diff --git a/channel/channelapp/source/ggets.h b/channel/channelapp/source/ggets.h new file mode 100644 index 0000000..db35bb4 --- /dev/null +++ b/channel/channelapp/source/ggets.h @@ -0,0 +1,45 @@ +/* File ggets.h - goodgets is a safe alternative to gets */ +/* By C.B. Falconer. Public domain 2002-06-22 */ +/* attribution appreciated. */ + + +/* Revised 2002-06-26 New prototype. + 2002-06-27 Incomplete final lines + */ + +/* fggets and ggets [which is fggets(ln, stdin)] set *ln to + a buffer filled with the next complete line from the text + stream f. The storage has been allocated within fggets, + and is normally reduced to be an exact fit. The trailing + \n has been removed, so the resultant line is ready for + dumping with puts. The buffer will be as large as is + required to hold the complete line. + + Note: this means a final file line without a \n terminator + has an effective \n appended, as EOF occurs within the read. + + If no error occurs fggets returns 0. If an EOF occurs on + the input file, EOF is returned. For memory allocation + errors some positive value is returned. In this case *ln + may point to a partial line. For other errors memory is + freed and *ln is set to NULL. + + Freeing of assigned storage is the callers responsibility + */ + +#ifndef ggets_h_ +# define ggets_h_ + +# ifdef __cplusplus + extern "C" { +# endif + +int fggets(char* *ln, FILE *f); + +#define ggets(ln) fggets(ln, stdin) + +# ifdef __cplusplus + } +# endif +#endif +/* END ggets.h */ diff --git a/channel/channelapp/source/http.c b/channel/channelapp/source/http.c new file mode 100644 index 0000000..73175e5 --- /dev/null +++ b/channel/channelapp/source/http.c @@ -0,0 +1,455 @@ +#include +#include +#include +#include + +#include +#include +#include +#include +#include + +#include "../config.h" + +#ifdef ENABLE_UPDATES + +#include "tcp.h" +#include "panic.h" + +#include "http.h" + +extern u32 ms_id; +extern u32 ng_id; + +typedef enum { + HTTPCMD_IDLE = 0, + HTTPCMD_EXIT, + HTTPCMD_FETCH +} http_cmd; + +typedef struct { + bool running; + bool done; + + http_cmd cmd; + char *host; + u16 port; + char *path; + u32 max_size; + + u16 sysmenu_version; + char *region; + char *area; + + mutex_t mutex; + mutex_t cmutex; + cond_t cond; + http_state state; + http_res res; + u32 http_status; + u32 content_length; + u32 progress; + u8 *data; +} http_arg; + +static lwp_t http_thread; +static u8 http_stack[HTTP_THREAD_STACKSIZE] ATTRIBUTE_ALIGN (32); + +static http_arg ta_http; + +static u16 get_tmd_version(u64 title) { + STACK_ALIGN(u8, tmdbuf, 1024, 32); + u32 tmd_view_size = 0; + s32 res; + + res = ES_GetTMDViewSize(title, &tmd_view_size); + if (res < 0) + return 0; + + if (tmd_view_size > 1024) + return 0; + + res = ES_GetTMDView(title, tmdbuf, tmd_view_size); + if (res < 0) + return 0; + + return (tmdbuf[88] << 8) | tmdbuf[89]; +} + +static bool http_split_url (char **host, char **path, const char *url) { + const char *p; + char *c; + + if (strncasecmp (url, "http://", 7)) + return false; + + p = url + 7; + c = strchr (p, '/'); + + if (c[0] == 0) + return false; + + *host = strndup (p, c - p); + *path = pstrdup (c); + + return true; +} + +static void * http_func (void *arg) { + http_arg *ta = (http_arg *) arg; + int s; + char *request, *r; + int linecount; + char *line; + bool b; + s64 t; + + ta->running = true; + LWP_MutexLock (ta->cmutex); + LWP_MutexLock (ta_http.mutex); + ta->state = HTTPS_IDLE; + ta->done = true; + LWP_MutexUnlock (ta_http.mutex); + + while (true) { + while (ta->cmd == HTTPCMD_IDLE) + LWP_CondWait(ta->cond, ta->cmutex); + + if (ta->cmd == HTTPCMD_EXIT) + break; + + ta->cmd = HTTPCMD_IDLE; + + s = tcp_connect (ta->host, ta->port); + + LWP_MutexLock (ta_http.mutex); + if (s < 0) { + ta->state = HTTPS_IDLE; + ta->done = true; + ta->res = HTTPR_ERR_CONNECT; + LWP_MutexUnlock (ta_http.mutex); + continue; + } + + ta->state = HTTPS_REQUESTING; + LWP_MutexUnlock (ta_http.mutex); + + request = (char *) pmalloc (2048); + r = request; + r += sprintf (r, "GET %s HTTP/1.1\r\n", ta->path); + r += sprintf (r, "Host: %s\r\n", ta->host); + r += sprintf (r, "Cache-Control: no-cache\r\n"); + r += sprintf (r, "User-Agent: TheHomebrewChannel/%s Wii/%08lx" + " (%lu; %u; %s-%s)\r\n", CHANNEL_VERSION_STR, + ng_id, ms_id, ta->sysmenu_version, ta->region, + ta->area); + + strcat (r, "\r\n"); + + b = tcp_write (s, (u8 *) request, strlen (request), NULL, NULL); + + free (request); + + if (!b) { + LWP_MutexLock (ta_http.mutex); + ta->state = HTTPS_IDLE; + ta->done = true; + ta->res = HTTPR_ERR_REQUEST; + LWP_MutexUnlock (ta_http.mutex); + net_close (s); + continue; + } + + linecount = 0; + t = gettime (); + while (true) { + line = tcp_readln (s, 0xff, t, HTTP_TIMEOUT); + if (!line) { + LWP_MutexLock (ta_http.mutex); + ta->http_status = 404; + ta->res = HTTPR_ERR_REQUEST; + break; + } + + if (strlen (line) < 1) { + free (line); + LWP_MutexLock (ta_http.mutex); + break; + } + + sscanf (line, "HTTP/1.%*u %lu", &ta->http_status); + sscanf (line, "Content-Length: %lu", &ta->content_length); + + free (line); + + linecount++; + if (linecount == 32) { + LWP_MutexLock (ta_http.mutex); + ta->http_status = 404; + ta->res = HTTPR_ERR_REQUEST; + break; + } + } + + if (ta->content_length < 1) + ta->http_status = 404; + + if (ta->http_status != 200) { + ta->res = HTTPR_ERR_STATUS; + ta->state = HTTPS_IDLE; + ta->done = true; + LWP_MutexUnlock (ta_http.mutex); + net_close (s); + + continue; + } + + if (ta->content_length > ta->max_size) { + ta->res = HTTPR_ERR_TOOBIG; + ta->state = HTTPS_IDLE; + ta->done = true; + LWP_MutexUnlock (ta_http.mutex); + net_close (s); + + continue; + } + + ta->state = HTTPS_RECEIVING; + + // safety, for char[] parsing + ta->data = (u8 *) pmalloc (ta->content_length + 1); + LWP_MutexUnlock (ta_http.mutex); + + b = tcp_read (s, ta->data, ta->content_length, &ta->mutex, + &ta->progress); + + if (!b) { + LWP_MutexLock (ta_http.mutex); + free (ta->data); + ta->data = NULL; + ta->res = HTTPR_ERR_RECEIVE; + ta->state = HTTPS_IDLE; + ta->done = true; + LWP_MutexUnlock (ta_http.mutex); + net_close (s); + + continue; + } + + gprintf("done with download\n"); + + LWP_MutexLock (ta_http.mutex); + ta->data[ta->content_length] = 0; + ta->res = HTTPR_OK; + ta->state = HTTPS_IDLE; + ta->done = true; + LWP_MutexUnlock (ta_http.mutex); + + net_close (s); + } + + LWP_MutexUnlock (ta->cmutex); + + gprintf("http thread done\n"); + + ta->running = false; + + return NULL; +} + +void http_init (void) { + s32 res; + + gprintf ("starting http thread\n"); + + memset (&ta_http, 0, sizeof (http_arg)); + + ta_http.sysmenu_version = get_tmd_version(0x0000000100000002ll); + + switch (CONF_GetRegion()) { + case CONF_REGION_JP: + ta_http.region = pstrdup("JP"); + break; + case CONF_REGION_US: + ta_http.region = pstrdup("US"); + break; + case CONF_REGION_EU: + ta_http.region = pstrdup("EU"); + break; + case CONF_REGION_KR: + ta_http.region = pstrdup("KR"); + break; + case CONF_REGION_CN: + ta_http.region = pstrdup("CN"); + break; + default: + ta_http.region = pstrdup("UNK"); + break; + } + + switch (CONF_GetArea()) { + case CONF_AREA_JPN: + ta_http.area = pstrdup("JPN"); + break; + case CONF_AREA_USA: + ta_http.area = pstrdup("USA"); + break; + case CONF_AREA_EUR: + ta_http.area = pstrdup("EUR"); + break; + case CONF_AREA_AUS: + ta_http.area = pstrdup("AUS"); + break; + case CONF_AREA_BRA: + ta_http.area = pstrdup("BRA"); + break; + case CONF_AREA_TWN: + ta_http.area = pstrdup("TWN"); + break; + case CONF_AREA_ROC: + ta_http.area = pstrdup("ROC"); + break; + case CONF_AREA_KOR: + ta_http.area = pstrdup("KOR"); + break; + case CONF_AREA_HKG: + ta_http.area = pstrdup("HKG"); + break; + case CONF_AREA_ASI: + ta_http.area = pstrdup("ASI"); + break; + case CONF_AREA_LTN: + ta_http.area = pstrdup("LTN"); + break; + case CONF_AREA_SAF: + ta_http.area = pstrdup("SAF"); + break; + case CONF_AREA_CHN: + ta_http.area = pstrdup("CHN"); + break; + default: + ta_http.area = pstrdup("UNK"); + break; + } + + res = LWP_MutexInit (&ta_http.mutex, false); + + if (res) { + gprintf ("error creating mutex: %ld\n", res); + return; + } + + res = LWP_MutexInit (&ta_http.cmutex, false); + + if (res) { + gprintf ("error creating cmutex: %ld\n", res); + return; + } + + res = LWP_CondInit (&ta_http.cond); + + if (res) { + gprintf ("error creating cond: %ld\n", res); + return; + } + + memset (&http_stack, 0, HTTP_THREAD_STACKSIZE); + res = LWP_CreateThread (&http_thread, http_func, &ta_http, http_stack, + HTTP_THREAD_STACKSIZE, HTTP_THREAD_PRIO); + + gprintf("created http thread\n"); + + if (res) { + gprintf ("error creating thread: %ld\n", res); + } +} + +void http_deinit (void) { + int i; + + if (ta_http.running) { + gprintf ("stopping http thread\n"); + + for (i = 0; i < 25; ++i) { + if (LWP_MutexTryLock (ta_http.cmutex) == 0) { + break; + } + usleep (20 * 1000); + } + if (i < 25) { + gprintf ("sending http entry thread the exit cmd\n"); + ta_http.cmd = HTTPCMD_EXIT; + LWP_SetThreadPriority (http_thread, LWP_PRIO_HIGHEST); + LWP_CondBroadcast (ta_http.cond); + LWP_MutexUnlock (ta_http.cmutex); + LWP_JoinThread(http_thread, NULL); + LWP_CondDestroy (ta_http.cond); + LWP_MutexDestroy (ta_http.cmutex); + LWP_MutexDestroy (ta_http.mutex); + free(ta_http.region); + free(ta_http.area); + return; + } + gprintf("http thread didn't shutdown gracefully!\n"); + } + + return; +} + +bool http_ready (void) { + return ta_http.running && ta_http.done; +} + +bool http_request (const char *url, u32 max_size) { + if (!http_ready () || !http_split_url (&ta_http.host, &ta_http.path, url)) + return false; + + LWP_MutexLock (ta_http.cmutex); + ta_http.done = false; + ta_http.cmd = HTTPCMD_FETCH; + ta_http.port = 80; + ta_http.max_size = max_size; + + LWP_CondBroadcast (ta_http.cond); + LWP_MutexUnlock (ta_http.cmutex); + + return true; +} + +bool http_get_state (http_state *state, u32 *content_length, u32 *progress) { + if (!ta_http.running) + return false; + + LWP_MutexLock (ta_http.mutex); + *state = ta_http.state; + *content_length = ta_http.content_length; + *progress = ta_http.progress; + LWP_MutexUnlock (ta_http.mutex); + + return true; +} + +bool http_get_result (http_res *res, u32 *http_status, u8 **content, + u32 *length) { + if (!http_ready()) + return false; + + *res = ta_http.res; + *http_status = ta_http.http_status; + if (ta_http.res == HTTPR_OK) { + *content = ta_http.data; + *length = ta_http.content_length; + } else { + *content = NULL; + *length = 0; + } + + free (ta_http.host); + ta_http.host = NULL; + free (ta_http.path); + ta_http.path = NULL; + + return true; +} + +#endif diff --git a/channel/channelapp/source/http.h b/channel/channelapp/source/http.h new file mode 100644 index 0000000..743bc5f --- /dev/null +++ b/channel/channelapp/source/http.h @@ -0,0 +1,33 @@ +#ifndef _HTTP_H_ +#define _HTTP_H_ + +#include + +typedef enum { + HTTPS_IDLE = 0, + HTTPS_CONNECTING, + HTTPS_REQUESTING, + HTTPS_RECEIVING +} http_state; + +typedef enum { + HTTPR_OK, + HTTPR_ERR_CONNECT, + HTTPR_ERR_REQUEST, + HTTPR_ERR_STATUS, + HTTPR_ERR_TOOBIG, + HTTPR_ERR_RECEIVE +} http_res; + +void http_init (void); +void http_deinit (void); + +bool http_ready (void); + +bool http_request (const char *url, u32 max_size); +bool http_get_state (http_state *state, u32 *content_length, u32 *progress); +bool http_get_result (http_res *res, u32 *http_status, u8 **content, + u32 *length); + +#endif + diff --git a/channel/channelapp/source/i18n.c b/channel/channelapp/source/i18n.c new file mode 100644 index 0000000..bed731c --- /dev/null +++ b/channel/channelapp/source/i18n.c @@ -0,0 +1,86 @@ +#include +#include +#include +#include + +#include "../config.h" + +struct mo_entry { + u32 length; + u32 offset; +} __attribute__((packed)); + +struct mo_hdr { + u32 magic; + u32 revision; + u32 count; + struct mo_entry *source; + struct mo_entry *target; + u32 hash_size; + void *hashes; +} __attribute__((packed)); + +static struct mo_hdr mo; +static int mo_inited = false; +static const char *mo_data = NULL; + +#define MAGIC_SWAB 0xde120495 +#define MAGIC 0x950412de + +#define SWAB16(x) ((((x)>>8)&0xFF) | (((x)&0xFF)<<8)) +#define SWAB32(x) ((SWAB16((x)&0xFFFF)<<16)|(SWAB16(((x)>>16)&0xFFFF))) + +#define ENDIAN(x) ((mo.magic == MAGIC_SWAB) ? SWAB32(x) : (x)) + +void i18n_set_mo(const void *mo_file) { + mo_inited = true; + if(!mo_file) { + mo_data = NULL; + gprintf("i18n: unset mo file\n"); + return; + } + + memcpy(&mo, mo_file, sizeof(struct mo_hdr)); + if(mo.magic == MAGIC_SWAB) { + mo.revision = SWAB32(mo.revision); + mo.count = SWAB32(mo.count); + mo.source = (struct mo_entry*)SWAB32((u32)mo.source); + mo.target = (struct mo_entry*)SWAB32((u32)mo.target); + mo.hash_size = SWAB32(mo.hash_size); + mo.hashes = (void*)SWAB32((u32)mo.magic); + } else if(mo.magic != MAGIC) { + gprintf("i18n: bad mo file magic 0x%08lx\n",mo.magic); + return; + } + + if(mo.revision != 0) + gprintf("i18n: bad mo file revision 0x%08lx\n",mo.revision); + + mo_data = (char*)mo_file; + mo.source = (struct mo_entry*)(mo_data + (u32)mo.source); + gprintf("i18n: configured mo file at %p\n",mo_data); + mo.target = (struct mo_entry*)(mo_data + (u32)mo.target); +} + +const char *i18n(char *english_string) { + int i; + + if(!mo_inited) + gprintf("i18n: warning: attempted to translate '%s' before init\n", english_string); + + if(!mo_data) + return english_string; + + for(i = 0; i < mo.count; i++) { + if(!strcmp(english_string, &mo_data[ENDIAN(mo.source[i].offset)])) + return &mo_data[ENDIAN(mo.target[i].offset)]; + } + + gprintf("i18n: could not find string for %s\n", english_string); + + return english_string; +} + +u32 i18n_have_mo(void) { + return mo_data ? 1 : 0; +} diff --git a/channel/channelapp/source/i18n.h b/channel/channelapp/source/i18n.h new file mode 100644 index 0000000..6891f6d --- /dev/null +++ b/channel/channelapp/source/i18n.h @@ -0,0 +1,13 @@ +#ifndef _I18N_H_ +#define _I18N_H_ + +#include + +#define _(x) i18n(x) + +void i18n_set_mo(const void *mo_file); +const char *i18n(char *english_string); +u32 i18n_have_mo(void); + +#endif + diff --git a/channel/channelapp/source/isfs.c b/channel/channelapp/source/isfs.c new file mode 100644 index 0000000..aa951f9 --- /dev/null +++ b/channel/channelapp/source/isfs.c @@ -0,0 +1,129 @@ +#include +#include +#include + +#include + +#include "../config.h" +#include "blob.h" +#include "panic.h" +#include "isfs.h" + +#define ROUNDUP32B(x) ((x + 32 - 1) & ~(32 - 1)) + +static fstats __st ATTRIBUTE_ALIGN(32); + +s32 isfs_get(const char *path, u8 **buf, u32 expected, u32 maxsize, bool use_blob) { + s32 ret; + s32 fd; + u8 *__buf; + + gprintf("reading %s (expecting %lu bytes)\n", path, expected); + ret = ISFS_Open(path, ISFS_OPEN_READ); + if (ret < 0) { + gprintf("ISFS_Open failed (%ld)\n", ret); + return ret; + } + + fd = ret; + + ret = ISFS_GetFileStats(fd, &__st); + if (ret < 0) { + gprintf("ISFS_GetFileStats failed (%ld)\n", ret); + return ret; + } + DCInvalidateRange(&__st, sizeof(__st)); + + if ((__st.file_length == 0) || + (maxsize && (__st.file_length > maxsize)) || + (expected && (__st.file_length != expected))) { + gprintf("invalid size: %lu\n", __st.file_length); + ISFS_Close(fd); + return -1; + } + + if (use_blob) { + __buf = blob_alloc(ROUNDUP32B(__st.file_length + 1)); + if (!__buf) + panic(); + } else { + __buf = pmemalign(32, ROUNDUP32B(__st.file_length + 1)); + } + + gprintf("reading %lu bytes\n", __st.file_length); + ret = ISFS_Read(fd, __buf, __st.file_length); + if (ret < 0) { + gprintf("ISFS_Read failed (%ld)\n", ret); + free(__buf); + ISFS_Close(fd); + return ret; + } + DCInvalidateRange(__buf, __st.file_length + 1); + __buf[__st.file_length] = 0; + + if (ret != __st.file_length) { + gprintf("ISFS_Read short read (%ld)\n", ret); + free(__buf); + ISFS_Close(fd); + return -1; + } + + ret = ISFS_Close(fd); + if (ret < 0) { + gprintf("ISFS_Close failed (%ld)\n", ret); + free(__buf); + return ret; + } + + *buf = __buf; + return __st.file_length; +} + +s32 isfs_put(const char *path, const void *buf, u32 len) { + s32 fd, ret; + + ISFS_Delete(path); + + gprintf("writing %s (%lu bytes)\n", path, len); + ret = ISFS_CreateFile(path, 0, 3, 3, 0); + if (ret < 0) { + gprintf("ISFS_CreateFile failed (%ld)\n", ret); + return ret; + } + + ret = ISFS_Open(path, 3); + if (ret < 0) { + gprintf("ISFS_Open failed (%ld)\n", ret); + ISFS_Delete(path); + return ret; + } + + fd = ret; + + DCFlushRange((void *) buf, len); + + ret = ISFS_Write(fd, buf, len); + if (ret < 0) { + gprintf("ISFS_Write failed (%ld)\n", ret); + ISFS_Close(fd); + ISFS_Delete(path); + return ret; + } + + if (ret != len) { + gprintf("ISFS_Write short write (%ld)\n", ret); + ISFS_Close(fd); + ISFS_Delete(path); + return -1; + } + + ret = ISFS_Close(fd); + if (ret < 0) { + gprintf("ISFS_Close failed (%ld)\n", ret); + ISFS_Delete(path); + return ret; + } + + return 0; +} + diff --git a/channel/channelapp/source/isfs.h b/channel/channelapp/source/isfs.h new file mode 100644 index 0000000..b01106d --- /dev/null +++ b/channel/channelapp/source/isfs.h @@ -0,0 +1,9 @@ +#ifndef ___ISFS_H__ +#define ___ISFS_H__ + +#include + +s32 isfs_get(const char *path, u8 **buf, u32 expected, u32 maxsize, bool use_blob); +s32 isfs_put(const char *path, const void *buf, u32 len); + +#endif diff --git a/channel/channelapp/source/loader.c b/channel/channelapp/source/loader.c new file mode 100644 index 0000000..7323285 --- /dev/null +++ b/channel/channelapp/source/loader.c @@ -0,0 +1,1009 @@ +#include +#include +#include +#include +#include +#include + +#include +#include +#include +#include +#include + +#include "../config.h" +#include "blob.h" +#include "tcp.h" +#include "appentry.h" +#include "manage.h" +#include "dialogs.h" +#include "i18n.h" +#include "panic.h" + +#include "loader.h" + +#define USBGECKO_RETRIES 1000 + +static const char *text_err_read; +static const char *text_err_receive; +static const char *text_err_uncompress; +static const char *text_err_invalid_bin; +static const char *text_extract_zip; +static const char *text_err_invalid_zip; +static const char *text_err_extract_zip; +static const char *text_warn_overwrite; +static const char *text_err_oom; + +static inline u16 buf_u16(const u8 *buf, u8 pos) { + return (buf[pos] << 8) | buf[pos + 1]; +} + +static inline u32 buf_u32(const u8 *buf, u8 pos) { + return (buf[pos] << 24) | (buf[pos + 1] << 16) | + (buf[pos + 2] << 8) | buf[pos + 3]; +} + +// gecko handshake thread + +static lwp_t ld_gecko_thread; +static u8 ld_gecko_stack[LD_THREAD_STACKSIZE] ATTRIBUTE_ALIGN (32); + +typedef enum { + LDGECKOCMD_IDLE = 0, + LDGECKOCMD_EXIT, + LDGECKOCMD_POLL, + +} ld_gecko_cmd; + +typedef struct { + bool running; + bool handshaked; + ld_gecko_cmd cmd; + u32 data_len; + u32 data_len_un; + u16 args_len; + mutex_t cmutex; + cond_t cond; +} ld_gecko_arg; + +static void * ld_gecko_func (void *arg) { + ld_gecko_arg *ta = (ld_gecko_arg *) arg; + u8 buf[16]; + u8 left; + int res; + s64 t; + u16 wiiload_version; + + ta->running = true; + + LWP_MutexLock (ta->cmutex); + while (true) { + usb_flush(USBGECKO_CHANNEL); + + while (ta->cmd == LDGECKOCMD_IDLE) + LWP_CondWait(ta->cond, ta->cmutex); + + if (ta->cmd == LDGECKOCMD_EXIT) + break; + + ta->cmd = LDGECKOCMD_IDLE; + + ta->handshaked = false; + + left = 16; + t = gettime (); + while (left) { + res = usb_recvbuffer_safe_ex(USBGECKO_CHANNEL, &buf[16 - left], + left, USBGECKO_RETRIES); + + if (res) { + left -= res; + + continue; + } + + if (ticks_to_millisecs (diff_ticks (t, gettime ())) > LD_TIMEOUT) + break; + } + + if (left) + continue; + + wiiload_version = buf_u16(buf, 4); + ta->args_len = buf_u16(buf, 6); + ta->data_len = buf_u32(buf, 8); + ta->data_len_un = buf_u32(buf, 12); + + if (strncmp((char *) buf, "HAXX", 4) || + (wiiload_version < WIILOAD_MIN_VERSION) || + (ta->args_len > ARGS_MAX_LEN) || + (!ta->data_len || ta->data_len > LD_MAX_SIZE) || + (ta->data_len_un > LD_MAX_SIZE)) { + continue; + } + + gprintf_enable(0); + + ta->handshaked = true; + } + + LWP_MutexUnlock (ta->cmutex); + + gprintf("gecko handshake thread done\n"); + + ta->running = false; + return NULL; +} + +// tcp handshake thread + +static lwp_t ld_tcp_thread; +static u8 ld_tcp_stack[LD_THREAD_STACKSIZE] ATTRIBUTE_ALIGN (32); + +typedef enum { + LDTCPCMD_IDLE = 0, + LDTCPCMD_EXIT, + LDTCPCMD_INIT, + LDTCPCMD_ACCEPT +} ld_tcp_cmd; + +typedef enum { + LDTCPS_UNINITIALIZED = 0, + LDTCPS_INITIALIZING, + LDTCPS_INITIALIZED +} ld_tcp_state; + +typedef struct { + bool running; + ld_tcp_cmd cmd; + ld_tcp_state state; + bool handshaked; + u16 args_len; + u32 data_len; + u32 data_len_un; + int s; + char *client; + mutex_t cmutex; + cond_t cond; +} ld_tcp_arg; + +static void * ld_tcp_func (void *arg) { + ld_tcp_arg *ta = (ld_tcp_arg *) arg; + int s = -1, sn; + struct sockaddr_in sa; + u32 mask, len_sa; + s32 res; + u8 buf[16]; + u16 wiiload_version; + u8 retries; + + mask = 0; + + ta->running = true; + LWP_MutexLock (ta->cmutex); + + ta->cmd = LDTCPCMD_INIT; + ta->state = LDTCPS_UNINITIALIZED; + ta->handshaked = false; + + while (true) { + while (ta->cmd == LDTCPCMD_IDLE) + LWP_CondWait(ta->cond, ta->cmutex); + + ta->handshaked = false; + + if (ta->cmd == LDTCPCMD_EXIT) { + break; + } + + if (ta->cmd == LDTCPCMD_INIT) { + ta->cmd = LDTCPCMD_IDLE; + + if (ta->state == LDTCPS_INITIALIZED) { + gprintf ("reinit net\n"); + net_close (s); + } + + retries = 32; + ta->state = LDTCPS_INITIALIZING; + + while (retries) { + res = net_init_async(NULL, NULL); + + if (res) { + gprintf("net_init_async failed: %ld\n", res); + break; + } + + res = net_get_status(); + while (res == -EBUSY) { + if (ta->cmd == LDTCPCMD_EXIT) { + gprintf("exit while net_init_async still busy\n"); + res = -1; + break; + } + + usleep(50 * 1000); + res = net_get_status(); + } + + if ((res == -EAGAIN) || (res == -ETIMEDOUT)) { + gprintf ("net_init failed: %ld, trying again...\n", res); + retries--; + usleep(50 * 1000); + continue; + } + + break; + } + + if (res < 0) { + gprintf ("net_init failed: %ld\n", res); + ta->state = LDTCPS_UNINITIALIZED; + continue; + } + + gprintf ("net_init success: %ld\n", res); + + mask = net_gethostip () & 0xffff0000; + + s = tcp_listen (LD_TCP_PORT, 3); + + if ((s == -ENETRESET) && retries) { + gprintf("ENETRESET, reiniting\n"); + net_deinit(); + ta->cmd = LDTCPCMD_INIT; + continue; + } + + if (s < 0) { + gprintf ("tcp_listen failed: %d\n",s); + ta->state = LDTCPS_UNINITIALIZED; + continue; + } + + ta->state = LDTCPS_INITIALIZED; + + continue; + } + + if (ta->cmd == LDTCPCMD_ACCEPT) { + ta->cmd = LDTCPCMD_IDLE; + memset (&sa, 0, sizeof (struct sockaddr_in)); + sa.sin_family = AF_INET; + sa.sin_len = sizeof (struct sockaddr_in); + + len_sa = sizeof (struct sockaddr_in); + + sn = net_accept (s, (struct sockaddr *) &sa, &len_sa); + if (sn == -EAGAIN) + continue; + + if (sn == -ENETRESET) { + gprintf("ENETRESET, reiniting\n"); + net_deinit(); + ta->cmd = LDTCPCMD_INIT; + continue; + } + + if (sn < 0) { + gprintf ("net_accept failed: %d\n", sn); + + net_close (s); + ta->state = LDTCPS_UNINITIALIZED; + + continue; + } + + if ((sa.sin_addr.s_addr & 0xffff0000) != mask) { + gprintf ("non local ip (%lx)\n", sa.sin_addr.s_addr); + net_close (sn); + continue; + } + + if (!tcp_read (sn, buf, 16, NULL, NULL)) { + net_close (sn); + continue; + } + + wiiload_version = buf_u16(buf, 4); + ta->args_len = buf_u16(buf, 6); + ta->data_len = buf_u32(buf, 8); + ta->data_len_un = buf_u32(buf, 12); + + if (strncmp((char *) buf, "HAXX", 4) || + (wiiload_version < WIILOAD_MIN_VERSION) || + (ta->args_len > ARGS_MAX_LEN) || + (!ta->data_len || ta->data_len > LD_MAX_SIZE) || + (ta->data_len_un > LD_MAX_SIZE)) { + gprintf ("invalid upload request\n"); + net_close (sn); + continue; + } + + ta->s = sn; + ta->client = inet_ntoa (sa.sin_addr); + ta->handshaked = true; + + continue; + } + } + + if (ta->state == LDTCPS_INITIALIZED) { + gprintf ("net_shutdown\n"); + res = net_shutdown (s, 2); + if (res) + gprintf ("net_shutdown failed: %ld\n", res); + + gprintf ("net_close\n"); + res = net_close (s); + if (res) + gprintf ("net_close failed: %ld\n", res); + } + + gprintf ("tcp thread deiniting\n"); + net_deinit(); + gprintf ("tcp thread exiting\n"); + ta->state = LDTCPS_UNINITIALIZED; + + ta->running = false; + LWP_MutexUnlock (ta->cmutex); + + return NULL; +} + +// thread setup + +static ld_gecko_arg ta_gecko; +static ld_tcp_arg ta_tcp; + +void loader_init (void) { + int res; + + if (usb_isgeckoalive (USBGECKO_CHANNEL)) { + gprintf ("starting gecko thread\n"); + + memset (&ta_gecko, 0, sizeof (ld_gecko_arg)); + + memset (&ld_gecko_stack, 0, LD_THREAD_STACKSIZE); + + res = LWP_MutexInit (&ta_gecko.cmutex, false); + if (res) { + gprintf ("error creating cmutex: %d\n", res); + return; + } + + res = LWP_CondInit (&ta_gecko.cond); + if (res) { + gprintf ("error creating cond: %d\n", res); + return; + } + + res = LWP_CreateThread (&ld_gecko_thread, ld_gecko_func, &ta_gecko, + ld_gecko_stack, LD_THREAD_STACKSIZE, + LD_THREAD_PRIO); + + if (res) { + gprintf ("error creating thread: %d\n", res); + } + } + + gprintf ("starting tcp thread\n"); + + memset (&ta_tcp, 0, sizeof (ld_tcp_arg)); + + memset (&ld_tcp_stack, 0, LD_THREAD_STACKSIZE); + res = LWP_MutexInit (&ta_tcp.cmutex, false); + if (res) { + gprintf ("error creating cmutex: %d\n", res); + return; + } + res = LWP_CondInit (&ta_tcp.cond); + if (res) { + gprintf ("error creating cond: %d\n", res); + return; + } + res = LWP_CreateThread (&ld_tcp_thread, ld_tcp_func, &ta_tcp, ld_tcp_stack, + LD_THREAD_STACKSIZE, LD_THREAD_PRIO); + if (res) { + gprintf ("error creating thread: %d\n", res); + } +} + +void loader_deinit (void) { + u8 i; + + // the tcp thread does stuff on exit; the ideal order is stopping it first + if (ta_tcp.running) { + gprintf ("stopping tcp thread\n"); + + for (i = 0; i < 25; ++i) { + if (LWP_MutexTryLock (ta_tcp.cmutex) == 0) + break; + usleep (20 * 1000); + } + if (i >= 25) { + gprintf("tcp thread didn't shutdown gracefully!\n"); + } else { + gprintf ("sending tcp entry thread the exit cmd\n"); + ta_tcp.cmd = LDTCPCMD_EXIT; + LWP_SetThreadPriority (ld_tcp_thread, LWP_PRIO_HIGHEST); + LWP_CondBroadcast (ta_tcp.cond); + LWP_MutexUnlock (ta_tcp.cmutex); + + for (i = 0; i < 25; ++i) { + if (LWP_MutexTryLock (ta_tcp.cmutex) == 0) { + if (!ta_tcp.running) + break; + LWP_MutexUnlock (ta_tcp.cmutex); + } + usleep (20 * 1000); + } + if (i >= 25) { + gprintf("tcp thread didn't shutdown gracefully!\n"); + } else { + LWP_MutexUnlock (ta_tcp.cmutex); + LWP_JoinThread(ld_tcp_thread, NULL); + LWP_CondDestroy (ta_tcp.cond); + LWP_MutexDestroy (ta_tcp.cmutex); + } + } + } + + if (ta_gecko.running) { + gprintf ("stopping gecko thread\n"); + + for (i = 0; i < 25; ++i) { + if (LWP_MutexTryLock (ta_gecko.cmutex) == 0) + break; + usleep (20 * 1000); + } + if (i < 25) { + gprintf ("sending gecko entry thread the exit cmd\n"); + ta_gecko.cmd = LDGECKOCMD_EXIT; + LWP_SetThreadPriority (ld_gecko_thread, LWP_PRIO_HIGHEST); + LWP_CondBroadcast (ta_gecko.cond); + LWP_MutexUnlock (ta_gecko.cmutex); + LWP_JoinThread(ld_gecko_thread, NULL); + LWP_CondDestroy (ta_gecko.cond); + LWP_MutexDestroy (ta_gecko.cmutex); + } else { + gprintf("gecko thread didn't shutdown gracefully!\n"); + } + } + +} + +void loader_tcp_init (void) { + if (LWP_MutexTryLock(ta_tcp.cmutex) != 0) + return; + + if (ta_tcp.running && ta_tcp.state == LDTCPS_UNINITIALIZED) { + ta_tcp.cmd = LDTCPCMD_INIT; + LWP_CondBroadcast(ta_tcp.cond); + } + LWP_MutexUnlock(ta_tcp.cmutex); +} + +void loader_signal_threads (void) { + if (LWP_MutexTryLock(ta_gecko.cmutex) == 0) { + if (loader_gecko_initialized () && !ta_gecko.handshaked) { + ta_gecko.cmd = LDGECKOCMD_POLL; + LWP_CondBroadcast(ta_gecko.cond); + } + LWP_MutexUnlock(ta_gecko.cmutex); + } + + if (LWP_MutexTryLock(ta_tcp.cmutex) == 0) { + if (loader_tcp_initialized () && !ta_tcp.handshaked) { + ta_tcp.cmd = LDTCPCMD_ACCEPT; + LWP_CondBroadcast(ta_tcp.cond); + } + LWP_MutexUnlock(ta_tcp.cmutex); + } +} + +bool loader_gecko_initialized (void) { + return ta_gecko.running; +} + +bool loader_tcp_initializing (void) { + return ta_tcp.running && ta_tcp.state == LDTCPS_INITIALIZING; +} + +bool loader_tcp_initialized (void) { + return ta_tcp.running && ta_tcp.state == LDTCPS_INITIALIZED; +} + +bool loader_handshaked (void) { + bool handshaked = false; + if (LWP_MutexTryLock(ta_gecko.cmutex) == 0) { + handshaked = handshaked || (ta_gecko.running && ta_gecko.handshaked); + LWP_MutexUnlock(ta_gecko.cmutex); + } + if (LWP_MutexTryLock(ta_tcp.cmutex) == 0) { + handshaked = handshaked || (ta_tcp.running && ta_tcp.handshaked); + LWP_MutexUnlock(ta_tcp.cmutex); + } + + return handshaked; +} + +// loading thread + +static lwp_t ld_load_thread; +static u8 ld_load_stack[LD_THREAD_STACKSIZE] ATTRIBUTE_ALIGN (32); + +typedef enum { + LDC_FILE = 0, + LDC_GECKO, + LDC_TCP +} ld_load_cmd; + +typedef enum { + LDS_RUNNING = 0, + LDS_SUCCESS, + LDS_ERR_READ, + LDS_ERR_RECEIVE, + LDS_ERR_UNCOMPRESS +} ld_load_state; + +typedef struct { + ld_load_cmd cmd; + + u32 data_len; + u8 *data; + + u32 data_len_un; + u8 *data_un; + + u16 args_len; + + int fd; + + mutex_t mutex; + ld_load_state state; + u32 progress; +} ld_load_arg; + +static void * ld_load_func (void *arg) { + ld_load_arg *ta = (ld_load_arg *) arg; + u8 *d; + u32 left, received; + s32 block; + int res; + s64 t; + + d = ta->data; + received = 0; + left = ta->data_len + ta->args_len; + + switch (ta->cmd) { + case LDC_FILE: + while (left) { + block = left; + if (block > 32 * 1024) + block = 32 * 1024; + + block = read (ta->fd, d, block); + + if (block < 0) { + gprintf ("read failed: %ld\n", block); + close (ta->fd); + + LWP_MutexLock (ta->mutex); + ta->state = LDS_ERR_READ; + LWP_MutexUnlock (ta->mutex); + + return NULL; + } else { + d += block; + received += block; + left -= block; + + LWP_MutexLock (ta->mutex); + ta->progress = received; + LWP_MutexUnlock (ta->mutex); + } + } + + close (ta->fd); + + break; + + case LDC_GECKO: + block = 0; + t = gettime (); + + while (left) { + res = usb_recvbuffer_ex(USBGECKO_CHANNEL, d, left, USBGECKO_RETRIES); + + if (res) { + d += res; + received += res; + left -= res; + block += res; + + if (block >= 1024) { + block = 0; + + LWP_MutexLock (ta->mutex); + ta->progress = received; + LWP_MutexUnlock (ta->mutex); + + t = gettime (); + } + } + + if (ticks_to_millisecs (diff_ticks (t, gettime ())) > LD_TIMEOUT) { + LWP_MutexLock (ta->mutex); + ta->state = LDS_ERR_RECEIVE; + LWP_MutexUnlock (ta->mutex); + + gprintf_enable(1); + + return NULL; + } + } + + gprintf_enable(1); + + break; + + case LDC_TCP: + if (!tcp_read (ta->fd, ta->data, left, &ta->mutex, &ta->progress)) { + LWP_MutexLock (ta->mutex); + ta->state = LDS_ERR_RECEIVE; + LWP_MutexUnlock (ta->mutex); + + net_close (ta->fd); + + return NULL; + } + + net_close (ta->fd); + + break; + } + + if (ta->data_un) { + int res; + uLongf len = ta->data_len_un; + + res = uncompress(ta->data_un, &len, ta->data, ta->data_len); + + if (res != Z_OK) { + gprintf("error uncompressing: %d\n", res); + + LWP_MutexLock (ta->mutex); + ta->state = LDS_ERR_UNCOMPRESS; + LWP_MutexUnlock (ta->mutex); + + return NULL; + } + + if (len != ta->data_len_un) { + gprintf("short uncompress: %lu\n", (u32) len); + + LWP_MutexLock (ta->mutex); + ta->state = LDS_ERR_UNCOMPRESS; + LWP_MutexUnlock (ta->mutex); + + return NULL; + } + } + + LWP_MutexLock (ta->mutex); + ta->state = LDS_SUCCESS; + LWP_MutexUnlock (ta->mutex); + + return NULL; +} + +// public loading function + +void loader_load(loader_result *result, view *sub_view, app_entry *entry) { + char caption[MAXPATHLEN + 32]; + char filename[MAXPATHLEN]; + + ld_load_arg ta; + s32 res; + bool running; + u32 progress; + + view *v; + + text_err_read = _("Error while reading the application from the SD card"); + text_err_receive = _("Error while receiving the application"); + text_err_uncompress = _("Error uncompressing the received data"); + text_err_invalid_bin = _("This is not a valid Wii application"); + text_err_invalid_zip = _("This is not a usable ZIP file"); + text_err_oom = _("Not enough memory"); + + memset(result, 0, sizeof (loader_result)); + memset(&ta, 0, sizeof (ld_load_arg)); + memset(filename, 0, sizeof(filename)); + + bool gecko_handshaked = false; + bool tcp_handshaked = false; + + if (LWP_MutexTryLock (ta_gecko.cmutex) == 0) { + gecko_handshaked = ta_gecko.handshaked; + ta_gecko.cmd = LDGECKOCMD_IDLE; + LWP_MutexUnlock (ta_gecko.cmutex); + } + + if (LWP_MutexTryLock (ta_tcp.cmutex) == 0) { + tcp_handshaked = ta_tcp.handshaked; + ta_gecko.cmd = LDTCPCMD_IDLE; + LWP_MutexUnlock (ta_tcp.cmutex); + } + + if (entry) { + char *name; + + if (!app_entry_get_filename(filename, entry)) + return; + + gprintf ("loading %s\n", filename); + + ta.cmd = LDC_FILE; + + ta.fd = open (filename, O_RDONLY); + if (ta.fd < 0) + return; + + ta.data_len = entry->size; + + if (entry->meta && entry->meta->name) + name = entry->meta->name; + else + name = entry->dirname; + + sprintf (caption, _("Loading %s"), name); + } else if (gecko_handshaked) { + ta.cmd = LDC_GECKO; + ta_gecko.handshaked = false; + + ta.data_len = ta_gecko.data_len; + ta.data_len_un = ta_gecko.data_len_un; + ta.args_len = ta_gecko.args_len; + + sprintf (caption, _("Receiving over USBGecko")); + } else if (tcp_handshaked) { + ta.cmd = LDC_TCP; + ta_tcp.handshaked = false; + + ta.data_len = ta_tcp.data_len; + ta.data_len_un = ta_tcp.data_len_un; + ta.args_len = ta_tcp.args_len; + ta.fd = ta_tcp.s; + + sprintf (caption, _("Receiving from %s"), ta_tcp.client); + } else { + return; + } + + if (ta.data_len == ta.data_len_un) + ta.data_len_un = 0; + + if (ta.data_len_un) { + ta.data_un = (u8 *) blob_alloc(ta.data_len_un); + + if (!ta.data_un) { + if (ta.cmd == LDC_TCP) + net_close (ta.fd); + show_message (sub_view, DLGMT_ERROR, DLGB_OK, text_err_oom, 0); + return; + } + } + + ta.data = (u8 *) blob_alloc(ta.data_len + ta.args_len); + + if (!ta.data) { + blob_free(ta.data_un); + if (ta.cmd == LDC_TCP) + net_close (ta.fd); + show_message (sub_view, DLGMT_ERROR, DLGB_OK, text_err_oom, 0); + return; + } + + + res = LWP_MutexInit (&ta.mutex, false); + + if (res) { + gprintf ("error creating mutex: %ld\n", res); + blob_free (ta.data); + blob_free (ta.data_un); + if (ta.cmd == LDC_TCP) + net_close (ta.fd); + panic(); // if this happens, let's find out + return; + } + + memset (&ld_load_stack, 0, LD_THREAD_STACKSIZE); + res = LWP_CreateThread (&ld_load_thread, ld_load_func, &ta, ld_load_stack, + LD_THREAD_STACKSIZE, LD_THREAD_PRIO); + + if (res) { + gprintf ("error creating thread: %ld\n", res); + blob_free (ta.data); + blob_free (ta.data_un); + if (ta.cmd == LDC_TCP) + net_close (ta.fd); + panic(); // if this happens, let's find out + return; + } + + v = dialog_progress (sub_view, caption, ta.data_len + ta.args_len); + + dialog_fade (v, true); + + running = true; + while (running) { + LWP_MutexLock (ta.mutex); + running = ta.state == LDS_RUNNING; + progress = ta.progress; + LWP_MutexUnlock (ta.mutex); + + dialog_set_progress (v, progress); + + view_plot (v, DIALOG_MASK_COLOR, NULL, NULL, NULL); + } + + dialog_fade (v, false); + + view_free (v); + + LWP_MutexDestroy (ta.mutex); + + if (ta.data_len_un) { + if (ta.args_len) { + memcpy(result->args, &ta.data[ta.data_len], ta.args_len); + result->args_len = ta.args_len; + } + + blob_free(ta.data); + ta.data = ta.data_un; + ta.data_len = ta.data_len_un; + ta.data_un = NULL; + ta.data_len_un = 0; + } + + const char *text = NULL; + + switch (ta.state) { + case LDS_RUNNING: + case LDS_SUCCESS: + break; + case LDS_ERR_READ: + text = text_err_read; + break; + case LDS_ERR_RECEIVE: + text = text_err_receive; + break; + case LDS_ERR_UNCOMPRESS: + text = text_err_uncompress; + break; + } + + if (text) { + blob_free(ta.data); + show_message (sub_view, DLGMT_ERROR, DLGB_OK, text, 0); + return; + } + + result->data = ta.data; + result->data_len = ta.data_len; + + if (ta.cmd == LDC_FILE) { + switch(entry->type) { + case AET_BOOT_ELF: + case AET_BOOT_DOL: + break; + + case AET_THEME: + result->type = LT_ZIP_THEME; + return; + } + + result->type = LT_EXECUTABLE; + + strcpy(result->args, filename); + result->args_len = strlen (result->args); + + if (entry->meta && entry->meta->args) { + result->args_len++; + memcpy(result->args + result->args_len, + entry->meta->args, entry->meta->argslen); + result->args_len += entry->meta->argslen; + } + + result->args[result->args_len + 1] = 0; + result->args_len += 2; + + return; + } + + if (manage_is_zip(ta.data)) { + gprintf("we got a .zip\n"); + + if (manage_check_zip_app(ta.data, ta.data_len, result->dirname, + &result->bytes)) + result->type = LT_ZIP_APP; + else if (manage_check_zip_theme(ta.data, ta.data_len)) + result->type = LT_ZIP_THEME; + + if (result->type == LT_UNKNOWN) { + blob_free(ta.data); + ta.data = NULL; + ta.data_len = 0; + show_message (sub_view, DLGMT_ERROR, DLGB_OK, + text_err_invalid_zip, 0); + } + + return; + } + + result->type = LT_EXECUTABLE; +} + +bool loader_load_executable(entry_point *ep, loader_result *result, + view *sub_view) { + bool res = loader_reloc(ep, result->data, result->data_len, + result->args, result->args_len, true); + + if (!res) + show_message (sub_view, DLGMT_ERROR, DLGB_OK, text_err_invalid_bin, 0); + + blob_free(result->data); + + return res; +} + +bool loader_handle_zip_app(loader_result *result, view *sub_view) { + char buf[1024]; + char buf2[1024]; + + text_extract_zip = _("Extract the received ZIP file?\n%s of free space are required."); + text_warn_overwrite = _("WARNING: Files in '%s' will be overwritten"); + text_err_extract_zip = _("Error while extracting the ZIP file"); + + if ((result->bytes / 1024u) > 999) + sprintf(buf2, "%.02f MB", + (float)(result->bytes) / (float)(1024 * 1024)); + else + sprintf(buf2, "%lu KB", result->bytes / 1024u); + sprintf(buf, text_extract_zip, buf2); + + if (!app_entry_get_path(buf2)) { + blob_free(result->data); + return false; + } + + strcat(buf2, ":/"); + strcat(buf2, result->dirname); + if (dir_exists(buf2)) { + strcat(buf, "\n\n"); + sprintf(buf2, text_warn_overwrite, result->dirname); + strcat(buf, buf2); + } + + if (show_message(sub_view, DLGMT_CONFIRM, DLGB_YESNO, + buf, 0) != 0) { + blob_free(result->data); + return false; + } + + if (!manage_run(sub_view, result->dirname, result->data, + result->data_len, result->bytes)) { + show_message (sub_view, DLGMT_ERROR, DLGB_OK, text_err_extract_zip, 0); + blob_free(result->data); + return false; + } + + blob_free(result->data); + + return true; +} + diff --git a/channel/channelapp/source/loader.h b/channel/channelapp/source/loader.h new file mode 100644 index 0000000..3b8158f --- /dev/null +++ b/channel/channelapp/source/loader.h @@ -0,0 +1,48 @@ +#ifndef _LOADER_H_ +#define _LOADER_H_ + +#include + +#include + +#include "loader_reloc.h" +#include "appentry.h" +#include "view.h" + +typedef enum { + LT_UNKNOWN = 0, + LT_EXECUTABLE, + LT_ZIP_APP, + LT_ZIP_THEME +} loader_type; + +typedef struct { + loader_type type; + + u8 *data; + u32 data_len; + + char args[ARGS_MAX_LEN]; + u32 args_len; + + char dirname[MAXPATHLEN]; + u32 bytes; +} loader_result; + +void loader_init (void); +void loader_deinit (void); + +void loader_tcp_init (void); +void loader_signal_threads (void); +bool loader_gecko_initialized (void); +bool loader_tcp_initializing (void); +bool loader_tcp_initialized (void); +bool loader_handshaked (void); + +void loader_load(loader_result *result, view *sub_view, app_entry *entry); +bool loader_load_executable(entry_point *ep, loader_result *result, + view *sub_view); +bool loader_handle_zip_app(loader_result *result, view *sub_view); + +#endif + diff --git a/channel/channelapp/source/loader_reloc.c b/channel/channelapp/source/loader_reloc.c new file mode 100644 index 0000000..d43ef40 --- /dev/null +++ b/channel/channelapp/source/loader_reloc.c @@ -0,0 +1,283 @@ +#include +#include + +#include +#include + +#include "../config.h" +#include "loader.h" + +#include "elf_abi.h" +#include "asm.h" + +extern void __exception_closeall (); + +typedef struct _dolheader { + u32 text_pos[7]; + u32 data_pos[11]; + u32 text_start[7]; + u32 data_start[11]; + u32 text_size[7]; + u32 data_size[11]; + u32 bss_start; + u32 bss_size; + u32 entry_point; +} dolheader; + +static void patch_crt0 (entry_point *ep) { + u8 *p = (u8 *) *ep; + + if (p[0x20] == 0x41) { + gprintf ("patching crt0\n"); + p[0x20] = 0x40; + + DCFlushRange ((void *) &p[0x20], 1); + } +} + +static void set_argv (entry_point *ep, const char *args, u16 arg_len) { + u32 *p = (u32 *) *ep; + struct __argv *argv; + char *cmdline; + + if (p[1] != ARGV_MAGIC) { + gprintf ("application does not support argv\n"); + return; + } + + gprintf ("setting argv\n"); + + argv = (struct __argv *) &p[2]; + cmdline = (char *) LD_ARGS_ADDR; + + memcpy (cmdline, args, arg_len); + +#ifdef DEBUG_APP + size_t i; + + gprintf ("cmdline='"); + for (i = 0; i < arg_len; ++i) + if (cmdline[i] == 0) + gprintf ("\\0"); + else + gprintf ("%c", cmdline[i]); + gprintf ("'\n"); +#endif + + argv->argvMagic = ARGV_MAGIC; + argv->commandLine = cmdline; + argv->length = arg_len; + + DCFlushRange (&p[2], 4); + DCFlushRange ((void *) LD_ARGS_ADDR, arg_len); +} + +static bool reloc_dol (entry_point *ep, const u8 *addr, u32 size, + bool check_overlap) { + u32 i; + dolheader *dolfile; + + dolfile = (dolheader *) addr; + for (i = 0; i < 7; i++) { + if (!dolfile->text_size[i]) + continue; + + gprintf ("loading text section %lu @ 0x%08lx (0x%08lx bytes)\n", i, + dolfile->text_start[i], dolfile->text_size[i]); + + if (dolfile->text_pos[i] + dolfile->text_size[i] > size) + return false; + + if (check_overlap && ((dolfile->text_start[i] < LD_MIN_ADDR) || + ((dolfile->text_start[i] + dolfile->text_size[i] > + LD_MAX_ADDR)))) + return false; + + + memmove ((void *) dolfile->text_start[i], addr + dolfile->text_pos[i], + dolfile->text_size[i]); + + DCFlushRange ((void *) dolfile->text_start[i], dolfile->text_size[i]); + ICInvalidateRange ((void *) dolfile->text_start[i], + dolfile->text_size[i]); + } + + for(i = 0; i < 11; i++) { + if (!dolfile->data_size[i]) + continue; + + gprintf ("loading data section %lu @ 0x%08lx (0x%08lx bytes)\n", i, + dolfile->data_start[i], dolfile->data_size[i]); + + if (dolfile->data_pos[i] + dolfile->data_size[i] > size) + return false; + + if (check_overlap && ((dolfile->data_start[i] < LD_MIN_ADDR) || + (dolfile->data_start[i] + dolfile->data_size[i] > LD_MAX_ADDR))) + return false; + + memmove ((void*) dolfile->data_start[i], addr + dolfile->data_pos[i], + dolfile->data_size[i]); + + DCFlushRange ((void *) dolfile->data_start[i], dolfile->data_size[i]); + } + + *ep = (entry_point) dolfile->entry_point; + + return true; +} + +static s8 is_valid_elf (const u8 *addr, u32 size) { + Elf32_Ehdr *ehdr; /* Elf header structure pointer */ + + ehdr = (Elf32_Ehdr *) addr; + + if (!IS_ELF (*ehdr)) + return 0; + + if (ehdr->e_ident[EI_CLASS] != ELFCLASS32) + return -1; + + if (ehdr->e_ident[EI_DATA] != ELFDATA2MSB) + return -1; + + if (ehdr->e_ident[EI_VERSION] != EV_CURRENT) + return -1; + + if (ehdr->e_type != ET_EXEC) + return -1; + + if (ehdr->e_machine != EM_PPC) + return -1; + + return 1; +} + +static bool reloc_elf (entry_point *ep, const u8 *addr, u32 size, + bool check_overlap) { + Elf32_Ehdr *ehdr; + Elf32_Phdr *phdrs; + u8 *image; + int i; + + ehdr = (Elf32_Ehdr *) addr; + + if(ehdr->e_phoff == 0 || ehdr->e_phnum == 0) { + gprintf("ELF has no phdrs\n"); + return false; + } + if(ehdr->e_phentsize != sizeof(Elf32_Phdr)) { + gprintf("Invalid ELF phdr size\n"); + return false; + } + + phdrs = (Elf32_Phdr*)(addr + ehdr->e_phoff); + + for(i=0;ie_phnum;i++) { + + if(phdrs[i].p_type != PT_LOAD) { + gprintf("skip PHDR %d of type %ld\n", i, phdrs[i].p_type); + } else { + phdrs[i].p_paddr &= 0x3FFFFFFF; + phdrs[i].p_paddr |= 0x80000000; + + gprintf ("PHDR %d 0x%08lx [0x%lx] -> 0x%08lx [0x%lx] <", i, + phdrs[i].p_offset, phdrs[i].p_filesz, + phdrs[i].p_paddr, phdrs[i].p_memsz); + + if(phdrs[i].p_flags & PF_R) + gprintf("R"); + if(phdrs[i].p_flags & PF_W) + gprintf("W"); + if(phdrs[i].p_flags & PF_X) + gprintf("X"); + gprintf(">\n"); + + if(phdrs[i].p_filesz > phdrs[i].p_memsz) { + gprintf ("-> file size > mem size\n"); + return false; + } + + if(phdrs[i].p_filesz) { + if (check_overlap && ((phdrs[i].p_paddr < LD_MIN_ADDR) || + (phdrs[i].p_paddr + phdrs[i].p_filesz) > LD_MAX_ADDR)) { + gprintf ("-> failed overlap check\n"); + return false; + } + gprintf ("-> load 0x%lx\n", phdrs[i].p_filesz); + image = (u8 *) (addr + phdrs[i].p_offset); + memmove ((void *) phdrs[i].p_paddr, (const void *) image, + phdrs[i].p_filesz); + + DCFlushRange ((void *) phdrs[i].p_paddr, phdrs[i].p_memsz); + if(phdrs[i].p_flags & PF_X) + ICInvalidateRange ((void *) phdrs[i].p_paddr, phdrs[i].p_memsz); + } else { + gprintf ("-> skip\n"); + } + } + } + + *ep = (entry_point) ((ehdr->e_entry & 0x3FFFFFFF) | 0x80000000); + + return true; +} + +bool loader_reloc (entry_point *ep, const u8 *addr, u32 size, const char *args, + u16 arg_len, bool check_overlap) { + s8 res; + bool b; + + res = is_valid_elf (addr, size); + + if (res < 0) + return false; + + if (res == 1) + b = reloc_elf (ep, addr, size, check_overlap); + else + b = reloc_dol (ep, addr, size, check_overlap); + + if (b) { + patch_crt0 (ep); + + if (args && arg_len) + set_argv (ep, args, arg_len); + } + + return b; +} + +static const u32 exec_stub[] = { + 0x7c6903a6, // mtctr r3 + 0x3d208133, // lis r9, 0x8133 + 0x3d408180, // lis r10, 0x8180 + 0x90090000, // 1: stw r0, 0(r9) + 0x39290004, // addi r9, r9, 4 + 0x7c295000, // cmpd r9, r10 + 0x4180fff4, // blt 1b + 0x4e800420 // bctr +}; + +void loader_exec (entry_point ep) { + gprintf ("shutting down services and vectoring...\n"); + SYS_ResetSystem (SYS_SHUTDOWN, 0, 0); + + __exception_closeall (); + + // these pokes make ninty SDK dols work, I'm told + *(vu32*)0x800000F8 = 0x0E7BE2C0; // Bus Clock Speed + *(vu32*)0x800000FC = 0x2B73A840; // CPU Clock Speed + + void *target = (void *)((int)(SYS_GetArena2Hi() - sizeof exec_stub) & ~31); + void (*f_exec_stub) (int) = target; + + memcpy(target, exec_stub, sizeof exec_stub); + DCFlushRange(target, sizeof exec_stub); + ICInvalidateRange(target, sizeof exec_stub); + + f_exec_stub((u32)ep); + + gprintf ("this cant be good\n"); +} + diff --git a/channel/channelapp/source/loader_reloc.h b/channel/channelapp/source/loader_reloc.h new file mode 100644 index 0000000..7026ec1 --- /dev/null +++ b/channel/channelapp/source/loader_reloc.h @@ -0,0 +1,13 @@ +#ifndef _LOADER_RELOC_H_ +#define _LOADER_RELOC_H_ + +#include + +typedef void (*entry_point) (void); + +bool loader_reloc (entry_point *ep, const u8 *addr, u32 size, const char *args, + u16 arg_len, bool check_overlap); +void loader_exec (entry_point ep); + +#endif + diff --git a/channel/channelapp/source/m_main.c b/channel/channelapp/source/m_main.c new file mode 100644 index 0000000..534065c --- /dev/null +++ b/channel/channelapp/source/m_main.c @@ -0,0 +1,141 @@ +#include +#include + +#include +#include + +#include "../config.h" +#include "theme.h" +#include "view.h" +#include "loader.h" +#include "i18n.h" +#include "panic.h" + +#include "m_main.h" + +static view *v_m_main; + +static const char *text_no_ip; +static const char *text_has_ip; + +static bool bootmii_ios = false; + +static bool bootmii_is_installed(u64 title_id) { + u32 tmd_view_size; + u8 *tmdbuf; + bool ret; + + if (ES_GetTMDViewSize(title_id, &tmd_view_size) < 0) + return false; + + if (tmd_view_size < 90) + return false; + + if (tmd_view_size > 1024) + return false; + + tmdbuf = pmemalign(32, 1024); + + if (ES_GetTMDView(title_id, tmdbuf, tmd_view_size) < 0) { + free(tmdbuf); + return false; + } + + if (tmdbuf[50] == 'B' && tmdbuf[51] == 'M') + ret = true; + else + ret = false; + + free(tmdbuf); + + return ret; +} + +static bool inited_widgets = false; + +view * m_main_init (void) { + bootmii_ios = bootmii_is_installed(TITLEID_BOOTMII); + + v_m_main = view_new (8, NULL, 0, 0, 0, 0); + + m_main_theme_reinit(); + m_main_update(); + + view_set_focus(v_m_main, 0); + + return v_m_main; +} + +void m_main_deinit(void) { + view_free (v_m_main); + inited_widgets = false; + v_m_main = NULL; +} + +void m_main_theme_reinit(void) { + u16 x, y, yadd; + int i; + char buffer[20]; + + text_no_ip = _("Network not initialized"); + text_has_ip = _("Your Wii's IP is %u.%u.%u.%u"); + + if (inited_widgets) + for (i = 0; i < v_m_main->widget_count; ++i) + widget_free(&v_m_main->widgets[i]); + + if (bootmii_ios) + yadd = 16; + else + yadd = 32; + + x = (view_width - theme_gfx[THEME_BUTTON]->w) / 2; + y = 80; + + widget_button (&v_m_main->widgets[0], x, y, 0, BTN_NORMAL, _("Back")); + y += theme_gfx[THEME_BUTTON]->h + yadd; + widget_button (&v_m_main->widgets[1], x, y, 0, BTN_NORMAL, _("About")); + y += theme_gfx[THEME_BUTTON]->h + yadd; + + if (bootmii_ios) { + widget_button (&v_m_main->widgets[2], x, y, 0, BTN_NORMAL, + _("Launch BootMii")); + y += theme_gfx[THEME_BUTTON]->h + yadd; + } + + widget_button (&v_m_main->widgets[3], x, y, 0, BTN_NORMAL, _("Exit to System Menu")); + y += theme_gfx[THEME_BUTTON]->h + yadd; + + widget_button (&v_m_main->widgets[4], x, y, 0, BTN_NORMAL, _("Shutdown")); + + widget_label (&v_m_main->widgets[5], view_width / 3 * 2 - 16, 32, 0, + CHANNEL_VERSION_STR, view_width / 3 - 32, FA_RIGHT, + FA_ASCENDER, FONT_LABEL); + + sprintf(buffer, "IOS%ld v%ld.%ld", IOS_GetVersion(), IOS_GetRevisionMajor(), + IOS_GetRevisionMinor()); + + widget_label (&v_m_main->widgets[6], view_width / 3 * 2 - 16, + 32 + font_get_y_spacing(FONT_LABEL), 0, buffer, + view_width / 3 - 32, FA_RIGHT, FA_ASCENDER, FONT_LABEL); + + inited_widgets = true; +} + +void m_main_update (void) { + u32 ip; + char buffer[64]; + + if (loader_tcp_initialized ()) { + ip = net_gethostip (); + sprintf (buffer, text_has_ip, (ip >> 24) & 0xff, (ip >> 16) & 0xff, + (ip >> 8) & 0xff, ip & 0xff); + widget_label (&v_m_main->widgets[7], 48, 32, 0, buffer, + view_width / 3 * 2 - 32, FA_LEFT, FA_ASCENDER, FONT_LABEL); + } else { + widget_label (&v_m_main->widgets[7], 48, 32, 0, text_no_ip, + view_width / 3 * 2 - 32, FA_LEFT, FA_ASCENDER, FONT_LABEL); + } + +} + diff --git a/channel/channelapp/source/m_main.h b/channel/channelapp/source/m_main.h new file mode 100644 index 0000000..58ca935 --- /dev/null +++ b/channel/channelapp/source/m_main.h @@ -0,0 +1,15 @@ +#ifndef _M_MAIN_H_ +#define _M_MAIN_H_ + +#include + +#include "view.h" + +view * m_main_init (void); +void m_main_deinit (void); +void m_main_theme_reinit (void); + +void m_main_update (void); + +#endif + diff --git a/channel/channelapp/source/main.c b/channel/channelapp/source/main.c new file mode 100644 index 0000000..29a3bdd --- /dev/null +++ b/channel/channelapp/source/main.c @@ -0,0 +1,825 @@ +#include +#include +#include +#include +#include +#include +#include +#include +#include + +#include +#include +#include +#include + +#include "../config.h" +#include "controls.h" +#include "appentry.h" +#include "playtime.h" +#include "gfx.h" +#include "tex.h" +#include "theme.h" +#include "cursors.h" +#include "font.h" +#include "widgets.h" +#include "view.h" +#include "bubbles.h" +#include "dialogs.h" +#include "browser.h" +#include "m_main.h" +#include "loader.h" +#ifdef ENABLE_UPDATES +#include "http.h" +#include "update.h" +#endif +#include "i18n.h" +#include "dvd.h" +#include "manage.h" +#include "xml.h" +#include "title.h" + +// Languages +#include "spanish_mo.h" +#include "dutch_mo.h" +#include "german_mo.h" +#include "french_mo.h" +#include "italian_mo.h" +#include "japanese_mo.h" + +#ifdef DEBUG_APP +//#define GDBSTUB +#else +#undef GDBSTUB +#endif + +#define WARN_SPLIT 360 + +extern const u8 stub_bin[]; +extern const u8 stub_bin_end; +extern const u32 stub_bin_size; + +u64 *conf_magic = STUB_ADDR_MAGIC; +u64 *conf_title_id = STUB_ADDR_TITLE; + +static bool should_exit; +static bool shutdown; +#ifdef GDBSTUB +static bool gdb; +#endif +static const char *text_delete; +static const char *text_error_delete; + +s32 __IOS_LoadStartupIOS(void) { +#if 0 + __ES_Init(); + __IOS_LaunchNewIOS(58); +#endif + return 0; +} + +#define MEM2_PROT 0x0D8B420A +#define ES_MODULE_START (u16*)0x939F0000 + +static const u16 ticket_check[] = { + 0x685B, // ldr r3,[r3,#4] ; get TMD pointer + 0x22EC, 0x0052, // movls r2, 0x1D8 + 0x189B, // adds r3, r3, r2; add offset of access rights field in TMD + 0x681B, // ldr r3, [r3] ; load access rights (haxxme!) + 0x4698, // mov r8, r3 ; store it for the DVD video bitcheck later + 0x07DB // lsls r3, r3, #31; check AHBPROT bit +}; + +static int patch_ahbprot_reset(void) +{ + u16 *patchme; + + if ((read32(0x0D800064) == 0xFFFFFFFF) ? 1 : 0) { + write16(MEM2_PROT, 2); + for (patchme=ES_MODULE_START; patchme < ES_MODULE_START+0x4000; ++patchme) { + if (!memcmp(patchme, ticket_check, sizeof(ticket_check))) + { + // write16/uncached poke doesn't work for MEM2 + patchme[4] = 0x23FF; // li r3, 0xFF + DCFlushRange(patchme+4, 2); + return 0; + } + } + return -1; + } else { + return -2; + } +} + +static void reset_cb(void) { +#ifdef GDBSTUB + gdb = true; +#else + should_exit = true; +#endif +} + +static void power_cb(void) { + should_exit = true; + shutdown = true; +} + +void config_language(void) { + s32 language; + const void *mo; + + // this is probably wrong, but we don't let the C library do any UTF-8 text + // processing worth mentioning anyway, and I'm scared of what might happen + // if I change this :P + setlocale(LC_CTYPE, "C-ISO-8859-1"); + +#ifdef FORCE_LANG + language = FORCE_LANG; +#else + language = CONF_GetLanguage(); + if (language < 0) + language = CONF_LANG_ENGLISH; +#endif + + mo = NULL; + + switch (language) { + case CONF_LANG_ENGLISH: + mo = NULL; + break; + + case CONF_LANG_SPANISH: + mo = spanish_mo; + break; + + case CONF_LANG_FRENCH: + mo = french_mo; + break; + + case CONF_LANG_GERMAN: + mo = german_mo; + break; + + case CONF_LANG_DUTCH: + mo = dutch_mo; + break; + + case CONF_LANG_ITALIAN: + mo = italian_mo; + break; + + case CONF_LANG_JAPANESE: + if (theme.langs & TLANG_JA) { + mo = japanese_mo; + } else { + gprintf("language ja disabled due to missing theme support\n"); + language = CONF_LANG_ENGLISH; + } + break; + + default: + gprintf("unsupported language %ld, defaulting to English\n", + language); + language = CONF_LANG_ENGLISH; + } + + gprintf("configuring language: %ld with mo file at %p\n", language, mo); + i18n_set_mo(mo); +} + + +static void main_pre(void) { +#ifdef DEBUG_APP + if(usb_isgeckoalive(USBGECKO_CHANNEL)) + CON_EnableGecko(USBGECKO_CHANNEL, 1); + +#ifdef GDBSTUB + DEBUG_Init(GDBSTUB_DEVICE_USB, USBGECKO_CHANNEL); +#endif +#endif + + AUDIO_Init(NULL); + DSP_Init(); + AUDIO_StopDMA(); + AUDIO_RegisterDMACallback(NULL); + + gfx_init_video(); + PAD_Init(); + + SYS_SetResetCallback(reset_cb); + title_init(); + SYS_SetPowerCallback(power_cb); + + gprintf("installing stub (%lu)\n", stub_bin_size); + +#ifdef DEBUG_APP + if (stub_bin_size > 0x1800) + gprintf("WARNING: stub too big!\n"); +#endif + + memcpy((u32 *) 0x80001800, stub_bin, stub_bin_size); + DCFlushRange((u32 *) 0x80001800, stub_bin_size); + + *conf_magic = 0; + + gprintf ("startup\n"); + gprintf("IOS Version: IOS%ld %ld.%ld\n", + IOS_GetVersion(), IOS_GetRevisionMajor(), IOS_GetRevisionMinor()); + + ISFS_Initialize(); + + WiiDVD_StopMotorAsync(); + + gfx_init(); + app_entry_init(); + theme_xml_init(); + theme_init(NULL, 0); + config_language(); + loader_init(); + controls_init(); + cursors_init(); + font_init(); + widgets_init(); + view_init(); + dialogs_init(); +} + +static void load_text(void) +{ + text_delete = _("Do you really want to delete this application?"); + text_error_delete = _("Error deleting '%s'"); +} + +static void refresh_theme(view *v, app_entry *app, u8 *data, u32 data_len) { + browser_gen_view(BA_REMOVE, NULL); + view_fade(v, TEX_LAYER_CURSOR + 1, 0, 0, 0, 0, 32, 8); + font_clear(); + theme_deinit(); + theme_init(data, data_len); + config_language(); + widgets_theme_reinit(); + bubbles_theme_reinit(); + view_theme_reinit(); + browser_theme_reinit(); + m_main_theme_reinit(); + dialogs_theme_reinit(); + view_fade(v, TEX_LAYER_CURSOR + 1, 0xff, 0xff, 0xff, 0xff, 31, -8); + browser_gen_view(BA_REFRESH, app); + load_text(); +} + +void main_real(void) { + view *v_last, *v_current, *v_m_main, *v_browser, *v_detail, *v_about; + + u8 fhw; + + u32 bd, bh; + s8 clicked; + s16 mm; + +#ifdef ENABLE_UPDATES + s16 update_check = -1; + bool update_in_progress = false; + bool update_available = false; +#endif + + app_entry *app_sel; + bool loading = false; + + loader_result ld_res; + entry_point ep; + bool reloced; + bool ahb_access; + bool launch_bootmii; + + u64 frame; + bool exit_about; + + char charbuf[MAXPATHLEN]; + + load_text(); + + playtime_destroy(); + + if (settings_load()) { + app_entry_set_prefered(settings.device); + app_entry_set_sort(settings.sort_order); + + if (settings.browse_mode == 1) + browser_switch_mode(); + } + +#ifdef ENABLE_UPDATES + update_check = 60; +#endif + + app_sel = NULL; + v_browser = browser_init(); + v_m_main = m_main_init (); + view_bubbles = true; + +#ifdef ENABLE_UPDATES + http_init (); +#endif + + fhw = font_get_y_spacing(FONT_MEMO); + + should_exit = false; + shutdown = false; +#ifdef GDBSTUB + gdb = false; +#endif + + reloced = false; + ahb_access = false; + launch_bootmii = false; + + frame = 0; + exit_about = false; + + v_last = v_current = v_browser; + v_detail = NULL; + v_about = NULL; + + view_fade(v_current, TEX_LAYER_CURSOR + 1, 0xff, 0xff, 0xff, 0xff, 31, -8); + + view_enable_cursor (true); + + while (!should_exit) { +#ifdef GDBSTUB + if (gdb) { + gprintf("attach gdb now!\n"); + _break(); + gdb = false; + SYS_SetResetCallback (reset_cb); + } +#endif + memstats(false); + + if (v_current == v_browser) { + switch (app_entry_action()) { + case AE_ACT_NONE: + break; + + case AE_ACT_REMOVE: + app_sel = browser_sel(); + if (app_sel) { + memset(settings.app_sel, 0, sizeof(settings.app_sel)); + strcpy(settings.app_sel, app_sel->dirname); + } + + bubble_popall(); + browser_gen_view(BA_REMOVE, NULL); + app_entries_free(); + memstats(true); + break; + + case AE_ACT_ADD: + if (strlen(settings.app_sel) > 0) + app_sel = app_entry_find(settings.app_sel); + else + app_sel = NULL; + + view_show_throbber(false); + browser_gen_view(BA_ADD, app_sel); + break; + } + } + + if (loading != app_entry_is_loading()) { + loading = !loading; + + view_show_throbber(loading); + } + + if (loading) + view_throbber_tickle(); + + if ((frame % 30) == 0) + app_entry_scan(); + + view_plot (v_current, DIALOG_MASK_COLOR, &bd, &bh, NULL); + + frame++; + if (v_last != v_current) { + frame = 0; + v_last = v_current; + } + + if (bd & PADS_HOME) { + if (v_current == v_browser) { + m_main_update (); + v_current = v_m_main; + view_set_focus (v_m_main, 0); + + continue; + } else { + if (v_current == v_m_main) + v_current = v_browser; + + continue; + } + } + + if (v_current == v_m_main) { + if (bd & PADS_B) { + v_current = v_browser; + + continue; + } + + if (bd & PADS_UP) + view_set_focus_prev (v_current); + + if (bd & PADS_DOWN) + view_set_focus_next (v_current); + + if (bd & PADS_A) { + switch (v_m_main->focus) { + case 0: + v_current = v_browser; + continue; + + case 1: + v_about = dialog_about (v_m_main); + v_current = v_about; + + view_enable_cursor (false); + + dialog_fade (v_current, true); + + exit_about = false; + + continue; + + case 2: + launch_bootmii = true; + should_exit = true; + break; + + case 3: + should_exit = true; + continue; + + case 4: + should_exit = true; + shutdown = true; + break; + } + } + + continue; + } + + if (v_current == v_browser) { + widget_set_flag (&v_browser->widgets[2], WF_ENABLED, + loader_gecko_initialized ()); + + if (!loader_tcp_initializing ()) + widget_set_flag (&v_browser->widgets[3], WF_ENABLED, + loader_tcp_initialized ()); + + if (loader_tcp_initializing () && (frame % 20 == 0)) + widget_toggle_flag (&v_browser->widgets[3], WF_ENABLED); + + if (bd & PADS_NET_INIT) + loader_tcp_init (); + +#ifdef ENABLE_UPDATES + if (loader_tcp_initialized () && (update_check > 0)) + update_check--; + + if (update_check == 0) { + update_check = -1; + update_in_progress = update_signal (); + } + + if (update_in_progress) { + if (!loading && !update_busy(&update_available)) + update_in_progress = false; + } + + if (update_available) { + update_available = false; + + reloced = update_execute (v_current, &ep); + should_exit = reloced; + + continue; + } +#endif + if (bd & PADS_1) { + dialog_options_result options; + options = show_options_dialog(v_current); + + if (options.confirmed) { + app_entry_set_sort(options.sort); + app_entry_set_device(options.device); + app_entry_set_prefered(options.device); + } + + continue; + } + + if (bd & PADS_2) { + browser_switch_mode(); + continue; + } + + if (bd & PADS_DPAD) { + browser_set_focus(bd); + continue; + } + + if (bd & PADS_A) { + clicked = view_widget_at_ir (v_browser); + + if (clicked == 0) { + browser_gen_view(BA_PREV, NULL); + continue; + } + + if (clicked == 1) { + browser_gen_view(BA_NEXT, NULL); + continue; + } + + if (clicked == 3) { + loader_tcp_init (); + continue; + } + + app_sel = browser_sel(); + + if (!app_sel) + continue; + + v_detail = dialog_app (app_sel, v_browser); + v_current = v_detail; + + dialog_fade (v_current, true); + + continue; + } + + if ((bd | bh) & PADS_MINUS) + browser_gen_view(BA_PREV, NULL); + + if ((bd | bh) & PADS_PLUS) + browser_gen_view(BA_NEXT, NULL); + + if (loader_handshaked ()) { + loader_load (&ld_res, v_browser, NULL); + + switch (ld_res.type) { + case LT_UNKNOWN: + break; + + case LT_EXECUTABLE: + if (loader_load_executable(&ep, &ld_res, v_browser)) { + reloced = true; + ahb_access = true; + should_exit = true; + } + + break; + + case LT_ZIP_APP: + if (loader_handle_zip_app(&ld_res, v_browser)) { + app_sel = app_entry_add(ld_res.dirname); + + if (app_sel) + browser_gen_view(BA_REFRESH, app_sel); + memstats(true); + } + + break; + + case LT_ZIP_THEME: + refresh_theme(v_current, app_sel, ld_res.data, + ld_res.data_len); + fhw = font_get_y_spacing(FONT_MEMO); + memstats(true); + break; + } + + continue; + } + + if (!reloced) + loader_signal_threads (); + + continue; + } + + if (v_current == v_detail) { + if (bd & PADS_LEFT) + view_set_focus_prev (v_current); + + if (bd & PADS_RIGHT) + view_set_focus_next (v_current); + + mm = 0; + if (bd & PADS_UP) + mm += fhw; + + if (bd & PADS_DOWN) + mm -= fhw; + + mm += controls_sticky() / 8; + + if (v_current->drag && (v_current->drag_widget == 6)) { + mm += -v_current->drag_y / 32; + } else if (bd & PADS_B) { + dialog_fade (v_current, false); + + v_current = v_browser; + view_free (v_detail); + v_detail = NULL; + + continue; + } + + widget_scroll_memo_deco (&v_detail->widgets[5], mm); + + if ((bd & PADS_A) && v_detail->focus == 10) { + dialog_fade (v_current, false); + + v_current = v_browser; + view_free (v_detail); + v_detail = NULL; + + continue; + } + + if ((bd & PADS_A) && v_detail->focus == 8) { + dialog_fade (v_current, false); + + v_current = v_browser; + view_free (v_detail); + v_detail = NULL; + + if (show_message (v_current, DLGMT_CONFIRM, DLGB_YESNO, + text_delete, 1) == 1) + continue; + + browser_gen_view(BA_REMOVE, NULL); + if (!manage_run(v_current, app_sel->dirname, NULL, 0, 0)) { + sprintf(charbuf, text_error_delete, app_sel->dirname); + show_message(v_current, DLGMT_ERROR, DLGB_OK, charbuf, 0); + } else { + app_entry_remove(app_sel); + app_sel = NULL; + } + browser_gen_view(BA_REFRESH, NULL); + + continue; + } + + if ((bd & PADS_A) && v_detail->focus == 9) { + dialog_fade (v_current, false); + + v_current = v_browser; + + view_free (v_detail); + v_detail = NULL; + + loader_load(&ld_res, v_browser, app_sel); + + switch (ld_res.type) { + case LT_UNKNOWN: + break; + + case LT_EXECUTABLE: + if (loader_load_executable(&ep, &ld_res, v_browser)) { + reloced = true; + + if (app_sel && app_sel->meta) + ahb_access = app_sel->meta->ahb_access; + + should_exit = true; + } + + break; + + case LT_ZIP_APP: + free(ld_res.data); + break; + + case LT_ZIP_THEME: + refresh_theme(v_current, app_sel, ld_res.data, + ld_res.data_len); + break; + } + + continue; + } + + continue; + } + + if (v_current == v_about) { + if (bd & (PADS_A | PADS_B)) + exit_about = true; + + if (exit_about) { + dialog_fade (v_current, false); + + v_current = v_m_main; + view_free (v_about); + v_about = NULL; + + view_enable_cursor (true); + + continue; + } + + if ((frame > 60 * 2) && (frame % 3) == 0) + widget_scroll_memo (&v_current->widgets[2], -1); + } + } + + gprintf ("exiting\n"); + + view_enable_cursor (false); + + if (v_current->sub_view) + dialog_fade (v_current, false); + + view_fade (v_current, TEX_LAYER_CURSOR + 1, 0, 0, 0, 0, 32, 8); + + WiiDVD_ShutDown(); + controls_deinit(); + + app_sel = browser_sel(); + if (app_sel) { + size_t i, s = strlen(app_sel->dirname); + memset(settings.app_sel, 0, sizeof(settings.app_sel)); + for (i = 0; i < s; ++i) + settings.app_sel[i] = tolower((int) app_sel->dirname[i]); + } + + // because the tcp thread is the only one that can block after the exit + // command is sent, deinit it first. this gives the other threads a chance + // to do any pending work if the tcp thread does block. + loader_deinit (); +#ifdef ENABLE_UPDATES + http_deinit (); +#endif + app_entry_deinit (); + + gfx_deinit (); + cursors_deinit (); + + browser_deinit (); + m_main_deinit (); + + dialogs_deinit (); + view_deinit (); + widgets_deinit (); + font_deinit (); + theme_deinit(); + + settings_save(); + + if (launch_bootmii) { + gprintf ("launching BootMii\n"); + __IOS_LaunchNewIOS(BOOTMII_IOS); + } + + if (reloced) { + if (ahb_access) { + gprintf ("patching IOS for AHB access post-reload...\n"); + int res = patch_ahbprot_reset(); + if (res) + gprintf ("patch failed (%d)\n", res); + } + + gprintf ("reloading to IOS%ld...\n", APPS_IOS_VERSION); + __IOS_LaunchNewIOS(APPS_IOS_VERSION); + + if (ahb_access) { + gprintf ("reenabling DVD access...\n"); + mask32(0x0D800180, 1<<21, 0); + } + + gprintf ("branching to %p\n", ep); + loader_exec (ep); + } + + if (shutdown) { + gprintf ("shutting down\n"); + SYS_ResetSystem(SYS_POWEROFF, 0, 0); + } + + gprintf ("returning to sysmenu\n"); + SYS_ResetSystem (SYS_RETURNTOMENU, 0, 0); +} + +int main(int argc, char *argv[]) { + main_pre(); + main_real(); + gprintf("uh oh\n"); + return 0; +} + diff --git a/channel/channelapp/source/manage.c b/channel/channelapp/source/manage.c new file mode 100644 index 0000000..53644e8 --- /dev/null +++ b/channel/channelapp/source/manage.c @@ -0,0 +1,561 @@ +#include +#include +#include +#include +#include +#include +#include +#include +#include + +#include + +#include "../config.h" +#include "appentry.h" +#include "theme.h" +#include "unzip.h" +#include "dialogs.h" +#include "i18n.h" +#include "panic.h" + +#include "manage.h" + +s32 dir_exists(char *dirname) { + struct stat st; + + if (stat(dirname, &st) != 0) + return 0; + + if (S_ISDIR(st.st_mode)) + return 1; + + gprintf("'%s' exists, but is no directory\n", dirname); + + return -1; +} + +static s32 mkdir_hier(char *dirname) { + char dir[MAXPATHLEN]; + size_t i; + s32 res; + + strcpy(dir, dirname); + + if (dir[strlen(dir) - 1] != '/') + strcat(dir, "/"); + + i = 1; + while (dir[i]) { + if (dir[i] == '/') { + dir[i] = 0; + + res = dir_exists(dir); + + if (res < 0) + return res; + + if (!res) { + gprintf("mkdir '%s'\n", dir); + if (mkdir(dir, 0755)) { + gprintf("mkdir failed: %d\n", errno); + return -1; + } + } + + dir[i] = '/'; + } + + i++; + } + + return 0; +} + +static s32 rmdir_hier_iter(const char *dirname) { + s32 res = -1; + DIR* d; + struct dirent *de; + + gprintf("rmdir_hier_iter '%s'\n", dirname); + + d = opendir(dirname); + if (!d) { + gprintf("opendir '%s' failed\n", dirname); + return -1; + } + + char newpath[MAXPATHLEN]; + + while ((de = readdir(d))) { + if (!strcmp(de->d_name, ".") || !strcmp(de->d_name, "..")) + continue; + + strcpy(newpath, dirname); + if (newpath[strlen(newpath) - 1] != '/') + strcat(newpath, "/"); + strcat(newpath, de->d_name); + + if (de->d_type == DT_DIR) { + res = rmdir_hier_iter(newpath); + if (res) + goto exit; + } + + gprintf("unlinking '%s'\n", newpath); + res = unlink(newpath); + if (res) { + gprintf("error unlinking '%s'\n", newpath); + goto exit; + } + } + + res = 0; + +exit: + closedir(d); + + return res; +} + +static s32 rmdir_hier(const char *dirname) { + char buf[MAXPATHLEN]; + + sprintf(buf, "%s/%s", app_path, dirname); + + gprintf("rmdir_hier '%s'\n", buf); + s32 res = rmdir_hier_iter(buf); + if (!res) { + gprintf("unlinking dir '%s'\n", (buf)); + res = unlink(buf); + if (res) + gprintf("error unlinking '%s'\n", buf); + } + + return res; +} + +bool manage_is_zip(const u8 *data) { + return memcmp(data, "PK\x03\x04", 4) == 0; +} + +bool manage_check_zip_app(u8 *data, u32 data_len, char *dirname, u32 *bytes) { + unzFile uf; + int res, i; + bool ret = false; + unz_global_info gi; + + uf = unzOpen(data, data_len); + if (!uf) { + gprintf("unzOpen failed\n"); + return false; + } + + res = unzGetGlobalInfo (uf, &gi); + if (res != UNZ_OK) { + gprintf("unzGetGlobalInfo failed: %d\n", res); + goto error; + } + + if ((gi.number_entry < 2) || (gi.number_entry > 1024)) { + gprintf("invalid file count\n"); + goto error; + } + + char filename[256]; + unz_file_info fi; + u8 got_elf = 0; + u8 got_dol = 0; + u8 got_theme = 0; + + dirname[0] = 0; + *bytes = 0; + + for (i = 0; i < gi.number_entry; ++i) { + res = unzGetCurrentFileInfo(uf, &fi, filename, sizeof(filename) ,NULL, 0, NULL, 0); + if (res != UNZ_OK) { + gprintf("unzGetCurrentFileInfo failed: %d\n", res); + goto error; + } + + gprintf("found '%s' %lu -> %lu\n", filename, fi.compressed_size, fi.uncompressed_size); + + if (filename[0] == '/' || strchr(filename, '\\') || strchr(filename, ':')) { + gprintf("invalid char in filename\n"); + goto error; + } + + if (fi.flag & 1) { + gprintf("encrypted entry\n"); + goto error; + } + + if (fi.uncompressed_size > 0) { + *bytes += fi.uncompressed_size; + + if (!dirname[0]) { + char *p = strchr(filename, '/'); + if (p) { + strncpy(dirname, filename, p - filename + 1); + dirname[p - filename + 1] = 0; + + gprintf("dirname='%s'\n", dirname); + } else { + gprintf("missing pathname\n"); + goto error; + } + } else { + if (strncmp(filename, dirname, strlen(dirname))) { + gprintf("additional pathname\n"); + goto error; + } + } + + if (!strcasecmp(filename + strlen(dirname), app_fn_boot_elf)) + got_elf = 1; + else if (!strcasecmp(filename + strlen(dirname), app_fn_boot_dol)) + got_dol = 1; + else if (!strcasecmp(filename + strlen(dirname), app_fn_theme)) + got_theme = 1; + } + + if (i != gi.number_entry - 1) { + res = unzGoToNextFile(uf); + if (res != UNZ_OK) { + gprintf("unzGoToNextFile failed: %d\n", res); + goto error; + } + } + } + + if (strlen(dirname) && ((got_elf + got_dol + got_theme) == 1)) { + ret = true; + dirname[strlen(dirname) - 1] = 0; + } + +error: + res = unzClose(uf); + if (res) + gprintf("unzClose failed: %d\n", res); + + return ret; +} + +bool manage_check_zip_theme(u8 *data, u32 data_len) { + unzFile uf; + int res, i; + bool ret = false; + unz_global_info gi; + + if (data_len > MAX_THEME_ZIP_SIZE) { + gprintf("theme size too big: %lu\n", data_len); + return false; + } + + uf = unzOpen(data, data_len); + if (!uf) { + gprintf("unzOpen failed\n"); + return false; + } + + res = unzGetGlobalInfo (uf, &gi); + if (res != UNZ_OK) { + gprintf("unzGetGlobalInfo failed: %d\n", res); + goto error; + } + + if ((gi.number_entry < 1) || (gi.number_entry > 64)) { + gprintf("invalid file count\n"); + goto error; + } + + char filename[256]; + unz_file_info fi; + bool got_xml = false; + + for (i = 0; i < gi.number_entry; ++i) { + res = unzGetCurrentFileInfo(uf, &fi, filename, sizeof(filename) ,NULL, 0, NULL, 0); + if (res != UNZ_OK) { + gprintf("unzGetCurrentFileInfo failed: %d\n", res); + goto error; + } + + gprintf("found '%s' %lu -> %lu\n", filename, fi.compressed_size, fi.uncompressed_size); + + if (filename[0] == '/' || strchr(filename, '\\') || strchr(filename, ':')) { + gprintf("invalid char in filename\n"); + goto error; + } + + if (fi.flag & 1) { + gprintf("encrypted entry\n"); + goto error; + } + + if (fi.uncompressed_size > 0) { + char *p = strchr(filename, '/'); + if (p) { + gprintf("directories not accepted\n"); + goto error; + } + + if (!strcasecmp(filename, theme_fn_xml)) { + got_xml = true; + } else if (!theme_is_valid_fn(filename)) { + gprintf("not a valid theme filename\n"); + goto error; + } + } + + if (i != gi.number_entry - 1) { + res = unzGoToNextFile(uf); + if (res != UNZ_OK) { + gprintf("unzGoToNextFile failed: %d\n", res); + goto error; + } + } + } + + if (got_xml) + ret = true; + +error: + res = unzClose(uf); + if (res) + gprintf("unzClose failed: %d\n", res); + + return ret; +} + +static bool manage_extract_zip(u8 *data, u32 data_len, + mutex_t *mutex, u32 *progress) { + unzFile uf; + int res, i; + bool ret = false; + unz_global_info gi; + u8 *buf = NULL; + + uf = unzOpen(data, data_len); + if (!uf) { + gprintf("unzOpen failed\n"); + return false; + } + + res = unzGetGlobalInfo (uf, &gi); + if (res != UNZ_OK) { + gprintf("unzGetGlobalInfo failed: %d\n", res); + goto error; + } + + unz_file_info fi; + char filename[256]; + char sd_filename[MAXPATHLEN]; + char *p; + int fd; + + buf = (u8 *) pmalloc(8 * 1024); + + for (i = 0; i < gi.number_entry; ++i) { + res = unzGetCurrentFileInfo(uf, &fi, filename, sizeof(filename) ,NULL, 0, NULL, 0); + if (res != UNZ_OK) { + gprintf("unzGetCurrentFileInfo failed: %d\n", res); + goto error; + } + + gprintf("extracting '%s' %lu -> %lu\n", filename, fi.compressed_size, fi.uncompressed_size); + + if (fi.uncompressed_size > 0) { + res = unzOpenCurrentFile(uf); + if (res != UNZ_OK) { + gprintf("unzOpenCurrentFile failed: %d\n", res); + goto error; + } + + sprintf(sd_filename, "%s/%s", app_path, filename); + + p = strrchr(sd_filename, '/'); + if (!p) { + gprintf("invalid path: %s\n", sd_filename); + goto error; + } + + *p = 0; + res = mkdir_hier(sd_filename); + if (res) { + gprintf("mkdir_hier failed: %d\n", res); + goto error; + } + *p = '/'; + + fd = open(sd_filename, O_CREAT|O_WRONLY|O_TRUNC); + if (fd < 0) { + gprintf("error opening file: %d\n", fd); + goto error; + } + + do { + res = unzReadCurrentFile(uf, buf, 8 * 1024); + + if (res < 0) { + gprintf("unzReadCurrentFile failed: %d\n", res); + break; + } + + if (res > 0) { + if (res != write(fd, buf, res)) { + gprintf("short write"); + res = -1; + break; + } + + LWP_MutexLock (*mutex); + *progress += res; + LWP_MutexUnlock (*mutex); + } + } while (res > 0); + + close(fd); + unzCloseCurrentFile(uf); + + if (res < 0) { + gprintf("error extracting file: %d\n", res); + goto error; + } + } + + if (i != gi.number_entry - 1) { + res = unzGoToNextFile(uf); + if (res != UNZ_OK) { + gprintf("unzGoToNextFile failed: %d\n", res); + goto error; + } + } + } + + ret = true; + +error: + free(buf); + + res = unzClose(uf); + if (res) + gprintf("unzClose failed: %d\n", res); + + return ret; +} + +static lwpq_t manage_queue; +static lwp_t manage_thread; +static u8 manage_stack[MANAGE_THREAD_STACKSIZE] ATTRIBUTE_ALIGN (32); + +typedef struct { + bool running; + + bool extract; + + // extract + u8 *data; + u32 data_len; + mutex_t mutex; + u32 progress; + + // delete + const char *dirname; + + bool success; +} manage_zip_arg; + +static void *manage_func (void *arg) { + manage_zip_arg *ta = (manage_zip_arg *) arg; + + if (ta->extract) + ta->success = manage_extract_zip(ta->data, ta->data_len, &ta->mutex, &ta->progress); + else + ta->success = 0 == rmdir_hier(ta->dirname); + + ta->running = false; + + return NULL; +} + +bool manage_run(view *sub_view, const char *dirname, + u8 *data, u32 data_len, u32 bytes) { + s32 res; + u32 progress = 0; + char caption[MAXPATHLEN]; + + view *v; + + manage_zip_arg ta; + + res = LWP_MutexInit(&ta.mutex, false); + if (res) { + gprintf ("error creating mutex: %ld\n", res); + return false; + } + + ta.running = true; + ta.data = data; + ta.data_len = data_len; + ta.progress = 0; + ta.dirname = dirname; + + ta.extract = data != NULL; + + memset(&manage_stack, 0, MANAGE_THREAD_STACKSIZE); + LWP_InitQueue(&manage_queue); + res = LWP_CreateThread(&manage_thread, manage_func, &ta, manage_stack, + MANAGE_THREAD_STACKSIZE, MANAGE_THREAD_PRIO); + + if (res) { + gprintf ("error creating thread: %ld\n", res); + LWP_CloseQueue(manage_queue); + return false; + } + + const char *text; + + if (data) { + text = _("Extracting"); + sprintf(caption, "%s '%s'...", text, dirname); + v = dialog_progress(sub_view, caption, bytes); + dialog_fade(v, true); + } else { + v = sub_view; + view_show_throbber(true); + } + + while (true) { + LWP_MutexLock(ta.mutex); + if (!ta.running) { + LWP_MutexUnlock(ta.mutex); + break; + } + + progress = ta.progress; + + LWP_MutexUnlock (ta.mutex); + + if (data) + dialog_set_progress(v, progress); + else + view_throbber_tickle(); + + view_plot(v, DIALOG_MASK_COLOR, NULL, NULL, NULL); + } + + if (data) { + dialog_fade(v, false); + view_free(v); + } else { + view_show_throbber(false); + } + + //LWP_SuspendThread(manage_thread); + LWP_CloseQueue(manage_queue); + + LWP_MutexDestroy(ta.mutex); + + return ta.success; +} + + diff --git a/channel/channelapp/source/manage.h b/channel/channelapp/source/manage.h new file mode 100644 index 0000000..013230c --- /dev/null +++ b/channel/channelapp/source/manage.h @@ -0,0 +1,18 @@ +#ifndef _MANAGE_H_ +#define _MANAGE_H_ + +#include + +#include "view.h" + +s32 dir_exists(char *dirname); + +bool manage_is_zip(const u8 *data); +bool manage_check_zip_app(u8 *data, u32 data_len, char *dirname, u32 *bytes); +bool manage_check_zip_theme(u8 *data, u32 data_len); + +bool manage_run(view *sub_view, const char *dirname, + u8 *data, u32 data_len, u32 bytes); + +#endif + diff --git a/channel/channelapp/source/panic.c b/channel/channelapp/source/panic.c new file mode 100644 index 0000000..1f5cd8e --- /dev/null +++ b/channel/channelapp/source/panic.c @@ -0,0 +1,137 @@ +#include +#include + +#include "panic.h" + +// steal from libogc +extern unsigned char console_font_8x16[]; + +#define BLACK 0x00800080 +#define RED 0x4C544CFF + +static vu32 *fb; + +static void pchar(u32 cx, u32 cy, char c) +{ + int x,y; + unsigned char *p = &console_font_8x16[16*c]; + + for (y=0; y<16; y++) { + char v = *p++; + for (x=0; x<4; x++) { + u32 c = RED; + switch (v&0xc0) { + case 0x00: + c = BLACK; + break; + case 0x40: + c = (RED & 0x0000FF00) | (BLACK & 0xFFFF00FF); + break; + case 0x80: + c = (RED & 0xFFFF00FF) | (BLACK & 0x0000FF00); + break; + case 0xc0: + c = RED; + break; + } + fb[320*(cy+y)+cx+x] = c; + v<<=2; + } + } +} + +static void putsc(u32 y, char *s) { + u32 x = (320-strlen(s)*4)/2; + while(*s) { + pchar(x, y, *s++); + x += 4; + } +} + +static void hex(u32 v, char *p) { + int i; + for (i=0; i<8; i++) { + if ((v>>28) >= 10) + *p++ = 'A' + (v>>28) - 10; + else + *p++ = '0' + (v>>28); + v <<= 4; + } +} + +#define HW_REG_BASE 0xd800000 +#define HW_RESETS (HW_REG_BASE + 0x194) + +// written to be as generic and reliable as possible +void _panic(u32 file, u32 line) +{ + u32 level; + u32 fbr; + int count = 30; + int x,y; + u32 bcolor = BLACK; + u16 vtr; + int lines; + char guru[] = "Guru Meditation #00000000.00000000"; + _CPU_ISR_Disable(level); + + fbr = read32(0xc00201c) & 0x1fffffff; + if (fbr&0x10000000) + fbr <<= 5; + + fb = (vu32*)(fbr | 0xC0000000); + + vtr = read16(0xc002000); + if ((vtr & 0xf) > 10) + lines = vtr >> 4; + else + lines = 2 * (vtr >> 4); + + for(y=(lines-1); y>=116; y--) + for(x=0; x<320; x++) + fb[y*320+x] = fb[(y-116)*320+x]; + for(y=0; y<116; y++) + for(x=0; x<320; x++) + fb[y*320+x] = BLACK; + + hex(file, &guru[17]); + hex(line, &guru[26]); + + putsc(42, "Software Failure. Press reset button to continue."); + putsc(62, guru); + + // wait for reset button + while(read32(0xcc003000)&0x10000) { + // blink + if (count >= 25) { + if (bcolor == RED) + bcolor = BLACK; + else + bcolor = RED; + + for(y=28; y<34; y++) + for(x=14; x<306; x++) + fb[y*320+x] = bcolor; + for(y=34; y<84; y++) { + for(x=14; x<17; x++) + fb[y*320+x] = bcolor; + for(x=303; x<306; x++) + fb[y*320+x] = bcolor; + } + for(y=84; y<90; y++) + for(x=14; x<306; x++) + fb[y*320+x] = bcolor; + + count = 0; + } + + // hacky waitvsync + while(read16(0xc00202c) >= 200); + while(read16(0xc00202c) < 200); + count++; + } + + // needs AHBPROT, is evil. so sue me. + write32(HW_RESETS, read32(HW_RESETS)&(~1)); + while(1); +} diff --git a/channel/channelapp/source/panic.h b/channel/channelapp/source/panic.h new file mode 100644 index 0000000..e4c7cb3 --- /dev/null +++ b/channel/channelapp/source/panic.h @@ -0,0 +1,53 @@ +#ifndef _PANIC_H_ +#define _PANIC_H_ + +#include +#include +#include + +// to skip 'source/' +#define FOFF 7 + +#define S2I(s) (((s)[FOFF+0]<<24)|((s)[FOFF+1]<<16)|((s)[FOFF+2]<<8)|((s)[FOFF+3])) + +#define panic() _panic(S2I(__FILE__)^0xDEADBEEF, __LINE__) +void _panic(u32 file, u32 line) __attribute__((noreturn)); + +#define pmalloc(s) _pmalloc(s, S2I(__FILE__)^0xDEADBEEF, __LINE__) +static inline void *_pmalloc(size_t s, u32 file, u32 line) +{ + void *p = malloc(s); + if (!p) + _panic(file, line); + return p; +} + +#define pmemalign(a,s) _pmemalign(a, s, S2I(__FILE__)^0xDEADBEEF, __LINE__) +static inline void *_pmemalign(size_t a, size_t s, u32 file, u32 line) +{ + void *p = memalign(a, s); + if (!p) + _panic(file, line); + return p; +} + +#define prealloc(p,s) _prealloc(p, s, S2I(__FILE__)^0xDEADBEEF, __LINE__) +static inline void *_prealloc(void *p, size_t s, u32 file, u32 line) +{ + p = realloc(p, s); + if (!p) + _panic(file, line); + return p; +} + +#define pstrdup(s) _pstrdup(s, S2I(__FILE__)^0xDEADBEEF, __LINE__) +static inline void *_pstrdup(const char *s, u32 file, u32 line) +{ + char *p; + p = strdup(s); + if (!p) + _panic(file, line); + return p; +} + +#endif diff --git a/channel/channelapp/source/playtime.c b/channel/channelapp/source/playtime.c new file mode 100644 index 0000000..84c64ad --- /dev/null +++ b/channel/channelapp/source/playtime.c @@ -0,0 +1,39 @@ +#include +#include +#include +#include +#include +#include +#include + +#include "../config.h" +#include "playtime.h" + +static char _playtime_path[] __attribute__((aligned(32))) = + "/title/00000001/00000002/data/play_rec.dat"; + +void playtime_destroy(void) { + s32 res; + s32 pt_fd = -1; + static u8 pt_buf[4] __attribute__((aligned(32))); + + gprintf("destroying playtime\n"); + + pt_fd = IOS_Open(_playtime_path, IPC_OPEN_RW); + if(pt_fd < 0) { + gprintf("playtime open failed: %ld\n", pt_fd); + return; + } + + memset(pt_buf, 0, sizeof(pt_buf)); + + res = IOS_Write(pt_fd, &pt_buf, sizeof(pt_buf)); + if (res != sizeof(pt_buf)) { + IOS_Close(pt_fd); + gprintf("error destroying playtime (%ld)\n", res); + return; + } + + IOS_Close(pt_fd); +} + diff --git a/channel/channelapp/source/playtime.h b/channel/channelapp/source/playtime.h new file mode 100644 index 0000000..e7f681a --- /dev/null +++ b/channel/channelapp/source/playtime.h @@ -0,0 +1,25 @@ +#ifndef _PLAYTIME_H_ +#define _PLAYTIME_H_ + +#include + +typedef union { + struct { + u32 checksum; + u16 name[0x28]; + u32 _pad1; + u64 ticks_boot; + u64 ticks_last; + u32 title_id; + u32 _pad2[5]; + }; + struct { + u32 _checksum; + u32 data[0x1f]; + }; +} __attribute__((packed)) playtime_t; + +void playtime_destroy(void); + +#endif + diff --git a/channel/channelapp/source/sha1.c b/channel/channelapp/source/sha1.c new file mode 100644 index 0000000..614c91b --- /dev/null +++ b/channel/channelapp/source/sha1.c @@ -0,0 +1,172 @@ +/* +SHA-1 in C +By Steve Reid +100% Public Domain + +Test Vectors (from FIPS PUB 180-1) +"abc" + A9993E36 4706816A BA3E2571 7850C26C 9CD0D89D +"abcdbcdecdefdefgefghfghighijhijkijkljklmklmnlmnomnopnopq" + 84983E44 1C3BD26E BAAE4AA1 F95129E5 E54670F1 +A million repetitions of "a" + 34AA973C D4C4DAA4 F61EEB2B DBAD2731 6534016F +*/ + +/* #define LITTLE_ENDIAN * This should be #define'd if true. */ +#define SHA1HANDSOFF + +#include +#include +#include "sha1.h" + +#define rol(value, bits) (((value) << (bits)) | ((value) >> (32 - (bits)))) + +/* blk0() and blk() perform the initial expand. */ +/* I got the idea of expanding during the round function from SSLeay */ +#ifdef LITTLE_ENDIAN +#define blk0(i) (block->l[i] = (rol(block->l[i],24)&0xFF00FF00) \ + |(rol(block->l[i],8)&0x00FF00FF)) +#else +#define blk0(i) block->l[i] +#endif +#define blk(i) (block->l[i&15] = rol(block->l[(i+13)&15]^block->l[(i+8)&15] \ + ^block->l[(i+2)&15]^block->l[i&15],1)) + +/* (R0+R1), R2, R3, R4 are the different operations used in SHA1 */ +#define R0(v,w,x,y,z,i) z+=((w&(x^y))^y)+blk0(i)+0x5A827999+rol(v,5);w=rol(w,30); +#define R1(v,w,x,y,z,i) z+=((w&(x^y))^y)+blk(i)+0x5A827999+rol(v,5);w=rol(w,30); +#define R2(v,w,x,y,z,i) z+=(w^x^y)+blk(i)+0x6ED9EBA1+rol(v,5);w=rol(w,30); +#define R3(v,w,x,y,z,i) z+=(((w|x)&y)|(w&x))+blk(i)+0x8F1BBCDC+rol(v,5);w=rol(w,30); +#define R4(v,w,x,y,z,i) z+=(w^x^y)+blk(i)+0xCA62C1D6+rol(v,5);w=rol(w,30); + + +/* Hash a single 512-bit block. This is the core of the algorithm. */ + +void SHA1Transform(unsigned long state[5], const unsigned char buffer[64]) +{ +unsigned long a, b, c, d, e; +typedef union { + unsigned char c[64]; + unsigned long l[16]; +} CHAR64LONG16; +CHAR64LONG16* block; +#ifdef SHA1HANDSOFF +static unsigned char workspace[64]; + block = (CHAR64LONG16*)workspace; + memcpy(block, buffer, 64); +#else + block = (CHAR64LONG16*)buffer; +#endif + /* Copy context->state[] to working vars */ + a = state[0]; + b = state[1]; + c = state[2]; + d = state[3]; + e = state[4]; + /* 4 rounds of 20 operations each. Loop unrolled. */ + R0(a,b,c,d,e, 0); R0(e,a,b,c,d, 1); R0(d,e,a,b,c, 2); R0(c,d,e,a,b, 3); + R0(b,c,d,e,a, 4); R0(a,b,c,d,e, 5); R0(e,a,b,c,d, 6); R0(d,e,a,b,c, 7); + R0(c,d,e,a,b, 8); R0(b,c,d,e,a, 9); R0(a,b,c,d,e,10); R0(e,a,b,c,d,11); + R0(d,e,a,b,c,12); R0(c,d,e,a,b,13); R0(b,c,d,e,a,14); R0(a,b,c,d,e,15); + R1(e,a,b,c,d,16); R1(d,e,a,b,c,17); R1(c,d,e,a,b,18); R1(b,c,d,e,a,19); + R2(a,b,c,d,e,20); R2(e,a,b,c,d,21); R2(d,e,a,b,c,22); R2(c,d,e,a,b,23); + R2(b,c,d,e,a,24); R2(a,b,c,d,e,25); R2(e,a,b,c,d,26); R2(d,e,a,b,c,27); + R2(c,d,e,a,b,28); R2(b,c,d,e,a,29); R2(a,b,c,d,e,30); R2(e,a,b,c,d,31); + R2(d,e,a,b,c,32); R2(c,d,e,a,b,33); R2(b,c,d,e,a,34); R2(a,b,c,d,e,35); + R2(e,a,b,c,d,36); R2(d,e,a,b,c,37); R2(c,d,e,a,b,38); R2(b,c,d,e,a,39); + R3(a,b,c,d,e,40); R3(e,a,b,c,d,41); R3(d,e,a,b,c,42); R3(c,d,e,a,b,43); + R3(b,c,d,e,a,44); R3(a,b,c,d,e,45); R3(e,a,b,c,d,46); R3(d,e,a,b,c,47); + R3(c,d,e,a,b,48); R3(b,c,d,e,a,49); R3(a,b,c,d,e,50); R3(e,a,b,c,d,51); + R3(d,e,a,b,c,52); R3(c,d,e,a,b,53); R3(b,c,d,e,a,54); R3(a,b,c,d,e,55); + R3(e,a,b,c,d,56); R3(d,e,a,b,c,57); R3(c,d,e,a,b,58); R3(b,c,d,e,a,59); + R4(a,b,c,d,e,60); R4(e,a,b,c,d,61); R4(d,e,a,b,c,62); R4(c,d,e,a,b,63); + R4(b,c,d,e,a,64); R4(a,b,c,d,e,65); R4(e,a,b,c,d,66); R4(d,e,a,b,c,67); + R4(c,d,e,a,b,68); R4(b,c,d,e,a,69); R4(a,b,c,d,e,70); R4(e,a,b,c,d,71); + R4(d,e,a,b,c,72); R4(c,d,e,a,b,73); R4(b,c,d,e,a,74); R4(a,b,c,d,e,75); + R4(e,a,b,c,d,76); R4(d,e,a,b,c,77); R4(c,d,e,a,b,78); R4(b,c,d,e,a,79); + /* Add the working vars back into context.state[] */ + state[0] += a; + state[1] += b; + state[2] += c; + state[3] += d; + state[4] += e; + /* Wipe variables */ + a = b = c = d = e = 0; +} + + +/* SHA1Init - Initialize new context */ + +void SHA1Init(SHA1_CTX* context) +{ + /* SHA1 initialization constants */ + context->state[0] = 0x67452301; + context->state[1] = 0xEFCDAB89; + context->state[2] = 0x98BADCFE; + context->state[3] = 0x10325476; + context->state[4] = 0xC3D2E1F0; + context->count[0] = context->count[1] = 0; +} + + +/* Run your data through this. */ + +void SHA1Update(SHA1_CTX* context, const unsigned char* data, unsigned int len) +{ +unsigned int i, j; + + j = (context->count[0] >> 3) & 63; + if ((context->count[0] += len << 3) < (len << 3)) context->count[1]++; + context->count[1] += (len >> 29); + if ((j + len) > 63) { + memcpy(&context->buffer[j], data, (i = 64-j)); + SHA1Transform(context->state, context->buffer); + for ( ; i + 63 < len; i += 64) { + SHA1Transform(context->state, &data[i]); + } + j = 0; + } + else i = 0; + memcpy(&context->buffer[j], &data[i], len - i); +} + + +/* Add padding and return the message digest. */ + +void SHA1Final(unsigned char digest[20], SHA1_CTX* context) +{ +unsigned long i, j; +unsigned char finalcount[8]; + + for (i = 0; i < 8; i++) { + finalcount[i] = (unsigned char)((context->count[(i >= 4 ? 0 : 1)] + >> ((3-(i & 3)) * 8) ) & 255); /* Endian independent */ + } + SHA1Update(context, (unsigned char *)"\200", 1); + while ((context->count[0] & 504) != 448) { + SHA1Update(context, (unsigned char *)"\0", 1); + } + SHA1Update(context, finalcount, 8); /* Should cause a SHA1Transform() */ + for (i = 0; i < 20; i++) { + digest[i] = (unsigned char) + ((context->state[i>>2] >> ((3-(i & 3)) * 8) ) & 255); + } + /* Wipe variables */ + i = j = 0; + memset(context->buffer, 0, 64); + memset(context->state, 0, 20); + memset(context->count, 0, 8); + memset(&finalcount, 0, 8); +#ifdef SHA1HANDSOFF /* make SHA1Transform overwrite it's own static vars */ + SHA1Transform(context->state, context->buffer); +#endif +} + +void SHA1(unsigned char *ptr, unsigned int size, unsigned char *outbuf) { + SHA1_CTX ctx; + + SHA1Init(&ctx); + SHA1Update(&ctx, ptr, size); + SHA1Final(outbuf, &ctx); +} + diff --git a/channel/channelapp/source/sha1.h b/channel/channelapp/source/sha1.h new file mode 100644 index 0000000..aae96b8 --- /dev/null +++ b/channel/channelapp/source/sha1.h @@ -0,0 +1,18 @@ +#ifndef __SHA1_H__ +#define __SHA1_H__ + +typedef struct { + unsigned long state[5]; + unsigned long count[2]; + unsigned char buffer[64]; +} SHA1_CTX; + +void SHA1Transform(unsigned long state[5], const unsigned char buffer[64]); +void SHA1Init(SHA1_CTX* context); +void SHA1Update(SHA1_CTX* context, const unsigned char* data, unsigned int len); +void SHA1Final(unsigned char digest[20], SHA1_CTX* context); + +void SHA1(unsigned char *ptr, unsigned int size, unsigned char *outbuf); + +#endif + diff --git a/channel/channelapp/source/tcp.c b/channel/channelapp/source/tcp.c new file mode 100644 index 0000000..a9eae7d --- /dev/null +++ b/channel/channelapp/source/tcp.c @@ -0,0 +1,297 @@ +#include +#include +#include +#include +#include +#include +#include + +#include +#include +#include +#include + +#include "../config.h" +#include "panic.h" + +#include "tcp.h" + +s32 tcp_socket (void) { + s32 s, res; + u32 val; + + s = net_socket (PF_INET, SOCK_STREAM, 0); + if (s < 0) { + gprintf ("net_socket failed: %ld\n", s); + return s; + } + + val = 1; + net_setsockopt(s, IPPROTO_TCP, TCP_NODELAY, &val, sizeof(val)); + + res = net_fcntl (s, F_GETFL, 0); + if (res < 0) { + gprintf ("F_GETFL failed: %ld\n", res); + net_close (s); + return res; + } + + res = net_fcntl (s, F_SETFL, res | 4); + if (res < 0) { + gprintf ("F_SETFL failed: %ld\n", res); + net_close (s); + return res; + } + + return s; +} + +s32 tcp_connect (char *host, u16 port) { + struct hostent *hp; + struct sockaddr_in sa; + s32 s, res; + s64 t; + + hp = net_gethostbyname (host); + if (!hp || !(hp->h_addrtype == PF_INET)) { + gprintf ("net_gethostbyname failed: %d\n", errno); + return errno; + } + + s = tcp_socket (); + if (s < 0) + return s; + + memset (&sa, 0, sizeof (struct sockaddr_in)); + sa.sin_family= PF_INET; + sa.sin_len = sizeof (struct sockaddr_in); + sa.sin_port= htons (port); + memcpy ((char *) &sa.sin_addr, hp->h_addr_list[0], hp->h_length); + + t = gettime (); + while (true) { + if (ticks_to_millisecs (diff_ticks (t, gettime ())) > + TCP_CONNECT_TIMEOUT) { + gprintf ("tcp_connect timeout\n"); + net_close (s); + + return -ETIMEDOUT; + } + + res = net_connect (s, (struct sockaddr *) &sa, + sizeof (struct sockaddr_in)); + + if (res < 0) { + if (res == -EISCONN) + break; + + if (res == -EINPROGRESS || res == -EALREADY) { + usleep (20 * 1000); + + continue; + } + + gprintf ("net_connect failed: %ld\n", res); + net_close (s); + + return res; + } + + break; + } + + return s; +} + +s32 tcp_listen (u16 port, s32 backlog) { + s32 s, res; + struct sockaddr_in sa; + + s = tcp_socket (); + if (s < 0) + return s; + + memset(&sa, 0, sizeof (struct sockaddr_in)); + sa.sin_family = PF_INET; + sa.sin_port = htons (port); + sa.sin_addr.s_addr = net_gethostip (); + sa.sin_len = sizeof (struct sockaddr_in); + + res = net_bind (s, (struct sockaddr *) &sa, sizeof (struct sockaddr_in)); + if (res < 0) { + gprintf ("net_bind failed: %ld\n", res); + net_close (s); + return res; + } + + res = net_listen (s, backlog); + if (res < 0) { + gprintf ("net_listen failed: %ld\n", res); + net_close (s); + return res; + } + + return s; +} + +char * tcp_readln (s32 s, u16 max_length, s64 start_time, u16 timeout) { + char *buf; + u16 c; + s32 res; + char *ret; + + buf = (char *) pmalloc (max_length); + + c = 0; + ret = NULL; + while (true) { + if (ticks_to_millisecs (diff_ticks (start_time, gettime ())) > timeout) + break; + + res = net_read (s, &buf[c], 1); + + if ((res == 0) || (res == -EAGAIN)) { + usleep (20 * 1000); + + continue; + } + + if (res < 0) { + gprintf ("tcp_readln failed: %ld\n", res); + + break; + } + + if ((c > 0) && (buf[c - 1] == '\r') && (buf[c] == '\n')) { + if (c == 1) { + ret = pstrdup (""); + + break; + } + + ret = strndup (buf, c - 1); + + break; + } + + c++; + + if (c == max_length) + break; + } + + free (buf); + return ret; +} + +bool tcp_read (s32 s, u8 *buffer, u32 length, const mutex_t *mutex, u32 *progress) { + u32 step, left, block, received; + s64 t; + s32 res; + + step = 0; + left = length; + received = 0; + + t = gettime (); + while (left) { + if (ticks_to_millisecs (diff_ticks (t, gettime ())) > + TCP_BLOCK_RECV_TIMEOUT) { + gprintf ("tcp_read timeout\n"); + + break; + } + + block = left; + if (block > 2048) + block = 2048; + + res = net_read (s, buffer, block); + + if ((res == 0) || (res == -EAGAIN)) { + usleep (20 * 1000); + + continue; + } + + if (res < 0) { + gprintf ("net_read failed: %ld\n", res); + + break; + } + + received += res; + left -= res; + buffer += res; + + if ((received / TCP_BLOCK_SIZE) > step) { + t = gettime (); + step++; + } + + if (mutex && progress) { + LWP_MutexLock (*mutex); + *progress = received; + LWP_MutexUnlock (*mutex); + } + } + + return left == 0; +} + +bool tcp_write (s32 s, const u8 *buffer, u32 length, const mutex_t *mutex, + u32 *progress) { + const u8 *p; + u32 step, left, block, sent; + s64 t; + s32 res; + + step = 0; + p = buffer; + left = length; + sent = 0; + + t = gettime (); + while (left) { + if (ticks_to_millisecs (diff_ticks (t, gettime ())) > + TCP_BLOCK_SEND_TIMEOUT) { + + gprintf ("tcp_write timeout\n"); + break; + } + + block = left; + if (block > 2048) + block = 2048; + + res = net_write (s, p, block); + + if ((res == 0) || (res == -EAGAIN)) { + usleep (20 * 1000); + continue; + } + + if (res < 0) { + gprintf ("net_write failed: %ld\n", res); + break; + } + + sent += res; + left -= res; + p += res; + + if ((sent / TCP_BLOCK_SIZE) > step) { + t = gettime (); + step++; + } + + if (mutex && progress) { + LWP_MutexLock (*mutex); + *progress = sent; + LWP_MutexUnlock (*mutex); + } + } + + return left == 0; +} + diff --git a/channel/channelapp/source/tcp.h b/channel/channelapp/source/tcp.h new file mode 100644 index 0000000..81e8cf0 --- /dev/null +++ b/channel/channelapp/source/tcp.h @@ -0,0 +1,17 @@ +#ifndef _TCP_H_ +#define _TCP_H_ + +#include +#include + +s32 tcp_socket (void); +s32 tcp_connect (char *host, u16 port); +s32 tcp_listen (u16 port, s32 backlog); + +char * tcp_readln (s32 s, u16 max_length, s64 start_time, u16 timeout); +bool tcp_read (s32 s, u8 *buffer, u32 length, const mutex_t *mutex, u32 *progress); +bool tcp_write (s32 s, const u8 *buffer, u32 length, const mutex_t *mutex, + u32 *progress); + +#endif + diff --git a/channel/channelapp/source/tex.c b/channel/channelapp/source/tex.c new file mode 100644 index 0000000..838edbf --- /dev/null +++ b/channel/channelapp/source/tex.c @@ -0,0 +1,262 @@ +#include +#include +#include +#include +#include +#include + +#include +#include + +#include "../config.h" +#include "tex.h" +#include "panic.h" + +#define SCREENSHOT_FILENAME "/hbc-%03lu.png" + +static u32 screenshot_index = 0; + +void tex_free (gfx_entity *entity) { + if (!entity) + return; + + free (entity->texture.pixels); + free (entity); +} + +static void pngcb_error (png_structp png_ptr, png_const_charp error_msg) { + gprintf ("ERROR: Couldn't load PNG image: %s\n", error_msg); +} + +static void pngcb_read (png_structp png_ptr, png_bytep data, + png_size_t length) { + u8 **p = (u8 **) png_get_io_ptr (png_ptr); + memcpy (data, *p, length); + *p += length; +} + +gfx_entity * tex_from_png(const u8 *data, u32 size, u16 width, u16 height) { + int res; + png_structp png_ptr; + png_infop info_ptr; + png_uint_32 w, h; + png_bytepp rows; + + u8 *pixels; + u8 *s, *d; + u32 x, y; + u8 r; + + gfx_entity *entity; + + res = png_sig_cmp ((u8 *) data, 0, 4); + if (res) { + gprintf ("png_sig_cmp failed: %d\n", res); + + return NULL; + } + + png_ptr = png_create_read_struct (PNG_LIBPNG_VER_STRING, NULL, pngcb_error, + NULL); + + if (!png_ptr) { + gprintf ("png_create_read_struct failed\n"); + + return NULL; + } + + info_ptr = png_create_info_struct (png_ptr); + if (!info_ptr) { + gprintf ("png_create_info_struct failed\n"); + png_destroy_read_struct (&png_ptr, (png_infopp)NULL, (png_infopp)NULL); + + return NULL; + } + + if (setjmp (png_jmpbuf (png_ptr))) { + gprintf ("setjmp failed\n"); + png_destroy_read_struct (&png_ptr, &info_ptr, (png_infopp)NULL); + + return NULL; + } + + png_set_read_fn (png_ptr, &data, pngcb_read); + png_set_user_limits (png_ptr, width, height); + png_set_add_alpha (png_ptr, 0xff, PNG_FILLER_BEFORE); + png_read_png (png_ptr, info_ptr, PNG_TRANSFORM_PACKING | + PNG_TRANSFORM_EXPAND | PNG_TRANSFORM_SWAP_ALPHA, + (png_voidp)NULL); + + w = png_get_image_width (png_ptr, info_ptr); + h = png_get_image_height (png_ptr, info_ptr); + + if ((w != width) || (h != height)) { + gprintf ("invalid png dimension!\n"); + png_destroy_read_struct (&png_ptr, &info_ptr, (png_infopp)NULL); + + return NULL; + } + + rows = png_get_rows (png_ptr, info_ptr); + + pixels = (u8 *) pmemalign (32, w * h * 4); + d = pixels; + + for (y = 0; y < h; y += 4) { + for (x = 0; x < w; x += 4) { + for (r = 0; r < 4; ++r) { + s = &rows[y + r][x << 2]; + + *d++ = s[0]; + *d++ = s[1]; + *d++ = s[4]; + *d++ = s[5]; + *d++ = s[8]; + *d++ = s[9]; + *d++ = s[12]; + *d++ = s[13]; + } + + for (r = 0; r < 4; ++r) { + s = &rows[y + r][x << 2]; + + *d++ = s[2]; + *d++ = s[3]; + *d++ = s[6]; + *d++ = s[7]; + *d++ = s[10]; + *d++ = s[11]; + *d++ = s[14]; + *d++ = s[15]; + } + + CHKBUFACC(d - 1, pixels, w * h * 4); + } + } + + png_destroy_read_struct (&png_ptr, &info_ptr, (png_infopp)NULL); + + entity = (gfx_entity *) pmalloc (sizeof (gfx_entity)); + gfx_gen_tex (entity, w, h, pixels, GFXT_RGBA8); + + return entity; +} + +gfx_entity * tex_from_png_file(const char *fn, const struct stat *st, + u16 width, u16 height) { + u8 *buf; + int fd; + gfx_entity *entity; + + fd = open (fn, O_RDONLY); + + if (fd == -1) + return NULL; + + buf = (u8 *) pmalloc (st->st_size); + + if (st->st_size != read (fd, buf, st->st_size)) { + free (buf); + close (fd); + return NULL; + } + + entity = tex_from_png (buf, st->st_size, width, height); + + close (fd); + free (buf); + + return entity; +} + +gfx_entity * tex_from_tex_vsplit(gfx_entity *ge, u16 hstart, u16 hend) +{ + gfx_entity *entity; + u16 h = hend - hstart; + + entity = (gfx_entity *) pmalloc (sizeof (gfx_entity)); + gfx_gen_tex (entity, ge->w, h, ge->texture.pixels + ge->w * hstart * 4, GFXT_RGBA8); + return entity; +} + +void save_rgba_png(u32 *buffer, u16 x, u16 y) { + char fn[16]; + struct stat st; + FILE *fp = NULL; + png_structp png_ptr = NULL; + png_infop info_ptr; + png_bytep *row_pointers; + u16 i; + + while (true) { + if (screenshot_index > 999) + return; + + sprintf(fn, SCREENSHOT_FILENAME, screenshot_index); + if (!stat(fn, &st)) { + screenshot_index++; + continue; + } + + if (errno == ENOENT) + break; + + gprintf("error looking for a screenshot spot %d\n", errno); + return; + } + + row_pointers = (png_bytep *) pmalloc(y * sizeof(png_bytep)); + + fp = fopen(fn, "wb"); + if (!fp) { + gprintf("couldnt open %s for writing\n", fn); + goto exit; + } + + setbuf(fp, NULL); + + png_ptr = png_create_write_struct(PNG_LIBPNG_VER_STRING, 0, 0, 0); + if (!png_ptr) { + gprintf ("png_create_write_struct failed\n"); + goto exit; + } + + info_ptr = png_create_info_struct(png_ptr); + if (!info_ptr) { + gprintf ("png_create_info_struct failed\n"); + goto exit; + } + + if (setjmp(png_jmpbuf(png_ptr))) { + gprintf ("setjmp failed\n"); + goto exit; + } + + png_init_io(png_ptr, fp); + + png_set_compression_level(png_ptr, Z_BEST_COMPRESSION); + png_set_IHDR(png_ptr, info_ptr, x, y, 8, + PNG_COLOR_TYPE_RGB_ALPHA, PNG_INTERLACE_NONE, + PNG_COMPRESSION_TYPE_DEFAULT, PNG_FILTER_TYPE_DEFAULT); + + png_write_info(png_ptr, info_ptr); + + for (i = 0; i < y; ++i) + row_pointers[i] = (png_bytep) (buffer + i * x); + + png_set_swap_alpha(png_ptr); + + png_write_image(png_ptr, row_pointers); + png_write_end(png_ptr, info_ptr); + + gprintf("saved %s\n", fn); + screenshot_index++; + +exit: + if (png_ptr) + png_destroy_write_struct(&png_ptr, (png_infopp)NULL); + + free(row_pointers); + fclose(fp); +} + diff --git a/channel/channelapp/source/tex.h b/channel/channelapp/source/tex.h new file mode 100644 index 0000000..afa5da2 --- /dev/null +++ b/channel/channelapp/source/tex.h @@ -0,0 +1,19 @@ +#ifndef _TEX_H_ +#define _TEX_H_ + +#include +#include + +#include "gfx.h" + +void tex_free (gfx_entity *entity); + +gfx_entity * tex_from_png(const u8 *data, u32 size, u16 width, u16 height); +gfx_entity * tex_from_png_file(const char *fn, const struct stat *st, + u16 width, u16 height); +gfx_entity * tex_from_tex_vsplit (gfx_entity *ge, u16 hstart, u16 hend); + +void save_rgba_png(u32 *buffer, u16 x, u16 y); + +#endif + diff --git a/channel/channelapp/source/theme.c b/channel/channelapp/source/theme.c new file mode 100644 index 0000000..21f5c08 --- /dev/null +++ b/channel/channelapp/source/theme.c @@ -0,0 +1,474 @@ +#include +#include +#include +#include +#include + +#include +#include + +#include "../config.h" +#include "tex.h" +#include "unzip.h" +#include "title.h" +#include "appentry.h" +#include "isfs.h" +#include "blob.h" +#include "panic.h" + +#include "theme.h" + +// view.c +#include "background_png.h" +#include "background_wide_png.h" +#include "logo_png.h" +// bubbles.c +#include "bubble1_png.h" +#include "bubble2_png.h" +#include "bubble3_png.h" +// browser.c +#include "apps_previous_png.h" +#include "apps_previous_hover_png.h" +#include "apps_next_png.h" +#include "apps_next_hover_png.h" +#include "icon_usbgecko_png.h" +#include "icon_usbgecko_active_png.h" +#include "icon_network_png.h" +#include "icon_network_active_png.h" +#include "throbber_png.h" +// dialogs.c +#include "about_png.h" +#include "dialog_background_png.h" +#include "dlg_info_png.h" +#include "dlg_confirm_png.h" +#include "dlg_warning_png.h" +#include "dlg_error_png.h" +// widgets.c +#include "button_png.h" +#include "button_focus_png.h" +#include "button_small_png.h" +#include "button_small_focus_png.h" +#include "button_tiny_png.h" +#include "button_tiny_focus_png.h" +#include "apps_list_png.h" +#include "apps_list_hover_png.h" +#include "apps_grid_png.h" +#include "apps_grid_hover_png.h" +#include "progress_png.h" +#include "content_arrow_up_png.h" +#include "content_arrow_down_png.h" + +#define _ENTRY(n, w, h, fn) \ + { \ + n##_png, &n##_png_size, w, h, \ + NULL, NULL, 0, \ + fn \ + } + +#define _ENTRY_WS(n, w, h, w_ws, fn) \ + { \ + n##_png, &n##_png_size, w, h, \ + n##_wide_png, &n##_wide_png_size, w_ws, \ + fn \ + } + +#define ENTRY(n, w, h) _ENTRY(n, w, h, #n) +#define ENTRY_RO(n, w, h) _ENTRY(n, w, h, NULL) +#define ENTRY_WS(n, w, h, w_ws) _ENTRY_WS(n, w, h, w_ws, #n) +#define ENTRY_WS_RO(n, w, h, w_ws) _ENTRY_WS(n, w, h, w_ws, NULL) + +gfx_entity *theme_gfx[THEME_LAST]; + +theme_font theme_fonts[FONT_MAX]; + +const char *theme_fn_xml = "theme.xml"; + +static const struct { + const u8 *data; + const u32 *size; + const u16 width; + const u16 height; + + const u8 *data_ws; + const u32 *size_ws; + const u16 width_ws; + + const char *filename; +} theme_data[THEME_LAST] = { + ENTRY_WS(background, 640, 480, 852), + ENTRY_RO(logo, 320, 64), + + ENTRY(bubble1, 64, 64), + ENTRY(bubble2, 64, 64), + ENTRY(bubble3, 64, 64), + + ENTRY(apps_previous, 64, 64), + ENTRY(apps_previous_hover, 64, 64), + ENTRY(apps_next, 64, 64), + ENTRY(apps_next_hover, 64, 64), + ENTRY(icon_usbgecko, 32, 32), + ENTRY(icon_usbgecko_active, 32, 32), + ENTRY(icon_network, 32, 32), + ENTRY(icon_network_active, 32, 32), + ENTRY(throbber, 64, 64), + + ENTRY_RO(about, 236, 104), + ENTRY(dialog_background, 520, 360), + ENTRY(dlg_info, 32, 32), + ENTRY(dlg_confirm, 32, 32), + ENTRY(dlg_warning, 32, 32), + ENTRY(dlg_error, 32, 32), + + ENTRY(button, 340, 48), + ENTRY(button_focus, 340, 48), + ENTRY(button_small, 200, 48), + ENTRY(button_small_focus, 200, 48), + ENTRY(button_tiny, 132, 48), + ENTRY(button_tiny_focus, 132, 48), + ENTRY(apps_list, 432, 64), + ENTRY(apps_list_hover, 432, 64), + ENTRY(apps_grid, 144, 64), + ENTRY(apps_grid_hover, 144, 64), + ENTRY(progress, 400, 112), + ENTRY(content_arrow_up, 32, 8), + ENTRY(content_arrow_down, 32, 8) +}; + +static bool theme_get_index(u32 *index, bool *ws, const char *filename) { + u32 i; + char buf[64]; + + for (i = 0; i < THEME_LAST; ++i) { + if (theme_data[i].filename) { + strcpy(buf, theme_data[i].filename); + strcat(buf, ".png"); + + if (!strcasecmp(buf, filename)) { + *index = i; + *ws = false; + return true; + } + + if (theme_data[i].data_ws) { + strcpy(buf, theme_data[i].filename); + strcat(buf, "_wide.png"); + + if (!strcasecmp(buf, filename)) { + *index = i; + *ws = true; + return true; + } + } + } + } + + return false; +} + +static void theme_load_fonts(unzFile uf) { + int i,j,res; + for (i=0; i 0) && + ((!strcasecmp(filename, theme_fn_xml)) || + ((theme_get_index(&index, &ws, filename)) && + (!theme_data[index].data_ws || (widescreen == ws))))) { + res = unzOpenCurrentFile(uf); + if (res != UNZ_OK) { + gprintf("unzOpenCurrentFile failed: %d\n", res); + goto error; + } + + buf = (u8 *) pmalloc(fi.uncompressed_size + 1); + + res = unzReadCurrentFile(uf, buf, fi.uncompressed_size); + if (res < 0) { + gprintf("unzReadCurrentFile failed: %d\n", res); + unzCloseCurrentFile(uf); + free(buf); + goto error; + } + + if (!strcasecmp(filename, theme_fn_xml)) { + gprintf("parsing theme.xml\n"); + buf[fi.uncompressed_size] = 0; + load_theme_xml((char *) buf); + } else { + u16 w, h; + + gprintf("loading from theme: %s (%lu @%lu)\n", filename, + (u32) fi.uncompressed_size, index); + + if (ws) { + w = theme_data[index].width_ws; + h = theme_data[index].height; + } else { + w = theme_data[index].width; + h = theme_data[index].height; + } + + theme_gfx[index] = tex_from_png(buf, fi.uncompressed_size, w, h); + } + + free(buf); + } + + if (i != gi.number_entry - 1) { + res = unzGoToNextFile(uf); + if (res != UNZ_OK) { + gprintf("unzGoToNextFile failed: %d\n", res); + goto error; + } + } + } + + theme_load_fonts(uf); + +error: + res = unzClose(uf); + if (res) + gprintf("unzClose failed: %d\n", res); +} + +void theme_init(u8 *data, u32 data_len) { + s32 res; + u32 i; + const char *titlepath; + STACK_ALIGN(char, fn, ISFS_MAXPATH, 32); + + memset(&theme, 0, sizeof(theme)); + + for (i = 0; i < THEME_LAST; ++i) + theme_gfx[i] = NULL; + + for (i = 0; i < FONT_MAX; ++i) { + theme_fonts[i].file = NULL; + theme_fonts[i].data = NULL; + theme_fonts[i].data_len = 0; + theme_fonts[i].size = 0; + theme_fonts[i].color = 0xffffffff; + } + + theme.progress.ul = 0xc8e1edff; + theme.progress.ur = 0xc8e1edff; + theme.progress.lr = 0x183848ff; + theme.progress.ll = 0x183848ff; + + titlepath = title_get_path(); + if (titlepath[0]) + sprintf(fn, "%s/%s", titlepath, app_fn_theme); + else + fn[0] = 0; + + if (data) { + theme_load(data, data_len); + if (fn[0]) + isfs_put(fn, data, data_len); + } else { + res = -1; + + if (fn[0]) { + res = isfs_get(fn, &data, 0, MAX_THEME_ZIP_SIZE, true); + if (res > 0) { + data_len = res; + gprintf("theme loaded from nand (%lu bytes)\n", data_len); + theme_load(data, data_len); + } + } + + if (res <= 0) { + data = NULL; + data_len = 0; + theme.description = pstrdup("Dark Water by drmr"); + } + } + + gprintf("theme.description=%s\n", theme.description); + gprintf("theme.progress ul=0x%08lx\n", theme.progress.ul); + gprintf("theme.progress ur=0x%08lx\n", theme.progress.ur); + gprintf("theme.progress lr=0x%08lx\n", theme.progress.lr); + gprintf("theme.progress ll=0x%08lx\n", theme.progress.ll); + gprintf("theme.langs=0x%x\n", theme.langs); + + for (i = 0; i < THEME_LAST; ++i) { + if (!theme_gfx[i]) { + if (data && theme_data[i].filename) { + gprintf("%s unavailable, using default\n", + theme_data[i].filename); + } + + if (widescreen && theme_data[i].data_ws) + theme_gfx[i] = tex_from_png(theme_data[i].data_ws, + *(theme_data[i].size_ws), + theme_data[i].width_ws, + theme_data[i].height); + else + theme_gfx[i] = tex_from_png(theme_data[i].data, + *(theme_data[i].size), + theme_data[i].width, + theme_data[i].height); + } + + if (!theme_gfx[i]) + gprintf("WARNING: '%s' unavailable!\n", theme_data[i].filename); + } + + if (data) + blob_free(data); +} + +void theme_deinit(void) { + u32 i,j; + + if (theme.description) { + free(theme.description); + theme.description = NULL; + } + + for (i = 0; i < FONT_MAX; ++i) { + // data can be shared... + if (theme_fonts[i].data) { + for (j = 0; j < FONT_MAX; ++j) { + if (i != j && theme_fonts[i].data == theme_fonts[j].data) + theme_fonts[j].data = NULL; + } + free(theme_fonts[i].data); + theme_fonts[i].data = NULL; + } + // filename is owned by xml.c, no need to free here + memset(&theme_fonts[i], 0, sizeof(theme_fonts[i])); + } + + for (i = 0; i < THEME_LAST; ++i) { + tex_free(theme_gfx[i]); + theme_gfx[i] = NULL; + } +} + +bool theme_is_valid_fn(const char *filename) { + u32 index; + bool ws; + + int l = strlen(filename); + + if (l >= 5 && + tolower((unsigned char)filename[l-1]) == 'f' && + tolower((unsigned char)filename[l-2]) == 't' && + tolower((unsigned char)filename[l-3]) == 't' && + filename[l-4] == '.') + return true; + + // allow random txt files (e.g. README.txt) + if (l >= 5 && + tolower((unsigned char)filename[l-1]) == 't' && + tolower((unsigned char)filename[l-2]) == 'x' && + tolower((unsigned char)filename[l-3]) == 't' && + filename[l-4] == '.') + return true; + + return theme_get_index(&index, &ws, filename); +} + diff --git a/channel/channelapp/source/theme.h b/channel/channelapp/source/theme.h new file mode 100644 index 0000000..a13a772 --- /dev/null +++ b/channel/channelapp/source/theme.h @@ -0,0 +1,76 @@ +#ifndef _THEME_H_ +#define _THEME_H_ + +#include + +#include "gfx.h" +#include "xml.h" + +typedef enum { + // view.c + THEME_BACKGROUND = 0, + THEME_LOGO, + + // bubbles.c + THEME_BUBBLE1, + THEME_BUBBLE2, + THEME_BUBBLE3, + + // browser.c + THEME_ARROW_LEFT, + THEME_ARROW_LEFT_FOCUS, + THEME_ARROW_RIGHT, + THEME_ARROW_RIGHT_FOCUS, + THEME_GECKO, + THEME_GECKO_ACTIVE, + THEME_LAN, + THEME_LAN_ACTIVE, + THEME_THROBBER, + + // dialogc.c + THEME_ABOUT, + THEME_DIALOG, + THEME_DLG_INFO, + THEME_DLG_CONFIRM, + THEME_DLG_WARNING, + THEME_DLG_ERROR, + + // widgets.c + THEME_BUTTON, + THEME_BUTTON_FOCUS, + THEME_BUTTON_SMALL, + THEME_BUTTON_SMALL_FOCUS, + THEME_BUTTON_TINY, + THEME_BUTTON_TINY_FOCUS, + THEME_APP_ENTRY, + THEME_APP_ENTRY_FOCUS, + THEME_GRID_APP_ENTRY, + THEME_GRID_APP_ENTRY_FOCUS, + THEME_PROGRESS, + THEME_CONTENT_ARROW_UP, + THEME_CONTENT_ARROW_DOWN, + + THEME_LAST +} theme_entry; + +typedef struct { + const char *file; + void *data; + u32 data_len; + int size; + u32 color; +} theme_font; + +extern gfx_entity *theme_gfx[THEME_LAST]; + +extern theme_font theme_fonts[FONT_MAX]; + +extern const char *theme_fn_xml; + +void theme_init(u8 *data, u32 data_len); +void theme_deinit(void); + +bool theme_is_valid_fn(const char *filename); + +#endif + diff --git a/channel/channelapp/source/title.c b/channel/channelapp/source/title.c new file mode 100644 index 0000000..e1f4966 --- /dev/null +++ b/channel/channelapp/source/title.c @@ -0,0 +1,95 @@ +#include + +#include +#include +#include +#include +#include "title.h" + +u32 ng_id = 0; +u32 ms_id = 0; + +static u64 title_id = 0; +static char title_path[ISFS_MAXPATH] __attribute__((aligned(0x20))); + +static u8 buf[0x1000] __attribute__((aligned(0x20))); + +static void title_get_ngid(void) { + s32 res; + + res = ES_GetDeviceID(&ng_id); + if (res < 0) { + gprintf("ES_GetDeviceID failed: %ld\n", res); + } +} + +static void title_get_msid(void) { + s32 ret; + + // check for dpki + ret = ES_GetDeviceCert(buf); + if (ret < 0) { + gprintf("ES_GetDeviceCert failed: %ld\n", ret); + return; + } + + ms_id = buf[0x99] - '0'; + + if (ms_id == 3) { + gprintf("We're on dpki\n"); + } else if (ms_id == 2) { + gprintf("We're on retail\n"); + } else { + gprintf("Unknown ms-id %ld?\n", ms_id); + } +} + +static void title_get_title_path(void) { + s32 res; + + res = ES_GetTitleID(&title_id); + if (res < 0) { + gprintf("ES_GetTitleID failed: %ld\n", res); + return; + } + + res = ES_GetDataDir(title_id, title_path); + if (res < 0) { + gprintf("ES_GetDataDir failed: %ld\n", res); + return; + } + + gprintf("data path is '%s'\n", title_path); +} + +const char *title_get_path(void) { + return title_path; +} + +static bool title_is_installed(u64 title_id) { + s32 ret; + u32 x; + + ret = ES_GetTitleContentsCount(title_id, &x); + + if (ret < 0) + return false; // title was never installed + + if (x <= 0) + return false; // title was installed but deleted via Channel Management + + return true; +} + +#define TITLEID_200 0x0000000100000200ll + +bool is_vwii(void) { + return title_is_installed(TITLEID_200); +} + +void title_init(void) { + memset(title_path, 0, sizeof(title_path)); + title_get_msid(); + title_get_ngid(); + title_get_title_path(); +} diff --git a/channel/channelapp/source/title.h b/channel/channelapp/source/title.h new file mode 100644 index 0000000..d0b7c1b --- /dev/null +++ b/channel/channelapp/source/title.h @@ -0,0 +1,12 @@ +#ifndef _TITLE_H_ +#define _TITLE_H_ + +#include +#include "../config.h" + +const char *title_get_path(void); +void title_init(void); +bool is_vwii(void); + +#endif + diff --git a/channel/channelapp/source/unzip.c b/channel/channelapp/source/unzip.c new file mode 100644 index 0000000..b806962 --- /dev/null +++ b/channel/channelapp/source/unzip.c @@ -0,0 +1,1576 @@ +/* unzip.c -- IO for uncompress .zip files using zlib + Version 1.01e, February 12th, 2005 + + Copyright (C) 1998-2005 Gilles Vollant + + Read unzip.h for more info +*/ + +/* Decryption code comes from crypt.c by Info-ZIP but has been greatly reduced in terms of +compatibility with older software. The following is from the original crypt.c. Code +woven in by Terry Thorsen 1/2003. +*/ +/* + Copyright (c) 1990-2000 Info-ZIP. All rights reserved. + + See the accompanying file LICENSE, version 2000-Apr-09 or later + (the contents of which are also included in zip.h) for terms of use. + If, for some reason, all these files are missing, the Info-ZIP license + also may be found at: ftp://ftp.info-zip.org/pub/infozip/license.html +*/ +/* + crypt.c (full version) by Info-ZIP. Last revised: [see crypt.h] + + The encryption/decryption parts of this source code (as opposed to the + non-echoing password parts) were originally written in Europe. The + whole source package can be freely distributed, including from the USA. + (Prior to January 2000, re-export from the US was a violation of US law.) + */ + +/* + This encryption code is a direct transcription of the algorithm from + Roger Schlafly, described by Phil Katz in the file appnote.txt. This + file (appnote.txt) is distributed with the PKZIP program (even in the + version without encryption capabilities). + */ + + +#include +#include +#include +#include + +#include "zlib.h" +#include "unzip.h" +#include "panic.h" + +#define NOUNCRYPT + +#ifndef CASESENSITIVITYDEFAULT_NO +# if !defined(unix) && !defined(CASESENSITIVITYDEFAULT_YES) +# define CASESENSITIVITYDEFAULT_NO +# endif +#endif + + +#ifndef UNZ_BUFSIZE +#define UNZ_BUFSIZE (16384) +#endif + +#ifndef UNZ_MAXFILENAMEINZIP +#define UNZ_MAXFILENAMEINZIP (256) +#endif + +#ifndef ALLOC +# define ALLOC(size) (pmalloc(size)) +#endif +#ifndef TRYFREE +# define TRYFREE(p) {if (p) free(p);} +#endif + +#define SIZECENTRALDIRITEM (0x2e) +#define SIZEZIPLOCALHEADER (0x1e) + +const char unz_copyright[] = + " unzip 1.01 Copyright 1998-2004 Gilles Vollant - http://www.winimage.com/zLibDll"; + +/* unz_file_info_interntal contain internal info about a file in zipfile*/ +typedef struct unz_file_info_internal_s +{ + uLong offset_curfile;/* relative offset of local header 4 bytes */ +} unz_file_info_internal; + + +/* file_in_zip_read_info_s contain internal information about a file in zipfile, + when reading and decompress it */ +typedef struct +{ + char *read_buffer; /* internal buffer for compressed data */ + z_stream stream; /* zLib stream structure for inflate */ + + uLong pos_in_zipfile; /* position in byte on the zipfile, for fseek*/ + uLong stream_initialised; /* flag set if stream structure is initialised*/ + + uLong offset_local_extrafield;/* offset of the local extra field */ + uInt size_local_extrafield;/* size of the local extra field */ + uLong pos_local_extrafield; /* position in the local extra field in read*/ + + uLong crc32; /* crc32 of all data uncompressed */ + uLong crc32_wait; /* crc32 we must obtain after decompress all */ + uLong rest_read_compressed; /* number of byte to be decompressed */ + uLong rest_read_uncompressed;/*number of byte to be obtained after decomp*/ + zlib_filefunc_def z_filefunc; + voidpf filestream; /* io structore of the zipfile */ + uLong compression_method; /* compression method (0==store) */ + uLong byte_before_the_zipfile;/* byte before the zipfile, (>0 for sfx)*/ + int raw; +} file_in_zip_read_info_s; + + +/* unz_s contain internal information about the zipfile +*/ +typedef struct +{ + zlib_filefunc_def z_filefunc; + voidpf filestream; /* io structore of the zipfile */ + unz_global_info gi; /* public global information */ + uLong byte_before_the_zipfile;/* byte before the zipfile, (>0 for sfx)*/ + uLong num_file; /* number of the current file in the zipfile*/ + uLong pos_in_central_dir; /* pos of the current file in the central dir*/ + uLong current_file_ok; /* flag about the usability of the current file*/ + uLong central_pos; /* position of the beginning of the central dir*/ + + uLong size_central_dir; /* size of the central directory */ + uLong offset_central_dir; /* offset of start of central directory with + respect to the starting disk number */ + + unz_file_info cur_file_info; /* public info about the current file in zip*/ + unz_file_info_internal cur_file_info_internal; /* private info about it*/ + file_in_zip_read_info_s* pfile_in_zip_read; /* structure about the current + file if we are decompressing it */ + int encrypted; +# ifndef NOUNCRYPT + unsigned long keys[3]; /* keys defining the pseudo-random sequence */ + const unsigned long* pcrc_32_tab; +# endif +} unz_s; + + +#ifndef NOUNCRYPT +#include "crypt.h" +#endif + +/* =========================================================================== + Read a byte from a gz_stream; update next_in and avail_in. Return EOF + for end of file. + IN assertion: the stream s has been sucessfully opened for reading. +*/ + + +static int unzlocal_getByte OF(( + const zlib_filefunc_def* pzlib_filefunc_def, + voidpf filestream, + int *pi)); + +static int unzlocal_getByte(pzlib_filefunc_def,filestream,pi) + const zlib_filefunc_def* pzlib_filefunc_def; + voidpf filestream; + int *pi; +{ + unsigned char c; + int err = (int)ZREAD(*pzlib_filefunc_def,filestream,&c,1); + if (err==1) + { + *pi = (int)c; + return UNZ_OK; + } + else + { + if (ZERROR(*pzlib_filefunc_def,filestream)) + return UNZ_ERRNO; + else + return UNZ_EOF; + } +} + + +/* =========================================================================== + Reads a long in LSB order from the given gz_stream. Sets +*/ +static int unzlocal_getShort OF(( + const zlib_filefunc_def* pzlib_filefunc_def, + voidpf filestream, + uLong *pX)); + +static int unzlocal_getShort (pzlib_filefunc_def,filestream,pX) + const zlib_filefunc_def* pzlib_filefunc_def; + voidpf filestream; + uLong *pX; +{ + uLong x ; + int i; + int err; + + err = unzlocal_getByte(pzlib_filefunc_def,filestream,&i); + x = (uLong)i; + + if (err==UNZ_OK) + err = unzlocal_getByte(pzlib_filefunc_def,filestream,&i); + x += ((uLong)i)<<8; + + if (err==UNZ_OK) + *pX = x; + else + *pX = 0; + return err; +} + +static int unzlocal_getLong OF(( + const zlib_filefunc_def* pzlib_filefunc_def, + voidpf filestream, + uLong *pX)); + +static int unzlocal_getLong (pzlib_filefunc_def,filestream,pX) + const zlib_filefunc_def* pzlib_filefunc_def; + voidpf filestream; + uLong *pX; +{ + uLong x ; + int i; + int err; + + err = unzlocal_getByte(pzlib_filefunc_def,filestream,&i); + x = (uLong)i; + + if (err==UNZ_OK) + err = unzlocal_getByte(pzlib_filefunc_def,filestream,&i); + x += ((uLong)i)<<8; + + if (err==UNZ_OK) + err = unzlocal_getByte(pzlib_filefunc_def,filestream,&i); + x += ((uLong)i)<<16; + + if (err==UNZ_OK) + err = unzlocal_getByte(pzlib_filefunc_def,filestream,&i); + x += ((uLong)i)<<24; + + if (err==UNZ_OK) + *pX = x; + else + *pX = 0; + return err; +} + + +/* My own strcmpi / strcasecmp */ +static int strcmpcasenosensitive_internal (fileName1,fileName2) + const char* fileName1; + const char* fileName2; +{ + for (;;) + { + char c1=*(fileName1++); + char c2=*(fileName2++); + if ((c1>='a') && (c1<='z')) + c1 -= 0x20; + if ((c2>='a') && (c2<='z')) + c2 -= 0x20; + if (c1=='\0') + return ((c2=='\0') ? 0 : -1); + if (c2=='\0') + return 1; + if (c1c2) + return 1; + } +} + + +#ifdef CASESENSITIVITYDEFAULT_NO +#define CASESENSITIVITYDEFAULTVALUE 2 +#else +#define CASESENSITIVITYDEFAULTVALUE 1 +#endif + +#ifndef STRCMPCASENOSENTIVEFUNCTION +#define STRCMPCASENOSENTIVEFUNCTION strcmpcasenosensitive_internal +#endif + +/* + Compare two filename (fileName1,fileName2). + If iCaseSenisivity = 1, comparision is case sensitivity (like strcmp) + If iCaseSenisivity = 2, comparision is not case sensitivity (like strcmpi + or strcasecmp) + If iCaseSenisivity = 0, case sensitivity is defaut of your operating system + (like 1 on Unix, 2 on Windows) + +*/ +extern int ZEXPORT unzStringFileNameCompare (fileName1,fileName2,iCaseSensitivity) + const char* fileName1; + const char* fileName2; + int iCaseSensitivity; +{ + if (iCaseSensitivity==0) + iCaseSensitivity=CASESENSITIVITYDEFAULTVALUE; + + if (iCaseSensitivity==1) + return strcmp(fileName1,fileName2); + + return STRCMPCASENOSENTIVEFUNCTION(fileName1,fileName2); +} + +#ifndef BUFREADCOMMENT +#define BUFREADCOMMENT (0x400) +#endif + +/* + Locate the Central directory of a zipfile (at the end, just before + the global comment) +*/ +static uLong unzlocal_SearchCentralDir OF(( + const zlib_filefunc_def* pzlib_filefunc_def, + voidpf filestream)); + +static uLong unzlocal_SearchCentralDir(pzlib_filefunc_def,filestream) + const zlib_filefunc_def* pzlib_filefunc_def; + voidpf filestream; +{ + unsigned char* buf; + uLong uSizeFile; + uLong uBackRead; + uLong uMaxBack=0xffff; /* maximum size of global comment */ + uLong uPosFound=0; + + if (ZSEEK(*pzlib_filefunc_def,filestream,0,ZLIB_FILEFUNC_SEEK_END) != 0) + return 0; + + + uSizeFile = ZTELL(*pzlib_filefunc_def,filestream); + + if (uMaxBack>uSizeFile) + uMaxBack = uSizeFile; + + buf = (unsigned char*)ALLOC(BUFREADCOMMENT+4); + if (buf==NULL) + return 0; + + uBackRead = 4; + while (uBackReaduMaxBack) + uBackRead = uMaxBack; + else + uBackRead+=BUFREADCOMMENT; + uReadPos = uSizeFile-uBackRead ; + + uReadSize = ((BUFREADCOMMENT+4) < (uSizeFile-uReadPos)) ? + (BUFREADCOMMENT+4) : (uSizeFile-uReadPos); + if (ZSEEK(*pzlib_filefunc_def,filestream,uReadPos,ZLIB_FILEFUNC_SEEK_SET)!=0) + break; + + if (ZREAD(*pzlib_filefunc_def,filestream,buf,uReadSize)!=uReadSize) + break; + + for (i=(int)uReadSize-3; (i--)>0;) + if (((*(buf+i))==0x50) && ((*(buf+i+1))==0x4b) && + ((*(buf+i+2))==0x05) && ((*(buf+i+3))==0x06)) + { + uPosFound = uReadPos+i; + break; + } + + if (uPosFound!=0) + break; + } + TRYFREE(buf); + return uPosFound; +} + +/* + Open a Zip file. path contain the full pathname (by example, + If the zipfile cannot be opened (file doesn't exist or in not valid), the + return value is NULL. + Else, the return value is a unzFile Handle, usable with other function + of this unzip package. +*/ +extern unzFile ZEXPORT unzOpen (buffer, buf_len) + void* buffer; + size_t buf_len; +{ + unz_s us; + unz_s *s; + uLong central_pos,uL; + + uLong number_disk; /* number of the current dist, used for + spaning ZIP, unsupported, always 0*/ + uLong number_disk_with_CD; /* number the the disk with central dir, used + for spaning ZIP, unsupported, always 0*/ + uLong number_entry_CD; /* total number of entries in + the central dir + (same than number_entry on nospan) */ + + int err=UNZ_OK; + + if (unz_copyright[0]!=' ') + return NULL; + + mem_simple_create_file(&us.z_filefunc); + + us.filestream= (*(us.z_filefunc.zopen_file))(us.z_filefunc.opaque, + buffer, + buf_len, + ZLIB_FILEFUNC_MODE_READ | + ZLIB_FILEFUNC_MODE_EXISTING); + if (us.filestream==NULL) + return NULL; + + central_pos = unzlocal_SearchCentralDir(&us.z_filefunc,us.filestream); + if (central_pos==0) + err=UNZ_ERRNO; + + if (ZSEEK(us.z_filefunc, us.filestream, + central_pos,ZLIB_FILEFUNC_SEEK_SET)!=0) + err=UNZ_ERRNO; + + /* the signature, already checked */ + if (unzlocal_getLong(&us.z_filefunc, us.filestream,&uL)!=UNZ_OK) + err=UNZ_ERRNO; + + /* number of this disk */ + if (unzlocal_getShort(&us.z_filefunc, us.filestream,&number_disk)!=UNZ_OK) + err=UNZ_ERRNO; + + /* number of the disk with the start of the central directory */ + if (unzlocal_getShort(&us.z_filefunc, us.filestream,&number_disk_with_CD)!=UNZ_OK) + err=UNZ_ERRNO; + + /* total number of entries in the central dir on this disk */ + if (unzlocal_getShort(&us.z_filefunc, us.filestream,&us.gi.number_entry)!=UNZ_OK) + err=UNZ_ERRNO; + + /* total number of entries in the central dir */ + if (unzlocal_getShort(&us.z_filefunc, us.filestream,&number_entry_CD)!=UNZ_OK) + err=UNZ_ERRNO; + + if ((number_entry_CD!=us.gi.number_entry) || + (number_disk_with_CD!=0) || + (number_disk!=0)) + err=UNZ_BADZIPFILE; + + /* size of the central directory */ + if (unzlocal_getLong(&us.z_filefunc, us.filestream,&us.size_central_dir)!=UNZ_OK) + err=UNZ_ERRNO; + + /* offset of start of central directory with respect to the + starting disk number */ + if (unzlocal_getLong(&us.z_filefunc, us.filestream,&us.offset_central_dir)!=UNZ_OK) + err=UNZ_ERRNO; + + /* zipfile comment length */ + if (unzlocal_getShort(&us.z_filefunc, us.filestream,&us.gi.size_comment)!=UNZ_OK) + err=UNZ_ERRNO; + + if ((central_pospfile_in_zip_read!=NULL) + unzCloseCurrentFile(file); + + ZCLOSE(s->z_filefunc, s->filestream); + TRYFREE(s); + return UNZ_OK; +} + + +/* + Write info about the ZipFile in the *pglobal_info structure. + No preparation of the structure is needed + return UNZ_OK if there is no problem. */ +extern int ZEXPORT unzGetGlobalInfo (file,pglobal_info) + unzFile file; + unz_global_info *pglobal_info; +{ + unz_s* s; + if (file==NULL) + return UNZ_PARAMERROR; + s=(unz_s*)file; + *pglobal_info=s->gi; + return UNZ_OK; +} + + +/* + Translate date/time from Dos format to tm_unz (readable more easilty) +*/ +static void unzlocal_DosDateToTmuDate (ulDosDate, ptm) + uLong ulDosDate; + tm_unz* ptm; +{ + uLong uDate; + uDate = (uLong)(ulDosDate>>16); + ptm->tm_mday = (uInt)(uDate&0x1f) ; + ptm->tm_mon = (uInt)((((uDate)&0x1E0)/0x20)-1) ; + ptm->tm_year = (uInt)(((uDate&0x0FE00)/0x0200)+1980) ; + + ptm->tm_hour = (uInt) ((ulDosDate &0xF800)/0x800); + ptm->tm_min = (uInt) ((ulDosDate&0x7E0)/0x20) ; + ptm->tm_sec = (uInt) (2*(ulDosDate&0x1f)) ; +} + +/* + Get Info about the current file in the zipfile, with internal only info +*/ +static int unzlocal_GetCurrentFileInfoInternal OF((unzFile file, + unz_file_info *pfile_info, + unz_file_info_internal + *pfile_info_internal, + char *szFileName, + uLong fileNameBufferSize, + void *extraField, + uLong extraFieldBufferSize, + char *szComment, + uLong commentBufferSize)); + +static int unzlocal_GetCurrentFileInfoInternal (file, + pfile_info, + pfile_info_internal, + szFileName, fileNameBufferSize, + extraField, extraFieldBufferSize, + szComment, commentBufferSize) + unzFile file; + unz_file_info *pfile_info; + unz_file_info_internal *pfile_info_internal; + char *szFileName; + uLong fileNameBufferSize; + void *extraField; + uLong extraFieldBufferSize; + char *szComment; + uLong commentBufferSize; +{ + unz_s* s; + unz_file_info file_info; + unz_file_info_internal file_info_internal; + int err=UNZ_OK; + uLong uMagic; + long lSeek=0; + + if (file==NULL) + return UNZ_PARAMERROR; + s=(unz_s*)file; + if (ZSEEK(s->z_filefunc, s->filestream, + s->pos_in_central_dir+s->byte_before_the_zipfile, + ZLIB_FILEFUNC_SEEK_SET)!=0) + err=UNZ_ERRNO; + + + /* we check the magic */ + if (err==UNZ_OK) { + if (unzlocal_getLong(&s->z_filefunc, s->filestream,&uMagic) != UNZ_OK) + err=UNZ_ERRNO; + else if (uMagic!=0x02014b50) + err=UNZ_BADZIPFILE; + } + + if (unzlocal_getShort(&s->z_filefunc, s->filestream,&file_info.version) != UNZ_OK) + err=UNZ_ERRNO; + + if (unzlocal_getShort(&s->z_filefunc, s->filestream,&file_info.version_needed) != UNZ_OK) + err=UNZ_ERRNO; + + if (unzlocal_getShort(&s->z_filefunc, s->filestream,&file_info.flag) != UNZ_OK) + err=UNZ_ERRNO; + + if (unzlocal_getShort(&s->z_filefunc, s->filestream,&file_info.compression_method) != UNZ_OK) + err=UNZ_ERRNO; + + if (unzlocal_getLong(&s->z_filefunc, s->filestream,&file_info.dosDate) != UNZ_OK) + err=UNZ_ERRNO; + + unzlocal_DosDateToTmuDate(file_info.dosDate,&file_info.tmu_date); + + if (unzlocal_getLong(&s->z_filefunc, s->filestream,&file_info.crc) != UNZ_OK) + err=UNZ_ERRNO; + + if (unzlocal_getLong(&s->z_filefunc, s->filestream,&file_info.compressed_size) != UNZ_OK) + err=UNZ_ERRNO; + + if (unzlocal_getLong(&s->z_filefunc, s->filestream,&file_info.uncompressed_size) != UNZ_OK) + err=UNZ_ERRNO; + + if (unzlocal_getShort(&s->z_filefunc, s->filestream,&file_info.size_filename) != UNZ_OK) + err=UNZ_ERRNO; + + if (unzlocal_getShort(&s->z_filefunc, s->filestream,&file_info.size_file_extra) != UNZ_OK) + err=UNZ_ERRNO; + + if (unzlocal_getShort(&s->z_filefunc, s->filestream,&file_info.size_file_comment) != UNZ_OK) + err=UNZ_ERRNO; + + if (unzlocal_getShort(&s->z_filefunc, s->filestream,&file_info.disk_num_start) != UNZ_OK) + err=UNZ_ERRNO; + + if (unzlocal_getShort(&s->z_filefunc, s->filestream,&file_info.internal_fa) != UNZ_OK) + err=UNZ_ERRNO; + + if (unzlocal_getLong(&s->z_filefunc, s->filestream,&file_info.external_fa) != UNZ_OK) + err=UNZ_ERRNO; + + if (unzlocal_getLong(&s->z_filefunc, s->filestream,&file_info_internal.offset_curfile) != UNZ_OK) + err=UNZ_ERRNO; + + lSeek+=file_info.size_filename; + if ((err==UNZ_OK) && (szFileName!=NULL)) + { + uLong uSizeRead ; + if (file_info.size_filename0) && (fileNameBufferSize>0)) + if (ZREAD(s->z_filefunc, s->filestream,szFileName,uSizeRead)!=uSizeRead) + err=UNZ_ERRNO; + lSeek -= uSizeRead; + } + + + if ((err==UNZ_OK) && (extraField!=NULL)) + { + uLong uSizeRead ; + if (file_info.size_file_extraz_filefunc, s->filestream,lSeek,ZLIB_FILEFUNC_SEEK_CUR)==0) + lSeek=0; + else + err=UNZ_ERRNO; + } + if ((file_info.size_file_extra>0) && (extraFieldBufferSize>0)) + if (ZREAD(s->z_filefunc, s->filestream,extraField,uSizeRead)!=uSizeRead) + err=UNZ_ERRNO; + lSeek += file_info.size_file_extra - uSizeRead; + } + else + lSeek+=file_info.size_file_extra; + + + if ((err==UNZ_OK) && (szComment!=NULL)) + { + uLong uSizeRead ; + if (file_info.size_file_commentz_filefunc, s->filestream,lSeek,ZLIB_FILEFUNC_SEEK_CUR)==0) + lSeek=0; + else + err=UNZ_ERRNO; + } + if ((file_info.size_file_comment>0) && (commentBufferSize>0)) + if (ZREAD(s->z_filefunc, s->filestream,szComment,uSizeRead)!=uSizeRead) + err=UNZ_ERRNO; + lSeek+=file_info.size_file_comment - uSizeRead; + } + else + lSeek+=file_info.size_file_comment; + + if ((err==UNZ_OK) && (pfile_info!=NULL)) + *pfile_info=file_info; + + if ((err==UNZ_OK) && (pfile_info_internal!=NULL)) + *pfile_info_internal=file_info_internal; + + return err; +} + + + +/* + Write info about the ZipFile in the *pglobal_info structure. + No preparation of the structure is needed + return UNZ_OK if there is no problem. +*/ +extern int ZEXPORT unzGetCurrentFileInfo (file, + pfile_info, + szFileName, fileNameBufferSize, + extraField, extraFieldBufferSize, + szComment, commentBufferSize) + unzFile file; + unz_file_info *pfile_info; + char *szFileName; + uLong fileNameBufferSize; + void *extraField; + uLong extraFieldBufferSize; + char *szComment; + uLong commentBufferSize; +{ + return unzlocal_GetCurrentFileInfoInternal(file,pfile_info,NULL, + szFileName,fileNameBufferSize, + extraField,extraFieldBufferSize, + szComment,commentBufferSize); +} + +/* + Set the current file of the zipfile to the first file. + return UNZ_OK if there is no problem +*/ +extern int ZEXPORT unzGoToFirstFile (file) + unzFile file; +{ + int err=UNZ_OK; + unz_s* s; + if (file==NULL) + return UNZ_PARAMERROR; + s=(unz_s*)file; + s->pos_in_central_dir=s->offset_central_dir; + s->num_file=0; + err=unzlocal_GetCurrentFileInfoInternal(file,&s->cur_file_info, + &s->cur_file_info_internal, + NULL,0,NULL,0,NULL,0); + s->current_file_ok = (err == UNZ_OK); + return err; +} + +/* + Set the current file of the zipfile to the next file. + return UNZ_OK if there is no problem + return UNZ_END_OF_LIST_OF_FILE if the actual file was the latest. +*/ +extern int ZEXPORT unzGoToNextFile (file) + unzFile file; +{ + unz_s* s; + int err; + + if (file==NULL) + return UNZ_PARAMERROR; + s=(unz_s*)file; + if (!s->current_file_ok) + return UNZ_END_OF_LIST_OF_FILE; + if (s->gi.number_entry != 0xffff) /* 2^16 files overflow hack */ + if (s->num_file+1==s->gi.number_entry) + return UNZ_END_OF_LIST_OF_FILE; + + s->pos_in_central_dir += SIZECENTRALDIRITEM + s->cur_file_info.size_filename + + s->cur_file_info.size_file_extra + s->cur_file_info.size_file_comment ; + s->num_file++; + err = unzlocal_GetCurrentFileInfoInternal(file,&s->cur_file_info, + &s->cur_file_info_internal, + NULL,0,NULL,0,NULL,0); + s->current_file_ok = (err == UNZ_OK); + return err; +} + + +/* + Try locate the file szFileName in the zipfile. + For the iCaseSensitivity signification, see unzipStringFileNameCompare + + return value : + UNZ_OK if the file is found. It becomes the current file. + UNZ_END_OF_LIST_OF_FILE if the file is not found +*/ +extern int ZEXPORT unzLocateFile (file, szFileName, iCaseSensitivity) + unzFile file; + const char *szFileName; + int iCaseSensitivity; +{ + unz_s* s; + int err; + + /* We remember the 'current' position in the file so that we can jump + * back there if we fail. + */ + unz_file_info cur_file_infoSaved; + unz_file_info_internal cur_file_info_internalSaved; + uLong num_fileSaved; + uLong pos_in_central_dirSaved; + + + if (file==NULL) + return UNZ_PARAMERROR; + + if (strlen(szFileName)>=UNZ_MAXFILENAMEINZIP) + return UNZ_PARAMERROR; + + s=(unz_s*)file; + if (!s->current_file_ok) + return UNZ_END_OF_LIST_OF_FILE; + + /* Save the current state */ + num_fileSaved = s->num_file; + pos_in_central_dirSaved = s->pos_in_central_dir; + cur_file_infoSaved = s->cur_file_info; + cur_file_info_internalSaved = s->cur_file_info_internal; + + err = unzGoToFirstFile(file); + + while (err == UNZ_OK) + { + char szCurrentFileName[UNZ_MAXFILENAMEINZIP+1]; + err = unzGetCurrentFileInfo(file,NULL, + szCurrentFileName,sizeof(szCurrentFileName)-1, + NULL,0,NULL,0); + if (err == UNZ_OK) + { + if (unzStringFileNameCompare(szCurrentFileName, + szFileName,iCaseSensitivity)==0) + return UNZ_OK; + err = unzGoToNextFile(file); + } + } + + /* We failed, so restore the state of the 'current file' to where we + * were. + */ + s->num_file = num_fileSaved ; + s->pos_in_central_dir = pos_in_central_dirSaved ; + s->cur_file_info = cur_file_infoSaved; + s->cur_file_info_internal = cur_file_info_internalSaved; + return err; +} + + +/* +/////////////////////////////////////////// +// Contributed by Ryan Haksi (mailto://cryogen@infoserve.net) +// I need random access +// +// Further optimization could be realized by adding an ability +// to cache the directory in memory. The goal being a single +// comprehensive file read to put the file I need in a memory. +*/ + +/* +typedef struct unz_file_pos_s +{ + uLong pos_in_zip_directory; // offset in file + uLong num_of_file; // # of file +} unz_file_pos; +*/ + +extern int ZEXPORT unzGetFilePos(file, file_pos) + unzFile file; + unz_file_pos* file_pos; +{ + unz_s* s; + + if (file==NULL || file_pos==NULL) + return UNZ_PARAMERROR; + s=(unz_s*)file; + if (!s->current_file_ok) + return UNZ_END_OF_LIST_OF_FILE; + + file_pos->pos_in_zip_directory = s->pos_in_central_dir; + file_pos->num_of_file = s->num_file; + + return UNZ_OK; +} + +extern int ZEXPORT unzGoToFilePos(file, file_pos) + unzFile file; + unz_file_pos* file_pos; +{ + unz_s* s; + int err; + + if (file==NULL || file_pos==NULL) + return UNZ_PARAMERROR; + s=(unz_s*)file; + + /* jump to the right spot */ + s->pos_in_central_dir = file_pos->pos_in_zip_directory; + s->num_file = file_pos->num_of_file; + + /* set the current file */ + err = unzlocal_GetCurrentFileInfoInternal(file,&s->cur_file_info, + &s->cur_file_info_internal, + NULL,0,NULL,0,NULL,0); + /* return results */ + s->current_file_ok = (err == UNZ_OK); + return err; +} + +/* +// Unzip Helper Functions - should be here? +/////////////////////////////////////////// +*/ + +/* + Read the local header of the current zipfile + Check the coherency of the local header and info in the end of central + directory about this file + store in *piSizeVar the size of extra info in local header + (filename and size of extra field data) +*/ +static int unzlocal_CheckCurrentFileCoherencyHeader (s,piSizeVar, + poffset_local_extrafield, + psize_local_extrafield) + unz_s* s; + uInt* piSizeVar; + uLong *poffset_local_extrafield; + uInt *psize_local_extrafield; +{ + uLong uMagic,uData,uFlags; + uLong size_filename; + uLong size_extra_field; + int err=UNZ_OK; + + *piSizeVar = 0; + *poffset_local_extrafield = 0; + *psize_local_extrafield = 0; + + if (ZSEEK(s->z_filefunc, s->filestream,s->cur_file_info_internal.offset_curfile + + s->byte_before_the_zipfile,ZLIB_FILEFUNC_SEEK_SET)!=0) + return UNZ_ERRNO; + + + if (err==UNZ_OK) { + if (unzlocal_getLong(&s->z_filefunc, s->filestream,&uMagic) != UNZ_OK) + err=UNZ_ERRNO; + else if (uMagic!=0x04034b50) + err=UNZ_BADZIPFILE; + } + + if (unzlocal_getShort(&s->z_filefunc, s->filestream,&uData) != UNZ_OK) + err=UNZ_ERRNO; +/* + else if ((err==UNZ_OK) && (uData!=s->cur_file_info.wVersion)) + err=UNZ_BADZIPFILE; +*/ + if (unzlocal_getShort(&s->z_filefunc, s->filestream,&uFlags) != UNZ_OK) + err=UNZ_ERRNO; + + if (unzlocal_getShort(&s->z_filefunc, s->filestream,&uData) != UNZ_OK) + err=UNZ_ERRNO; + else if ((err==UNZ_OK) && (uData!=s->cur_file_info.compression_method)) + err=UNZ_BADZIPFILE; + + if ((err==UNZ_OK) && (s->cur_file_info.compression_method!=0) && + (s->cur_file_info.compression_method!=Z_DEFLATED)) + err=UNZ_BADZIPFILE; + + if (unzlocal_getLong(&s->z_filefunc, s->filestream,&uData) != UNZ_OK) /* date/time */ + err=UNZ_ERRNO; + + if (unzlocal_getLong(&s->z_filefunc, s->filestream,&uData) != UNZ_OK) /* crc */ + err=UNZ_ERRNO; + else if ((err==UNZ_OK) && (uData!=s->cur_file_info.crc) && + ((uFlags & 8)==0)) + err=UNZ_BADZIPFILE; + + if (unzlocal_getLong(&s->z_filefunc, s->filestream,&uData) != UNZ_OK) /* size compr */ + err=UNZ_ERRNO; + else if ((err==UNZ_OK) && (uData!=s->cur_file_info.compressed_size) && + ((uFlags & 8)==0)) + err=UNZ_BADZIPFILE; + + if (unzlocal_getLong(&s->z_filefunc, s->filestream,&uData) != UNZ_OK) /* size uncompr */ + err=UNZ_ERRNO; + else if ((err==UNZ_OK) && (uData!=s->cur_file_info.uncompressed_size) && + ((uFlags & 8)==0)) + err=UNZ_BADZIPFILE; + + + if (unzlocal_getShort(&s->z_filefunc, s->filestream,&size_filename) != UNZ_OK) + err=UNZ_ERRNO; + else if ((err==UNZ_OK) && (size_filename!=s->cur_file_info.size_filename)) + err=UNZ_BADZIPFILE; + + *piSizeVar += (uInt)size_filename; + + if (unzlocal_getShort(&s->z_filefunc, s->filestream,&size_extra_field) != UNZ_OK) + err=UNZ_ERRNO; + *poffset_local_extrafield= s->cur_file_info_internal.offset_curfile + + SIZEZIPLOCALHEADER + size_filename; + *psize_local_extrafield = (uInt)size_extra_field; + + *piSizeVar += (uInt)size_extra_field; + + return err; +} + +/* + Open for reading data the current file in the zipfile. + If there is no error and the file is opened, the return value is UNZ_OK. +*/ +extern int ZEXPORT unzOpenCurrentFile3 (file, method, level, raw, password) + unzFile file; + int* method; + int* level; + int raw; + const char* password; +{ + int err=UNZ_OK; + uInt iSizeVar; + unz_s* s; + file_in_zip_read_info_s* pfile_in_zip_read_info; + uLong offset_local_extrafield; /* offset of the local extra field */ + uInt size_local_extrafield; /* size of the local extra field */ +# ifndef NOUNCRYPT + char source[12]; +# else + if (password != NULL) + return UNZ_PARAMERROR; +# endif + + if (file==NULL) + return UNZ_PARAMERROR; + s=(unz_s*)file; + if (!s->current_file_ok) + return UNZ_PARAMERROR; + + if (s->pfile_in_zip_read != NULL) + unzCloseCurrentFile(file); + + if (unzlocal_CheckCurrentFileCoherencyHeader(s,&iSizeVar, + &offset_local_extrafield,&size_local_extrafield)!=UNZ_OK) + return UNZ_BADZIPFILE; + + pfile_in_zip_read_info = (file_in_zip_read_info_s*) + ALLOC(sizeof(file_in_zip_read_info_s)); + if (pfile_in_zip_read_info==NULL) + return UNZ_INTERNALERROR; + + pfile_in_zip_read_info->read_buffer=(char*)ALLOC(UNZ_BUFSIZE); + pfile_in_zip_read_info->offset_local_extrafield = offset_local_extrafield; + pfile_in_zip_read_info->size_local_extrafield = size_local_extrafield; + pfile_in_zip_read_info->pos_local_extrafield=0; + pfile_in_zip_read_info->raw=raw; + + if (pfile_in_zip_read_info->read_buffer==NULL) + { + TRYFREE(pfile_in_zip_read_info); + return UNZ_INTERNALERROR; + } + + pfile_in_zip_read_info->stream_initialised=0; + + if (method!=NULL) + *method = (int)s->cur_file_info.compression_method; + + if (level!=NULL) + { + *level = 6; + switch (s->cur_file_info.flag & 0x06) + { + case 6 : *level = 1; break; + case 4 : *level = 2; break; + case 2 : *level = 9; break; + } + } + + if ((s->cur_file_info.compression_method!=0) && + (s->cur_file_info.compression_method!=Z_DEFLATED)) + err=UNZ_BADZIPFILE; + + pfile_in_zip_read_info->crc32_wait=s->cur_file_info.crc; + pfile_in_zip_read_info->crc32=0; + pfile_in_zip_read_info->compression_method = + s->cur_file_info.compression_method; + pfile_in_zip_read_info->filestream=s->filestream; + pfile_in_zip_read_info->z_filefunc=s->z_filefunc; + pfile_in_zip_read_info->byte_before_the_zipfile=s->byte_before_the_zipfile; + + pfile_in_zip_read_info->stream.total_out = 0; + + if ((s->cur_file_info.compression_method==Z_DEFLATED) && + (!raw)) + { + pfile_in_zip_read_info->stream.zalloc = (alloc_func)0; + pfile_in_zip_read_info->stream.zfree = (free_func)0; + pfile_in_zip_read_info->stream.opaque = (voidpf)0; + pfile_in_zip_read_info->stream.next_in = (voidpf)0; + pfile_in_zip_read_info->stream.avail_in = 0; + + err=inflateInit2(&pfile_in_zip_read_info->stream, -MAX_WBITS); + if (err == Z_OK) + pfile_in_zip_read_info->stream_initialised=1; + else + { + TRYFREE(pfile_in_zip_read_info); + return err; + } + /* windowBits is passed < 0 to tell that there is no zlib header. + * Note that in this case inflate *requires* an extra "dummy" byte + * after the compressed stream in order to complete decompression and + * return Z_STREAM_END. + * In unzip, i don't wait absolutely Z_STREAM_END because I known the + * size of both compressed and uncompressed data + */ + } + pfile_in_zip_read_info->rest_read_compressed = + s->cur_file_info.compressed_size ; + pfile_in_zip_read_info->rest_read_uncompressed = + s->cur_file_info.uncompressed_size ; + + + pfile_in_zip_read_info->pos_in_zipfile = + s->cur_file_info_internal.offset_curfile + SIZEZIPLOCALHEADER + + iSizeVar; + + pfile_in_zip_read_info->stream.avail_in = (uInt)0; + + s->pfile_in_zip_read = pfile_in_zip_read_info; + +# ifndef NOUNCRYPT + if (password != NULL) + { + int i; + s->pcrc_32_tab = get_crc_table(); + init_keys(password,s->keys,s->pcrc_32_tab); + if (ZSEEK(s->z_filefunc, s->filestream, + s->pfile_in_zip_read->pos_in_zipfile + + s->pfile_in_zip_read->byte_before_the_zipfile, + SEEK_SET)!=0) + return UNZ_INTERNALERROR; + if(ZREAD(s->z_filefunc, s->filestream,source, 12)<12) + return UNZ_INTERNALERROR; + + for (i = 0; i<12; i++) + zdecode(s->keys,s->pcrc_32_tab,source[i]); + + s->pfile_in_zip_read->pos_in_zipfile+=12; + s->encrypted=1; + } +# endif + + + return UNZ_OK; +} + +extern int ZEXPORT unzOpenCurrentFile (file) + unzFile file; +{ + return unzOpenCurrentFile3(file, NULL, NULL, 0, NULL); +} + +extern int ZEXPORT unzOpenCurrentFilePassword (file, password) + unzFile file; + const char* password; +{ + return unzOpenCurrentFile3(file, NULL, NULL, 0, password); +} + +extern int ZEXPORT unzOpenCurrentFile2 (file,method,level,raw) + unzFile file; + int* method; + int* level; + int raw; +{ + return unzOpenCurrentFile3(file, method, level, raw, NULL); +} + +/* + Read bytes from the current file. + buf contain buffer where data must be copied + len the size of buf. + + return the number of byte copied if somes bytes are copied + return 0 if the end of file was reached + return <0 with error code if there is an error + (UNZ_ERRNO for IO error, or zLib error for uncompress error) +*/ +extern int ZEXPORT unzReadCurrentFile (file, buf, len) + unzFile file; + voidp buf; + unsigned len; +{ + int err=UNZ_OK; + uInt iRead = 0; + unz_s* s; + file_in_zip_read_info_s* pfile_in_zip_read_info; + if (file==NULL) + return UNZ_PARAMERROR; + s=(unz_s*)file; + pfile_in_zip_read_info=s->pfile_in_zip_read; + + if (pfile_in_zip_read_info==NULL) + return UNZ_PARAMERROR; + + + if ((pfile_in_zip_read_info->read_buffer == NULL)) + return UNZ_END_OF_LIST_OF_FILE; + if (len==0) + return 0; + + pfile_in_zip_read_info->stream.next_out = (Bytef*)buf; + + pfile_in_zip_read_info->stream.avail_out = (uInt)len; + + if ((len>pfile_in_zip_read_info->rest_read_uncompressed) && + (!(pfile_in_zip_read_info->raw))) + pfile_in_zip_read_info->stream.avail_out = + (uInt)pfile_in_zip_read_info->rest_read_uncompressed; + + if ((len>pfile_in_zip_read_info->rest_read_compressed+ + pfile_in_zip_read_info->stream.avail_in) && + (pfile_in_zip_read_info->raw)) + pfile_in_zip_read_info->stream.avail_out = + (uInt)pfile_in_zip_read_info->rest_read_compressed+ + pfile_in_zip_read_info->stream.avail_in; + + while (pfile_in_zip_read_info->stream.avail_out>0) + { + if ((pfile_in_zip_read_info->stream.avail_in==0) && + (pfile_in_zip_read_info->rest_read_compressed>0)) + { + uInt uReadThis = UNZ_BUFSIZE; + if (pfile_in_zip_read_info->rest_read_compressedrest_read_compressed; + if (uReadThis == 0) + return UNZ_EOF; + if (ZSEEK(pfile_in_zip_read_info->z_filefunc, + pfile_in_zip_read_info->filestream, + pfile_in_zip_read_info->pos_in_zipfile + + pfile_in_zip_read_info->byte_before_the_zipfile, + ZLIB_FILEFUNC_SEEK_SET)!=0) + return UNZ_ERRNO; + if (ZREAD(pfile_in_zip_read_info->z_filefunc, + pfile_in_zip_read_info->filestream, + pfile_in_zip_read_info->read_buffer, + uReadThis)!=uReadThis) + return UNZ_ERRNO; + + +# ifndef NOUNCRYPT + if(s->encrypted) + { + uInt i; + for(i=0;iread_buffer[i] = + zdecode(s->keys,s->pcrc_32_tab, + pfile_in_zip_read_info->read_buffer[i]); + } +# endif + + + pfile_in_zip_read_info->pos_in_zipfile += uReadThis; + + pfile_in_zip_read_info->rest_read_compressed-=uReadThis; + + pfile_in_zip_read_info->stream.next_in = + (Bytef*)pfile_in_zip_read_info->read_buffer; + pfile_in_zip_read_info->stream.avail_in = (uInt)uReadThis; + } + + if ((pfile_in_zip_read_info->compression_method==0) || (pfile_in_zip_read_info->raw)) + { + uInt uDoCopy,i ; + + if ((pfile_in_zip_read_info->stream.avail_in == 0) && + (pfile_in_zip_read_info->rest_read_compressed == 0)) + return (iRead==0) ? UNZ_EOF : iRead; + + if (pfile_in_zip_read_info->stream.avail_out < + pfile_in_zip_read_info->stream.avail_in) + uDoCopy = pfile_in_zip_read_info->stream.avail_out ; + else + uDoCopy = pfile_in_zip_read_info->stream.avail_in ; + + for (i=0;istream.next_out+i) = + *(pfile_in_zip_read_info->stream.next_in+i); + + pfile_in_zip_read_info->crc32 = crc32(pfile_in_zip_read_info->crc32, + pfile_in_zip_read_info->stream.next_out, + uDoCopy); + pfile_in_zip_read_info->rest_read_uncompressed-=uDoCopy; + pfile_in_zip_read_info->stream.avail_in -= uDoCopy; + pfile_in_zip_read_info->stream.avail_out -= uDoCopy; + pfile_in_zip_read_info->stream.next_out += uDoCopy; + pfile_in_zip_read_info->stream.next_in += uDoCopy; + pfile_in_zip_read_info->stream.total_out += uDoCopy; + iRead += uDoCopy; + } + else + { + uLong uTotalOutBefore,uTotalOutAfter; + const Bytef *bufBefore; + uLong uOutThis; + int flush=Z_SYNC_FLUSH; + + uTotalOutBefore = pfile_in_zip_read_info->stream.total_out; + bufBefore = pfile_in_zip_read_info->stream.next_out; + + /* + if ((pfile_in_zip_read_info->rest_read_uncompressed == + pfile_in_zip_read_info->stream.avail_out) && + (pfile_in_zip_read_info->rest_read_compressed == 0)) + flush = Z_FINISH; + */ + err=inflate(&pfile_in_zip_read_info->stream,flush); + + if ((err>=0) && (pfile_in_zip_read_info->stream.msg!=NULL)) + err = Z_DATA_ERROR; + + uTotalOutAfter = pfile_in_zip_read_info->stream.total_out; + uOutThis = uTotalOutAfter-uTotalOutBefore; + + pfile_in_zip_read_info->crc32 = + crc32(pfile_in_zip_read_info->crc32,bufBefore, + (uInt)(uOutThis)); + + pfile_in_zip_read_info->rest_read_uncompressed -= + uOutThis; + + iRead += (uInt)(uTotalOutAfter - uTotalOutBefore); + + if (err==Z_STREAM_END) + return (iRead==0) ? UNZ_EOF : iRead; + if (err!=Z_OK) + break; + } + } + + if (err==Z_OK) + return iRead; + return err; +} + + +/* + Give the current position in uncompressed data +*/ +extern z_off_t ZEXPORT unztell (file) + unzFile file; +{ + unz_s* s; + file_in_zip_read_info_s* pfile_in_zip_read_info; + if (file==NULL) + return UNZ_PARAMERROR; + s=(unz_s*)file; + pfile_in_zip_read_info=s->pfile_in_zip_read; + + if (pfile_in_zip_read_info==NULL) + return UNZ_PARAMERROR; + + return (z_off_t)pfile_in_zip_read_info->stream.total_out; +} + + +/* + return 1 if the end of file was reached, 0 elsewhere +*/ +extern int ZEXPORT unzeof (file) + unzFile file; +{ + unz_s* s; + file_in_zip_read_info_s* pfile_in_zip_read_info; + if (file==NULL) + return UNZ_PARAMERROR; + s=(unz_s*)file; + pfile_in_zip_read_info=s->pfile_in_zip_read; + + if (pfile_in_zip_read_info==NULL) + return UNZ_PARAMERROR; + + if (pfile_in_zip_read_info->rest_read_uncompressed == 0) + return 1; + else + return 0; +} + + + +/* + Read extra field from the current file (opened by unzOpenCurrentFile) + This is the local-header version of the extra field (sometimes, there is + more info in the local-header version than in the central-header) + + if buf==NULL, it return the size of the local extra field that can be read + + if buf!=NULL, len is the size of the buffer, the extra header is copied in + buf. + the return value is the number of bytes copied in buf, or (if <0) + the error code +*/ +extern int ZEXPORT unzGetLocalExtrafield (file,buf,len) + unzFile file; + voidp buf; + unsigned len; +{ + unz_s* s; + file_in_zip_read_info_s* pfile_in_zip_read_info; + uInt read_now; + uLong size_to_read; + + if (file==NULL) + return UNZ_PARAMERROR; + s=(unz_s*)file; + pfile_in_zip_read_info=s->pfile_in_zip_read; + + if (pfile_in_zip_read_info==NULL) + return UNZ_PARAMERROR; + + size_to_read = (pfile_in_zip_read_info->size_local_extrafield - + pfile_in_zip_read_info->pos_local_extrafield); + + if (buf==NULL) + return (int)size_to_read; + + if (len>size_to_read) + read_now = (uInt)size_to_read; + else + read_now = (uInt)len ; + + if (read_now==0) + return 0; + + if (ZSEEK(pfile_in_zip_read_info->z_filefunc, + pfile_in_zip_read_info->filestream, + pfile_in_zip_read_info->offset_local_extrafield + + pfile_in_zip_read_info->pos_local_extrafield, + ZLIB_FILEFUNC_SEEK_SET)!=0) + return UNZ_ERRNO; + + if (ZREAD(pfile_in_zip_read_info->z_filefunc, + pfile_in_zip_read_info->filestream, + buf,read_now)!=read_now) + return UNZ_ERRNO; + + return (int)read_now; +} + +/* + Close the file in zip opened with unzipOpenCurrentFile + Return UNZ_CRCERROR if all the file was read but the CRC is not good +*/ +extern int ZEXPORT unzCloseCurrentFile (file) + unzFile file; +{ + int err=UNZ_OK; + + unz_s* s; + file_in_zip_read_info_s* pfile_in_zip_read_info; + if (file==NULL) + return UNZ_PARAMERROR; + s=(unz_s*)file; + pfile_in_zip_read_info=s->pfile_in_zip_read; + + if (pfile_in_zip_read_info==NULL) + return UNZ_PARAMERROR; + + + if ((pfile_in_zip_read_info->rest_read_uncompressed == 0) && + (!pfile_in_zip_read_info->raw)) + { + if (pfile_in_zip_read_info->crc32 != pfile_in_zip_read_info->crc32_wait) + err=UNZ_CRCERROR; + } + + + TRYFREE(pfile_in_zip_read_info->read_buffer); + pfile_in_zip_read_info->read_buffer = NULL; + if (pfile_in_zip_read_info->stream_initialised) + inflateEnd(&pfile_in_zip_read_info->stream); + + pfile_in_zip_read_info->stream_initialised = 0; + TRYFREE(pfile_in_zip_read_info); + + s->pfile_in_zip_read=NULL; + + return err; +} + + +/* + Get the global comment string of the ZipFile, in the szComment buffer. + uSizeBuf is the size of the szComment buffer. + return the number of byte copied or an error code <0 +*/ +extern int ZEXPORT unzGetGlobalComment (file, szComment, uSizeBuf) + unzFile file; + char *szComment; + uLong uSizeBuf; +{ + unz_s* s; + uLong uReadThis ; + if (file==NULL) + return UNZ_PARAMERROR; + s=(unz_s*)file; + + uReadThis = uSizeBuf; + if (uReadThis>s->gi.size_comment) + uReadThis = s->gi.size_comment; + + if (ZSEEK(s->z_filefunc,s->filestream,s->central_pos+22,ZLIB_FILEFUNC_SEEK_SET)!=0) + return UNZ_ERRNO; + + if (uReadThis>0) + { + *szComment='\0'; + if (ZREAD(s->z_filefunc,s->filestream,szComment,uReadThis)!=uReadThis) + return UNZ_ERRNO; + } + + if ((szComment != NULL) && (uSizeBuf > s->gi.size_comment)) + *(szComment+s->gi.size_comment)='\0'; + return (int)uReadThis; +} + +/* Additions by RX '2004 */ +extern uLong ZEXPORT unzGetOffset (file) + unzFile file; +{ + unz_s* s; + + if (file==NULL) + return UNZ_PARAMERROR; + s=(unz_s*)file; + if (!s->current_file_ok) + return 0; + if (s->gi.number_entry != 0 && s->gi.number_entry != 0xffff) + if (s->num_file==s->gi.number_entry) + return 0; + return s->pos_in_central_dir; +} + +extern int ZEXPORT unzSetOffset (file, pos) + unzFile file; + uLong pos; +{ + unz_s* s; + int err; + + if (file==NULL) + return UNZ_PARAMERROR; + s=(unz_s*)file; + + s->pos_in_central_dir = pos; + s->num_file = s->gi.number_entry; /* hack */ + err = unzlocal_GetCurrentFileInfoInternal(file,&s->cur_file_info, + &s->cur_file_info_internal, + NULL,0,NULL,0,NULL,0); + s->current_file_ok = (err == UNZ_OK); + return err; +} + diff --git a/channel/channelapp/source/unzip.h b/channel/channelapp/source/unzip.h new file mode 100644 index 0000000..5909b1d --- /dev/null +++ b/channel/channelapp/source/unzip.h @@ -0,0 +1,331 @@ +/* unzip.h -- IO for uncompress .zip files using zlib + Version 1.01e, February 12th, 2005 + + Copyright (C) 1998-2005 Gilles Vollant + + This unzip package allow extract file from .ZIP file, compatible with PKZip 2.04g + WinZip, InfoZip tools and compatible. + + Multi volume ZipFile (span) are not supported. + Encryption compatible with pkzip 2.04g only supported + Old compressions used by old PKZip 1.x are not supported + + + I WAIT FEEDBACK at mail info@winimage.com + Visit also http://www.winimage.com/zLibDll/unzip.htm for evolution + + Condition of use and distribution are the same than zlib : + + This software is provided 'as-is', without any express or implied + warranty. In no event will the authors be held liable for any damages + arising from the use of this software. + + Permission is granted to anyone to use this software for any purpose, + including commercial applications, and to alter it and redistribute it + freely, subject to the following restrictions: + + 1. The origin of this software must not be misrepresented; you must not + claim that you wrote the original software. If you use this software + in a product, an acknowledgment in the product documentation would be + appreciated but is not required. + 2. Altered source versions must be plainly marked as such, and must not be + misrepresented as being the original software. + 3. This notice may not be removed or altered from any source distribution. + + +*/ + +/* for more info about .ZIP format, see + http://www.info-zip.org/pub/infozip/doc/appnote-981119-iz.zip + http://www.info-zip.org/pub/infozip/doc/ + PkWare has also a specification at : + ftp://ftp.pkware.com/probdesc.zip +*/ + +#ifndef _UNZIP_H_ +#define _UNZIP_H_ + +#include + +#include "unzip_io.h" + +#if defined(STRICTUNZIP) || defined(STRICTZIPUNZIP) +/* like the STRICT of WIN32, we define a pointer that cannot be converted + from (void*) without cast */ +typedef struct TagunzFile__ { int unused; } unzFile__; +typedef unzFile__ *unzFile; +#else +typedef voidp unzFile; +#endif + + +#define UNZ_OK (0) +#define UNZ_END_OF_LIST_OF_FILE (-100) +#define UNZ_ERRNO (Z_ERRNO) +#define UNZ_EOF (0) +#define UNZ_PARAMERROR (-102) +#define UNZ_BADZIPFILE (-103) +#define UNZ_INTERNALERROR (-104) +#define UNZ_CRCERROR (-105) + +/* tm_unz contain date/time info */ +typedef struct tm_unz_s +{ + uInt tm_sec; /* seconds after the minute - [0,59] */ + uInt tm_min; /* minutes after the hour - [0,59] */ + uInt tm_hour; /* hours since midnight - [0,23] */ + uInt tm_mday; /* day of the month - [1,31] */ + uInt tm_mon; /* months since January - [0,11] */ + uInt tm_year; /* years - [1980..2044] */ +} tm_unz; + +/* unz_global_info structure contain global data about the ZIPfile + These data comes from the end of central dir */ +typedef struct unz_global_info_s +{ + uLong number_entry; /* total number of entries in + the central dir on this disk */ + uLong size_comment; /* size of the global comment of the zipfile */ +} unz_global_info; + + +/* unz_file_info contain information about a file in the zipfile */ +typedef struct unz_file_info_s +{ + uLong version; /* version made by 2 bytes */ + uLong version_needed; /* version needed to extract 2 bytes */ + uLong flag; /* general purpose bit flag 2 bytes */ + uLong compression_method; /* compression method 2 bytes */ + uLong dosDate; /* last mod file date in Dos fmt 4 bytes */ + uLong crc; /* crc-32 4 bytes */ + uLong compressed_size; /* compressed size 4 bytes */ + uLong uncompressed_size; /* uncompressed size 4 bytes */ + uLong size_filename; /* filename length 2 bytes */ + uLong size_file_extra; /* extra field length 2 bytes */ + uLong size_file_comment; /* file comment length 2 bytes */ + + uLong disk_num_start; /* disk number start 2 bytes */ + uLong internal_fa; /* internal file attributes 2 bytes */ + uLong external_fa; /* external file attributes 4 bytes */ + + tm_unz tmu_date; +} unz_file_info; + +extern int ZEXPORT unzStringFileNameCompare OF ((const char* fileName1, + const char* fileName2, + int iCaseSensitivity)); +/* + Compare two filename (fileName1,fileName2). + If iCaseSenisivity = 1, comparision is case sensitivity (like strcmp) + If iCaseSenisivity = 2, comparision is not case sensitivity (like strcmpi + or strcasecmp) + If iCaseSenisivity = 0, case sensitivity is defaut of your operating system + (like 1 on Unix, 2 on Windows) +*/ + +extern unzFile ZEXPORT unzOpen OF((void *buffer, size_t buf_len)); +/* + Open a Zip file. path contain the full pathname (by example, + If the zipfile cannot be opened (file doesn't exist or in not valid), the + return value is NULL. + Else, the return value is a unzFile Handle, usable with other function + of this unzip package. +*/ + +extern int ZEXPORT unzClose OF((unzFile file)); +/* + Close a ZipFile opened with unzipOpen. + If there is files inside the .Zip opened with unzOpenCurrentFile (see later), + these files MUST be closed with unzipCloseCurrentFile before call unzipClose. + return UNZ_OK if there is no problem. */ + +extern int ZEXPORT unzGetGlobalInfo OF((unzFile file, + unz_global_info *pglobal_info)); +/* + Write info about the ZipFile in the *pglobal_info structure. + No preparation of the structure is needed + return UNZ_OK if there is no problem. */ + + +extern int ZEXPORT unzGetGlobalComment OF((unzFile file, + char *szComment, + uLong uSizeBuf)); +/* + Get the global comment string of the ZipFile, in the szComment buffer. + uSizeBuf is the size of the szComment buffer. + return the number of byte copied or an error code <0 +*/ + + +/***************************************************************************/ +/* Unzip package allow you browse the directory of the zipfile */ + +extern int ZEXPORT unzGoToFirstFile OF((unzFile file)); +/* + Set the current file of the zipfile to the first file. + return UNZ_OK if there is no problem +*/ + +extern int ZEXPORT unzGoToNextFile OF((unzFile file)); +/* + Set the current file of the zipfile to the next file. + return UNZ_OK if there is no problem + return UNZ_END_OF_LIST_OF_FILE if the actual file was the latest. +*/ + +extern int ZEXPORT unzLocateFile OF((unzFile file, + const char *szFileName, + int iCaseSensitivity)); +/* + Try locate the file szFileName in the zipfile. + For the iCaseSensitivity signification, see unzStringFileNameCompare + + return value : + UNZ_OK if the file is found. It becomes the current file. + UNZ_END_OF_LIST_OF_FILE if the file is not found +*/ + + +/* ****************************************** */ +/* Ryan supplied functions */ +/* unz_file_info contain information about a file in the zipfile */ +typedef struct unz_file_pos_s +{ + uLong pos_in_zip_directory; /* offset in zip file directory */ + uLong num_of_file; /* # of file */ +} unz_file_pos; + +extern int ZEXPORT unzGetFilePos( + unzFile file, + unz_file_pos* file_pos); + +extern int ZEXPORT unzGoToFilePos( + unzFile file, + unz_file_pos* file_pos); + +/* ****************************************** */ + +extern int ZEXPORT unzGetCurrentFileInfo OF((unzFile file, + unz_file_info *pfile_info, + char *szFileName, + uLong fileNameBufferSize, + void *extraField, + uLong extraFieldBufferSize, + char *szComment, + uLong commentBufferSize)); +/* + Get Info about the current file + if pfile_info!=NULL, the *pfile_info structure will contain somes info about + the current file + if szFileName!=NULL, the filemane string will be copied in szFileName + (fileNameBufferSize is the size of the buffer) + if extraField!=NULL, the extra field information will be copied in extraField + (extraFieldBufferSize is the size of the buffer). + This is the Central-header version of the extra field + if szComment!=NULL, the comment string of the file will be copied in szComment + (commentBufferSize is the size of the buffer) +*/ + +/***************************************************************************/ +/* for reading the content of the current zipfile, you can open it, read data + from it, and close it (you can close it before reading all the file) + */ + +extern int ZEXPORT unzOpenCurrentFile OF((unzFile file)); +/* + Open for reading data the current file in the zipfile. + If there is no error, the return value is UNZ_OK. +*/ + +extern int ZEXPORT unzOpenCurrentFilePassword OF((unzFile file, + const char* password)); +/* + Open for reading data the current file in the zipfile. + password is a crypting password + If there is no error, the return value is UNZ_OK. +*/ + +extern int ZEXPORT unzOpenCurrentFile2 OF((unzFile file, + int* method, + int* level, + int raw)); +/* + Same than unzOpenCurrentFile, but open for read raw the file (not uncompress) + if raw==1 + *method will receive method of compression, *level will receive level of + compression + note : you can set level parameter as NULL (if you did not want known level, + but you CANNOT set method parameter as NULL +*/ + +extern int ZEXPORT unzOpenCurrentFile3 OF((unzFile file, + int* method, + int* level, + int raw, + const char* password)); +/* + Same than unzOpenCurrentFile, but open for read raw the file (not uncompress) + if raw==1 + *method will receive method of compression, *level will receive level of + compression + note : you can set level parameter as NULL (if you did not want known level, + but you CANNOT set method parameter as NULL +*/ + + +extern int ZEXPORT unzCloseCurrentFile OF((unzFile file)); +/* + Close the file in zip opened with unzOpenCurrentFile + Return UNZ_CRCERROR if all the file was read but the CRC is not good +*/ + +extern int ZEXPORT unzReadCurrentFile OF((unzFile file, + voidp buf, + unsigned len)); +/* + Read bytes from the current file (opened by unzOpenCurrentFile) + buf contain buffer where data must be copied + len the size of buf. + + return the number of byte copied if somes bytes are copied + return 0 if the end of file was reached + return <0 with error code if there is an error + (UNZ_ERRNO for IO error, or zLib error for uncompress error) +*/ + +extern z_off_t ZEXPORT unztell OF((unzFile file)); +/* + Give the current position in uncompressed data +*/ + +extern int ZEXPORT unzeof OF((unzFile file)); +/* + return 1 if the end of file was reached, 0 elsewhere +*/ + +extern int ZEXPORT unzGetLocalExtrafield OF((unzFile file, + voidp buf, + unsigned len)); +/* + Read extra field from the current file (opened by unzOpenCurrentFile) + This is the local-header version of the extra field (sometimes, there is + more info in the local-header version than in the central-header) + + if buf==NULL, it return the size of the local extra field + + if buf!=NULL, len is the size of the buffer, the extra header is copied in + buf. + the return value is the number of bytes copied in buf, or (if <0) + the error code +*/ + +/***************************************************************************/ + +/* Get the current file offset */ +extern uLong ZEXPORT unzGetOffset (unzFile file); + +/* Set the current file offset */ +extern int ZEXPORT unzSetOffset (unzFile file, uLong pos); + +#endif + diff --git a/channel/channelapp/source/unzip_io.c b/channel/channelapp/source/unzip_io.c new file mode 100644 index 0000000..10f2863 --- /dev/null +++ b/channel/channelapp/source/unzip_io.c @@ -0,0 +1,170 @@ +/* ioapi_mem2.c -- IO base function header for compress/uncompress .zip + files using zlib + zip or unzip API + + This version of ioapi is designed to access memory rather than files. + We do use a region of memory to put data in to and take it out of. We do + not have auto-extending buffers and do not inform anyone else that the + data has been written. It is really intended for accessing a zip archive + embedded in an application such that I can write an installer with no + external files. Creation of archives has not been attempted, although + parts of the framework are present. + + Based on Unzip ioapi.c version 0.22, May 19th, 2003 + + Copyright (C) 1998-2003 Gilles Vollant + (C) 2003 Justin Fletcher + + Dynamically allocated memory version. Troels K 2004 + mem_close deletes the data: file is single-session. No filenames. + + This file is under the same license as the Unzip tool it is distributed + with. +*/ + + +#include +#include +#include + +#include + +#include "panic.h" +#include "unzip_io.h" + +static voidpf ZCALLBACK mem_open OF(( + voidpf opaque, + void* buffer, + size_t buf_len, + int mode)); + +static uLong ZCALLBACK mem_read OF(( + voidpf opaque, + voidpf stream, + void* buf, + uLong size)); + +static long ZCALLBACK mem_tell OF(( + voidpf opaque, + voidpf stream)); + +static long ZCALLBACK mem_seek OF(( + voidpf opaque, + voidpf stream, + uLong offset, + int origin)); + +static int ZCALLBACK mem_close OF(( + voidpf opaque, + voidpf stream)); + +static int ZCALLBACK mem_error OF(( + voidpf opaque, + voidpf stream)); + +typedef struct _MEMFILE +{ + void* buffer; /* Base of the region of memory we're using */ + long length; /* Size of the region of memory we're using */ + long position; /* Current offset in the area */ +} MEMFILE; + +static voidpf ZCALLBACK mem_open (opaque, buffer, buf_len, mode) + voidpf opaque; + void* buffer; + size_t buf_len; + int mode; +{ + MEMFILE* handle = pmalloc(sizeof(*handle)); + + handle->position = 0; + handle->buffer = buffer; + handle->length = buf_len; + return handle; +} + +static uLong ZCALLBACK mem_read (opaque, stream, buf, size) + voidpf opaque; + voidpf stream; + void* buf; + uLong size; +{ + MEMFILE* handle = (MEMFILE*) stream; + + if ( (handle->position + size) > handle->length) + { + size = handle->length - handle->position; + } + memcpy(buf, ((char*)handle->buffer) + handle->position, size); + handle->position+=size; + return size; +} + +static long ZCALLBACK mem_tell (opaque, stream) + voidpf opaque; + voidpf stream; +{ + MEMFILE *handle = (MEMFILE *)stream; + return handle->position; +} + +static long ZCALLBACK mem_seek (opaque, stream, offset, origin) + voidpf opaque; + voidpf stream; + uLong offset; + int origin; +{ + MEMFILE* handle = (MEMFILE*)stream; + + int bOK = 1; + switch (origin) + { + case SEEK_SET : + //bOK = (offset >= 0) && (offset <= size); + if (bOK) handle->position = offset; + break; + case SEEK_CUR : + bOK = ((offset + handle->position) >= 0) && (((offset + handle->position) <= handle->length)); + if (bOK) handle->position = offset + handle->position; + break; + case SEEK_END: + bOK = ((handle->length - offset) >= 0) && (((handle->length - offset) <= handle->length)); + if (bOK) handle->position = offset + handle->length - 0; + break; + default: + bOK = 0; + break; + } + return bOK ? 0 : -1; +} + +int ZCALLBACK mem_close (opaque, stream) + voidpf opaque; + voidpf stream; +{ + MEMFILE *handle = (MEMFILE *)stream; + + free (handle); + return 0; +} + +int ZCALLBACK mem_error (opaque, stream) + voidpf opaque; + voidpf stream; +{ + //MEMFILE *handle = (MEMFILE *)stream; + /* We never return errors */ + return 0; +} + +void mem_simple_create_file(zlib_filefunc_def* api) +{ + api->zopen_file = mem_open; + api->zread_file = mem_read; + api->zwrite_file = NULL; + api->ztell_file = mem_tell; + api->zseek_file = mem_seek; + api->zclose_file = mem_close; + api->zerror_file = mem_error; + api->opaque = NULL; +} + diff --git a/channel/channelapp/source/unzip_io.h b/channel/channelapp/source/unzip_io.h new file mode 100644 index 0000000..031cd54 --- /dev/null +++ b/channel/channelapp/source/unzip_io.h @@ -0,0 +1,58 @@ +#ifndef _UNZIP_IO_H_ +#define _UNZIP_IO_H_ + +#define ZLIB_FILEFUNC_SEEK_CUR (1) +#define ZLIB_FILEFUNC_SEEK_END (2) +#define ZLIB_FILEFUNC_SEEK_SET (0) + +#define ZLIB_FILEFUNC_MODE_READ (1) +#define ZLIB_FILEFUNC_MODE_WRITE (2) +#define ZLIB_FILEFUNC_MODE_READWRITEFILTER (3) + +#define ZLIB_FILEFUNC_MODE_EXISTING (4) +#define ZLIB_FILEFUNC_MODE_CREATE (8) + + +#ifndef ZCALLBACK + +#if (defined(WIN32) || defined (WINDOWS) || defined (_WINDOWS)) && defined(CALLBACK) && defined (USEWINDOWS_CALLBACK) +#define ZCALLBACK CALLBACK +#else +#define ZCALLBACK +#endif + +#endif + +typedef voidpf (ZCALLBACK *open_file_func) OF((voidpf opaque, void* buffer, size_t buf_len, int mode)); +typedef uLong (ZCALLBACK *read_file_func) OF((voidpf opaque, voidpf stream, void* buf, uLong size)); +typedef uLong (ZCALLBACK *write_file_func) OF((voidpf opaque, voidpf stream, const void* buf, uLong size)); +typedef long (ZCALLBACK *tell_file_func) OF((voidpf opaque, voidpf stream)); +typedef long (ZCALLBACK *seek_file_func) OF((voidpf opaque, voidpf stream, uLong offset, int origin)); +typedef int (ZCALLBACK *close_file_func) OF((voidpf opaque, voidpf stream)); +typedef int (ZCALLBACK *testerror_file_func) OF((voidpf opaque, voidpf stream)); + +typedef struct zlib_filefunc_def_s +{ + open_file_func zopen_file; + read_file_func zread_file; + write_file_func zwrite_file; + tell_file_func ztell_file; + seek_file_func zseek_file; + close_file_func zclose_file; + testerror_file_func zerror_file; + voidpf opaque; +} zlib_filefunc_def; + + +extern void mem_simple_create_file(zlib_filefunc_def* api); + +#define ZREAD(filefunc,filestream,buf,size) ((*((filefunc).zread_file))((filefunc).opaque,filestream,buf,size)) +#define ZWRITE(filefunc,filestream,buf,size) ((*((filefunc).zwrite_file))((filefunc).opaque,filestream,buf,size)) +#define ZTELL(filefunc,filestream) ((*((filefunc).ztell_file))((filefunc).opaque,filestream)) +#define ZSEEK(filefunc,filestream,pos,mode) ((*((filefunc).zseek_file))((filefunc).opaque,filestream,pos,mode)) +#define ZCLOSE(filefunc,filestream) ((*((filefunc).zclose_file))((filefunc).opaque,filestream)) +#define ZERROR(filefunc,filestream) ((*((filefunc).zerror_file))((filefunc).opaque,filestream)) + + +#endif + diff --git a/channel/channelapp/source/update.c b/channel/channelapp/source/update.c new file mode 100644 index 0000000..bf36480 --- /dev/null +++ b/channel/channelapp/source/update.c @@ -0,0 +1,316 @@ + +#include +#include +#include + +#include + +#include "../config.h" + +#ifdef ENABLE_UPDATES + +#include "view.h" +#include "dialogs.h" +#include "http.h" +#include "i18n.h" +#include "sha1.h" +#include "ecdsa.h" + +#include "update.h" + +#define UPDATE_HEADER "SIG0" +#define UPDATE_OFFSET_R 4 +#define UPDATE_OFFSET_S 4+30 +#define UPDATE_OFFSET_XML 64 + + +static const u8 update_key[] = UPDATE_PUBLIC_KEY; + +static u8 nibble2hex(u8 byte) +{ + byte &= 0xf; + if (byte <= 9) + return '0' + byte; + else + return 'a' + byte - 0xa; +} + +static void hash2ascii(u8 *hash, u8 *ascii) +{ + u32 i; + for (i = 0; i < 20; i++) { + ascii[2*i] = nibble2hex(hash[i] >> 4); + ascii[(2*i) + 1] = nibble2hex(hash[i]); + } + ascii[40] = 0; +} + +typedef struct { + u8 *sha_data; + u32 sha_len; + + u8 *ecdsa_r; + u8 *ecdsa_s; + + bool valid; + bool running; +} update_arg; + +static lwpq_t update_queue; +static lwp_t update_thread; +static u8 update_stack[UPDATE_THREAD_STACKSIZE] ATTRIBUTE_ALIGN (32); +static update_arg ta_ua; + +static void *update_func(void *arg) { + u8 hash[20]; + update_arg *ua = (update_arg *) arg; + + SHA1(ua->sha_data, ua->sha_len, hash); + if (check_ecdsa(update_key, ua->ecdsa_r, ua->ecdsa_s, hash)) + ua->valid = true; + + ua->running = false; + return NULL; +} + +typedef enum { + UPDATE_IDLE = 0, + UPDATE_FETCH_META, + UPDATE_VALIDATE, + UPDATE_NO_UPDATE, + UPDATE_CAN_UPDATE +} update_state; + +static const char *update_url = UPDATE_URL; +static update_state us = UPDATE_IDLE; +static update_info *info = NULL; + +bool update_signal(void) { + if ((us == UPDATE_FETCH_META) || us == UPDATE_CAN_UPDATE) + return false; + + gprintf("starting update check\n"); + + if (http_request(update_url, 8 * 1024)) { + us = UPDATE_FETCH_META; + return true; + } + + return false; +} + +static u8 *update_data = NULL; +static u32 update_len = 0; + +bool update_busy(bool *update_available) { + http_res http_result; + u32 status; + s32 res; + + if ((us != UPDATE_FETCH_META) && (us != UPDATE_VALIDATE)) + return false; + + if (us == UPDATE_FETCH_META) { + if (!http_get_result(&http_result, &status, &update_data, &update_len)) + return true; + + *update_available = false; + us = UPDATE_NO_UPDATE; + + gprintf("update check done, status=%u len=%u\n", status, update_len); + + if (http_result != HTTPR_OK) + return false; + + gprintf("fetched update xml\n"); + + if (update_len < 64) { + gprintf("update.sxml length is invalid\n"); + free(update_data); + return false; + } + + if (memcmp(UPDATE_HEADER, update_data, 4)) { + gprintf("SIG0 header not found.\n"); + free(update_data); + return false; + } + + memset(&ta_ua, 0, sizeof(ta_ua)); + ta_ua.sha_data = update_data + UPDATE_OFFSET_XML; + ta_ua.sha_len = update_len - UPDATE_OFFSET_XML; + ta_ua.ecdsa_r = update_data + UPDATE_OFFSET_R; + ta_ua.ecdsa_s = update_data + UPDATE_OFFSET_S; + ta_ua.running = true; + + memset(&update_stack, 0, UPDATE_THREAD_STACKSIZE); + LWP_InitQueue(&update_queue); + res = LWP_CreateThread(&update_thread, update_func, &ta_ua, + update_stack, UPDATE_THREAD_STACKSIZE, + UPDATE_THREAD_PRIO); + + if (res) { + gprintf("error creating thread: %ld\n", res); + LWP_CloseQueue(update_queue); + free(update_data); + return false; + } + + us = UPDATE_VALIDATE; + return true; + } + + if (ta_ua.running) + return true; + + LWP_CloseQueue(update_queue); + + us = UPDATE_NO_UPDATE; + + if (!ta_ua.valid) { + gprintf("update.sxml signature is invalid.\n"); + free(update_data); + return false; + } + + gprintf("update.sxml signature is valid.\n"); + + info = update_parse((char *) update_data + UPDATE_OFFSET_XML); + free(update_data); + + if (!info) { + gprintf("Failed to parse update.sxml\n"); + return false; + } + + if (!info->hash) { + gprintf("no node in update.sxml\n"); + update_free(info); + info = NULL; + return false; + } + + if (strnlen(info->hash, 40) != 40) { + gprintf("Invalid length\n"); + update_free(info); + info = NULL; + return false; + } + + if (info->date > CHANNEL_VERSION_DATE) { + gprintf("update available\n"); + us = UPDATE_CAN_UPDATE; + *update_available = true; + } else { + gprintf("hbc is up2date\n"); + update_free(info); + info = NULL; + } + + return false; +} + +#define UPDATE_MSG_SIZE 4096 + +bool update_execute (view *sub_view, entry_point *ep) { + http_state state; + http_res res; + u32 status, length, progress; + u8 *data; + view *v = sub_view; + bool downloading = false; + static char text[UPDATE_MSG_SIZE]; + bool b = false; + u8 hash[20]; + u8 ascii_hash[41]; + + if (us != UPDATE_CAN_UPDATE) + return false; + + us = UPDATE_IDLE; + + snprintf (text, UPDATE_MSG_SIZE, _("An update to the Homebrew Channel " + "(version %s, replacing the installed version %s) " + "is available for installation, do you want " + "to update now?\n\n" + "Release notes:\n\n%s"), + info->version, CHANNEL_VERSION_STR, info->notes); + text[UPDATE_MSG_SIZE-1] = 0; + + if (show_message (sub_view, DLGMT_CONFIRM, DLGB_YESNO, text, 0) == 1) { + update_free(info); + info = NULL; + return false; + } + + if (!http_request(info->uri, 4 * 1024 * 1024)) { + gprintf ("error requesting update download\n"); + update_free(info); + info = NULL; + return false; + } + + while (true) { + view_plot (v, DIALOG_MASK_COLOR, NULL, NULL, NULL); + + if (!http_get_state (&state, &length, &progress)) + break; + + if ((!downloading) && (state == HTTPS_RECEIVING)) { + downloading = true; + + snprintf (text, 32, _("Downloading Update (%u kB)"), length / 1024); + + v = dialog_progress (sub_view, text, length); + + dialog_fade (v, true); + } + + if (downloading) + dialog_set_progress (v, progress); + + if (http_get_result (&res, &status, &data, &length)) { + gprintf("update download done, status=%u len=%u\n", status, length); + + if (res == HTTPR_OK) { + SHA1(data, length, hash); + hash2ascii(hash, ascii_hash); + gprintf("Calculated hash : %s\n", ascii_hash); + gprintf("Expected hash : %s\n", info->hash); + + if (memcmp(ascii_hash, info->hash, 40)) { + gprintf("installer.elf hash is invalid.\n"); + free(data); + update_free(info); + info = NULL; + break; + } + + gprintf("installer.elf hash is valid.\n"); + b = true; + } + + break; + } + } + + update_free(info); + info = NULL; + + if (downloading) + dialog_fade (v, false); + + if (!b) { + show_message (sub_view, DLGMT_ERROR, DLGB_OK, _("Download failed"), 0); + + return false; + } + + b = loader_reloc(ep, data, length, + "installer.elf\0-updatehbc\0\0", 26, true); + + free (data); + + return b; +} +#endif diff --git a/channel/channelapp/source/update.h b/channel/channelapp/source/update.h new file mode 100644 index 0000000..ff0c66d --- /dev/null +++ b/channel/channelapp/source/update.h @@ -0,0 +1,13 @@ +#ifndef _UPDATE_H_ +#define _UPDATE_H_ + +#include + +#include "loader_reloc.h" + +bool update_signal(void); +bool update_busy(bool *update_available); +bool update_execute(view *sub_view, entry_point *ep); + +#endif + diff --git a/channel/channelapp/source/view.c b/channel/channelapp/source/view.c new file mode 100644 index 0000000..3d5f362 --- /dev/null +++ b/channel/channelapp/source/view.c @@ -0,0 +1,404 @@ +#include +#include +#include + +#include + +#include "../config.h" +#include "controls.h" +#include "theme.h" +#include "cursors.h" +#include "widgets.h" +#include "bubbles.h" +#include "panic.h" +#include "view.h" + +#define FOCUS_FLAGS (WF_VISIBLE | WF_ENABLED | WF_FOCUSABLE) +#define CURSOR_FLAGS (WF_VISIBLE | WF_ENABLED) + +#define RUMBLE_TIME 3 +#define RUMBLE_DELAY 12 + +u8 *cursor_data; +u32 cursor_data_size; +u8 *cursor_shade_data; +u32 cursor_shade_data_size; + +static gfx_queue_entry entry_bg; +static gfx_queue_entry entry_logo; +static gfx_queue_entry entry_throbber; + +static gfx_entity gradient_subview; +static gfx_queue_entry entry_subview; + +static bool cursor_enabled; +static bool rumble_enabled; +static bool throbber_enabled; + +static gfx_queue_entry cur[2]; + +static s8 rumble_timeout = -RUMBLE_DELAY; + +bool view_bubbles = false; + +void view_init (void) { + cursor_enabled = false; + rumble_enabled = (CONF_GetPadMotorMode() == 0) ? 0 : 1; + throbber_enabled = false; + + view_theme_reinit(); + bubbles_init(); +} + +void view_deinit (void) { + cursor_enabled = false; + bubbles_deinit(); +} + +void view_theme_reinit(void) { + gfx_qe_entity(&entry_bg, theme_gfx[THEME_BACKGROUND], + 0, 0, -3, COL_DEFAULT); + gfx_qe_entity(&entry_logo, theme_gfx[THEME_LOGO], 0, 416, 0, COL_DEFAULT); + gfx_qe_entity(&entry_throbber, theme_gfx[THEME_THROBBER], + (view_width - theme_gfx[THEME_THROBBER]->w) / 2, + (view_height - theme_gfx[THEME_THROBBER]->h) / 2, + 0, COL_DEFAULT); +} + +view * view_new (u8 widget_count, const view *sub_view, s16 x, s16 y, s16 z, + u32 drag_btn) { + view *v; + + v = (view *) pmalloc (sizeof (view)); + memset (v, 0, sizeof (view)); + + v->coords.x = x; + v->coords.y = y; + v->coords.z = z; + gfx_qe_origin_push (&v->qe_coords_push, &v->coords); + gfx_qe_origin_pop (&v->qe_coords_pop); + + v->widget_count = widget_count; + + v->widgets = (widget *) pmalloc (widget_count * sizeof (widget)); + memset (v->widgets, 0, widget_count * sizeof (widget)); + + v->sub_view = sub_view; + + v->focus = -1; + v->cursor = -1; + + v->drag = false; + v->drag_btn = drag_btn; + + return v; +} + +void view_free (view *v) { + u8 i; + + if (!v) + return; + + for (i = 0; i < v->widget_count; ++i) + widget_free(&v->widgets[i]); + + free(v->widgets); + free(v); +} + +static void view_push_view (const view *v, u32 subview_alpha) { + int i, j; + widget *w; + widget_layer *l; + u32 f; + + if (v->sub_view) { + view_push_view (v->sub_view, false); + gfx_gen_gradient (&gradient_subview, view_width + 10, + view_height + 10, subview_alpha, + subview_alpha, subview_alpha, subview_alpha); + + gfx_qe_entity (&entry_subview, &gradient_subview, -5, -5, + v->coords.z - 1, COL_DEFAULT); + + gfx_frame_push (&entry_subview, 1); + } + + gfx_frame_push (&v->qe_coords_push, 1); + + for (i = 0; i < v->widget_count; ++i) { + w = &v->widgets[i]; + + if (w->flags & WF_VISIBLE) { + gfx_frame_push (&w->qe_coords_push, 1); + for (j = 0; j < w->layer_count; ++j) { + l = &w->layers[j]; + + if (l->flags) { + f = w->flags ^ l->flags_invert; + f = f & l->flags & WF_FLAGS_MASK; + + if (!f) + continue; + + if (l->flags & WF_FLAGS_AND && + f != (l->flags & WF_FLAGS_MASK)) + continue; + } + + gfx_frame_push (w->layers[j].gfx_entries, + w->layers[j].gfx_entry_count); + } + + gfx_frame_push (&w->qe_coords_pop, 1); + } + } + + gfx_frame_push (&v->qe_coords_pop, 1); +} + +void view_plot (view *v, u32 alpha, u32 *down, u32 *held, u32 *up) { + u32 bd, bh, bu; + bool wm; + s32 x, y; + f32 roll; + s8 w, c; + cursor_type ct; +#ifdef ENABLE_SCREENSHOTS + u16 ss_x, ss_y; + u32 *ss_buf = NULL; +#endif + + controls_scan (&bd, &bh, &bu); + wm = controls_ir (&x, &y, &roll); + w = view_widget_at_xy (v, x - v->coords.x, y - v->coords.y); + ct = CUR_STD; + + if (v->drag_btn) { + if (bu & v->drag_btn) + v->drag = false; + + if ((w != -1) && (bd & v->drag_btn)) { + v->drag = true; + + v->drag_widget = w; + + v->drag_start_x = x; + v->drag_start_y = y; + } + + if (v->drag && (bh & v->drag_btn)) { + v->drag_x = x - v->drag_start_x; + v->drag_y = y - v->drag_start_y; + + ct = v->widgets[v->drag_widget].cur; + } + } + + c = v->cursor; + + if (v->cursor != -1) { + widget_set_flag (&v->widgets[v->cursor], WF_CURSOR, false); + v->cursor = -1; + } + + wm = wm && cursor_enabled; + + if (wm) { + view_set_focus (v, -1); + + if (w != -1) { + if ((v->widgets[w].flags & CURSOR_FLAGS) == CURSOR_FLAGS) { + widget_set_flag (&v->widgets[w], WF_CURSOR, true); + v->cursor = w; + + if (rumble_enabled && (c != w) && (ct != CUR_DRAG) && + (v->widgets[w].flags & WF_RUMBLE) && + (rumble_timeout == -RUMBLE_DELAY)) { + rumble_timeout = RUMBLE_TIME; + controls_rumble(1); + } + } + + if ((v->widgets[w].flags & FOCUS_FLAGS) == FOCUS_FLAGS) + view_set_focus (v, w); + } + } + + gfx_frame_start (); + + gfx_frame_push (&entry_bg, 1); + + if (view_bubbles) + bubble_update(wm, x, y); + + gfx_frame_push (&entry_logo, 1); + + view_push_view (v, alpha); + + if (throbber_enabled) + gfx_frame_push(&entry_throbber, 1); + + if (wm) { + if ((w != -1) && (ct != CUR_DRAG)) + ct = v->widgets[w].cur; + + cursors_queue (cur, ct, x, y, roll); + + gfx_frame_push (cur, 2); + } + +#ifdef ENABLE_SCREENSHOTS + // ZR + ZL on Classic Controller + if ((bh & PADS_NET_INIT) && (bd & PADS_SCREENSHOT)) { + gfx_get_efb_size(&ss_x, &ss_y); + ss_buf = (u32 *) pmalloc(ss_x * ss_y * sizeof(u32)); + + gfx_set_efb_buffer(ss_buf); + } +#endif + + gfx_frame_end (); + +#ifdef ENABLE_SCREENSHOTS + if (ss_buf) { + save_rgba_png(ss_buf, ss_x, ss_y); + free(ss_buf); + } +#endif + + if (down) + *down = bd; + + if (held) + *held = bh; + + if (up) + *up = bu; + + if (rumble_timeout > -RUMBLE_DELAY) + rumble_timeout--; + + if (rumble_timeout < 1) + controls_rumble(0); +} + +void view_fade (view *v, s16 z, u32 c1, u32 c2, u32 c3, u32 c4, u8 steps, + s8 modifier) { + view *vf; + u8 i; + + vf = view_new (1, v, 0, 0, 0, 0); + + widget_gradient (&vf->widgets[0], -32, -32, z, view_width + 64, + view_height + 64, c1, c2, c3, c4); + + for (i = 0; i < steps; ++i) { + widget_fade_gradient (&vf->widgets[0], modifier); + + view_plot (vf, 0, NULL, NULL, NULL); + } +} + +void view_set_focus (view *v, s8 new_focus) { + if (v->focus != -1) + v->widgets[v->focus].flags &= ~((u32) WF_FOCUSED); + + v->focus = new_focus; + + if (v->focus != -1) + v->widgets[v->focus].flags |= WF_FOCUSED; +} + +u8 view_set_focus_prev (view *v) { + s16 i; + + if (v->focus < 1) + return v->focus; + + for (i = v->focus - 1; i >= 0; --i) + if ((v->widgets[i].flags & FOCUS_FLAGS) == FOCUS_FLAGS) { + view_set_focus (v, i); + + break; + } + + return v->focus; +} + +u8 view_set_focus_next (view *v) { + u8 i; + + if (v->focus == v->widget_count - 1) + return v->focus; + + for (i = v->focus + 1; i < v->widget_count; ++i) + if ((v->widgets[i].flags & FOCUS_FLAGS) == FOCUS_FLAGS) { + view_set_focus (v, i); + + break; + } + + return v->focus; +} + +u8 view_move_focus(view *v, s8 mod) { + if (v->focus + mod < 1) + return v->focus; + + if (v->focus + mod > v->widget_count - 1) + return v->focus; + + if ((v->widgets[v->focus + mod].flags & FOCUS_FLAGS) == FOCUS_FLAGS) + view_set_focus (v, v->focus + mod); + + return v->focus; +} + +void view_enable_cursor (bool enable) { + cursor_enabled = enable; +} + +s8 view_widget_at_xy (const view *v, s32 x, s32 y) { + s16 i; + widget *w; + s16 wx, wy; + + for (i = v->widget_count - 1; i >= 0; --i) { + w = &v->widgets[i]; + + if (w->type == WT_MEMO) { + wx = w->coords.x; + wy = w->memo.y_max; + } else { + wx = w->coords.x; + wy = w->coords.y; + } + + if (w->width && w->height && (x >= wx) && (y >= wy) && + (x <= wx + w->width) && (y <= wy + w->height)) + return i; + } + + return -1; +} + +s8 view_widget_at_ir (const view *v) { + s32 x, y; + + if (!cursor_enabled || !controls_ir (&x, &y, NULL)) + return -1; + + return view_widget_at_xy (v, x - v->coords.x, y - v->coords.y); +} + +void view_show_throbber(bool show) { + entry_throbber.entity.rad = 0; + throbber_enabled = show; +} + +void view_throbber_tickle(void) { + entry_throbber.entity.rad -= 0.1f; +} + diff --git a/channel/channelapp/source/view.h b/channel/channelapp/source/view.h new file mode 100644 index 0000000..63ddd85 --- /dev/null +++ b/channel/channelapp/source/view.h @@ -0,0 +1,54 @@ +#ifndef _VIEW_H_ +#define _VIEW_H_ + +#include + +#include "widgets.h" + +typedef struct _view { + gfx_coordinates coords; + gfx_queue_entry qe_coords_push, qe_coords_pop; + + u8 widget_count; + widget *widgets; + + const struct _view *sub_view; + + s8 focus; + s8 cursor; + + bool drag; + s8 drag_widget; + u32 drag_btn; + s32 drag_start_x, drag_start_y, drag_x, drag_y; +} view; + +extern bool view_bubbles; +extern bool allow_screenshot; + +void view_init (); +void view_deinit (); +void view_theme_reinit(void); + +view * view_new (u8 widget_count, const view *sub_view, s16 x, s16 y, s16 z, + u32 drag_btn); +void view_free (view *v); + +void view_plot (view *v, u32 alpha, u32 *down, u32 *held, u32 *up); +void view_fade (view *v, s16 z, u32 c1, u32 c2, u32 c3, u32 c4, u8 steps, + s8 modifier); + +void view_set_focus (view *v, s8 new_focus); +u8 view_set_focus_prev (view *v); +u8 view_set_focus_next (view *v); +u8 view_move_focus(view *v, s8 mod); + +void view_enable_cursor (bool enable); +s8 view_widget_at_xy (const view *v, s32 x, s32 y); +s8 view_widget_at_ir (const view *v); + +void view_show_throbber(bool show); +void view_throbber_tickle(void); + +#endif + diff --git a/channel/channelapp/source/widgets.c b/channel/channelapp/source/widgets.c new file mode 100644 index 0000000..315b203 --- /dev/null +++ b/channel/channelapp/source/widgets.c @@ -0,0 +1,613 @@ +#include +#include +#include +#include + +#include "../config.h" +#include "theme.h" +#include "i18n.h" +#include "panic.h" + +#include "widgets.h" + +static const char *app_entry_default_description; + +void widgets_theme_reinit (void) { + app_entry_default_description = _(""); +} + +void widgets_init (void) { + widgets_theme_reinit(); +} + +void widgets_deinit (void) { +} + +void widget_free (widget *w) { + u32 i; + + for (i = 0; i < w->layer_count; ++i) + free (w->layers[i].gfx_entries); + + switch (w->type) { + case WT_LABEL: + case WT_BUTTON: + case WT_APP_ENTRY: + case WT_MEMO: + case WT_IMAGE: + break; + + case WT_PROGRESS: + free (w->progress.gradient); + break; + + case WT_GRADIENT: + free (w->gradient.gradient); + break; + } + + free (w->layers); + + memset(w, 0, sizeof(widget)); +} + +void widget_set_flag (widget *w, u32 flag, bool set) { + if (set) + w->flags |= flag; + else + w->flags &= ~((u32) flag); +} + +void widget_toggle_flag (widget *w, u32 flag) { + widget_set_flag (w, flag, (w->flags & flag) ? false : true); +} + +void widget_set_progress (widget *w, u32 progress) { + float m = 0; + char p[5]; + + if (progress > 0) + m = (float) progress / w->progress.max; + + if (m > 1) + m = 1; + + gfx_gen_gradient (w->progress.gradient, roundf ((400 - 36 * 2 + 2) * m), + 112 - 36 * 2 + 2, theme.progress.ul, theme.progress.ur, + theme.progress.lr, theme.progress.ll); + gfx_qe_entity (&w->layers[0].gfx_entries[0], w->progress.gradient, 35, 35, + 0, COL_DEFAULT); + + sprintf (p, "%3.f%%", roundf (100.0 * m)); + font_plot_string (&w->layers[2].gfx_entries[0], 4, FONT_LABEL, p, + 32, theme_gfx[THEME_PROGRESS]->h - 20, 2, + theme_gfx[THEME_PROGRESS]->w - 70, + FA_RIGHT, FA_EM_CENTERED); +} + +#define CCLAMP(x) (((x)>255)?255:(((x)<0)?0:(x))) + +void widget_fade_gradient (widget *w, s8 modifier) { + w->layers[0].gfx_entries[0].entity.entity->gradient.c1 = + CCLAMP(w->layers[0].gfx_entries[0].entity.entity->gradient.c1 + modifier); + w->layers[0].gfx_entries[0].entity.entity->gradient.c2 = + CCLAMP(w->layers[0].gfx_entries[0].entity.entity->gradient.c2 + modifier); + w->layers[0].gfx_entries[0].entity.entity->gradient.c3 = + CCLAMP(w->layers[0].gfx_entries[0].entity.entity->gradient.c3 + modifier); + w->layers[0].gfx_entries[0].entity.entity->gradient.c4 = + CCLAMP(w->layers[0].gfx_entries[0].entity.entity->gradient.c4 + modifier); +} + +bool widget_scroll_memo (widget *w, s16 modifier) { + s16 y; + + y = w->coords.y + modifier; + + if (y < w->memo.y_min) + y = w->memo.y_min; + + if (y > w->memo.y_max) + y = w->memo.y_max; + + if (w->coords.y == y) + return false; + + w->coords.y = y; + + return true; +} + +bool widget_scroll_memo_deco (widget *w, s16 modifier) { + bool res; + + res = widget_scroll_memo (&w[1], modifier); + + widget_set_flag (&w[0], WF_VISIBLE, w[1].coords.y < w[1].memo.y_max); + widget_set_flag (&w[2], WF_VISIBLE, w[1].coords.y > w[1].memo.y_min); + + return res; +} + +static void widget_init (widget *w, widget_type type, u32 focusable, + s16 x, s16 y, s16 z, u16 width, u16 height, + int layer_count) { + w->type = type; + w->flags = WF_VISIBLE | WF_ENABLED; + + w->width = width; + w->height = height; + + w->cur = CUR_STD; + + if (focusable) + w->flags |= WF_FOCUSABLE; + + w->coords.x = x; + w->coords.y = y; + w->coords.z = TEX_LAYER_WIDGETS + z; + gfx_qe_origin_push (&w->qe_coords_push, &w->coords); + gfx_qe_origin_pop (&w->qe_coords_pop); + + w->layer_count = layer_count; + w->layers = (widget_layer *) + pmalloc (w->layer_count * sizeof (widget_layer)); +} + +void widget_label (widget *w, s16 x, s16 y, s16 z, const char *caption, + u16 width, font_xalign xalign, font_yalign yalign, + font_id font) { + widget_layer *l; + + widget_init (w, WT_LABEL, false, x, y, z, 0, 0, 1); + + l = &w->layers[0]; + l->flags = 0; + l->gfx_entry_count = font_get_char_count (font, caption, width); + l->gfx_entries = (gfx_queue_entry *) + pmalloc (l->gfx_entry_count * sizeof (gfx_queue_entry)); + font_plot_string (&l->gfx_entries[0], l->gfx_entry_count, font, caption, + 0, 0, 0, width, xalign, yalign); +} + +void widget_image(widget *w, s16 x, s16 y, s16 z, gfx_entity *image, + gfx_entity *image_disabled, bool rumble, + gfx_entity *image_cursor) { + int c; + widget_layer *l; + + c = 1; + + if (image_disabled) + c++; + + if (image_cursor) + c++; + + widget_init (w, WT_IMAGE, false, x, y, z, image->w, image->h, c); + + if (rumble) + w->flags |= WF_RUMBLE; + + c = 0; + + l = &w->layers[c]; + if(image_cursor) { + l->flags = WF_ENABLED | WF_FLAGS_AND | WF_CURSOR; + l->flags_invert = WF_CURSOR; + } else { + l->flags = WF_ENABLED; + l->flags_invert = 0; + } + + l->gfx_entry_count = 1; + l->gfx_entries = (gfx_queue_entry *) + pmalloc (l->gfx_entry_count * sizeof (gfx_queue_entry)); + gfx_qe_entity (&l->gfx_entries[0], image, 0, 0, 0, COL_DEFAULT); + + c++; + + if (image_disabled) { + l = &w->layers[c]; + l->flags = WF_ENABLED; + l->flags_invert = WF_ENABLED; + l->gfx_entry_count = 1; + l->gfx_entries = (gfx_queue_entry *) pmalloc (l->gfx_entry_count * + sizeof (gfx_queue_entry)); + gfx_qe_entity (&l->gfx_entries[0], image_disabled, 0, 0, 1, COL_DEFAULT); + + c++; + } + + if (image_cursor) { + l = &w->layers[c]; + l->flags = WF_ENABLED | WF_FLAGS_AND | WF_CURSOR; + l->flags_invert = 0; + l->gfx_entry_count = 1; + l->gfx_entries = (gfx_queue_entry *) pmalloc (l->gfx_entry_count * + sizeof (gfx_queue_entry)); + gfx_qe_entity (&l->gfx_entries[0], image_cursor, 0, 0, 2, COL_DEFAULT); + } +} + +void widget_button (widget *w, s16 x, s16 y, s16 z, button_size size, + const char *caption) { + gfx_entity *tex, *tex_focus; + widget_layer *l; + + switch (size) { + case BTN_SMALL: + tex = theme_gfx[THEME_BUTTON_SMALL]; + tex_focus = theme_gfx[THEME_BUTTON_SMALL_FOCUS]; + break; + + case BTN_TINY: + tex = theme_gfx[THEME_BUTTON_TINY]; + tex_focus = theme_gfx[THEME_BUTTON_TINY_FOCUS]; + break; + + default: + tex = theme_gfx[THEME_BUTTON]; + tex_focus = theme_gfx[THEME_BUTTON_FOCUS]; + break; + } + + widget_init (w, WT_BUTTON, true, x, y, z, tex->w, tex->h, 4); + + w->flags |= WF_RUMBLE; + + l = &w->layers[0]; + l->flags = WF_FOCUSED | WF_FLAGS_AND | WF_ENABLED; + l->flags_invert = WF_FOCUSED; + l->gfx_entry_count = 1; + l->gfx_entries = (gfx_queue_entry *) + pmalloc (l->gfx_entry_count * sizeof (gfx_queue_entry)); + + gfx_qe_entity (&l->gfx_entries[0], tex, 0, 0, 0, COL_DEFAULT); + + l = &w->layers[1]; + l->flags = WF_ENABLED; + l->flags_invert = WF_ENABLED; + l->gfx_entry_count = 1; + l->gfx_entries = (gfx_queue_entry *) + pmalloc (l->gfx_entry_count * sizeof (gfx_queue_entry)); + + gfx_qe_entity (&l->gfx_entries[0], tex, 0, 0, 0, WIDGET_DISABLED_COLOR); + + l = &w->layers[2]; + l->flags = WF_FOCUSED | WF_FLAGS_AND | WF_ENABLED; + l->flags_invert = 0; + l->gfx_entry_count = 1; + l->gfx_entries = (gfx_queue_entry *) + pmalloc (l->gfx_entry_count * sizeof (gfx_queue_entry)); + gfx_qe_entity (&l->gfx_entries[0], tex_focus, 0, 0, 0, COL_DEFAULT); + + l = &w->layers[3]; + l->flags = 0; + l->flags_invert = 0; + l->gfx_entry_count = 0; + l->gfx_entries = NULL; + + widget_button_set_caption(w, FONT_BUTTON, caption); +} + +void widget_button_set_caption(widget *w, font_id font, const char *caption) { + int c; + u16 bw, bh, oy; + widget_layer *l; + + l = &w->layers[3]; + free(l->gfx_entries); + l->gfx_entry_count = 0; + l->gfx_entries = NULL; + + if (!caption) + return; + + bw = w->layers[0].gfx_entries[0].entity.entity->w; + bh = w->layers[0].gfx_entries[0].entity.entity->h; + + c = font_get_char_count(font, caption, bw - 32); + + oy = bh/2; + + l->gfx_entry_count = c; + l->gfx_entries = (gfx_queue_entry *) + pmalloc (l->gfx_entry_count * sizeof (gfx_queue_entry)); + + font_plot_string (&l->gfx_entries[0], c, font, caption, 16, oy, 1, + bw - 32, FA_CENTERED, FA_EM_CENTERED); +} + +void widget_grid_app_entry(widget *w, s16 x, s16 y, s16 z, + const app_entry *entry) { + + widget_layer *l; + + if (entry->icon) + widget_init(w, WT_APP_ENTRY, true, x, y, z, + theme_gfx[THEME_GRID_APP_ENTRY]->w, + theme_gfx[THEME_GRID_APP_ENTRY]->h, 4); + else + widget_init(w, WT_APP_ENTRY, true, x, y, z, + theme_gfx[THEME_GRID_APP_ENTRY]->w, + theme_gfx[THEME_GRID_APP_ENTRY]->h, 3); + + // Enable rumbling for this widget + w->flags |= WF_RUMBLE; + + l = &w->layers[0]; + l->flags = WF_FOCUSED | WF_FLAGS_AND | WF_ENABLED; + l->flags_invert = WF_FOCUSED; + l->gfx_entry_count = 1; + l->gfx_entries = (gfx_queue_entry *) + pmalloc (l->gfx_entry_count * sizeof (gfx_queue_entry)); + gfx_qe_entity (&l->gfx_entries[0], theme_gfx[THEME_GRID_APP_ENTRY], + 0, 0, 0, COL_DEFAULT); + + l = &w->layers[1]; + l->flags = WF_ENABLED; + l->flags_invert = WF_ENABLED; + l->gfx_entry_count = 1; + l->gfx_entries = (gfx_queue_entry *) + pmalloc (l->gfx_entry_count * sizeof (gfx_queue_entry)); + + gfx_qe_entity (&l->gfx_entries[0], theme_gfx[THEME_GRID_APP_ENTRY], + 0, 0, 0, WIDGET_DISABLED_COLOR); + + l = &w->layers[2]; + l->flags = WF_FOCUSED | WF_FLAGS_AND | WF_ENABLED; + l->flags_invert = 0; + l->gfx_entry_count = 1; + l->gfx_entries = (gfx_queue_entry *) + pmalloc (l->gfx_entry_count * sizeof (gfx_queue_entry)); + gfx_qe_entity (&l->gfx_entries[0], theme_gfx[THEME_GRID_APP_ENTRY_FOCUS], + 0, 0, 0, COL_DEFAULT); + + if (entry->icon) { + l = &w->layers[3]; + l->flags = 0; + l->flags_invert = 0; + l->gfx_entry_count = 1; + + l->gfx_entry_count = 1; + l->gfx_entries = (gfx_queue_entry *) + pmalloc (l->gfx_entry_count * sizeof (gfx_queue_entry)); + + gfx_qe_entity (&l->gfx_entries[0], entry->icon, GRID_APP_ENTRY_ICON_X, + APP_ENTRY_ICON_Y, 1, COL_DEFAULT); + } +} + +void widget_app_entry (widget *w, s16 x, s16 y, s16 z, const app_entry *entry) { + const char *line1, *line2; + int l1, l2; + + widget_layer *l; + + widget_init (w, WT_APP_ENTRY, true, x, y, z, theme_gfx[THEME_APP_ENTRY]->w, + theme_gfx[THEME_APP_ENTRY]->h, 4); + + w->flags |= WF_RUMBLE; + + l = &w->layers[0]; + l->flags = WF_FOCUSED | WF_FLAGS_AND | WF_ENABLED; + l->flags_invert = WF_FOCUSED; + l->gfx_entry_count = 1; + l->gfx_entries = (gfx_queue_entry *) + pmalloc (l->gfx_entry_count * sizeof (gfx_queue_entry)); + gfx_qe_entity (&l->gfx_entries[0], theme_gfx[THEME_APP_ENTRY], + 0, 0, 0, COL_DEFAULT); + + l = &w->layers[1]; + l->flags = WF_ENABLED; + l->flags_invert = WF_ENABLED; + l->gfx_entry_count = 1; + l->gfx_entries = (gfx_queue_entry *) + pmalloc (l->gfx_entry_count * sizeof (gfx_queue_entry)); + + gfx_qe_entity (&l->gfx_entries[0], theme_gfx[THEME_APP_ENTRY], + 0, 0, 0, WIDGET_DISABLED_COLOR); + + l = &w->layers[2]; + l->flags = WF_FOCUSED | WF_FLAGS_AND | WF_ENABLED; + l->flags_invert = 0; + l->gfx_entry_count = 1; + l->gfx_entries = (gfx_queue_entry *) + pmalloc (l->gfx_entry_count * sizeof (gfx_queue_entry)); + gfx_qe_entity (&l->gfx_entries[0], theme_gfx[THEME_APP_ENTRY_FOCUS], + 0, 0, 0, COL_DEFAULT); + + line1 = NULL; + line2 = NULL; + + if (entry->meta) { + line1 = entry->meta->name; + line2 = entry->meta->short_description; + } + + if (!line1) + line1 = entry->dirname; + + if (!line2) + line2 = app_entry_default_description; + + l1 = font_get_char_count(FONT_APPNAME, line1, + theme_gfx[THEME_APP_ENTRY]->w - + APP_ENTRY_TEXT1_X - 16); + l2 = font_get_char_count(FONT_APPDESC, line2, + theme_gfx[THEME_APP_ENTRY]->w - + APP_ENTRY_TEXT2_X - 16); + + l = &w->layers[3]; + l->flags = 0; + l->flags_invert = 0; + l->gfx_entry_count = l1 + l2; + + if (entry->icon) + l->gfx_entry_count++; + + l->gfx_entries = (gfx_queue_entry *) + pmalloc (l->gfx_entry_count * sizeof (gfx_queue_entry)); + font_plot_string (&l->gfx_entries[0], l1, FONT_APPNAME, line1, + APP_ENTRY_TEXT1_X, APP_ENTRY_TEXT1_Y, 1, + theme_gfx[THEME_APP_ENTRY]->w - APP_ENTRY_TEXT1_X - + 16, FA_LEFT, FA_ASCENDER); + font_plot_string (&l->gfx_entries[l1], l2, FONT_APPDESC, line2, + APP_ENTRY_TEXT2_X, APP_ENTRY_TEXT2_Y, 1, + theme_gfx[THEME_APP_ENTRY]->w - APP_ENTRY_TEXT2_X - + 16, FA_LEFT, FA_DESCENDER); + + if (entry->icon) + gfx_qe_entity (&l->gfx_entries[l1 + l2], entry->icon, + APP_ENTRY_ICON_X, APP_ENTRY_ICON_Y, 1, COL_DEFAULT); +} + +void widget_progress (widget *w, s16 x, s16 y, s16 z, const char *caption, + u32 max) { + widget_layer *l; + + widget_init (w, WT_PROGRESS, false, x, y, z, 0, 0, 3); + + l = &w->layers[0]; + l->flags = 0; + l->flags_invert = 0; + l->gfx_entry_count = 1; + l->gfx_entries = (gfx_queue_entry *) + pmalloc (l->gfx_entry_count * sizeof (gfx_queue_entry)); + + l = &w->layers[1]; + l->flags = 0; + l->flags_invert = 0; + l->gfx_entry_count = 1; + l->gfx_entries = (gfx_queue_entry *) + pmalloc (l->gfx_entry_count * sizeof (gfx_queue_entry)); + gfx_qe_entity (&l->gfx_entries[0], theme_gfx[THEME_PROGRESS], + 0, 0, 1, COL_DEFAULT); + + int chars = font_get_char_count(FONT_LABEL, caption, + theme_gfx[THEME_PROGRESS]->w - 64); + + l = &w->layers[2]; + l->flags = 0; + l->flags_invert = 0; + l->gfx_entry_count = 4 + chars; + l->gfx_entries = (gfx_queue_entry *) + pmalloc (l->gfx_entry_count * sizeof (gfx_queue_entry)); + font_plot_string (&l->gfx_entries[4], chars, FONT_LABEL, caption, 38, 19, 2, + theme_gfx[THEME_PROGRESS]->w - 64, FA_LEFT, FA_EM_CENTERED); + + w->progress.max = max; + w->progress.gradient = (gfx_entity *) pmalloc (sizeof (gfx_entity)); +} + +void widget_gradient (widget *w, s16 x, s16 y, s16 z, + u16 width, u16 height, + u32 c1, u32 c2, u32 c3, + u32 c4) { + widget_layer *l; + + widget_init (w, WT_GRADIENT, false, x, y, z, 0, 0, 1); + + w->gradient.gradient = (gfx_entity *) pmalloc (sizeof (gfx_entity)); + gfx_gen_gradient (w->gradient.gradient, width, height, c1, c2, c3, c4); + + l = &w->layers[0]; + l->flags = 0; + l->flags_invert = 0; + l->gfx_entry_count = 1; + l->gfx_entries = (gfx_queue_entry *) + pmalloc (l->gfx_entry_count * sizeof (gfx_queue_entry)); + + gfx_qe_entity (&l->gfx_entries[0], w->gradient.gradient, 0, 0, 0, + COL_DEFAULT); +} + +void widget_memo (widget *w, s16 x, s16 y, s16 z, u16 width, u16 height, + const char *text, font_xalign align) { + widget_layer *l; + int hf; + int count, i, c; + char **lines; + int cl, ot; + int oy; + + hf = font_get_y_spacing(FONT_MEMO); + + count = font_wrap_string (&lines, FONT_MEMO, text, width); + + c = 0; + for (i = 0; i < count; ++i) + if (lines[i]) + c++; + + ot = 0; + cl = (height + hf - 1) / hf; + if (cl > count) + ot = ((cl - count) / 2) * hf; + + widget_init (w, WT_MEMO, false, x, y + ot, z, width, height, c + 2); + + if (c > cl) + w->cur = CUR_DRAG; + + l = &w->layers[0]; + l->flags = 0; + l->flags_invert = 0; + l->gfx_entry_count = 1; + l->gfx_entries = (gfx_queue_entry *) + pmalloc (l->gfx_entry_count * sizeof (gfx_queue_entry)); + + gfx_qe_scissor (&l->gfx_entries[0], x, y, z, width, height); + + c = 1; + oy = 0; + + w->memo.y_max = w->coords.y; + + for (i = 0; i < count; ++i) { + if (!lines[i]) { + oy += hf; + continue; + } + + l = &w->layers[c]; + l->flags = 0; + l->flags_invert = 0; + l->gfx_entry_count = font_get_char_count(FONT_MEMO, lines[i], 0); + l->gfx_entries = (gfx_queue_entry *) pmalloc (l->gfx_entry_count * + sizeof (gfx_queue_entry)); + font_plot_string (&l->gfx_entries[0], l->gfx_entry_count, FONT_MEMO, + lines[i], 0, oy, 0, width, align, FA_ASCENDER); + c++; + oy += hf; + } + + font_free_lines (lines, count); + + l = &w->layers[c]; + l->flags = 0; + l->flags_invert = 0; + l->gfx_entry_count = 1; + l->gfx_entries = (gfx_queue_entry *) + pmalloc (l->gfx_entry_count * sizeof (gfx_queue_entry)); + + gfx_qe_scissor_reset (&l->gfx_entries[0]); + + w->memo.y_min = w->memo.y_max - oy + height; +} + +void widget_memo_deco (widget *w, s16 x, s16 y, s16 z, u16 width, u16 height, + const char *text, font_xalign align) { + widget_image (&w[0], x + (width - theme_gfx[THEME_CONTENT_ARROW_UP]->w) / 2, + y, z, theme_gfx[THEME_CONTENT_ARROW_UP], NULL, false, NULL); + widget_memo (&w[1], x, y + theme_gfx[THEME_CONTENT_ARROW_UP]->h + 8, z, + width, height - theme_gfx[THEME_CONTENT_ARROW_UP]->h - + 8 - theme_gfx[THEME_CONTENT_ARROW_DOWN]->h - 8, text, + align); + widget_image (&w[2], + x + (width - theme_gfx[THEME_CONTENT_ARROW_DOWN]->w) / 2, + y + height - theme_gfx[THEME_CONTENT_ARROW_DOWN]->h, z, + theme_gfx[THEME_CONTENT_ARROW_DOWN], NULL, false, NULL); + widget_scroll_memo_deco (w, 0); +} + diff --git a/channel/channelapp/source/widgets.h b/channel/channelapp/source/widgets.h new file mode 100644 index 0000000..64ea378 --- /dev/null +++ b/channel/channelapp/source/widgets.h @@ -0,0 +1,122 @@ +#ifndef _WIDGETS_H_ +#define _WIDGETS_H_ + +#include + +#include "gfx.h" +#include "cursors.h" +#include "appentry.h" +#include "font.h" + +#define WF_VISIBLE 1 +#define WF_ENABLED 2 +#define WF_FOCUSABLE 4 +#define WF_RUMBLE 8 + +#define WF_CURSOR 32 +#define WF_FOCUSED 64 + +#define WF_FLAGS_MASK 0xff +#define WF_FLAGS_OR 0 +#define WF_FLAGS_AND 256 + +typedef enum { + WT_LABEL, + WT_IMAGE, + WT_BUTTON, + WT_APP_ENTRY, + WT_PROGRESS, + WT_GRADIENT, + WT_MEMO +} widget_type; + +typedef struct { + u32 flags; + u32 flags_invert; + u16 gfx_entry_count; + gfx_queue_entry *gfx_entries; +} widget_layer; + +typedef struct { + widget_type type; + u32 flags; + u16 width, height; + + cursor_type cur; + + gfx_coordinates coords; + gfx_queue_entry qe_coords_push, qe_coords_pop; + + int layer_count; + widget_layer *layers; + + union { + struct { + gfx_entity *mask; + } image; + + struct { + u32 max; + gfx_entity *gradient; + } progress; + + struct { + gfx_entity *gradient; + } gradient; + + struct { + s16 y_min, y_max; + } memo; + }; +} widget; + +typedef enum { + BTN_NORMAL = 0, + BTN_SMALL, + BTN_TINY +} button_size; + +void widgets_init (void); +void widgets_theme_reinit (void); +void widgets_deinit (void); + +void widget_free (widget *w); + +void widget_set_flag (widget *w, u32 flag, bool set); +void widget_toggle_flag (widget *w, u32 flag); +void widget_set_progress (widget *w, u32 progress); +void widget_fade_gradient (widget *w, s8 modifier); +bool widget_scroll_memo (widget *w, s16 modifier); +bool widget_scroll_memo_deco (widget *w, s16 modifier); + +void widget_label (widget *w, s16 x, s16 y, s16 z, const char *caption, + u16 width, font_xalign xalign, font_yalign yalign, + font_id font); + +void widget_image(widget *w, s16 x, s16 y, s16 z, + gfx_entity *image, gfx_entity *image_disabled, + bool rumble, gfx_entity *image_cursor); + +void widget_button (widget *w, s16 x, s16 y, s16 z, + button_size size, const char *caption); +void widget_button_set_caption(widget *w, font_id font, const char *caption); + +void widget_app_entry (widget *w, s16 x, s16 y, s16 z, + const app_entry *entry); +void widget_grid_app_entry (widget *w, s16 x, s16 y, s16 z, + const app_entry *entry); + +void widget_progress (widget *w, s16 x, s16 y, s16 z, + const char *caption, u32 max); +void widget_gradient (widget *w, s16 x, s16 y, s16 z, + u16 width, u16 height, + u32 c1, u32 c2, u32 c3, u32 c4); +void widget_memo (widget *w, s16 x, s16 y, s16 z, + u16 width, u16 height, + const char *text, font_xalign align); +void widget_memo_deco (widget *w, s16 x, s16 y, s16 z, + u16 width, u16 height, + const char *text, font_xalign align); + +#endif + diff --git a/channel/channelapp/source/xml.c b/channel/channelapp/source/xml.c new file mode 100644 index 0000000..4634965 --- /dev/null +++ b/channel/channelapp/source/xml.c @@ -0,0 +1,612 @@ +#include +#include +#include +#include + +#include +#include +#include + +#include "../config.h" +#include "title.h" +#include "isfs.h" +#include "xml.h" +#include "panic.h" + +#include "banner_bin.h" + +#define FN_BANNER "banner.bin" +#define FN_SETTINGS "settings.xml" +#define MAX_SETTINGS_XML_SIZE (16 * 1024) + +settings_t settings; +theme_t theme; + +static settings_t _loaded_settings; +static bool write_banner = true; + +static inline char *_xmldup(const char *str) { + if (!str) + return NULL; + + return pstrdup(str); +} + +static char *_get_cdata(mxml_node_t *node) { + if (!node) + return NULL; + + mxml_node_t *n = node->child; + + while (n) { + if (n->type == MXML_OPAQUE) + return n->value.opaque; + + n = mxmlWalkNext(n, node, MXML_NO_DESCEND); + } + + return NULL; +} + +static char *_get_elem_cdata(mxml_node_t *node, const char *element) { + if (!node) + return NULL; + + return _get_cdata(mxmlFindElement(node, node, element, + NULL, NULL, MXML_DESCEND_FIRST)); +} + +static int _get_elem_int(mxml_node_t *node, const char *element, int std) { + char *cdata; + + if (!node) + return std; + + cdata = _get_cdata(mxmlFindElement(node, node, element, + NULL, NULL, MXML_DESCEND_FIRST)); + + if (!cdata) + return std; + + return atoi(cdata); +} + +static bool _get_color(mxml_node_t *node, const char *element, u32 *color) { + u32 res, i; + int c; + const char *const channel[] = { "red", "green", "blue", "alpha" }; + + if (!node) + return false; + + node = mxmlFindElement(node, node, element, NULL, NULL, MXML_DESCEND_FIRST); + + if (!node) + return false; + + res = 0; + for (i = 0; i < 4; ++i) { + res <<= 8; + c = _get_elem_int(node, channel[i], -1); + if ((c < 0) || (c > 255)) + return false; + res |= c & 0xff; + } + + *color = res; + return true; +} + +static bool _get_gradient(mxml_node_t *node, const char *element, + theme_gradient_t *gradient) { + u32 i, c[4]; + const char *const corner[] = { + "upper_left", "upper_right", "lower_right", "lower_left" + }; + + if (!node) + return false; + + node = mxmlFindElement(node, node, element, NULL, NULL, MXML_DESCEND_FIRST); + + if (!node) + return false; + + for (i = 0; i < 4; ++i) + if (!_get_color(node, corner[i], &c[i])) + return false; + + gradient->ul = c[0]; + gradient->ur = c[1]; + gradient->ll = c[2]; + gradient->lr = c[3]; + + return true; +} + +// sync with font.h +static const char *targets[FONT_MAX] = { + "label", + "dlgtitle", + "memo", + "appname", + "appdesc", + "button", + "button_desel" +}; + +static void _get_font(mxml_node_t *node) { + theme_font_t f; + f.file = _xmldup(_get_elem_cdata(node, "file")); + f.size = _get_elem_int(node, "size", 0); + f.color = NO_COLOR; + _get_color(node, "color", &f.color); + + const char *t = mxmlElementGetAttr(node, "target"); + if (t && t[0]) { + char *trg = pstrdup(t); + char *tok; + tok = strtok(trg, ", "); + while (tok) { + int i; + for (i=0; i ARGS_MAX_LEN) + return NULL; + + char *ret; + + ret = pmalloc(len); + + len = 0; + + for (n = mxmlFindElement(node, node, "arg", NULL, NULL, MXML_DESCEND_FIRST); + n != NULL; n = mxmlFindElement(n, node, "arg", NULL, NULL, + MXML_NO_DESCEND)) { + arg = _get_cdata(n); + + if (arg) { + if (len) { + ret[len] = 0; + len++; + } + + strcpy(ret + len, arg); + len += strlen(arg); + } + } + + ret[len] = 0; + + *length = len; + + return ret; +} + +meta_info *meta_parse(char *fn) { + int fd; + mxml_node_t *root, *node; + meta_info *res; + char *s; + struct tm t; + + fd = open(fn, O_RDONLY); + if (fd < 0) { + gprintf("error opening '%s'\n", fn); + return NULL; + } + + root = mxmlLoadFd(NULL, fd, MXML_OPAQUE_CALLBACK); + + close(fd); + + if (!root) { + gprintf("error parsing '%s'\n", fn); + return NULL; + } + + node = mxmlFindElement(root, root, "app", NULL, NULL, MXML_DESCEND_FIRST); + + if (!node) { + gprintf("no app node '%s'\n", fn); + mxmlDelete(root); + return NULL; + } + + res = (meta_info *) pmalloc(sizeof(meta_info)); + memset(res, 0, sizeof(meta_info)); + + res->name = _xmldup(_get_elem_cdata(node, "name")); + res->coder = _xmldup(_get_elem_cdata(node, "author")); + if (!res->coder) + res->coder = _xmldup(_get_elem_cdata(node, "coder")); + res->version = _xmldup(_get_elem_cdata(node, "version")); + res->short_description = _xmldup(_get_elem_cdata(node, "short_description")); + res->long_description = _xmldup(_get_elem_cdata(node, "long_description")); + + s = _get_elem_cdata(node, "release_date"); + + if (s) { + memset(&t, 0, sizeof(struct tm)); + if (sscanf(s, "%4d%2d%2d%2d%2d%2d", &t.tm_year, &t.tm_mon, &t.tm_mday, + &t.tm_hour, &t.tm_min, &t.tm_sec) == 6) { + t.tm_year -= 1900; + + res->release_date = mktime (&t); + } else if (sscanf(s, "%4d%2d%2d", &t.tm_year, &t.tm_mon, &t.tm_mday) == 3) { + t.tm_hour = 1; + t.tm_min = 0; + t.tm_sec = 0; + t.tm_year -= 1900; + + res->release_date = mktime (&t); + } + } + + res->args = _get_args(&res->argslen, node, "arguments"); + + if (mxmlFindElement(node, node, "ahb_access", NULL, NULL, + MXML_DESCEND_FIRST)) + res->ahb_access = true; + if (mxmlFindElement(node, node, "no_ios_reload", NULL, NULL, + MXML_DESCEND_FIRST)) + res->ahb_access = true; + + mxmlDelete(root); + + return res; +} + +void meta_free(meta_info *info) { + if (!info) + return; + + free(info->name); + free(info->coder); + free(info->version); + free(info->short_description); + free(info->long_description); + free(info->args); + + free(info); +} + +update_info *update_parse(char *buf) { + mxml_node_t *root, *node; + char *s; + update_info *res; + + root = mxmlLoadString(NULL, buf, MXML_OPAQUE_CALLBACK); + + if (!root) { + gprintf("error parsing update.xml!\n"); + return NULL; + } + + node = mxmlFindElement(root, root, "app", NULL, NULL, MXML_DESCEND_FIRST); + + if (!node) { + gprintf("no app node for update.xml!'\n"); + mxmlDelete(root); + return NULL; + } + + res = (update_info *) pmalloc(sizeof(update_info)); + memset(res, 0, sizeof(update_info)); + + res->version = _xmldup(_get_elem_cdata(node, "version")); + + s = _get_elem_cdata(node, "date"); + if (s) { + if (sscanf(s, "%llu", &res->date) != 1) + res->date = 0; + } + + res->notes = _xmldup(_get_elem_cdata(node, "notes")); + res->uri = _xmldup(_get_elem_cdata(node, "uri")); + res->hash = _xmldup(_get_elem_cdata(node, "hash")); + + mxmlDelete(root); + + return res; +} + +void update_free(update_info *info) { + if (!info) + return; + + free(info->version); + free(info->notes); + free(info->uri); + + free(info); +} + +bool settings_load(void) { + s32 res; + u8 *buf; + mxml_node_t *root, *node; + char *s; + const char *titlepath; + STACK_ALIGN(char, fn, ISFS_MAXPATH, 32); + + settings.device = -1; + settings.sort_order = 0; + settings.browse_mode = 0; + memset(settings.app_sel, 0, sizeof(settings.app_sel)); + + memcpy(&_loaded_settings, &settings, sizeof(settings_t)); + + titlepath = title_get_path(); + if (!titlepath[0]) + return false; + + sprintf(fn, "%s/" FN_BANNER, titlepath); + + buf = NULL; + res = isfs_get(fn, &buf, banner_bin_size, 0, false); + if (res < 0) { + gprintf("deleting banner: %ld\n", res); + ISFS_Delete(fn); + } else if (memcmp(buf, banner_bin, banner_bin_size)) { + gprintf("banner.bin mismatch, deleting\n"); + ISFS_Delete(fn); + } else { + write_banner = false; + } + + if (buf) { + free(buf); + buf = NULL; + } + + sprintf(fn, "%s/" FN_SETTINGS, titlepath); + res = isfs_get(fn, &buf, 0, MAX_SETTINGS_XML_SIZE, false); + if (res < 0) { + gprintf("isfs_get failed: %ld\n", res); + return false; + } + + root = mxmlLoadString(NULL, (char *) buf, MXML_OPAQUE_CALLBACK); + free(buf); + buf = NULL; + + if (!root) { + gprintf("error parsing settings.xml!\n"); + return false; + } + + node = mxmlFindElement(root, root, "hbc", NULL, NULL, MXML_DESCEND_FIRST); + + if (!node) { + gprintf("no hbc node for settings.xml!'\n"); + mxmlDelete(root); + return false; + } + + settings.device = _get_elem_int(node, "device", -1); + settings.sort_order = _get_elem_int(node, "sort_order", 0); + settings.browse_mode = _get_elem_int(node, "browse_mode", 0); + s = _get_elem_cdata(node, "app_sel"); + if (s) { + strncpy(settings.app_sel, s, sizeof(settings.app_sel)); + settings.app_sel[sizeof(settings.app_sel) - 1] = 0; + } + + mxmlDelete(root); + + memcpy(&_loaded_settings, &settings, sizeof(settings_t)); + //hexdump(&_loaded_settings,sizeof(settings_t)); + + return true; +} + +bool settings_save(void) { + s32 res; + char *x; + const char *titlepath; + int size; + + STACK_ALIGN(char, fn, ISFS_MAXPATH, 32); + + titlepath = title_get_path(); + if (!titlepath[0]) + return false; + + if (!memcmp(&_loaded_settings, &settings, sizeof(settings_t))) + return false; + + //hexdump(&settings,sizeof(settings_t)); + x = (char *) pmemalign(32, MAX_SETTINGS_XML_SIZE); + + mxml_node_t *xml; + mxml_node_t *data; + mxml_node_t *node; + + xml = mxmlNewXML("1.0"); + + data = mxmlNewElement(xml, "hbc"); + + node = mxmlNewElement(data, "device"); + mxmlNewInteger(node, settings.device); + + node = mxmlNewElement(data, "sort_order"); + mxmlNewInteger(node, settings.sort_order); + + node = mxmlNewElement(data, "browse_mode"); + mxmlNewInteger(node, settings.browse_mode); + + if (strlen(settings.app_sel) > 0) { + node = mxmlNewElement(data, "app_sel"); + mxmlNewText(node, 0, settings.app_sel); + } + + size = mxmlSaveString (xml, x, MAX_SETTINGS_XML_SIZE, MXML_NO_CALLBACK); + x[MAX_SETTINGS_XML_SIZE - 1] = 0; + + if ((size < 16) || (size > MAX_SETTINGS_XML_SIZE - 1)) { + gprintf("mxmlSaveString failed!\n"); + free(x); + return false; + } + + if (write_banner) { + sprintf(fn, "%s/" FN_BANNER, titlepath); + res = isfs_put(fn, banner_bin, banner_bin_size); + if ((res < 0) && (res != -105)) { + gprintf("isfs_put banner failed: %ld\n", res); + free(x); + return false; + } + } + + sprintf(fn, "%s/" FN_SETTINGS, titlepath); + + res = isfs_put(fn, x, strlen(x)); + if (res < 0) { + gprintf("isfs_put xml failed: %ld\n", res); + free(x); + sprintf(fn, "%s/" FN_BANNER, titlepath); + ISFS_Delete(fn); + return false; + } + + free(x); + return true; +} + +void theme_xml_init(void) { + memset(&theme, 0, sizeof(theme)); +} + +bool load_theme_xml(char *buf) { + int i; + mxml_node_t *root, *node, *fnode; + + // free prior theme + if (theme.description) + free(theme.description); + if (theme.default_font.file) + free(theme.default_font.file); + for (i=0; i 64)) + theme.description[64] = 0; + + theme.default_font.color = NO_COLOR; + theme.default_font.file = NULL; + theme.default_font.size = 0; + for (i=0; i + +#include + +#include "font.h" + +#define NO_COLOR 0x0DEADF00 + +typedef struct { + char *name; + char *coder; + char *version; + time_t release_date; + char *short_description; + char *long_description; + char *args; + u16 argslen; + bool ahb_access; +} meta_info; + +typedef struct { + char *version; + u64 date; + char *notes; + char *uri; + char *hash; +} update_info; + +typedef struct { + int device; + int sort_order; + int browse_mode; + char app_sel[64]; +} settings_t; + +typedef struct { + u32 ul, ur, lr, ll; +} theme_gradient_t; + +typedef struct { + char *file; + int size; + u32 color; +} theme_font_t; + +typedef enum { + TLANG_JA = 0x01, + TLANG_KO = 0x02, + TLANG_ZH = 0x04, +} theme_lang_t; + +typedef struct { + theme_gradient_t progress; + char *description; + theme_font_t default_font; + theme_font_t fonts[FONT_MAX]; + theme_lang_t langs; +} theme_t; + +extern settings_t settings; +extern theme_t theme; + +meta_info *meta_parse(char *fn); +void meta_free(meta_info *info); + +update_info *update_parse(char *buf); +void update_free(update_info *info); + +bool settings_load(void); +bool settings_save(void); + +void theme_xml_init(void); +bool load_theme_xml(char *buf); + +#endif + diff --git a/channel/channelapp/stub/asm.h b/channel/channelapp/stub/asm.h new file mode 100644 index 0000000..3404fd4 --- /dev/null +++ b/channel/channelapp/stub/asm.h @@ -0,0 +1,340 @@ +// this file was taken from libogc, see http://www.devkitpro.org/ + +#ifndef __ASM_H__ +#define __ASM_H__ + +#ifdef _LANGUAGE_ASSEMBLY +/* Condition Register Bit Fields */ + +#define cr0 0 +#define cr1 1 +#define cr2 2 +#define cr3 3 +#define cr4 4 +#define cr5 5 +#define cr6 6 +#define cr7 7 + + +/* General Purpose Registers (GPRs) */ + +#define r0 0 +#define r1 1 +#define sp 1 +#define r2 2 +#define toc 2 +#define r3 3 +#define r4 4 +#define r5 5 +#define r6 6 +#define r7 7 +#define r8 8 +#define r9 9 +#define r10 10 +#define r11 11 +#define r12 12 +#define r13 13 +#define r14 14 +#define r15 15 +#define r16 16 +#define r17 17 +#define r18 18 +#define r19 19 +#define r20 20 +#define r21 21 +#define r22 22 +#define r23 23 +#define r24 24 +#define r25 25 +#define r26 26 +#define r27 27 +#define r28 28 +#define r29 29 +#define r30 30 +#define r31 31 + + +/* Floating Point Registers (FPRs) */ + +#define fr0 0 +#define fr1 1 +#define fr2 2 +#define fr3 3 +#define fr4 4 +#define fr5 5 +#define fr6 6 +#define fr7 7 +#define fr8 8 +#define fr9 9 +#define fr10 10 +#define fr11 11 +#define fr12 12 +#define fr13 13 +#define fr14 14 +#define fr15 15 +#define fr16 16 +#define fr17 17 +#define fr18 18 +#define fr19 19 +#define fr20 20 +#define fr21 21 +#define fr22 22 +#define fr23 23 +#define fr24 24 +#define fr25 25 +#define fr26 26 +#define fr27 27 +#define fr28 28 +#define fr29 29 +#define fr30 30 +#define fr31 31 + +#define vr0 0 +#define vr1 1 +#define vr2 2 +#define vr3 3 +#define vr4 4 +#define vr5 5 +#define vr6 6 +#define vr7 7 +#define vr8 8 +#define vr9 9 +#define vr10 10 +#define vr11 11 +#define vr12 12 +#define vr13 13 +#define vr14 14 +#define vr15 15 +#define vr16 16 +#define vr17 17 +#define vr18 18 +#define vr19 19 +#define vr20 20 +#define vr21 21 +#define vr22 22 +#define vr23 23 +#define vr24 24 +#define vr25 25 +#define vr26 26 +#define vr27 27 +#define vr28 28 +#define vr29 29 +#define vr30 30 +#define vr31 31 + +#endif //_LANGUAGE_ASSEMBLY + +#define SPRG0 272 +#define SPRG1 273 +#define SPRG2 274 +#define SPRG3 275 + +#define PMC1 953 +#define PMC2 954 +#define PMC3 957 +#define PMC4 958 + +#define MMCR0 952 +#define MMCR1 956 + + +#define LINK_REGISTER_CALLEE_UPDATE_ROOM 4 +#define EXCEPTION_NUMBER 8 +#define SRR0_OFFSET 12 +#define SRR1_OFFSET 16 +#define GPR0_OFFSET 20 +#define GPR1_OFFSET 24 +#define GPR2_OFFSET 28 +#define GPR3_OFFSET 32 +#define GPR4_OFFSET 36 +#define GPR5_OFFSET 40 +#define GPR6_OFFSET 44 +#define GPR7_OFFSET 48 +#define GPR8_OFFSET 52 +#define GPR9_OFFSET 56 +#define GPR10_OFFSET 60 +#define GPR11_OFFSET 64 +#define GPR12_OFFSET 68 +#define GPR13_OFFSET 72 +#define GPR14_OFFSET 76 +#define GPR15_OFFSET 80 +#define GPR16_OFFSET 84 +#define GPR17_OFFSET 88 +#define GPR18_OFFSET 92 +#define GPR19_OFFSET 96 +#define GPR20_OFFSET 100 +#define GPR21_OFFSET 104 +#define GPR22_OFFSET 108 +#define GPR23_OFFSET 112 +#define GPR24_OFFSET 116 +#define GPR25_OFFSET 120 +#define GPR26_OFFSET 124 +#define GPR27_OFFSET 128 +#define GPR28_OFFSET 132 +#define GPR29_OFFSET 136 +#define GPR30_OFFSET 140 +#define GPR31_OFFSET 144 + +#define GQR0_OFFSET 148 +#define GQR1_OFFSET 152 +#define GQR2_OFFSET 156 +#define GQR3_OFFSET 160 +#define GQR4_OFFSET 164 +#define GQR5_OFFSET 168 +#define GQR6_OFFSET 172 +#define GQR7_OFFSET 176 + +#define CR_OFFSET 180 +#define LR_OFFSET 184 +#define CTR_OFFSET 188 +#define XER_OFFSET 192 +#define MSR_OFFSET 196 +#define DAR_OFFSET 200 + +#define STATE_OFFSET 204 +#define MODE_OFFSET 206 + +#define FPR0_OFFSET 208 +#define FPR1_OFFSET 216 +#define FPR2_OFFSET 224 +#define FPR3_OFFSET 232 +#define FPR4_OFFSET 240 +#define FPR5_OFFSET 248 +#define FPR6_OFFSET 256 +#define FPR7_OFFSET 264 +#define FPR8_OFFSET 272 +#define FPR9_OFFSET 280 +#define FPR10_OFFSET 288 +#define FPR11_OFFSET 296 +#define FPR12_OFFSET 304 +#define FPR13_OFFSET 312 +#define FPR14_OFFSET 320 +#define FPR15_OFFSET 328 +#define FPR16_OFFSET 336 +#define FPR17_OFFSET 344 +#define FPR18_OFFSET 352 +#define FPR19_OFFSET 360 +#define FPR20_OFFSET 368 +#define FPR21_OFFSET 376 +#define FPR22_OFFSET 384 +#define FPR23_OFFSET 392 +#define FPR24_OFFSET 400 +#define FPR25_OFFSET 408 +#define FPR26_OFFSET 416 +#define FPR27_OFFSET 424 +#define FPR28_OFFSET 432 +#define FPR29_OFFSET 440 +#define FPR30_OFFSET 448 +#define FPR31_OFFSET 456 + +#define FPSCR_OFFSET 464 + +#define PSR0_OFFSET 472 +#define PSR1_OFFSET 480 +#define PSR2_OFFSET 488 +#define PSR3_OFFSET 496 +#define PSR4_OFFSET 504 +#define PSR5_OFFSET 512 +#define PSR6_OFFSET 520 +#define PSR7_OFFSET 528 +#define PSR8_OFFSET 536 +#define PSR9_OFFSET 544 +#define PSR10_OFFSET 552 +#define PSR11_OFFSET 560 +#define PSR12_OFFSET 568 +#define PSR13_OFFSET 576 +#define PSR14_OFFSET 584 +#define PSR15_OFFSET 592 +#define PSR16_OFFSET 600 +#define PSR17_OFFSET 608 +#define PSR18_OFFSET 616 +#define PSR19_OFFSET 624 +#define PSR20_OFFSET 632 +#define PSR21_OFFSET 640 +#define PSR22_OFFSET 648 +#define PSR23_OFFSET 656 +#define PSR24_OFFSET 664 +#define PSR25_OFFSET 672 +#define PSR26_OFFSET 680 +#define PSR27_OFFSET 688 +#define PSR28_OFFSET 696 +#define PSR29_OFFSET 704 +#define PSR30_OFFSET 712 +#define PSR31_OFFSET 720 +/* + * maintain the EABI requested 8 bytes aligment + * As SVR4 ABI requires 16, make it 16 (as some + * exception may need more registers to be processed...) + */ +#define EXCEPTION_FRAME_END 728 + +#define IBAT0U 528 +#define IBAT0L 529 +#define IBAT1U 530 +#define IBAT1L 531 +#define IBAT2U 532 +#define IBAT2L 533 +#define IBAT3U 534 +#define IBAT3L 535 +#define IBAT4U 560 +#define IBAT4L 561 +#define IBAT5U 562 +#define IBAT5L 563 +#define IBAT6U 564 +#define IBAT6L 565 +#define IBAT7U 566 +#define IBAT7L 567 + +#define DBAT0U 536 +#define DBAT0L 537 +#define DBAT1U 538 +#define DBAT1L 538 +#define DBAT2U 540 +#define DBAT2L 541 +#define DBAT3U 542 +#define DBAT3L 543 +#define DBAT4U 568 +#define DBAT4L 569 +#define DBAT5U 570 +#define DBAT5L 571 +#define DBAT6U 572 +#define DBAT6L 573 +#define DBAT7U 574 +#define DBAT7L 575 + +#define HID0 1008 +#define HID1 1009 +#define HID2 920 +#define HID4 1011 + +#define GQR0 912 +#define GQR1 913 +#define GQR2 914 +#define GQR3 915 +#define GQR4 916 +#define GQR5 917 +#define GQR6 918 +#define GQR7 919 + +#define L2CR 1017 + +#define WPAR 921 + +#define DMAU 922 +#define DMAL 923 + +#define MSR_RI 0x00000002 +#define MSR_DR 0x00000010 +#define MSR_IR 0x00000020 +#define MSR_IP 0x00000040 +#define MSR_SE 0x00000400 +#define MSR_ME 0x00001000 +#define MSR_FP 0x00002000 +#define MSR_POW 0x00004000 +#define MSR_EE 0x00008000 + +#define PPC_ALIGNMENT 8 + +#define PPC_CACHE_ALIGNMENT 32 + +#endif //__ASM_H__ diff --git a/channel/channelapp/stub/cache.h b/channel/channelapp/stub/cache.h new file mode 100644 index 0000000..e75ac18 --- /dev/null +++ b/channel/channelapp/stub/cache.h @@ -0,0 +1,310 @@ +/*------------------------------------------------------------- + + +cache.h -- Cache interface + +Copyright (C) 2004 +Michael Wiedenbauer (shagkur) +Dave Murphy (WinterMute) + +This software is provided 'as-is', without any express or implied +warranty. In no event will the authors be held liable for any +damages arising from the use of this software. + +Permission is granted to anyone to use this software for any +purpose, including commercial applications, and to alter it and +redistribute it freely, subject to the following restrictions: + +1. The origin of this software must not be misrepresented; you +must not claim that you wrote the original software. If you use +this software in a product, an acknowledgment in the product +documentation would be appreciated but is not required. + +2. Altered source versions must be plainly marked as such, and +must not be misrepresented as being the original software. + +3. This notice may not be removed or altered from any source +distribution. + + +-------------------------------------------------------------*/ + + +#ifndef __CACHE_H__ +#define __CACHE_H__ + +/*! \file cache.h +\brief Cache subsystem + +*/ + +#include + +#define LC_BASEPREFIX 0xe000 +#define LC_BASE (LC_BASEPREFIX<<16) + +#ifdef __cplusplus + extern "C" { +#endif /* __cplusplus */ + + +/*! + * \fn void DCEnable() + * \brief Enable L1 d-cache + * + * \return none + */ +void DCEnable(); + + +/*! + * \fn void DCDisable() + * \brief Disable L1 d-cache + * + * \return none + */ +void DCDisable(); + + +/*! + * \fn void DCFreeze() + * \brief Current contents of the L1 d-cache are locked down and will not be cast out. + * + * Hits are still serviced, but misses go straight to L2 or 60x bus. Most cache operations, such as DCFlushRange(), will still execute regardless of whether the cache is frozen.
+ * NOTE: In PowerPC architecture jargon, this feature is referred to as "locking" the data cache. We use the word "freeze" to distinguish it from the locked cache and DMA features. + * + * \return none + */ +void DCFreeze(); + + +/*! + * \fn void DCUnfreeze() + * \brief Undoes actions of DCFreeze(). + * + * Old cache blocks will now be cast out on subsequent L1 misses.
+ * NOTE: In PowerPC architecture jargon, this feature is referred to as "locking" the data cache. We use the word "freeze" to distinguish it from the locked cache and DMA features. + * + * \return none + */ +void DCUnfreeze(); + + +/*! + * \fn void DCFlashInvalidate() + * \brief Invalidate L1 d-cache. + * + * An invalidate operation is issued that marks the state of each data cache block as invalid without writing back modified cache blocks to memory.
+ * Cache access is blocked during this time.Bus accesses to the cache are signaled as a miss during invalidate-all operations. + * + * \return none + */ +void DCFlashInvalidate(); + + +/*! + * \fn void DCInvalidateRange(void *startaddress,u32 len) + * \brief Invalidates a given range of the d-cache. + * + * If any part of the range hits in the d-cache, the corresponding block will be invalidated. + * + * \param[in] startaddress pointer to the startaddress of the memory range to invalidate. NOTE: Has to be aligned on a 32byte boundery + * \param[in] len length of the range to invalidate. NOTE: Should be a multiple of 32 + * + * \return none + */ +void DCInvalidateRange(void *startaddress,u32 len); + + +/*! + * \fn void DCFlushRange(void *startaddress,u32 len) + * \brief Flushes a given range. + * + * If any part of the range hits in the d-cache the corresponding block will be flushed to main memory and invalidated.
+ * NOTE: This function invokes a "sync" after flushing the range. This means the function will stall until the CPU knows that the data has been writen to main memory + * + * \param[in] startaddress pointer to the startaddress of the memory range to flush. NOTE: Has to be aligned on a 32byte boundery + * \param[in] len length of range to be flushed. NOTE: Should be a multiple of 32 + * + *\return none + */ +void DCFlushRange(void *startaddress,u32 len); + +/*! + * \fn void DCStoreRange(void *startaddress,u32 len) + * \brief Ensures a range of memory is updated with any modified data in the cache. + * + * NOTE: This function invokes a "sync" after storing the range. This means the function will stall until the CPU knows that the data has been writen to main memory + * + * \param[in] startaddress pointer to the startaddress of the memory range to store. NOTE: Has to be aligned on a 32byte boundery + * \param[in] len length of the range to store. NOTE: Should be a multiple of 32 + * + * \return none + */ +void DCStoreRange(void *startaddress,u32 len); + + +/*! + * \fn void DCFlushRangeNoSync(void *startaddress,u32 len) + * \brief Flushes a given range. + * + * If any part of the range hits in the d-cache the corresponding block will be flushed to main memory and invalidated.
+ * NOTE: This routine does not perform a "sync" to ensure that the range has been flushed to memory. That is, the cache blocks are sent to the bus interface unit for storage to main memory, but by the time this function returns, you are not guaranteed that the blocks have been written to memory. + * + * \param[in] startaddress pointer to the startaddress of the memory range to flush. NOTE: Has to be aligned on a 32byte boundery + * \param[in] len length of range to be flushed. NOTE: Should be a multiple of 32 + * + * \return none + */ +void DCFlushRangeNoSync(void *startaddress,u32 len); + + +/*! + * \fn void DCStoreRangeNoSync(void *startaddress,u32 len) + * \brief Ensures a range of memory is updated with any modified data in the cache. + * + * NOTE: This routine does not perform a "sync" to ensure that the range has been flushed to memory. That is, the cache blocks are sent to the bus interface unit for storage to main memory, but by the time this function returns, you are not guaranteed that the blocks have been written to memory + * + * \param[in] startaddress pointer to the startaddress of the memory range to store. NOTE: Has to be aligned on a 32byte boundery + * \param[in] len length of the range to store. NOTE: Should be a multiple of 32 + * + * \return none + */ +void DCStoreRangeNoSync(void *startaddress,u32 len); + + +/*! + * \fn void DCZeroRange(void *startaddress,u32 len) + * \brief Loads a range of memory into cache and zeroes all the cache lines. + * + * \param[in] startaddress pointer to the startaddress of the memory range to load/zero. NOTE: Has to be aligned on a 32byte boundery + * \param[in] len length of the range to load/zero. NOTE: Should be a multiple of 32 + * + * \return none + */ +void DCZeroRange(void *startaddress,u32 len); + + +/*! + * \fn void DCTouchRange(void *startaddress,u32 len) + * \brief Loads a range of memory into cache. + * + * \param[in] startaddress pointer to the startaddress of the memory range to load. NOTE: Has to be aligned on a 32byte boundery + * \param[in] len length of the range to load. NOTE: Should be a multiple of 32 + * + * \return none + */ +void DCTouchRange(void *startaddress,u32 len); + + +/*! + * \fn void ICSync() + * \brief Performs an instruction cache synchronization. + * + * This ensures that all instructions preceding this instruction have completed before this instruction completes. + * + * \return none + */ +void ICSync(); + + +/*! + * \fn void ICFlashInvalidate() + * \brief Invalidate the L1 i-cache. + * + * An invalidate operation is issued that marks the state of each instruction cache block as invalid without writing back modified cache blocks to memory.
+ * Cache access is blocked during this time. Bus accesses to the cache are signaled as a miss during invalidate-all operations. + * + * \return none + */ +void ICFlashInvalidate(); + + +/*! + * \fn void ICEnable() + * \brief Enable L1 i-cache + * + * \return none + */ +void ICEnable(); + + +/*! + * \fn void ICDisable() + * \brief Disable L1 i-cache + * + * \return none + */ +void ICDisable(); + + +/*! + * \fn void ICFreeze() + * \brief Current contents of the L1 i-cache are locked down and will not be cast out. + * + * Hits are still serviced, but misses go straight to L2 or 60x bus.
+ * NOTE: In PowerPC architecture jargon, this feature is referred to as "locking" the data cache. We use the word "freeze" to distinguish it from the locked cache and DMA features. + * + * \return none + */ +void ICFreeze(); + + +/*! + * \fn void ICUnfreeze() + * \brief Undoes actions of ICFreeze(). + * + * Old cache blocks will now be cast out on subsequent L1 misses.
+ * NOTE: In PowerPC architecture jargon, this feature is referred to as "locking" the data cache. We use the word "freeze" to distinguish it from the locked cache and DMA features. + * + * \return none + */ +void ICUnfreeze(); + + +/*! + * \fn void ICBlockInvalidate(void *startaddress) + * \brief Invalidates a block in the i-cache. + * + * If the block hits in the i-cache, the corresponding block will be invalidated. + * + * \param[in] startaddress pointer to the startaddress of the memory block to invalidate. NOTE: Has to be aligned on a 32byte boundery + * + *\return none + */ +void ICBlockInvalidate(void *startaddress); + + +/*! + * \fn void ICInvalidateRange(void *startaddress,u32 len) + * \brief Invalidate a range in the L1 i-cache. + * + * If any part of the range hits in the i-cache, the corresponding block will be invalidated. + * + * \param[in] startaddress pointer to the startaddress of the memory range to invalidate. NOTE: Has to be aligned on a 32byte boundery + * \param[in] len length of the range to invalidate. NOTE: Should be a multiple of 32 + * + * \return none + */ +void ICInvalidateRange(void *startaddress,u32 len); + +void LCEnable(); +void LCDisable(); +void LCLoadBlocks(void *,void *,u32); +void LCStoreBlocks(void *,void *,u32); +u32 LCLoadData(void *,void *,u32); +u32 LCStoreData(void *,void *,u32); +u32 LCQueueLength(); +u32 LCQueueWait(u32); +void LCFlushQueue(); +void LCAlloc(void *,u32); +void LCAllocNoInvalidate(void *,u32); +void LCAllocOneTag(BOOL,void *); +void LCAllocTags(BOOL,void *,u32); + +#define LCGetBase() ((void*)LC_BASE) +#ifdef __cplusplus + } +#endif /* __cplusplus */ + +#endif diff --git a/channel/channelapp/stub/cache_asm.S b/channel/channelapp/stub/cache_asm.S new file mode 100644 index 0000000..088ec62 --- /dev/null +++ b/channel/channelapp/stub/cache_asm.S @@ -0,0 +1,465 @@ +/*------------------------------------------------------------- + +cache_asm.S -- Cache interface + +Copyright (C) 2004 +Michael Wiedenbauer (shagkur) +Dave Murphy (WinterMute) + +This software is provided 'as-is', without any express or implied +warranty. In no event will the authors be held liable for any +damages arising from the use of this software. + +Permission is granted to anyone to use this software for any +purpose, including commercial applications, and to alter it and +redistribute it freely, subject to the following restrictions: + +1. The origin of this software must not be misrepresented; you +must not claim that you wrote the original software. If you use +this software in a product, an acknowledgment in the product +documentation would be appreciated but is not required. + +2. Altered source versions must be plainly marked as such, and +must not be misrepresented as being the original software. + +3. This notice may not be removed or altered from any source +distribution. + + +-------------------------------------------------------------*/ + + +#include "asm.h" + + .globl DCFlashInvalidate +DCFlashInvalidate: + mfspr r3,HID0 + ori r3,r3,0x0400 + mtspr HID0,r3 + blr + + .globl DCEnable +DCEnable: + sync + mfspr r3,HID0 + ori r3,r3,0x4000 + mtspr HID0,r3 + blr + +/* .globl DCDisable +DCDisable: + sync + mfspr r3,HID0 + rlwinm r3,r3,0,18,16 + mtspr HID0,r3 + blr +*/ +/* .globl DCFreeze +DCFreeze: + sync + mfspr r3,HID0 + ori r3,r3,0x1000 + mtspr HID0,r3 + blr + + .globl DCUnfreeze +DCUnfreeze: + mfspr r3,HID0 + rlwinm r3,r3,0,20,18 + mtspr HID0,r3 + blr + + .globl DCTouchLoad +DCTouchLoad: + dcbt r0,r3 + blr + + .globl DCBlockZero +DCBlockZero: + dcbz r0,r3 + blr + + .globl DCBlockStore +DCBlockStore: + dcbst r0,r3 + blr + + .globl DCBlockFlush +DCBlockFlush: + dcbf r0,r3 + blr + + .globl DCBlockInvalidate +DCBlockInvalidate: + dcbi r0,r3 + blr +*/ + .globl DCInvalidateRange +DCInvalidateRange: + cmplwi r4, 0 # zero or negative size? + blelr + clrlwi. r5, r3, 27 # check for lower bits set in address + beq 1f + addi r4, r4, 0x20 +1: + addi r4, r4, 0x1f + srwi r4, r4, 5 + mtctr r4 +2: + dcbi r0, r3 + addi r3, r3, 0x20 + bdnz 2b + blr + + .globl DCFlushRange +DCFlushRange: + cmplwi r4, 0 # zero or negative size? + blelr + clrlwi. r5, r3, 27 # check for lower bits set in address + beq 1f + addi r4, r4, 0x20 +1: + addi r4, r4, 0x1f + srwi r4, r4, 5 + mtctr r4 +2: + dcbf r0, r3 + addi r3, r3, 0x20 + bdnz 2b + sc + blr + +/* .globl DCStoreRange +DCStoreRange: + cmplwi r4, 0 # zero or negative size? + blelr + clrlwi. r5, r3, 27 # check for lower bits set in address + beq 1f + addi r4, r4, 0x20 +1: + addi r4, r4, 0x1f + srwi r4, r4, 5 + mtctr r4 +2: + dcbst r0, r3 + addi r3, r3, 0x20 + bdnz 2b + sc + blr +*/ + .globl DCFlushRangeNoSync +DCFlushRangeNoSync: + cmplwi r4, 0 # zero or negative size? + blelr + clrlwi. r5, r3, 27 # check for lower bits set in address + beq 1f + addi r4, r4, 0x20 +1: + addi r4, r4, 0x1f + srwi r4, r4, 5 + mtctr r4 +2: + dcbf r0, r3 + addi r3, r3, 0x20 + bdnz 2b + blr + +/* .globl DCStoreRangeNoSync +DCStoreRangeNoSync: + cmplwi r4, 0 # zero or negative size? + blelr + clrlwi. r5, r3, 27 # check for lower bits set in address + beq 1f + addi r4, r4, 0x20 +1: + addi r4, r4, 0x1f + srwi r4, r4, 5 + mtctr r4 +2: + dcbst r0, r3 + addi r3, r3, 0x20 + bdnz 2b + blr + + .globl DCZeroRange +DCZeroRange: + cmplwi r4, 0 # zero or negative size? + blelr + clrlwi. r5, r3, 27 # check for lower bits set in address + beq 1f + addi r4, r4, 0x20 +1: + addi r4, r4, 0x1f + srwi r4, r4, 5 + mtctr r4 +2: + dcbz r0, r3 + addi r3, r3, 0x20 + bdnz 2b + blr + + .globl DCTouchRange +DCTouchRange: + cmplwi r4, 0 # zero or negative size? + blelr + clrlwi. r5, r3, 27 # check for lower bits set in address + beq 1f + addi r4, r4, 0x20 +1: + addi r4, r4, 0x1f + srwi r4, r4, 5 + mtctr r4 +2: + dcbt r0, r3 + addi r3, r3, 0x20 + bdnz 2b + blr +*/ + .globl ICInvalidateRange +ICInvalidateRange: + cmplwi r4, 0 # zero or negative size? + blelr + clrlwi. r5, r3, 27 # check for lower bits set in address + beq 1f + addi r4, r4, 0x20 +1: + addi r4, r4, 0x1f + srwi r4, r4, 5 + mtctr r4 +2: + icbi r0, r3 + addi r3, r3, 0x20 + bdnz 2b + sync + isync + blr + + .globl ICFlashInvalidate +ICFlashInvalidate: + mfspr r3,HID0 + ori r3,r3,0x0800 + mtspr HID0,r3 + blr + + .globl ICEnable +ICEnable: + isync + mfspr r3,HID0 + ori r3,r3,0x8000 + mtspr HID0,r3 + blr + + .globl ICDisable +ICDisable: + isync + mfspr r3,HID0 + rlwinm r3,r3,0,17,15 + mtspr HID0,r3 + blr + +/* .globl ICFreeze +ICFreeze: + isync + mfspr r3,HID0 + ori r3,r3,0x2000 + mtspr HID0,r3 + blr + + .globl ICUnfreeze +ICUnfreeze: + mfspr r3,HID0 + rlwinm r3,r3,0,19,17 + mtspr HID0,r3 + blr + + .globl ICBlockInvalidate +ICBlockInvalidate: + icbi r0,r3 + blr +*/ + .globl ICSync +ICSync: + isync + blr + + .globl L2Init +L2Init: + mflr r0 + stw r0,4(sp) + stwu sp,-16(sp) + stw r31,12(sp) + mfmsr r3 + mr r31,r3 + sync + li r3,48 + mtmsr r3 + sync + bl L2Disable + bl L2GlobalInvalidate + mr r3,r31 + mtmsr r3 + lwz r0,20(sp) + lwz r31,12(sp) + mtlr r0 + blr + + .globl L2Enable +L2Enable: + mfspr r3,L2CR; + oris r0,r3,0x8000 + rlwinm r3,r0,0,11,9 + mtspr L2CR,r3 + blr + + .globl L2Disable +L2Disable: + sync + mfspr r3,L2CR + clrlwi r3,r3,1 + mtspr L2CR,r3 + sync + blr + + .globl L2GlobalInvalidate +L2GlobalInvalidate: + mflr r0 + stw r0,4(sp) + stwu sp,-8(sp) + bl L2Disable + mfspr r3,L2CR + oris r3,r3,0x0020 + mtspr L2CR,r3 +1: mfspr r3,L2CR + clrlwi r0,r3,31 + cmplwi r0,0x0000 + bne 1b + mfspr r3,L2CR + rlwinm r3,r3,0,11,9 + mtspr L2CR,r3 +2: mfspr r3,L2CR + clrlwi r0,r3,31 + cmplwi r0,0x0000 + bne 2b + lwz r0,12(sp) + addi sp,sp,8 + mtlr r0 + blr + +/* .globl __LCEnable +__LCEnable: + mfmsr r5 + ori r5,r5,0x1000 + mtmsr r5 + lis r3,0x8000 + li r4,1024 + mtctr r4 +1: dcbt r0,r3 + dcbst r0,r3 + bdnz 1b + mfspr r4,HID2 + oris r4,r4,0x100f + mtspr HID2,r4 + nop + nop + nop + nop + nop + nop + nop + nop + nop + nop + nop + nop + lis r3,0xe000 + ori r3,r3,0x0002 + mtspr DBAT3L,r3 + ori r3,r3,0x01fe + mtspr DBAT3U,r3 + isync + lis r3,0xe000 + li r6,512 + mtctr r6 + li r6,0 +2: dcbz_l r6,r3 + addi r3,r3,32 + bdnz 2b + nop + nop + nop + nop + nop + nop + nop + nop + nop + nop + nop + nop + blr + + .globl LCDisable +LCDisable: + lis r3,0xe000 + li r4,512 + mtctr r4 +1: dcbi r0,r3 + addi r3,r3,32 + bdnz 1b + mfspr r4,HID2 + rlwinm r4,r4,0,4,2 + mtspr HID2,r4 + blr + + .globl LCAllocOneTag +LCAllocOneTag: + cmpwi r3,0 + beq 1f + dcbi r0,r4 +1: dcbz_l r0,r4 + blr + + .globl LCAllocTags +LCAllocTags: + mflr r6 + cmplwi r5,0 + ble 2f + mtctr r5 + cmpwi r3,0 + beq 3f +1: dcbi r0,r4 + dcbz_l r0,r4 + addi r4,r4,32 + bdnz 1b + b 2f +3: dcbz_l r0,r4 + addi r4,r4,32 + bdnz 3b +2: mtlr r6 + blr + + .globl LCLoadBlocks +LCLoadBlocks: + extrwi r6,r5,5,25 + clrlwi r4,r4,4 + or r6,r6,r4 + mtspr DMAU,r6 + clrlslwi r6,r5,30,2 + or r6,r6,r3 + ori r6,r6,0x0012 + mtspr DMAL,r6 + blr + + .globl LCStoreBlocks +LCStoreBlocks: + extrwi r6,r5,5,25 + clrlwi r4,r4,4 + or r6,r6,r3 + mtspr DMAU,r6 + clrlslwi r6,r5,30,2 + or r6,r6,r4 + ori r6,r6,0x0002 + mtspr DMAL,r6 + blr + +*/ diff --git a/channel/channelapp/stub/crt0.S b/channel/channelapp/stub/crt0.S new file mode 100644 index 0000000..57ff0ec --- /dev/null +++ b/channel/channelapp/stub/crt0.S @@ -0,0 +1,207 @@ +// this file was taken from libogc, see http://www.devkitpro.org/ + +#include "asm.h" + +#define _SDA_BASE_ 32768 +#define _SDA2_BASE_ 32768 + + + .text + .section .init + + # crt0.s file for the GameCube V1.1 by Costis (costis@gbaemu.com)! + # + # Updates: Added support for clearing the BSS section so that global + # variables are cleared to 0 upon start-up. + # + # This is start-up code for initializing the GameCube system and hardware + # before executing the actual user program code. It clears the GPR's, + # initializes the FPR's, initializes the Data, Code, and L2 caches, clears + # and initializes SPR's, and disables exceptions (interrupts). + # + # Have fun!!! Please e-mail any suggestions or bugs to costis@gbaemu.com. + + # Entry Point + + .extern __PSInit + .extern __SyscallInit + .extern __CacheInit + .extern __SystemInit + .extern __sbss_start, __bss_end + .extern __bat_config + .extern _main + .globl _start, __main +_start: + b startup + .ascii "STUB" + .ascii "HAXX" + .long 0 +startup: + # Disable interrupts first thing + mfmsr r3 + rlwinm r4,r3,0,17,15 + rlwinm r4,r4,0,26,24 + mtmsr r4 + + # Go into real mode + isync + lis r3,real@h + ori r3,r3,real@l + clrlwi r3,r3,2 + mtsrr0 r3 + + mfmsr r3 + li r4,0x30 + andc r3,r3,r4 + mtsrr1 r3 + rfi + +real: + # Set up the BATs the way we like them + // HID0 = 00110c64: + // bus checkstops off, sleep modes off, + // caches off, caches invalidate, + // store gathering off, enable data cache + // flush assist, enable branch target cache, + // enable branch history table + lis 3,0x0011 ; ori 3,3,0x0c64 ; mtspr 1008,3 ; isync + + // MSR = 00002000 (FP on) + li 4,0x2000 ; mtmsr 4 + + // HID0 |= 0000c000 (caches on) + ori 3,3,0xc000 ; mtspr 1008,3 ; isync + + // clear all BATs + li 0,0 + mtspr 528,0 ; mtspr 530,0 ; mtspr 532,0 ; mtspr 534,0 // IBATU 0..3 + mtspr 536,0 ; mtspr 538,0 ; mtspr 540,0 ; mtspr 542,0 // DBATU 0..3 + mtspr 560,0 ; mtspr 562,0 ; mtspr 564,0 ; mtspr 566,0 // IBATU 4..7 + mtspr 568,0 ; mtspr 570,0 ; mtspr 572,0 ; mtspr 574,0 // DBATU 4..7 + isync + + // clear all SRs + lis 0,0x8000 + mtsr 0,0 ; mtsr 1,0 ; mtsr 2,0 ; mtsr 3,0 + mtsr 4,0 ; mtsr 5,0 ; mtsr 6,0 ; mtsr 7,0 + mtsr 8,0 ; mtsr 9,0 ; mtsr 10,0 ; mtsr 11,0 + mtsr 12,0 ; mtsr 13,0 ; mtsr 14,0 ; mtsr 15,0 + isync + + // set [DI]BAT0 for 256MB@80000000, + // real 00000000, WIMG=0000, R/W + li 3,2 ; lis 4,0x8000 ; ori 4,4,0x1fff + mtspr IBAT0L,3 ; mtspr IBAT0U,4 ; mtspr DBAT0L,3 ; mtspr DBAT0U,4 ; isync + + // set [DI]BAT4 for 256MB@90000000, + // real 10000000, WIMG=0000, R/W + addis 3,3,0x1000 ; addis 4,4,0x1000 + mtspr IBAT4L,3 ; mtspr IBAT4U,4 ; mtspr DBAT4L,3 ; mtspr DBAT4U,4 ; isync + + // set DBAT1 for 256MB@c0000000, + // real 00000000, WIMG=0101, R/W + li 3,0x2a ; lis 4,0xc000 ; ori 4,4,0x1fff + mtspr DBAT1L,3 ; mtspr DBAT1U,4 ; isync + + // set DBAT5 for 256MB@d0000000, + // real 10000000, WIMG=0101, R/W + addis 3,3,0x1000 ; addis 4,4,0x1000 + mtspr DBAT5L,3 ; mtspr DBAT5U,4 ; isync + + // enable [DI]BAT4-7 in HID4 + lis 3, 0x8200 + mtspr 1011,3 + + // set MSR[DR:IR] = 11, jump to _start + lis 3,virtual@h ; ori 3,3, virtual@l ; mtsrr0 3 + + mfmsr 3 ; ori 3,3,0x30 ; mtsrr1 3 + rfi + +virtual: + + bl InitGPRS # Initialize the General Purpose Registers + bl __CacheInit # Initialize the system caches + bl __SyscallInit # Initialize the System Call handler + bl __SystemInit # Initialize more cache aspects, clear a few SPR's, and disable interrupts. + + # Clear the BSS section! + lis r3,__sbss_start@h + ori r3,r3,__sbss_start@l + li r4,0 + lis r5,__bss_end@h + ori r5,r5,__bss_end@l + sub r5,r5,r3 + bl _memset + + bl _main # Branch to the user code! + +eloop: + b eloop # If the main function returns, which should never happen then just loop endlessly. + +InitGPRS: + # Clear all of the GPR's to 0 + li r0,0 + li r3,0 + li r4,0 + li r5,0 + li r6,0 + li r7,0 + li r8,0 + li r9,0 + li r10,0 + li r11,0 + li r12,0 + li r14,0 + li r15,0 + li r16,0 + li r17,0 + li r18,0 + li r19,0 + li r20,0 + li r21,0 + li r22,0 + li r23,0 + li r24,0 + li r25,0 + li r26,0 + li r27,0 + li r28,0 + li r29,0 + li r30,0 + li r31,0 + + lis sp,__crt0stack@h # we take 0x8173FFF0 as the topmost starting point for our stack,this gives us ~128Kb Stack + ori sp,sp,__crt0stack@l + addi sp,sp,-4 + stw r0,0(sp) + stwu sp,-56(sp) + + lis r2,_SDA2_BASE_@h + ori r2,r2,_SDA2_BASE_@l # Set the Small Data 2 (Read Only) base register. + lis r13,_SDA_BASE_@h + ori r13,r13,_SDA_BASE_@l # Set the Small Data (Read\Write) base register. + blr + + //r3 = ptr, r4 = fill, r5 = size + .globl _memset +_memset: + clrlwi. r6,r5,29 + srwi r5,r5,2 + subi r3,r3,4 + mtctr r5 +1: stwu r4,4(r3) + bdnz 1b + cmplwi r6,0 + beq 3f +2: stbu r4,1(r3) + addic. r6,r6,-1 + bne+ 2b +3: blr + + .section .bss + .balign 8 +__crt0stack_end: + .space 0x8000 +__crt0stack: + diff --git a/channel/channelapp/stub/ios.c b/channel/channelapp/stub/ios.c new file mode 100644 index 0000000..7cfe290 --- /dev/null +++ b/channel/channelapp/stub/ios.c @@ -0,0 +1,290 @@ +/* + * Copyright (C) 2008 segher, #wiidev efnet + * Copyright (C) 2008 bushing, #wiidev efnet + * Copyright (C) 2008 dhewg, #wiidev efnet + * Copyright (C) 2008 marcan, #wiidev efnet + * + * this file is part of the Homebrew Channel + * + * 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., 59 Temple Place, Suite 330, Boston, MA 02111-1307 USA + */ +#include "stub_debug.h" +#include "ios.h" +#include "processor.h" +#include "cache.h" +#include "system.h" + +#define virt_to_phys(x) ((u32*)(((u32)(x))&0x7FFFFFFF)) +#define phys_to_virt(x) ((u32*)(((u32)(x))|0x80000000)) + +// Timebase frequency is core frequency / 8. Ignore roundoff, this +// doesn't have to be very accurate. +#define TICKS_PER_USEC (729/8) + +static u32 mftb(void) +{ + u32 x; + + asm volatile("mftb %0" : "=r"(x)); + + return x; +} + +static void __delay(u32 ticks) +{ + u32 start = mftb(); + + while (mftb() - start < ticks) + ; +} + +void udelay(u32 us) +{ + __delay(TICKS_PER_USEC * us); +} + + +// Low-level IPC access. + +static inline u32 read32(u32 addr) +{ + u32 x; + + asm volatile("lwz %0,0(%1) ; sync" : "=r"(x) : "b"(0xc0000000 | addr)); + + return x; +} + +static inline void write32(u32 addr, u32 x) +{ + asm volatile("stw %0,0(%1) ; eieio" : : "r"(x), "b"(0xc0000000 | addr)); +} + + +static u32 ipc_read(u32 reg) { + return read32(0x0d000000 + 4*reg); +} + +static void ipc_write(u32 reg, u32 value) { + write32(0x0d000000 + 4*reg, value); +} + +static void ipc_bell(u32 w) +{ + ipc_write(1, (ipc_read(1) & 0x30) | w); +} + +static void ipc_wait_ack(void) +{ + while ((ipc_read(1) & 0x22) != 0x22) + ; + udelay(100); +} + +static void ipc_wait_reply(void) +{ + while ((ipc_read(1) & 0x14) != 0x14) + ; + udelay(100); +} + +static void ipc_irq_ack(void) +{ + ipc_write(12, 0x40000000); +} + + +// Mid-level IPC access. + +struct ipc { + u32 cmd; + int result; + int fd; + u32 arg[5]; + + u32 user[8]; +}; + +static struct ipc ipc __attribute__((aligned(64))); + +static void ipc_send_request(void) +{ + DCFlushRange(&ipc, 0x40); + + ipc_write(0, (u32)virt_to_phys(&ipc)); + ipc_bell(1); + + ipc_wait_ack(); + + ipc_bell(2); + ipc_irq_ack(); +} + +void ipc_send_twoack(void) +{ + DCFlushRange(&ipc, 0x40); + + ipc_write(0, (u32)virt_to_phys(&ipc)); + ipc_bell(1); + + ipc_wait_ack(); + ipc_irq_ack(); + ipc_bell(2); + + ipc_wait_ack(); + ipc_irq_ack(); + ipc_bell(2); + ipc_bell(8); +} + +static void ipc_recv_reply(void) +{ + for (;;) { + u32 reply; + + ipc_wait_reply(); + + reply = ipc_read(2); + ipc_bell(4); + + ipc_irq_ack(); + ipc_bell(8); + + if (((u32*)reply) == virt_to_phys(&ipc)) + break; + + debug_string("Ignoring unexpected IPC reply @"); + debug_uint((u32)reply); + debug_string("\n\r"); + } + + DCInvalidateRange(&ipc, sizeof ipc); +} + + +// High-level IPC access. + +int ios_open(const char *filename, u32 mode) +{ + DCFlushRange((void*)filename, strlen(filename) + 1); + memset(&ipc, 0, sizeof ipc); + + ipc.cmd = 1; + ipc.fd = 0; + ipc.arg[0] = (u32)virt_to_phys(filename); + ipc.arg[1] = mode; + + ipc_send_request(); + ipc_recv_reply(); + + return ipc.result; +} + +int ios_close(int fd) +{ + memset(&ipc, 0, sizeof ipc); + + ipc.cmd = 2; + ipc.fd = fd; + + ipc_send_request(); + ipc_recv_reply(); + + return ipc.result; +} + +int _ios_ioctlv(int fd, u32 n, u32 in_count, u32 out_count, struct ioctlv *vec, int reboot) +{ + u32 i; + + memset(&ipc, 0, sizeof ipc); + + for (i = 0; i < in_count + out_count; i++) + if (vec[i].data) { + DCFlushRange(vec[i].data, vec[i].len); + vec[i].data = (void *)virt_to_phys(vec[i].data); + } + + DCFlushRange(vec, (in_count + out_count) * sizeof *vec); + + ipc.cmd = 7; + ipc.fd = fd; + ipc.arg[0] = n; + ipc.arg[1] = in_count; + ipc.arg[2] = out_count; + ipc.arg[3] = (u32)virt_to_phys(vec); + + if(reboot) { + ipc_send_twoack(); + return 0; + } else { + ipc_send_request(); + ipc_recv_reply(); + + for (i = in_count; i < in_count + out_count; i++) + if (vec[i].data) { + vec[i].data = phys_to_virt((u32)vec[i].data); + DCInvalidateRange(vec[i].data, vec[i].len); + } + return ipc.result; + } +} + +int ios_ioctlv(int fd, u32 n, u32 in_count, u32 out_count, struct ioctlv *vec) { + return _ios_ioctlv(fd, n, in_count, out_count, vec, 0); +} + +int ios_ioctlvreboot(int fd, u32 n, u32 in_count, u32 out_count, struct ioctlv *vec) { + return _ios_ioctlv(fd, n, in_count, out_count, vec, 1); +} + +// Cleanup any old state. + +static void ipc_cleanup_reply(void) { + if ((ipc_read(1) & 0x14) != 0x14) + return; + + ipc_read(2); + ipc_bell(4); + + ipc_irq_ack(); + ipc_bell(8); +} + +static void ipc_cleanup_request(void) { + if ((ipc_read(1) & 0x22) == 0x22) + ipc_bell(2); +} + +void reset_ios(void) { + int i; + + debug_string("Flushing IPC transactions"); + for (i = 0; i < 10; i++) { + ipc_cleanup_request(); + ipc_cleanup_reply(); + ipc_irq_ack(); + udelay(1000); + debug_string("."); + } + debug_string(" Done.\n\r"); + + debug_string("Closing file descriptors"); + for (i = 0; i < 32; i++) { + ios_close(i); + debug_string("."); + } + debug_string(" Done.\n\r"); +} diff --git a/channel/channelapp/stub/ios.h b/channel/channelapp/stub/ios.h new file mode 100644 index 0000000..e53fcb4 --- /dev/null +++ b/channel/channelapp/stub/ios.h @@ -0,0 +1,11 @@ + +struct ioctlv { + void *data; + u32 len; +}; + +int ios_open(const char *filename, u32 mode); +int ios_close(int fd); +int ios_ioctlv(int fd, u32 n, u32 in_count, u32 out_count, struct ioctlv *vec); +int ios_ioctlvreboot(int fd, u32 n, u32 in_count, u32 out_count, struct ioctlv *vec); +void reset_ios(void); diff --git a/channel/channelapp/stub/processor.h b/channel/channelapp/stub/processor.h new file mode 100644 index 0000000..7b2f567 --- /dev/null +++ b/channel/channelapp/stub/processor.h @@ -0,0 +1,115 @@ +// this file was taken from libogc, see http://www.devkitpro.org/ + +#ifndef __PROCESSOR_H__ +#define __PROCESSOR_H__ + +#include + +#define __stringify(rn) #rn +#define ATTRIBUTE_ALIGN(v) __attribute__((aligned(v))) + +#define _sync() asm volatile("sync") +#define _nop() asm volatile("nop") +#define ppcsync() asm volatile("sc") +#define ppchalt() ({ \ + asm volatile("sync"); \ + while(1) { \ + asm volatile("nop"); \ + asm volatile("li 3,0"); \ + asm volatile("nop"); \ + } \ +}) + +#define mfdcr(_rn) ({register u32 _rval; \ + asm volatile("mfdcr %0," __stringify(_rn) \ + : "=r" (_rval)); _rval;}) +#define mtdcr(rn, val) asm volatile("mtdcr " __stringify(rn) ",%0" : : "r" (val)) + +#define mfmsr() ({register u32 _rval; \ + asm volatile("mfmsr %0" : "=r" (_rval)); _rval;}) +#define mtmsr(val) asm volatile("mtmsr %0" : : "r" (val)) + +#define mfdec() ({register u32 _rval; \ + asm volatile("mfdec %0" : "=r" (_rval)); _rval;}) +#define mtdec(_val) asm volatile("mtdec %0" : : "r" (_val)) + +#define mfspr(_rn) \ +({ register u32 _rval = 0; \ + asm volatile("mfspr %0," __stringify(_rn) \ + : "=r" (_rval));\ + _rval; \ +}) + +#define mtspr(_rn, _val) asm volatile("mtspr " __stringify(_rn) ",%0" : : "r" (_val)) + +#define mfwpar() mfspr(WPAR) +#define mtwpar(_val) mtspr(WPAR,_val) + +#define mfmmcr0() mfspr(MMCR0) +#define mtmmcr0(_val) mtspr(MMCR0,_val) +#define mfmmcr1() mfspr(MMCR1) +#define mtmmcr1(_val) mtspr(MMCR1,_val) + +#define mfpmc1() mfspr(PMC1) +#define mtpmc1(_val) mtspr(PMC1,_val) +#define mfpmc2() mfspr(PMC2) +#define mtpmc2(_val) mtspr(PMC2,_val) +#define mfpmc3() mfspr(PMC3) +#define mtpmc3(_val) mtspr(PMC3,_val) +#define mfpmc4() mfspr(PMC4) +#define mtpmc4(_val) mtspr(PMC4,_val) + +#define mfhid0() mfspr(HID0) +#define mthid0(_val) mtspr(HID0,_val) +#define mfhid1() mfspr(HID1) +#define mthid1(_val) mtspr(HID1,_val) +#define mfhid2() mfspr(HID2) +#define mthid2(_val) mtspr(HID2,_val) +#define mfhid4() mfspr(HID4) +#define mthid4(_val) mtspr(HID4,_val) + +#define cntlzw(_val) ({register u32 _rval; \ + asm volatile("cntlzw %0, %1" : "=r"((_rval)) : "r"((_val))); _rval;}) + +#define _CPU_MSR_GET( _msr_value ) \ + do { \ + _msr_value = 0; \ + asm volatile ("mfmsr %0" : "=&r" ((_msr_value)) : "0" ((_msr_value))); \ + } while (0) + +#define _CPU_MSR_SET( _msr_value ) \ +{ asm volatile ("mtmsr %0" : "=&r" ((_msr_value)) : "0" ((_msr_value))); } + +#define _CPU_ISR_Enable() \ + { register u32 _val = 0; \ + asm volatile ("mfmsr %0; ori %0,%0,0x8000; mtmsr %0" : \ + "=&r" (_val) : "0" (_val));\ + } + +#define _CPU_ISR_Disable( _isr_cookie ) \ + { register u32 _disable_mask = MSR_EE; \ + _isr_cookie = 0; \ + asm volatile ( \ + "mfmsr %0; andc %1,%0,%1; mtmsr %1" : \ + "=&r" ((_isr_cookie)), "=&r" ((_disable_mask)) : \ + "0" ((_isr_cookie)), "1" ((_disable_mask)) \ + ); \ + } + +#define _CPU_ISR_Restore( _isr_cookie ) \ + { \ + asm volatile ( "mtmsr %0" : \ + "=r" ((_isr_cookie)) : \ + "0" ((_isr_cookie))); \ + } + +#define _CPU_ISR_Flash( _isr_cookie ) \ + { register u32 _disable_mask = MSR_EE; \ + asm volatile ( \ + "mtmsr %0; andc %1,%0,%1; mtmsr %1" : \ + "=r" ((_isr_cookie)), "=r" ((_disable_mask)) : \ + "0" ((_isr_cookie)), "1" ((_disable_mask)) \ + ); \ + } + +#endif diff --git a/channel/channelapp/stub/stub.c b/channel/channelapp/stub/stub.c new file mode 100644 index 0000000..db9978d --- /dev/null +++ b/channel/channelapp/stub/stub.c @@ -0,0 +1,151 @@ +/* + * Copyright (C) 2008 dhewg, #wiidev efnet + * Copyright (C) 2008 marcan, #wiidev efnet + * + * this file is part of the Homebrew Channel + * + * 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., 59 Temple Place, Suite 330, Boston, MA 02111-1307 USA + */ + +#include "system.h" +#include "stub_debug.h" +#include "usb.h" +#include "ios.h" +#include "cache.h" +#include "../config.h" + +#define IOCTL_ES_LAUNCH 0x08 +#define IOCTL_ES_GETVIEWCNT 0x12 +#define IOCTL_ES_GETVIEWS 0x13 + +struct ioctlv vecs[3] __attribute__((aligned(32))); + +u64 *conf_magic = STUB_ADDR_MAGIC; +u64 *conf_titleID = STUB_ADDR_TITLE; + +int es_fd; + +int LaunchTitle(u64 titleID) { + static u64 xtitleID __attribute__((aligned(32))); + static u32 cntviews __attribute__((aligned(32))); + static u8 tikviews[0xd8*4] __attribute__((aligned(32))); + int ret; + + debug_string("LaunchTitle: "); + debug_uint(titleID>>32); + debug_string("-"); + debug_uint(titleID&0xFFFFFFFF); + + xtitleID = titleID; + + debug_string("\n\rGetTicketViewCount: "); + + vecs[0].data = &xtitleID; + vecs[0].len = 8; + vecs[1].data = &cntviews; + vecs[1].len = 4; + ret = ios_ioctlv(es_fd, IOCTL_ES_GETVIEWCNT, 1, 1, vecs); + debug_uint(ret); + debug_string(", views: "); + debug_uint(cntviews); + debug_string("\n\r"); + if(ret<0) return ret; + if(cntviews>4) return -1; + + debug_string("GetTicketViews: "); + vecs[0].data = &xtitleID; + vecs[0].len = 8; + vecs[1].data = &cntviews; + vecs[1].len = 4; + vecs[2].data = tikviews; + vecs[2].len = 0xd8*cntviews; + ret = ios_ioctlv(es_fd, IOCTL_ES_GETVIEWS, 2, 1, vecs); + debug_uint(ret); + debug_string("\n\r"); + if(ret<0) return ret; + debug_string("Attempting to launch...\n\r"); + vecs[0].data = &xtitleID; + vecs[0].len = 8; + vecs[1].data = tikviews; + vecs[1].len = 0xd8; + ret = ios_ioctlvreboot(es_fd, IOCTL_ES_LAUNCH, 2, 0, vecs); + if(ret < 0) { + debug_string("Launch failed: "); + debug_uint(ret); + debug_string("\r\n"); + } + return ret; +} + +s32 IOS_GetVersion() +{ + u32 vercode; + u16 version; + DCInvalidateRange((void*)0x80003140,8); + vercode = *((u32*)0x80003140); + version = vercode >> 16; + if(version == 0) return -1; + if(version > 0xff) return -1; + return version; +} + +void printversion(void) { + debug_string("IOS version: "); + debug_uint(IOS_GetVersion()); + debug_string("\n\r"); +} + +int es_init(void) { + debug_string("Opening /dev/es: "); + es_fd = ios_open("/dev/es", 0); + debug_uint(es_fd); + debug_string("\n\r"); + return es_fd; +} + + +void _main (void) { + int iosver; + u64 titleID = MY_TITLEID; + + debug_string("\n\rHomebrew Channel stub code\n\r"); + + if(*conf_magic == STUB_MAGIC) titleID = *conf_titleID; + + reset_ios(); + + if(es_init() < 0) goto fail; + + iosver = STUB_LOAD_IOS_VERSION; + if(iosver < 0) + iosver = 21; //bah + printversion(); + debug_string("\n\rReloading IOS...\n\r"); + LaunchTitle(0x0000000100000000LL | iosver); + printversion(); + + if(es_init() < 0) goto fail; + debug_string("\n\rLoading requested channel...\n\r"); + LaunchTitle(titleID); + // if fail, try system menu + debug_string("\n\rChannel load failed, trying with system menu...\n\r"); + LaunchTitle(0x0000000100000002LL); + printversion(); + +fail: + debug_string("FAILURE\n\r"); + while(1); +} + diff --git a/channel/channelapp/stub/stub_debug.c b/channel/channelapp/stub/stub_debug.c new file mode 100644 index 0000000..95b41c5 --- /dev/null +++ b/channel/channelapp/stub/stub_debug.c @@ -0,0 +1,48 @@ +/* + * Copyright (C) 2008 dhewg, #wiidev efnet + * + * this file is part of the Homebrew Channel + * + * 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., 59 Temple Place, Suite 330, Boston, MA 02111-1307 USA + */ + +#include "stub_debug.h" +#include "usb.h" + +#ifdef DEBUG_STUB + +static char int2hex[] = "0123456789abcdef"; + +void debug_uint (u32 i) { + int j; + + usb_sendbuffersafe ("0x", 2); + for (j = 0; j < 8; ++j) { + usb_sendbuffersafe (&int2hex[(i >> 28) & 0xf], 1); + i <<= 4; + } +} + +void debug_string (const char *s) { + u32 i = 0; + + while (s[i]) + i++; + + usb_sendbuffersafe (s, i); +} + +#endif + diff --git a/channel/channelapp/stub/stub_debug.h b/channel/channelapp/stub/stub_debug.h new file mode 100644 index 0000000..7fd7e4e --- /dev/null +++ b/channel/channelapp/stub/stub_debug.h @@ -0,0 +1,41 @@ +/* + * Copyright (C) 2008 dhewg, #wiidev efnet + * + * this file is part of the Homebrew Channel + * + * 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., 59 Temple Place, Suite 330, Boston, MA 02111-1307 USA + */ + +#ifndef _STUB_DEBUG_H_ +#define _STUB_DEBUG_H_ + +#include + +#include "../config.h" + +#ifdef DEBUG_STUB + +void debug_uint (u32 i); +void debug_string (const char *s); + +#else + +#define debug_uint(x) +#define debug_string(x) + +#endif + +#endif + diff --git a/channel/channelapp/stub/system.c b/channel/channelapp/stub/system.c new file mode 100644 index 0000000..b843373 --- /dev/null +++ b/channel/channelapp/stub/system.c @@ -0,0 +1,28 @@ +#include "asm.h" +#include "cache.h" +#include "system.h" +#include "processor.h" + +extern char systemcallhandler_start[], systemcallhandler_end[]; + +void __SyscallInit(void) +{ + memcpy((void*)0x80000c00, systemcallhandler_start, + systemcallhandler_end - systemcallhandler_start); + DCFlushRangeNoSync((void*)0x80000c00, 0x100); + ICInvalidateRange((void*)0x80000c00, 0x100); + _sync(); +} + +void *memcpy(void *ptr, const void *src, int size) { + char* ptr2 = ptr; + const char* src2 = src; + while(size--) *ptr2++ = *src2++; + return ptr; +} + +int strlen(const char *ptr) { + int i=0; + while(*ptr++) i++; + return i; +} diff --git a/channel/channelapp/stub/system.h b/channel/channelapp/stub/system.h new file mode 100644 index 0000000..d5bab8e --- /dev/null +++ b/channel/channelapp/stub/system.h @@ -0,0 +1,24 @@ +// this code was taken from libogc, see http://www.devkitpro.org/ + +#ifndef __SYSTEM_H__ +#define __SYSTEM_H__ + +#include + +void ICInvalidateRange(void *startaddress,u32 len); +void DCInvalidateRange(void *startaddress,u32 len); +void DCFlushRange(void *startaddress,u32 len); +void DCFlushRangeNoSync(void *startaddress,u32 len); + +void dol_relocstart(void *dolstart); + +void* memmove(void *dst0,const void *src0,register int length); +extern void _memset(void *,int,int); +//void *memset(void *ptr, int c, int size); +#define memset _memset +void *memcpy(void *ptr, const void *src, int size); +int strlen(const char *ptr); + +extern void __exception_closeall(); + +#endif diff --git a/channel/channelapp/stub/system_asm.S b/channel/channelapp/stub/system_asm.S new file mode 100644 index 0000000..a8ffa4e --- /dev/null +++ b/channel/channelapp/stub/system_asm.S @@ -0,0 +1,100 @@ +// this code was taken from libogc, see http://www.devkitpro.org/ + +#include "asm.h" + + .extern ICFlashInvalidate + .extern ICEnable + .extern DCEnable + .extern L2Init + .extern L2Enable + .globl __CacheInit +__CacheInit: + mflr r0 + stw r0, 4(sp) + stwu sp, -16(sp) + stw r31, 12(sp) + + mfspr r3,HID0 # (HID0) + rlwinm r0,r3, 0, 16, 16 + cmplwi r0, 0x0000 # Check if the Instruction Cache has been enabled or not. + bne ICEnabled + + bl ICEnable +ICEnabled: + mfspr r3, HID0 # bl PPCMfhid0 + rlwinm r0, r3, 0, 17, 17 + cmplwi r0, 0x0000 # Check if the Data Cache has been enabled or not. + bne DCEnabled + + bl DCEnable +DCEnabled: + + mfspr r3, L2CR # (L2CR) + clrrwi r0, r3, 31 # Clear all of the bits except 31 + cmplwi r0, 0x0000 + bne L2Enabled + + bl L2Init + bl L2Enable + +L2Enabled: + # Restore the non-volatile registers to their previous values and return. + lwz r0, 20(sp) + lwz r31, 12(sp) + addi sp, sp, 16 + mtlr r0 + blr + + .globl __SystemInit +__SystemInit: + mflr r0 + stw r0, 4(sp) + stwu sp, -24(sp) + stw r31, 20(sp) + stw r30, 16(sp) + stw r29, 12(sp) + + # Clear various SPR's + li r3,0 + mtspr 952, r3 + mtspr 956, r3 + mtspr 953, r3 + mtspr 954, r3 + mtspr 957, r3 + mtspr 958, r3 + +#if 0 + lis r3,0x8390 //bits set: H4A(HID4 access), SBE(2nd BAT enabled), SR0(store 0), LPE(PS LE exception), L2CFI(L2 castout prior to L2 inv. flash) + mtspr HID4,r3 +#endif + + # Disable Speculative Bus Accesses to non-guarded space from both caches. + mfspr r3, HID0 + ori r3, r3, 0x0200 + mtspr HID0, r3 + + mfspr r3,HID2 # (HID2) + rlwinm r3, r3, 0, 2, 0 + mtspr HID2,r3 # (HID2) + + # Restore the non-volatile registers to their previous values and return. + lwz r0, 28(sp) + lwz r31,20(sp) + lwz r30,16(sp) + lwz r29,12(sp) + addi sp, sp, 24 + mtlr r0 + blr + + + .global systemcallhandler_start,systemcallhandler_end +systemcallhandler_start: + mfspr r3,HID0 + ori r4,r3,0x0008 + mtspr HID0,r4 + isync + sync + mtspr HID0,r3 + rfi +systemcallhandler_end: + nop diff --git a/channel/channelapp/stub/usb.c b/channel/channelapp/stub/usb.c new file mode 100644 index 0000000..b0e588f --- /dev/null +++ b/channel/channelapp/stub/usb.c @@ -0,0 +1,221 @@ +/*--------------------------------------------------------------------------------------------- + * USB Gecko Development Kit - http://www.usbgecko.com + * -------------------------------------------------------------------------------------------- + * + * + * usb.c - V1.2 functions for the USB Gecko adapter (www.usbgecko.com). + * Now works for Wii Mode - use WIIMODE define in usb.h to set + * Copyright (c) 2008 - Nuke - + * + *---------------------------------------------------------------------------------------------*/ + + +#include // If not using libogc need types +#include "usb.h" + +#ifdef DEBUG_STUB + +/*---------------------------------------------------------------------------------------------* + Name: usb_sendbyte + Description: Send byte to Gamecube/Wii over EXI memory card port +*----------------------------------------------------------------------------------------------*/ + +static int __usb_sendbyte (char sendbyte) +{ + s32 i; + + exi_chan1sr = 0x000000D0; + exi_chan1data = 0xB0000000 | (sendbyte<<20); + exi_chan1cr = 0x19; + while((exi_chan1cr)&1); + i = exi_chan1data; + exi_chan1sr = 0; + if (i&0x04000000){ + return 1; + } + return 0; +} + +/*---------------------------------------------------------------------------------------------* + Name: usb_receivebyte + Description: Receive byte from Gamecube/Wii over EXI memory card port +*----------------------------------------------------------------------------------------------*/ + +static int __usb_receivebyte (char *receivebyte) +{ + s32 i = 0; + + exi_chan1sr = 0x000000D0; + exi_chan1data = 0xA0000000; + exi_chan1cr = 0x19; + while((exi_chan1cr)&1); + i = exi_chan1data; + exi_chan1sr = 0; + if (i&0x08000000){ + *receivebyte=(i>>16)&0xff; + return 1; + } + return 0; +} + +/*---------------------------------------------------------------------------------------------* + Name: usb_checksendstatus + Description: Chesk the FIFO is ready to send +*----------------------------------------------------------------------------------------------*/ + +static int __usb_checksendstatus() +{ + s32 i = 0; + + exi_chan1sr = 0x000000D0; + exi_chan1data = 0xC0000000; + exi_chan1cr = 0x19; + while((exi_chan1cr)&1); + i = exi_chan1data; + exi_chan1sr = 0x0; + if (i&0x04000000){ + return 1; + } + return 0; +} + +/*---------------------------------------------------------------------------------------------* + Name: usb_checkreceivestatus + Description: Check the FIFO is ready to receive +*----------------------------------------------------------------------------------------------*/ + +static int __usb_checkreceivestatus() +{ + s32 i = 0; + exi_chan1sr = 0x000000D0; + exi_chan1data = 0xD0000000; + exi_chan1cr = 0x19; + while((exi_chan1cr)&1); + i = exi_chan1data; + exi_chan1sr = 0x0; + if (i&0x04000000){ + return 1; + } + return 0; +} + +/*---------------------------------------------------------------------------------------------* + Name: usb_sendbuffer + Description: Simple buffer send routine +*----------------------------------------------------------------------------------------------*/ + +void usb_sendbuffer (const void *buffer, int size) +{ + char *sendbyte = (char*) buffer; + s32 bytesleft = size; + s32 returnvalue; + + while (bytesleft > 0) + { + returnvalue = __usb_sendbyte(*sendbyte); + if(returnvalue) { + sendbyte++; + bytesleft--; + } + } +} + +/*---------------------------------------------------------------------------------------------* + Name: usb_receivebuffer + Description: Simple buffer receive routine +*----------------------------------------------------------------------------------------------*/ + +void usb_receivebuffer (void *buffer, int size) +{ + char *receivebyte = (char*)buffer; + s32 bytesleft = size; + s32 returnvalue; + + while (bytesleft > 0) + { + returnvalue = __usb_receivebyte(receivebyte); + if(returnvalue) { + receivebyte++; + bytesleft--; + } + } +} + +/*---------------------------------------------------------------------------------------------* + Name: usb_sendbuffersafe + Description: Simple buffer send routine with fifo check (use for large transfers) +*----------------------------------------------------------------------------------------------*/ + +void usb_sendbuffersafe (const void *buffer, int size) +{ + char *sendbyte = (char*) buffer; + s32 bytesleft = size; + s32 returnvalue; + + while (bytesleft > 0) + { + if(__usb_checksendstatus()){ + returnvalue = __usb_sendbyte(*sendbyte); + if(returnvalue) { + sendbyte++; + bytesleft--; + } + } + } +} + +/*---------------------------------------------------------------------------------------------* + Name: usb_receivebuffersafe + Description: Simple buffer receive routine with fifo check (use for large transfers) +*----------------------------------------------------------------------------------------------*/ + +void usb_receivebuffersafe (void *buffer, int size) +{ + char *receivebyte = (char*)buffer; + s32 bytesleft = size; + s32 returnvalue; + + while (bytesleft > 0) + { + if(__usb_checkreceivestatus()){ + returnvalue = __usb_receivebyte(receivebyte); + if(returnvalue) { + receivebyte++; + bytesleft--; + } + } + } +} + +/*---------------------------------------------------------------------------------------------* + Name: usb_checkgecko + Description: Chesk the Gecko is connected +*----------------------------------------------------------------------------------------------*/ +int usb_checkgecko() +{ + s32 i = 0; + + exi_chan1sr = 0x000000D0; + exi_chan1data = 0x90000000; + exi_chan1cr = 0x19; + while((exi_chan1cr)&1); + i = exi_chan1data; + exi_chan1sr = 0x0; + if (i==0x04700000){ + return 1; + } + return 0; +} + +/*---------------------------------------------------------------------------------------------* + Name: usb_flush + Description: Flushes the FIFO, Use at the start of your program to avoid trash +*----------------------------------------------------------------------------------------------*/ + +void usb_flush() +{ + char tempbyte; + + while (__usb_receivebyte(&tempbyte)); +} +#endif diff --git a/channel/channelapp/stub/usb.h b/channel/channelapp/stub/usb.h new file mode 100644 index 0000000..7ab8af6 --- /dev/null +++ b/channel/channelapp/stub/usb.h @@ -0,0 +1,65 @@ +/*--------------------------------------------------------------------------------------------- + * USB Gecko Development Kit - http://www.usbgecko.com + * -------------------------------------------------------------------------------------------- + * + * + * usb.h - functions for the USB Gecko adapter (www.usbgecko.com). + * + * Copyright (c) 2008 - Nuke - + * + *---------------------------------------------------------------------------------------------*/ + +#ifndef __USB_H__ +#define __USB_H__ + +#include "../config.h" + +#ifdef DEBUG_STUB + +#define WIIMODE +// Wii Mode +#ifdef WIIMODE + #define exi_chan0sr *(volatile unsigned int*) 0xCD006800 // Channel 0 Status Register + #define exi_chan1sr *(volatile unsigned int*) 0xCD006814 // Channel 1 Status Register + #define exi_chan2sr *(volatile unsigned int*) 0xCD006828 // Channel 2 Status Register + #define exi_chan0cr *(volatile unsigned int*) 0xCD00680c // Channel 0 Control Register + #define exi_chan1cr *(volatile unsigned int*) 0xCD006820 // Channel 1 Control Register + #define exi_chan2cr *(volatile unsigned int*) 0xCD006834 // Channel 2 Control Register + #define exi_chan0data *(volatile unsigned int*) 0xCD006810 // Channel 0 Immediate Data + #define exi_chan1data *(volatile unsigned int*) 0xCD006824 // Channel 1 Immediate Data + #define exi_chan2data *(volatile unsigned int*) 0xCD006838 // Channel 2 Immediate Data + #define exi_chan0dmasta *(volatile unsigned int*) 0xCD006804 // Channel 0 DMA Start address + #define exi_chan1dmasta *(volatile unsigned int*) 0xCD006818 // Channel 1 DMA Start address + #define exi_chan2dmasta *(volatile unsigned int*) 0xCD00682c // Channel 2 DMA Start address + #define exi_chan0dmalen *(volatile unsigned int*) 0xCD006808 // Channel 0 DMA Length + #define exi_chan1dmalen *(volatile unsigned int*) 0xCD00681c // Channel 1 DMA Length + #define exi_chan2dmalen *(volatile unsigned int*) 0xCD006830 // Channel 2 DMA Length +#else +// GC Mode + #define exi_chan0sr *(volatile unsigned int*) 0xCC006800 // Channel 0 Status Register + #define exi_chan1sr *(volatile unsigned int*) 0xCC006814 // Channel 1 Status Register + #define exi_chan2sr *(volatile unsigned int*) 0xCC006828 // Channel 2 Status Register + #define exi_chan0cr *(volatile unsigned int*) 0xCC00680c // Channel 0 Control Register + #define exi_chan1cr *(volatile unsigned int*) 0xCC006820 // Channel 1 Control Register + #define exi_chan2cr *(volatile unsigned int*) 0xCC006834 // Channel 2 Control Register + #define exi_chan0data *(volatile unsigned int*) 0xCC006810 // Channel 0 Immediate Data + #define exi_chan1data *(volatile unsigned int*) 0xCC006824 // Channel 1 Immediate Data + #define exi_chan2data *(volatile unsigned int*) 0xCC006838 // Channel 2 Immediate Data + #define exi_chan0dmasta *(volatile unsigned int*) 0xCC006804 // Channel 0 DMA Start address + #define exi_chan1dmasta *(volatile unsigned int*) 0xCC006818 // Channel 1 DMA Start address + #define exi_chan2dmasta *(volatile unsigned int*) 0xCC00682c // Channel 2 DMA Start address + #define exi_chan0dmalen *(volatile unsigned int*) 0xCC006808 // Channel 0 DMA Length + #define exi_chan1dmalen *(volatile unsigned int*) 0xCC00681c // Channel 1 DMA Length + #define exi_chan2dmalen *(volatile unsigned int*) 0xCC006830 // Channel 2 DMA Length +#endif + +// Function prototypes +void usb_flush(); +int usb_checkgecko(); +void usb_sendbuffer (const void *buffer, int size); +void usb_receivebuffer (void *buffer, int size); +void usb_sendbuffersafe (const void *buffer, int size); +void usb_receivebuffersafe (void *buffer, int size); +#endif + +#endif // __USB_H__ diff --git a/channel/title/.gitignore b/channel/title/.gitignore new file mode 100644 index 0000000..5ee97c4 --- /dev/null +++ b/channel/title/.gitignore @@ -0,0 +1,7 @@ +00000000 +00000001 +retail/cetk +retail/tmd +dpki/cetk +dpki/tmd +*.wad diff --git a/channel/title/Makefile b/channel/title/Makefile new file mode 100644 index 0000000..5211b5b --- /dev/null +++ b/channel/title/Makefile @@ -0,0 +1,52 @@ +INSTALLER_VER_MAJOR = 1 +INSTALLER_VER_MINOR = 2 + +TOOLS = $(CURDIR)/../../pywii/pywii-tools +CERTS = $(CURDIR)/dpki/certs +DPKI_ISSUER_TIK = Root-CA00000002-XS00000006 +DPKI_ISSUER_TMD = Root-CA00000002-CP00000007 + +INSTALLERFILES = certs.dpki certs.retail cetk.dpki cetk.retail tmd.dpki tmd.retail 00000000 00000001 + +all: channel_retail.wad + +dpki: channel_dpki.wad + +channel_retail.wad: retail/cetk retail/tmd 00000000 00000001 + @$(TOOLS)/wadpack.py $@ retail/ + +channel_dpki.wad: dpki/cetk dpki/tmd 00000000 00000001 + @$(TOOLS)/wadpack.py -dpki $@ dpki/ + +00000000: ../banner/channel.imet + @cat footer $< > $@ + +00000001: ../channelapp/channelapp-channel.dol + @cp $< $@ + +retail/cetk: cetk.template + @cp $< $@ + @$(TOOLS)/tikfix.py $@ + +dpki/cetk: retail/cetk + @$(TOOLS)/dpkisign.py -cetk $< $@ $(CERTS) $(DPKI_ISSUER_TIK) + +retail/tmd: tmd.template 00000000 00000001 + @cp $< $@ + @$(TOOLS)/tmdupdatecr.py $@ $(CURDIR) + @$(TOOLS)/tmdvers.py $@ $(INSTALLER_VER_MAJOR) $(INSTALLER_VER_MINOR) + +dpki/tmd: retail/tmd + @$(TOOLS)/dpkisign.py -tmd $< $@ $(CERTS) $(DPKI_ISSUER_TMD) + +check: all + @echo ===== RETAIL ===== + @$(TOOLS)/tikinfo.py cetk.retail certs.retail + @$(TOOLS)/tmdinfo.py tmd.retail certs.retail + @echo ===== DPKI ===== + @$(TOOLS)/tikinfo.py -dpki cetk.dpki certs.dpki + @$(TOOLS)/tmdinfo.py -dpki tmd.dpki certs.dpki + +clean: + rm -f retail/cetk dpki/cetk retail/tmd retail/tmd 00000000 00000001 + diff --git a/channel/title/cetk.template b/channel/title/cetk.template new file mode 100644 index 0000000..cc5857d Binary files /dev/null and b/channel/title/cetk.template differ diff --git a/channel/title/dpki/certs b/channel/title/dpki/certs new file mode 100644 index 0000000..9368fbb Binary files /dev/null and b/channel/title/dpki/certs differ diff --git a/channel/title/dpki/footer b/channel/title/dpki/footer new file mode 120000 index 0000000..74c13ec --- /dev/null +++ b/channel/title/dpki/footer @@ -0,0 +1 @@ +../footer \ No newline at end of file diff --git a/channel/title/footer b/channel/title/footer new file mode 100644 index 0000000..5dd0f44 Binary files /dev/null and b/channel/title/footer differ diff --git a/channel/title/pywii b/channel/title/pywii new file mode 120000 index 0000000..fe33837 --- /dev/null +++ b/channel/title/pywii @@ -0,0 +1 @@ +../../pywii/Common/pywii \ No newline at end of file diff --git a/channel/title/retail/certs b/channel/title/retail/certs new file mode 100644 index 0000000..2184107 Binary files /dev/null and b/channel/title/retail/certs differ diff --git a/channel/title/retail/footer b/channel/title/retail/footer new file mode 120000 index 0000000..74c13ec --- /dev/null +++ b/channel/title/retail/footer @@ -0,0 +1 @@ +../footer \ No newline at end of file diff --git a/channel/title/tmd.template b/channel/title/tmd.template new file mode 100644 index 0000000..709109e Binary files /dev/null and b/channel/title/tmd.template differ diff --git a/channel/wiiload/.gitignore b/channel/wiiload/.gitignore new file mode 100644 index 0000000..9b0eee4 --- /dev/null +++ b/channel/wiiload/.gitignore @@ -0,0 +1,3 @@ +wiiload +wiiload.exe + diff --git a/channel/wiiload/Makefile b/channel/wiiload/Makefile new file mode 100644 index 0000000..4eda0e0 --- /dev/null +++ b/channel/wiiload/Makefile @@ -0,0 +1,53 @@ +CFLAGS += -Wall -Wextra -Os -g -pipe + +ifeq ($(WIN32), 1) +PREFIX ?= i586-mingw32msvc- +BIN_EXT = .exe +CFLAGS += -Iwin32/include +LDFLAGS += -Lwin32/lib -lws2_32 -lz +else +PREFIX ?= +BIN_EXT = +LDFLAGS += -lz +endif + +ifeq ($(FTDI), 1) +CFLAGS += -DUSE_LIBFTDI +LDFLAGS += -lftdi -lusb +endif + +CC = $(PREFIX)gcc +STRIP = $(PREFIX)strip + +BIN = wiiload$(BIN_EXT) + +OBJS = gecko.o main.o + +all: $(BIN) + +clean: + @rm -f *.o core core.* $(BIN) + +strip: all + $(STRIP) $(BIN) + +install: strip + install -m 755 $(BIN) $(DEVKITPPC)/bin + +$(BIN): $(OBJS) + $(CC) $^ $(CFLAGS) $(LDFLAGS) -o $@ + +%o: %.c + $(CC) $(CFLAGS) -c $< -o $@ + +osx: + $(MAKE) clean + PREFIX=/opt/toolchains/powerpc-apple-darwin8-10.2/bin/ppc-apple-darwin8- CFLAGS=-fabi-version=1 $(MAKE) strip + cp $(BIN) $(BIN)_ppc + $(MAKE) clean + PREFIX=/opt/toolchains/i686-apple-darwin9-10.4/bin/i686-apple-darwin9- $(MAKE) strip + cp $(BIN) $(BIN)_intel + $(MAKE) clean + /opt/toolchains/i686-apple-darwin9-10.4/bin/i686-apple-darwin9-lipo -create $(BIN)_ppc $(BIN)_intel -output $(BIN) + rm $(BIN)_ppc $(BIN)_intel + diff --git a/channel/wiiload/gecko.c b/channel/wiiload/gecko.c new file mode 100644 index 0000000..4e90dca --- /dev/null +++ b/channel/wiiload/gecko.c @@ -0,0 +1,245 @@ +/* + * Copyright (C) 2008 dhewg, #wiidev efnet + * + * this file is part of wiifuse + * http://wiibrew.org/index.php?title=Wiifuse + * + * 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., 59 Temple Place, Suite 330, Boston, MA 02111-1307 USA + */ + +#include +#include +#include +#include +#include + +#ifndef __WIN32__ +#include +#else +#define WIN32_LEAN_AND_MEAN +#include +#endif + +#include "gecko.h" + +#ifndef O_SYNC +#define O_SYNC 0 +#endif + +#define FTDI_PACKET_SIZE 3968 + +#ifndef __WIN32__ +static int fd_gecko = -1; +#else +static HANDLE handle_gecko = NULL; +#endif + +#ifdef USE_LIBFTDI +#include "ftdi.h" +#define DEFAULT_FTDI_VID 0x0403 +#define DEFAULT_FTDI_PID 0x6001 +struct ftdi_context gFTDIContext; +int gUsingFTDI = 0; +#endif + +int gecko_open(const char *dev) { +#ifdef USE_LIBFTDI + int err; + ftdi_init(&gFTDIContext); + if((err = ftdi_usb_open(&gFTDIContext, DEFAULT_FTDI_VID, DEFAULT_FTDI_PID) < 0)) { + fprintf(stderr, "ftdi_usb_open_desc failed: %d (%s)\n", err, ftdi_get_error_string(&gFTDIContext)); + fprintf(stderr, "Perhaps you need to unload the FTDI kext with e.g.\n"); + fprintf(stderr, "\tsudo kextunload -b com.FTDI.driver.FTDIUSBSerialDriver\n"); + } else { + gUsingFTDI=1; + fprintf(stderr, "Found libftdi device\n"); + goto done; + } +#endif +#ifndef __WIN32__ + struct termios newtio; + + fd_gecko = open(dev, O_RDWR | O_NOCTTY | O_SYNC | O_NONBLOCK); + + if (fd_gecko == -1) { + perror("gecko_open"); + return 1; + } + + if (fcntl(fd_gecko, F_SETFL, 0)) { + perror("F_SETFL on serial port"); + return 1; + } + + if (tcgetattr(fd_gecko, &newtio)) { + perror("tcgetattr"); + return 1; + } + + cfmakeraw(&newtio); + + newtio.c_cflag |= CRTSCTS | CS8 | CLOCAL | CREAD; + + if (tcsetattr(fd_gecko, TCSANOW, &newtio)) { + perror("tcsetattr"); + return 1; + } +#else + COMMTIMEOUTS timeouts; + + handle_gecko = CreateFile(dev, GENERIC_READ | GENERIC_WRITE, 0, 0, + OPEN_EXISTING, FILE_ATTRIBUTE_NORMAL, 0); + + GetCommTimeouts(handle_gecko, &timeouts); + + timeouts.ReadIntervalTimeout = MAXDWORD; + timeouts.ReadTotalTimeoutMultiplier = 0; + timeouts.ReadTotalTimeoutConstant = 0; + timeouts.WriteTotalTimeoutMultiplier = 0; + timeouts.WriteTotalTimeoutConstant = 0; + + if (!SetCommTimeouts(handle_gecko, &timeouts)) { + fprintf(stderr, "error setting timeouts on port\n"); + return 1; + } + + if (!SetCommMask(handle_gecko, 0)) { + fprintf(stderr, "error setting communications event mask\n"); + return 1; + } +#endif +done: + gecko_flush(); + + return 0; +} + +void gecko_close() { +#ifdef USE_LIBFTDI + if (gUsingFTDI) { + ftdi_usb_close(&gFTDIContext); + return; + } +#endif +#ifndef __WIN32__ + if (fd_gecko > 0) + close(fd_gecko); +#else + CloseHandle(handle_gecko); +#endif +} + +void gecko_flush() { +#ifdef USE_LIBFTDI + if (gUsingFTDI) { + ftdi_usb_purge_buffers(&gFTDIContext); + return; + } +#endif +#ifndef __WIN32__ + tcflush(fd_gecko, TCIOFLUSH); +#else + PurgeComm(handle_gecko, PURGE_RXCLEAR | PURGE_TXCLEAR | + PURGE_TXABORT | PURGE_RXABORT); + +#endif +} + +int gecko_read(void *buf, size_t count) { + size_t left, chunk; +#ifndef __WIN32__ + size_t res; +#else + DWORD res; +#endif + + left = count; + while (left) { + chunk = left; + if (chunk > FTDI_PACKET_SIZE) + chunk = FTDI_PACKET_SIZE; +#ifdef USE_LIBFTDI + if (gUsingFTDI) + res = ftdi_read_data(&gFTDIContext, buf, chunk); + else +#endif +#ifndef __WIN32__ + res = read(fd_gecko, buf, chunk); + if (res < 1) { + perror("gecko_read"); + return 1; + } +#else + if (!ReadFile(handle_gecko, buf, chunk, &res, NULL)) { + fprintf(stderr, "gecko_read\n"); + return 1; + } +#endif + + left -= res; + buf += res; + } + + return 0; +} + +int gecko_write(const void *buf, size_t count) { + size_t left, chunk; +#ifndef __WIN32__ + size_t res; +#else + DWORD res; +#endif + + left = count; + + while (left) { + chunk = left; + if (chunk > FTDI_PACKET_SIZE) + chunk = FTDI_PACKET_SIZE; + +#ifdef USE_LIBFTDI + if (gUsingFTDI) + res = ftdi_write_data(&gFTDIContext, (unsigned char *)buf, chunk); + else +#endif +#ifndef __WIN32__ + res = write(fd_gecko, buf, count); + if (res < 1) { + perror("gecko_write"); + return 1; + } +#else + if (!WriteFile(handle_gecko, buf, chunk, &res, NULL)) { + fprintf (stderr, "gecko_write\n"); + return 1; + } +#endif + + left -= res; + buf += res; + +#if !defined(__WIN32__) && !defined(USE_LIBFTDI) + // does this work with ftdi-sio? + if (tcdrain(fd_gecko)) { + perror ("gecko_drain"); + return 1; + } +#endif + } + + return 0; +} + diff --git a/channel/wiiload/gecko.h b/channel/wiiload/gecko.h new file mode 100644 index 0000000..e92991a --- /dev/null +++ b/channel/wiiload/gecko.h @@ -0,0 +1,32 @@ +/* + * Copyright (C) 2008 dhewg, #wiidev efnet + * + * this file is part of wiifuse + * http://wiibrew.org/index.php?title=Wiifuse + * + * 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., 59 Temple Place, Suite 330, Boston, MA 02111-1307 USA + */ + +#ifndef _GECKO_H_ +#define _GECKO_H_ + +int gecko_open (const char *dev); +void gecko_close (); +void gecko_flush (); +int gecko_read (void *buf, size_t count); +int gecko_write (const void *buf, size_t count); + +#endif + diff --git a/channel/wiiload/main.c b/channel/wiiload/main.c new file mode 100644 index 0000000..f28199a --- /dev/null +++ b/channel/wiiload/main.c @@ -0,0 +1,525 @@ +/* + * Copyright (C) 2008 dhewg, #wiidev efnet + * + * this file is part of geckoloader + * http://wiibrew.org/index.php?title=Geckoloader + * + * 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., 59 Temple Place, Suite 330, Boston, MA 02111-1307 USA + */ + +#include +#include +#include +#include +#include +#include +#include +#include +#include +#ifndef _WIN32 +#include +#include +#include +#include +#else +#include +#endif + +#include + +#include "gecko.h" + +#define WIILOAD_VERSION_MAYOR 0 +#define WIILOAD_VERSION_MINOR 5 + +#define LD_TCP_PORT 4299 + +#define MAX_ARGS_LEN 1024 + +#ifndef O_BINARY +#define O_BINARY 0 +#endif + +typedef unsigned char u8; +typedef unsigned short u16; +typedef unsigned int u32; +typedef unsigned long long u64; +typedef signed char s8; +typedef signed short s16; +typedef signed int s32; +typedef signed long long s64; + +typedef enum { false, true } bool; + +#ifndef __WIN32__ +static const char *desc_export = "export"; +#ifndef __APPLE__ +static const char *desc_gecko = "/dev/ttyUSB0"; +#else +static const char *desc_gecko = "/dev/tty.usbserial-GECKUSB0"; +#endif +#else +static const char *desc_export = "set"; +static const char *desc_gecko = "COM4"; +#endif + +static const char *envvar = "WIILOAD"; + +static bool send_gecko (const char *dev, const u8 *buf, u32 len, u32 len_un, + const char *args, u16 args_len) { + u8 b[4]; + u32 left, block; + const u8 *p; + + if (gecko_open (dev)) { + fprintf (stderr, "unable to open the device '%s'\n", dev); + return false; + } + + printf ("sending upload request\n"); + + b[0] = 'H'; + b[1] = 'A'; + b[2] = 'X'; + b[3] = 'X'; + + if (gecko_write (b, 4)) { + gecko_close (); + fprintf (stderr, "error sending data\n"); + return false; + } + + b[0] = WIILOAD_VERSION_MAYOR; + b[1] = WIILOAD_VERSION_MINOR; + b[2] = (args_len >> 8) & 0xff; + b[3] = args_len & 0xff; + + if (gecko_write (b, 4)) { + gecko_close (); + fprintf (stderr, "error sending data\n"); + return false; + } + + printf ("sending file size (%u bytes)\n", len); + + b[0] = (len >> 24) & 0xff; + b[1] = (len >> 16) & 0xff; + b[2] = (len >> 8) & 0xff; + b[3] = len & 0xff; + + if (gecko_write (b, 4)) { + gecko_close (); + fprintf (stderr, "error sending data\n"); + return false; + } + + b[0] = (len_un >> 24) & 0xff; + b[1] = (len_un >> 16) & 0xff; + b[2] = (len_un >> 8) & 0xff; + b[3] = len_un & 0xff; + + if (gecko_write (b, 4)) { + gecko_close (); + fprintf (stderr, "error sending data\n"); + return false; + } + + printf ("sending data"); + fflush (stdout); + + left = len; + p = buf; + while (left) { + block = left; + if (block > 63488) + block = 63488; + left -= block; + + if (gecko_write (p, block)) { + fprintf (stderr, "error sending block\n"); + break; + } + p += block; + + printf ("."); + fflush (stdout); + + } + printf ("\n"); + + if (args_len) { + printf ("sending arguments (%u bytes)\n", args_len); + + if (gecko_write ((u8 *) args, args_len)) { + gecko_close (); + return false; + } + } + + gecko_close (); + + return true; +} + +static bool tcp_write (int s, const u8 *buf, u32 len) { + s32 left, block; + const u8 *p; + + left = len; + p = buf; + while (left) { + block = send (s, p, left, 0); + + if (block < 0) { + perror ("send failed"); + return false; + } + + left -= block; + p += block; + } + + return true; +} + +static bool send_tcp (const char *host, const u8 *buf, u32 len, u32 len_un, + const char *args, u16 args_len) { + struct sockaddr_in sa; + struct hostent *he; + int s, bc; + u8 b[4]; + off_t left, block; + const u8 *p; + +#ifdef __WIN32__ + WSADATA wsa_data; + if (WSAStartup (MAKEWORD(2,2), &wsa_data)) { + printf ("WSAStartup failed\n"); + return false; + } +#endif + + memset (&sa, 0, sizeof (sa)); + + sa.sin_addr.s_addr = inet_addr (host); + + if (sa.sin_addr.s_addr == INADDR_NONE) { + printf ("resolving %s\n", host); + + he = gethostbyname (host); + + if (!he) { +#ifndef __WIN32__ + herror ("error resolving hostname"); +#else + fprintf (stderr, "error resolving hostname\n"); +#endif + return false; + } + + if (he->h_addrtype != AF_INET) { + fprintf (stderr, "unsupported address"); + return false; + } + + sa.sin_addr.s_addr = *((u32 *) he->h_addr); + } + + s = socket (PF_INET, SOCK_STREAM, 0); + + if (s < 0) { + perror ("error creating socket"); + return false; + } + + sa.sin_port = htons (LD_TCP_PORT); + sa.sin_family = AF_INET; + + printf ("connecting to %s:%d\n", inet_ntoa (sa.sin_addr), LD_TCP_PORT); + + if (connect (s, (struct sockaddr *) &sa, sizeof (sa)) == -1) { + perror ("error connecting"); + close (s); + return false; + } + + printf ("sending upload request\n"); + + b[0] = 'H'; + b[1] = 'A'; + b[2] = 'X'; + b[3] = 'X'; + + if (!tcp_write (s, b, 4)) { + close (s); + return false; + } + + b[0] = WIILOAD_VERSION_MAYOR; + b[1] = WIILOAD_VERSION_MINOR; + b[2] = (args_len >> 8) & 0xff; + b[3] = args_len & 0xff; + + if (!tcp_write (s, b, 4)) { + close (s); + return false; + } + + printf ("sending file size (%u bytes)\n", len); + + b[0] = (len >> 24) & 0xff; + b[1] = (len >> 16) & 0xff; + b[2] = (len >> 8) & 0xff; + b[3] = len & 0xff; + + if (!tcp_write (s, b, 4)) { + close (s); + return false; + } + + b[0] = (len_un >> 24) & 0xff; + b[1] = (len_un >> 16) & 0xff; + b[2] = (len_un >> 8) & 0xff; + b[3] = len_un & 0xff; + + if (!tcp_write (s, b, 4)) { + close (s); + return false; + } + + printf ("sending data"); + fflush (stdout); + + left = len; + p = buf; + bc = 0; + while (left) { + block = left; + if (block > 4 * 1024) + block = 4 * 1024; + left -= block; + + if (!tcp_write (s, p, block)) { + close (s); + return false; + } + + p += block; + bc++; + + if (!(bc % 16)) { + printf ("."); + fflush (stdout); + } + } + + printf ("\n"); + + if (args_len) { + printf ("sending arguments (%u bytes)\n", args_len); + + if (!tcp_write (s, (u8 *) args, args_len)) { + close (s); + return false; + } + } + +#ifndef __WIN32__ + close (s); +#else + shutdown (s, SD_SEND); + closesocket (s); + WSACleanup (); +#endif + + return true; +} + +static void usage (const char *argv0) { + fprintf (stderr, "set the environment variable %s to a valid " + "destination.\n\n" + "examples:\n" + "\tusbgecko mode:\n" + "\t\t%s %s=%s\n\n" + "\ttcp mode:\n" + "\t\t%s %s=tcp:wii\n" + "\t\t%s %s=tcp:192.168.0.30\n\n" + "usage:\n" + "\t%s \n\n", + envvar, + desc_export, envvar, desc_gecko, + desc_export, envvar, + desc_export, envvar, + argv0); + exit (EXIT_FAILURE); +} + +int main (int argc, char **argv) { + int fd; + struct stat st; + char *ev; + bool compress = true; + u8 *buf, *bufz; + off_t fsize; + uLongf bufzlen = 0; + u32 len, len_un; + + int i, c; + char args[MAX_ARGS_LEN]; + char *arg_pos; + u16 args_len, args_left; + + bool res; + + printf ("wiiload v%u.%u\n" + "coded by dhewg, #wiidev efnet\n\n", + WIILOAD_VERSION_MAYOR, WIILOAD_VERSION_MINOR); + + if (argc < 2) + usage (*argv); + + ev = getenv (envvar); + if (!ev) + usage (*argv); + + fd = open (argv[1], O_RDONLY | O_BINARY); + if (fd < 0) { + perror ("error opening the file"); + exit (EXIT_FAILURE); + } + + if (fstat (fd, &st)) { + close (fd); + perror ("error stat'ing the file"); + exit (EXIT_FAILURE); + } + + fsize = st.st_size; + + if (fsize < 64 || fsize > 20 * 1024 * 1024) { + close (fd); + fprintf (stderr, "error: invalid file size\n"); + exit (EXIT_FAILURE); + } + + buf = malloc (fsize); + if (!buf) { + close (fd); + fprintf (stderr, "out of memory\n"); + exit (EXIT_FAILURE); + } + + if (read (fd, buf, fsize) != fsize) { + close (fd); + free (buf); + perror ("error reading the file"); + exit (EXIT_FAILURE); + } + close (fd); + + len = fsize; + len_un = 0; + + if (!memcmp(buf, "PK\x03\x04", 4)) + compress = false; + + if (compress) { + bufzlen = (uLongf) ((float) fsize * 1.02); + + bufz = malloc (bufzlen); + if (!bufz) { + fprintf (stderr, "out of memory\n"); + exit (EXIT_FAILURE); + } + + printf("compressing %u bytes...", (u32) fsize); + fflush(stdout); + + res = compress2 (bufz, &bufzlen, buf, fsize, 6); + if (res != Z_OK) { + free(buf); + free(bufz); + fprintf (stderr, "error compressing data: %d\n", res); + exit (EXIT_FAILURE); + } + + if (bufzlen < (u32) fsize) { + printf(" %.2f%%\n", 100.0f * (float) bufzlen / (float) fsize); + + len = bufzlen; + len_un = fsize; + free(buf); + buf = bufz; + } else { + printf(" compressed size gained size, discarding\n"); + free(bufz); + } + } + + args_len = 0; + + arg_pos = args; + args_left = MAX_ARGS_LEN; + + c = snprintf (arg_pos, args_left, "%s", basename (argv[1])); + arg_pos += c + 1; + args_left -= c + 1; + + if (argc > 2) { + for (i = 2; i < argc; ++i) { + c = snprintf (arg_pos, args_left, "%s", argv[i]); + + if (c >= args_left) { + free (buf); + fprintf (stderr, "argument string too long\n"); + exit (EXIT_FAILURE); + } + + arg_pos += c + 1; + args_left -= c + 1; + } + + if (args_left < 1) { + free (buf); + fprintf (stderr, "argument string too long\n"); + exit (EXIT_FAILURE); + } + } + + arg_pos[0] = 0; + args_len = MAX_ARGS_LEN - args_left + 1; + + if (strncmp (ev, "tcp:", 4)) { + if (stat (ev, &st)) + usage (*argv); + + res = send_gecko (ev, buf, len, len_un, args, args_len); + } else { + if (strlen (ev) < 5) + usage (*argv); + + res = send_tcp (&ev[4], buf, len, len_un, args, args_len); + } + + if (res) + printf ("done.\n"); + else + printf ("transfer failed.\n"); + + free (buf); + + return 0; +} + diff --git a/channel/wiiload/win32/include/zconf.h b/channel/wiiload/win32/include/zconf.h new file mode 100644 index 0000000..6d450fc --- /dev/null +++ b/channel/wiiload/win32/include/zconf.h @@ -0,0 +1,279 @@ +/* zconf.h -- configuration of the zlib compression library + * Copyright (C) 1995-1998 Jean-loup Gailly. + * For conditions of distribution and use, see copyright notice in zlib.h + */ + +/* @(#) $Id$ */ + +#ifndef _ZCONF_H +#define _ZCONF_H + +/* + * If you *really* need a unique prefix for all types and library functions, + * compile with -DZ_PREFIX. The "standard" zlib should be compiled without it. + */ +#ifdef Z_PREFIX +# define deflateInit_ z_deflateInit_ +# define deflate z_deflate +# define deflateEnd z_deflateEnd +# define inflateInit_ z_inflateInit_ +# define inflate z_inflate +# define inflateEnd z_inflateEnd +# define deflateInit2_ z_deflateInit2_ +# define deflateSetDictionary z_deflateSetDictionary +# define deflateCopy z_deflateCopy +# define deflateReset z_deflateReset +# define deflateParams z_deflateParams +# define inflateInit2_ z_inflateInit2_ +# define inflateSetDictionary z_inflateSetDictionary +# define inflateSync z_inflateSync +# define inflateSyncPoint z_inflateSyncPoint +# define inflateReset z_inflateReset +# define compress z_compress +# define compress2 z_compress2 +# define uncompress z_uncompress +# define adler32 z_adler32 +# define crc32 z_crc32 +# define get_crc_table z_get_crc_table + +# define Byte z_Byte +# define uInt z_uInt +# define uLong z_uLong +# define Bytef z_Bytef +# define charf z_charf +# define intf z_intf +# define uIntf z_uIntf +# define uLongf z_uLongf +# define voidpf z_voidpf +# define voidp z_voidp +#endif + +#if (defined(_WIN32) || defined(__WIN32__)) && !defined(WIN32) +# define WIN32 +#endif +#if defined(__GNUC__) || defined(WIN32) || defined(__386__) || defined(i386) +# ifndef __32BIT__ +# define __32BIT__ +# endif +#endif +#if defined(__MSDOS__) && !defined(MSDOS) +# define MSDOS +#endif + +/* + * Compile with -DMAXSEG_64K if the alloc function cannot allocate more + * than 64k bytes at a time (needed on systems with 16-bit int). + */ +#if defined(MSDOS) && !defined(__32BIT__) +# define MAXSEG_64K +#endif +#ifdef MSDOS +# define UNALIGNED_OK +#endif + +#if (defined(MSDOS) || defined(_WINDOWS) || defined(WIN32)) && !defined(STDC) +# define STDC +#endif +#if defined(__STDC__) || defined(__cplusplus) || defined(__OS2__) +# ifndef STDC +# define STDC +# endif +#endif + +#ifndef STDC +# ifndef const /* cannot use !defined(STDC) && !defined(const) on Mac */ +# define const +# endif +#endif + +/* Some Mac compilers merge all .h files incorrectly: */ +#if defined(__MWERKS__) || defined(applec) ||defined(THINK_C) ||defined(__SC__) +# define NO_DUMMY_DECL +#endif + +/* Old Borland C incorrectly complains about missing returns: */ +#if defined(__BORLANDC__) && (__BORLANDC__ < 0x500) +# define NEED_DUMMY_RETURN +#endif + + +/* Maximum value for memLevel in deflateInit2 */ +#ifndef MAX_MEM_LEVEL +# ifdef MAXSEG_64K +# define MAX_MEM_LEVEL 8 +# else +# define MAX_MEM_LEVEL 9 +# endif +#endif + +/* Maximum value for windowBits in deflateInit2 and inflateInit2. + * WARNING: reducing MAX_WBITS makes minigzip unable to extract .gz files + * created by gzip. (Files created by minigzip can still be extracted by + * gzip.) + */ +#ifndef MAX_WBITS +# define MAX_WBITS 15 /* 32K LZ77 window */ +#endif + +/* The memory requirements for deflate are (in bytes): + (1 << (windowBits+2)) + (1 << (memLevel+9)) + that is: 128K for windowBits=15 + 128K for memLevel = 8 (default values) + plus a few kilobytes for small objects. For example, if you want to reduce + the default memory requirements from 256K to 128K, compile with + make CFLAGS="-O -DMAX_WBITS=14 -DMAX_MEM_LEVEL=7" + Of course this will generally degrade compression (there's no free lunch). + + The memory requirements for inflate are (in bytes) 1 << windowBits + that is, 32K for windowBits=15 (default value) plus a few kilobytes + for small objects. +*/ + + /* Type declarations */ + +#ifndef OF /* function prototypes */ +# ifdef STDC +# define OF(args) args +# else +# define OF(args) () +# endif +#endif + +/* The following definitions for FAR are needed only for MSDOS mixed + * model programming (small or medium model with some far allocations). + * This was tested only with MSC; for other MSDOS compilers you may have + * to define NO_MEMCPY in zutil.h. If you don't need the mixed model, + * just define FAR to be empty. + */ +#if (defined(M_I86SM) || defined(M_I86MM)) && !defined(__32BIT__) + /* MSC small or medium model */ +# define SMALL_MEDIUM +# ifdef _MSC_VER +# define FAR _far +# else +# define FAR far +# endif +#endif +#if defined(__BORLANDC__) && (defined(__SMALL__) || defined(__MEDIUM__)) +# ifndef __32BIT__ +# define SMALL_MEDIUM +# define FAR _far +# endif +#endif + +/* Compile with -DZLIB_DLL for Windows DLL support */ +#if defined(ZLIB_DLL) +# if defined(_WINDOWS) || defined(WINDOWS) +# ifdef FAR +# undef FAR +# endif +# include +# define ZEXPORT WINAPI +# ifdef WIN32 +# define ZEXPORTVA WINAPIV +# else +# define ZEXPORTVA FAR _cdecl _export +# endif +# endif +# if defined (__BORLANDC__) +# if (__BORLANDC__ >= 0x0500) && defined (WIN32) +# include +# define ZEXPORT __declspec(dllexport) WINAPI +# define ZEXPORTRVA __declspec(dllexport) WINAPIV +# else +# if defined (_Windows) && defined (__DLL__) +# define ZEXPORT _export +# define ZEXPORTVA _export +# endif +# endif +# endif +#endif + +#if defined (__BEOS__) +# if defined (ZLIB_DLL) +# define ZEXTERN extern __declspec(dllexport) +# else +# define ZEXTERN extern __declspec(dllimport) +# endif +#endif + +#ifndef ZEXPORT +# define ZEXPORT +#endif +#ifndef ZEXPORTVA +# define ZEXPORTVA +#endif +#ifndef ZEXTERN +# define ZEXTERN extern +#endif + +#ifndef FAR +# define FAR +#endif + +#if !defined(MACOS) && !defined(TARGET_OS_MAC) +typedef unsigned char Byte; /* 8 bits */ +#endif +typedef unsigned int uInt; /* 16 bits or more */ +typedef unsigned long uLong; /* 32 bits or more */ + +#ifdef SMALL_MEDIUM + /* Borland C/C++ and some old MSC versions ignore FAR inside typedef */ +# define Bytef Byte FAR +#else + typedef Byte FAR Bytef; +#endif +typedef char FAR charf; +typedef int FAR intf; +typedef uInt FAR uIntf; +typedef uLong FAR uLongf; + +#ifdef STDC + typedef void FAR *voidpf; + typedef void *voidp; +#else + typedef Byte FAR *voidpf; + typedef Byte *voidp; +#endif + +#ifdef HAVE_UNISTD_H +# include /* for off_t */ +# include /* for SEEK_* and off_t */ +# define z_off_t off_t +#endif +#ifndef SEEK_SET +# define SEEK_SET 0 /* Seek from beginning of file. */ +# define SEEK_CUR 1 /* Seek from current position. */ +# define SEEK_END 2 /* Set file pointer to EOF plus "offset" */ +#endif +#ifndef z_off_t +# define z_off_t long +#endif + +/* MVS linker does not support external names larger than 8 bytes */ +#if defined(__MVS__) +# pragma map(deflateInit_,"DEIN") +# pragma map(deflateInit2_,"DEIN2") +# pragma map(deflateEnd,"DEEND") +# pragma map(inflateInit_,"ININ") +# pragma map(inflateInit2_,"ININ2") +# pragma map(inflateEnd,"INEND") +# pragma map(inflateSync,"INSY") +# pragma map(inflateSetDictionary,"INSEDI") +# pragma map(inflate_blocks,"INBL") +# pragma map(inflate_blocks_new,"INBLNE") +# pragma map(inflate_blocks_free,"INBLFR") +# pragma map(inflate_blocks_reset,"INBLRE") +# pragma map(inflate_codes_free,"INCOFR") +# pragma map(inflate_codes,"INCO") +# pragma map(inflate_fast,"INFA") +# pragma map(inflate_flush,"INFLU") +# pragma map(inflate_mask,"INMA") +# pragma map(inflate_set_dictionary,"INSEDI2") +# pragma map(inflate_copyright,"INCOPY") +# pragma map(inflate_trees_bits,"INTRBI") +# pragma map(inflate_trees_dynamic,"INTRDY") +# pragma map(inflate_trees_fixed,"INTRFI") +# pragma map(inflate_trees_free,"INTRFR") +#endif + +#endif /* _ZCONF_H */ diff --git a/channel/wiiload/win32/include/zlib.h b/channel/wiiload/win32/include/zlib.h new file mode 100644 index 0000000..49f56b4 --- /dev/null +++ b/channel/wiiload/win32/include/zlib.h @@ -0,0 +1,893 @@ +/* zlib.h -- interface of the 'zlib' general purpose compression library + version 1.1.3, July 9th, 1998 + + Copyright (C) 1995-1998 Jean-loup Gailly and Mark Adler + + This software is provided 'as-is', without any express or implied + warranty. In no event will the authors be held liable for any damages + arising from the use of this software. + + Permission is granted to anyone to use this software for any purpose, + including commercial applications, and to alter it and redistribute it + freely, subject to the following restrictions: + + 1. The origin of this software must not be misrepresented; you must not + claim that you wrote the original software. If you use this software + in a product, an acknowledgment in the product documentation would be + appreciated but is not required. + 2. Altered source versions must be plainly marked as such, and must not be + misrepresented as being the original software. + 3. This notice may not be removed or altered from any source distribution. + + Jean-loup Gailly Mark Adler + jloup@gzip.org madler@alumni.caltech.edu + + + The data format used by the zlib library is described by RFCs (Request for + Comments) 1950 to 1952 in the files ftp://ds.internic.net/rfc/rfc1950.txt + (zlib format), rfc1951.txt (deflate format) and rfc1952.txt (gzip format). +*/ + +#ifndef _ZLIB_H +#define _ZLIB_H + +#include "zconf.h" + +#ifdef __cplusplus +extern "C" { +#endif + +#define ZLIB_VERSION "1.1.3" + +/* + The 'zlib' compression library provides in-memory compression and + decompression functions, including integrity checks of the uncompressed + data. This version of the library supports only one compression method + (deflation) but other algorithms will be added later and will have the same + stream interface. + + Compression can be done in a single step if the buffers are large + enough (for example if an input file is mmap'ed), or can be done by + repeated calls of the compression function. In the latter case, the + application must provide more input and/or consume the output + (providing more output space) before each call. + + The library also supports reading and writing files in gzip (.gz) format + with an interface similar to that of stdio. + + The library does not install any signal handler. The decoder checks + the consistency of the compressed data, so the library should never + crash even in case of corrupted input. +*/ + +typedef voidpf (*alloc_func) OF((voidpf opaque, uInt items, uInt size)); +typedef void (*free_func) OF((voidpf opaque, voidpf address)); + +struct internal_state; + +typedef struct z_stream_s { + Bytef *next_in; /* next input byte */ + uInt avail_in; /* number of bytes available at next_in */ + uLong total_in; /* total nb of input bytes read so far */ + + Bytef *next_out; /* next output byte should be put there */ + uInt avail_out; /* remaining free space at next_out */ + uLong total_out; /* total nb of bytes output so far */ + + char *msg; /* last error message, NULL if no error */ + struct internal_state FAR *state; /* not visible by applications */ + + alloc_func zalloc; /* used to allocate the internal state */ + free_func zfree; /* used to free the internal state */ + voidpf opaque; /* private data object passed to zalloc and zfree */ + + int data_type; /* best guess about the data type: ascii or binary */ + uLong adler; /* adler32 value of the uncompressed data */ + uLong reserved; /* reserved for future use */ +} z_stream; + +typedef z_stream FAR *z_streamp; + +/* + The application must update next_in and avail_in when avail_in has + dropped to zero. It must update next_out and avail_out when avail_out + has dropped to zero. The application must initialize zalloc, zfree and + opaque before calling the init function. All other fields are set by the + compression library and must not be updated by the application. + + The opaque value provided by the application will be passed as the first + parameter for calls of zalloc and zfree. This can be useful for custom + memory management. The compression library attaches no meaning to the + opaque value. + + zalloc must return Z_NULL if there is not enough memory for the object. + If zlib is used in a multi-threaded application, zalloc and zfree must be + thread safe. + + On 16-bit systems, the functions zalloc and zfree must be able to allocate + exactly 65536 bytes, but will not be required to allocate more than this + if the symbol MAXSEG_64K is defined (see zconf.h). WARNING: On MSDOS, + pointers returned by zalloc for objects of exactly 65536 bytes *must* + have their offset normalized to zero. The default allocation function + provided by this library ensures this (see zutil.c). To reduce memory + requirements and avoid any allocation of 64K objects, at the expense of + compression ratio, compile the library with -DMAX_WBITS=14 (see zconf.h). + + The fields total_in and total_out can be used for statistics or + progress reports. After compression, total_in holds the total size of + the uncompressed data and may be saved for use in the decompressor + (particularly if the decompressor wants to decompress everything in + a single step). +*/ + + /* constants */ + +#define Z_NO_FLUSH 0 +#define Z_PARTIAL_FLUSH 1 /* will be removed, use Z_SYNC_FLUSH instead */ +#define Z_SYNC_FLUSH 2 +#define Z_FULL_FLUSH 3 +#define Z_FINISH 4 +/* Allowed flush values; see deflate() below for details */ + +#define Z_OK 0 +#define Z_STREAM_END 1 +#define Z_NEED_DICT 2 +#define Z_ERRNO (-1) +#define Z_STREAM_ERROR (-2) +#define Z_DATA_ERROR (-3) +#define Z_MEM_ERROR (-4) +#define Z_BUF_ERROR (-5) +#define Z_VERSION_ERROR (-6) +/* Return codes for the compression/decompression functions. Negative + * values are errors, positive values are used for special but normal events. + */ + +#define Z_NO_COMPRESSION 0 +#define Z_BEST_SPEED 1 +#define Z_BEST_COMPRESSION 9 +#define Z_DEFAULT_COMPRESSION (-1) +/* compression levels */ + +#define Z_FILTERED 1 +#define Z_HUFFMAN_ONLY 2 +#define Z_DEFAULT_STRATEGY 0 +/* compression strategy; see deflateInit2() below for details */ + +#define Z_BINARY 0 +#define Z_ASCII 1 +#define Z_UNKNOWN 2 +/* Possible values of the data_type field */ + +#define Z_DEFLATED 8 +/* The deflate compression method (the only one supported in this version) */ + +#define Z_NULL 0 /* for initializing zalloc, zfree, opaque */ + +#define zlib_version zlibVersion() +/* for compatibility with versions < 1.0.2 */ + + /* basic functions */ + +ZEXTERN const char * ZEXPORT zlibVersion OF((void)); +/* The application can compare zlibVersion and ZLIB_VERSION for consistency. + If the first character differs, the library code actually used is + not compatible with the zlib.h header file used by the application. + This check is automatically made by deflateInit and inflateInit. + */ + +/* +ZEXTERN int ZEXPORT deflateInit OF((z_streamp strm, int level)); + + Initializes the internal stream state for compression. The fields + zalloc, zfree and opaque must be initialized before by the caller. + If zalloc and zfree are set to Z_NULL, deflateInit updates them to + use default allocation functions. + + The compression level must be Z_DEFAULT_COMPRESSION, or between 0 and 9: + 1 gives best speed, 9 gives best compression, 0 gives no compression at + all (the input data is simply copied a block at a time). + Z_DEFAULT_COMPRESSION requests a default compromise between speed and + compression (currently equivalent to level 6). + + deflateInit returns Z_OK if success, Z_MEM_ERROR if there was not + enough memory, Z_STREAM_ERROR if level is not a valid compression level, + Z_VERSION_ERROR if the zlib library version (zlib_version) is incompatible + with the version assumed by the caller (ZLIB_VERSION). + msg is set to null if there is no error message. deflateInit does not + perform any compression: this will be done by deflate(). +*/ + + +ZEXTERN int ZEXPORT deflate OF((z_streamp strm, int flush)); +/* + deflate compresses as much data as possible, and stops when the input + buffer becomes empty or the output buffer becomes full. It may introduce some + output latency (reading input without producing any output) except when + forced to flush. + + The detailed semantics are as follows. deflate performs one or both of the + following actions: + + - Compress more input starting at next_in and update next_in and avail_in + accordingly. If not all input can be processed (because there is not + enough room in the output buffer), next_in and avail_in are updated and + processing will resume at this point for the next call of deflate(). + + - Provide more output starting at next_out and update next_out and avail_out + accordingly. This action is forced if the parameter flush is non zero. + Forcing flush frequently degrades the compression ratio, so this parameter + should be set only when necessary (in interactive applications). + Some output may be provided even if flush is not set. + + Before the call of deflate(), the application should ensure that at least + one of the actions is possible, by providing more input and/or consuming + more output, and updating avail_in or avail_out accordingly; avail_out + should never be zero before the call. The application can consume the + compressed output when it wants, for example when the output buffer is full + (avail_out == 0), or after each call of deflate(). If deflate returns Z_OK + and with zero avail_out, it must be called again after making room in the + output buffer because there might be more output pending. + + If the parameter flush is set to Z_SYNC_FLUSH, all pending output is + flushed to the output buffer and the output is aligned on a byte boundary, so + that the decompressor can get all input data available so far. (In particular + avail_in is zero after the call if enough output space has been provided + before the call.) Flushing may degrade compression for some compression + algorithms and so it should be used only when necessary. + + If flush is set to Z_FULL_FLUSH, all output is flushed as with + Z_SYNC_FLUSH, and the compression state is reset so that decompression can + restart from this point if previous compressed data has been damaged or if + random access is desired. Using Z_FULL_FLUSH too often can seriously degrade + the compression. + + If deflate returns with avail_out == 0, this function must be called again + with the same value of the flush parameter and more output space (updated + avail_out), until the flush is complete (deflate returns with non-zero + avail_out). + + If the parameter flush is set to Z_FINISH, pending input is processed, + pending output is flushed and deflate returns with Z_STREAM_END if there + was enough output space; if deflate returns with Z_OK, this function must be + called again with Z_FINISH and more output space (updated avail_out) but no + more input data, until it returns with Z_STREAM_END or an error. After + deflate has returned Z_STREAM_END, the only possible operations on the + stream are deflateReset or deflateEnd. + + Z_FINISH can be used immediately after deflateInit if all the compression + is to be done in a single step. In this case, avail_out must be at least + 0.1% larger than avail_in plus 12 bytes. If deflate does not return + Z_STREAM_END, then it must be called again as described above. + + deflate() sets strm->adler to the adler32 checksum of all input read + so far (that is, total_in bytes). + + deflate() may update data_type if it can make a good guess about + the input data type (Z_ASCII or Z_BINARY). In doubt, the data is considered + binary. This field is only for information purposes and does not affect + the compression algorithm in any manner. + + deflate() returns Z_OK if some progress has been made (more input + processed or more output produced), Z_STREAM_END if all input has been + consumed and all output has been produced (only when flush is set to + Z_FINISH), Z_STREAM_ERROR if the stream state was inconsistent (for example + if next_in or next_out was NULL), Z_BUF_ERROR if no progress is possible + (for example avail_in or avail_out was zero). +*/ + + +ZEXTERN int ZEXPORT deflateEnd OF((z_streamp strm)); +/* + All dynamically allocated data structures for this stream are freed. + This function discards any unprocessed input and does not flush any + pending output. + + deflateEnd returns Z_OK if success, Z_STREAM_ERROR if the + stream state was inconsistent, Z_DATA_ERROR if the stream was freed + prematurely (some input or output was discarded). In the error case, + msg may be set but then points to a static string (which must not be + deallocated). +*/ + + +/* +ZEXTERN int ZEXPORT inflateInit OF((z_streamp strm)); + + Initializes the internal stream state for decompression. The fields + next_in, avail_in, zalloc, zfree and opaque must be initialized before by + the caller. If next_in is not Z_NULL and avail_in is large enough (the exact + value depends on the compression method), inflateInit determines the + compression method from the zlib header and allocates all data structures + accordingly; otherwise the allocation will be deferred to the first call of + inflate. If zalloc and zfree are set to Z_NULL, inflateInit updates them to + use default allocation functions. + + inflateInit returns Z_OK if success, Z_MEM_ERROR if there was not enough + memory, Z_VERSION_ERROR if the zlib library version is incompatible with the + version assumed by the caller. msg is set to null if there is no error + message. inflateInit does not perform any decompression apart from reading + the zlib header if present: this will be done by inflate(). (So next_in and + avail_in may be modified, but next_out and avail_out are unchanged.) +*/ + + +ZEXTERN int ZEXPORT inflate OF((z_streamp strm, int flush)); +/* + inflate decompresses as much data as possible, and stops when the input + buffer becomes empty or the output buffer becomes full. It may some + introduce some output latency (reading input without producing any output) + except when forced to flush. + + The detailed semantics are as follows. inflate performs one or both of the + following actions: + + - Decompress more input starting at next_in and update next_in and avail_in + accordingly. If not all input can be processed (because there is not + enough room in the output buffer), next_in is updated and processing + will resume at this point for the next call of inflate(). + + - Provide more output starting at next_out and update next_out and avail_out + accordingly. inflate() provides as much output as possible, until there + is no more input data or no more space in the output buffer (see below + about the flush parameter). + + Before the call of inflate(), the application should ensure that at least + one of the actions is possible, by providing more input and/or consuming + more output, and updating the next_* and avail_* values accordingly. + The application can consume the uncompressed output when it wants, for + example when the output buffer is full (avail_out == 0), or after each + call of inflate(). If inflate returns Z_OK and with zero avail_out, it + must be called again after making room in the output buffer because there + might be more output pending. + + If the parameter flush is set to Z_SYNC_FLUSH, inflate flushes as much + output as possible to the output buffer. The flushing behavior of inflate is + not specified for values of the flush parameter other than Z_SYNC_FLUSH + and Z_FINISH, but the current implementation actually flushes as much output + as possible anyway. + + inflate() should normally be called until it returns Z_STREAM_END or an + error. However if all decompression is to be performed in a single step + (a single call of inflate), the parameter flush should be set to + Z_FINISH. In this case all pending input is processed and all pending + output is flushed; avail_out must be large enough to hold all the + uncompressed data. (The size of the uncompressed data may have been saved + by the compressor for this purpose.) The next operation on this stream must + be inflateEnd to deallocate the decompression state. The use of Z_FINISH + is never required, but can be used to inform inflate that a faster routine + may be used for the single inflate() call. + + If a preset dictionary is needed at this point (see inflateSetDictionary + below), inflate sets strm-adler to the adler32 checksum of the + dictionary chosen by the compressor and returns Z_NEED_DICT; otherwise + it sets strm->adler to the adler32 checksum of all output produced + so far (that is, total_out bytes) and returns Z_OK, Z_STREAM_END or + an error code as described below. At the end of the stream, inflate() + checks that its computed adler32 checksum is equal to that saved by the + compressor and returns Z_STREAM_END only if the checksum is correct. + + inflate() returns Z_OK if some progress has been made (more input processed + or more output produced), Z_STREAM_END if the end of the compressed data has + been reached and all uncompressed output has been produced, Z_NEED_DICT if a + preset dictionary is needed at this point, Z_DATA_ERROR if the input data was + corrupted (input stream not conforming to the zlib format or incorrect + adler32 checksum), Z_STREAM_ERROR if the stream structure was inconsistent + (for example if next_in or next_out was NULL), Z_MEM_ERROR if there was not + enough memory, Z_BUF_ERROR if no progress is possible or if there was not + enough room in the output buffer when Z_FINISH is used. In the Z_DATA_ERROR + case, the application may then call inflateSync to look for a good + compression block. +*/ + + +ZEXTERN int ZEXPORT inflateEnd OF((z_streamp strm)); +/* + All dynamically allocated data structures for this stream are freed. + This function discards any unprocessed input and does not flush any + pending output. + + inflateEnd returns Z_OK if success, Z_STREAM_ERROR if the stream state + was inconsistent. In the error case, msg may be set but then points to a + static string (which must not be deallocated). +*/ + + /* Advanced functions */ + +/* + The following functions are needed only in some special applications. +*/ + +/* +ZEXTERN int ZEXPORT deflateInit2 OF((z_streamp strm, + int level, + int method, + int windowBits, + int memLevel, + int strategy)); + + This is another version of deflateInit with more compression options. The + fields next_in, zalloc, zfree and opaque must be initialized before by + the caller. + + The method parameter is the compression method. It must be Z_DEFLATED in + this version of the library. + + The windowBits parameter is the base two logarithm of the window size + (the size of the history buffer). It should be in the range 8..15 for this + version of the library. Larger values of this parameter result in better + compression at the expense of memory usage. The default value is 15 if + deflateInit is used instead. + + The memLevel parameter specifies how much memory should be allocated + for the internal compression state. memLevel=1 uses minimum memory but + is slow and reduces compression ratio; memLevel=9 uses maximum memory + for optimal speed. The default value is 8. See zconf.h for total memory + usage as a function of windowBits and memLevel. + + The strategy parameter is used to tune the compression algorithm. Use the + value Z_DEFAULT_STRATEGY for normal data, Z_FILTERED for data produced by a + filter (or predictor), or Z_HUFFMAN_ONLY to force Huffman encoding only (no + string match). Filtered data consists mostly of small values with a + somewhat random distribution. In this case, the compression algorithm is + tuned to compress them better. The effect of Z_FILTERED is to force more + Huffman coding and less string matching; it is somewhat intermediate + between Z_DEFAULT and Z_HUFFMAN_ONLY. The strategy parameter only affects + the compression ratio but not the correctness of the compressed output even + if it is not set appropriately. + + deflateInit2 returns Z_OK if success, Z_MEM_ERROR if there was not enough + memory, Z_STREAM_ERROR if a parameter is invalid (such as an invalid + method). msg is set to null if there is no error message. deflateInit2 does + not perform any compression: this will be done by deflate(). +*/ + +ZEXTERN int ZEXPORT deflateSetDictionary OF((z_streamp strm, + const Bytef *dictionary, + uInt dictLength)); +/* + Initializes the compression dictionary from the given byte sequence + without producing any compressed output. This function must be called + immediately after deflateInit, deflateInit2 or deflateReset, before any + call of deflate. The compressor and decompressor must use exactly the same + dictionary (see inflateSetDictionary). + + The dictionary should consist of strings (byte sequences) that are likely + to be encountered later in the data to be compressed, with the most commonly + used strings preferably put towards the end of the dictionary. Using a + dictionary is most useful when the data to be compressed is short and can be + predicted with good accuracy; the data can then be compressed better than + with the default empty dictionary. + + Depending on the size of the compression data structures selected by + deflateInit or deflateInit2, a part of the dictionary may in effect be + discarded, for example if the dictionary is larger than the window size in + deflate or deflate2. Thus the strings most likely to be useful should be + put at the end of the dictionary, not at the front. + + Upon return of this function, strm->adler is set to the Adler32 value + of the dictionary; the decompressor may later use this value to determine + which dictionary has been used by the compressor. (The Adler32 value + applies to the whole dictionary even if only a subset of the dictionary is + actually used by the compressor.) + + deflateSetDictionary returns Z_OK if success, or Z_STREAM_ERROR if a + parameter is invalid (such as NULL dictionary) or the stream state is + inconsistent (for example if deflate has already been called for this stream + or if the compression method is bsort). deflateSetDictionary does not + perform any compression: this will be done by deflate(). +*/ + +ZEXTERN int ZEXPORT deflateCopy OF((z_streamp dest, + z_streamp source)); +/* + Sets the destination stream as a complete copy of the source stream. + + This function can be useful when several compression strategies will be + tried, for example when there are several ways of pre-processing the input + data with a filter. The streams that will be discarded should then be freed + by calling deflateEnd. Note that deflateCopy duplicates the internal + compression state which can be quite large, so this strategy is slow and + can consume lots of memory. + + deflateCopy returns Z_OK if success, Z_MEM_ERROR if there was not + enough memory, Z_STREAM_ERROR if the source stream state was inconsistent + (such as zalloc being NULL). msg is left unchanged in both source and + destination. +*/ + +ZEXTERN int ZEXPORT deflateReset OF((z_streamp strm)); +/* + This function is equivalent to deflateEnd followed by deflateInit, + but does not free and reallocate all the internal compression state. + The stream will keep the same compression level and any other attributes + that may have been set by deflateInit2. + + deflateReset returns Z_OK if success, or Z_STREAM_ERROR if the source + stream state was inconsistent (such as zalloc or state being NULL). +*/ + +ZEXTERN int ZEXPORT deflateParams OF((z_streamp strm, + int level, + int strategy)); +/* + Dynamically update the compression level and compression strategy. The + interpretation of level and strategy is as in deflateInit2. This can be + used to switch between compression and straight copy of the input data, or + to switch to a different kind of input data requiring a different + strategy. If the compression level is changed, the input available so far + is compressed with the old level (and may be flushed); the new level will + take effect only at the next call of deflate(). + + Before the call of deflateParams, the stream state must be set as for + a call of deflate(), since the currently available input may have to + be compressed and flushed. In particular, strm->avail_out must be non-zero. + + deflateParams returns Z_OK if success, Z_STREAM_ERROR if the source + stream state was inconsistent or if a parameter was invalid, Z_BUF_ERROR + if strm->avail_out was zero. +*/ + +/* +ZEXTERN int ZEXPORT inflateInit2 OF((z_streamp strm, + int windowBits)); + + This is another version of inflateInit with an extra parameter. The + fields next_in, avail_in, zalloc, zfree and opaque must be initialized + before by the caller. + + The windowBits parameter is the base two logarithm of the maximum window + size (the size of the history buffer). It should be in the range 8..15 for + this version of the library. The default value is 15 if inflateInit is used + instead. If a compressed stream with a larger window size is given as + input, inflate() will return with the error code Z_DATA_ERROR instead of + trying to allocate a larger window. + + inflateInit2 returns Z_OK if success, Z_MEM_ERROR if there was not enough + memory, Z_STREAM_ERROR if a parameter is invalid (such as a negative + memLevel). msg is set to null if there is no error message. inflateInit2 + does not perform any decompression apart from reading the zlib header if + present: this will be done by inflate(). (So next_in and avail_in may be + modified, but next_out and avail_out are unchanged.) +*/ + +ZEXTERN int ZEXPORT inflateSetDictionary OF((z_streamp strm, + const Bytef *dictionary, + uInt dictLength)); +/* + Initializes the decompression dictionary from the given uncompressed byte + sequence. This function must be called immediately after a call of inflate + if this call returned Z_NEED_DICT. The dictionary chosen by the compressor + can be determined from the Adler32 value returned by this call of + inflate. The compressor and decompressor must use exactly the same + dictionary (see deflateSetDictionary). + + inflateSetDictionary returns Z_OK if success, Z_STREAM_ERROR if a + parameter is invalid (such as NULL dictionary) or the stream state is + inconsistent, Z_DATA_ERROR if the given dictionary doesn't match the + expected one (incorrect Adler32 value). inflateSetDictionary does not + perform any decompression: this will be done by subsequent calls of + inflate(). +*/ + +ZEXTERN int ZEXPORT inflateSync OF((z_streamp strm)); +/* + Skips invalid compressed data until a full flush point (see above the + description of deflate with Z_FULL_FLUSH) can be found, or until all + available input is skipped. No output is provided. + + inflateSync returns Z_OK if a full flush point has been found, Z_BUF_ERROR + if no more input was provided, Z_DATA_ERROR if no flush point has been found, + or Z_STREAM_ERROR if the stream structure was inconsistent. In the success + case, the application may save the current current value of total_in which + indicates where valid compressed data was found. In the error case, the + application may repeatedly call inflateSync, providing more input each time, + until success or end of the input data. +*/ + +ZEXTERN int ZEXPORT inflateReset OF((z_streamp strm)); +/* + This function is equivalent to inflateEnd followed by inflateInit, + but does not free and reallocate all the internal decompression state. + The stream will keep attributes that may have been set by inflateInit2. + + inflateReset returns Z_OK if success, or Z_STREAM_ERROR if the source + stream state was inconsistent (such as zalloc or state being NULL). +*/ + + + /* utility functions */ + +/* + The following utility functions are implemented on top of the + basic stream-oriented functions. To simplify the interface, some + default options are assumed (compression level and memory usage, + standard memory allocation functions). The source code of these + utility functions can easily be modified if you need special options. +*/ + +ZEXTERN int ZEXPORT compress OF((Bytef *dest, uLongf *destLen, + const Bytef *source, uLong sourceLen)); +/* + Compresses the source buffer into the destination buffer. sourceLen is + the byte length of the source buffer. Upon entry, destLen is the total + size of the destination buffer, which must be at least 0.1% larger than + sourceLen plus 12 bytes. Upon exit, destLen is the actual size of the + compressed buffer. + This function can be used to compress a whole file at once if the + input file is mmap'ed. + compress returns Z_OK if success, Z_MEM_ERROR if there was not + enough memory, Z_BUF_ERROR if there was not enough room in the output + buffer. +*/ + +ZEXTERN int ZEXPORT compress2 OF((Bytef *dest, uLongf *destLen, + const Bytef *source, uLong sourceLen, + int level)); +/* + Compresses the source buffer into the destination buffer. The level + parameter has the same meaning as in deflateInit. sourceLen is the byte + length of the source buffer. Upon entry, destLen is the total size of the + destination buffer, which must be at least 0.1% larger than sourceLen plus + 12 bytes. Upon exit, destLen is the actual size of the compressed buffer. + + compress2 returns Z_OK if success, Z_MEM_ERROR if there was not enough + memory, Z_BUF_ERROR if there was not enough room in the output buffer, + Z_STREAM_ERROR if the level parameter is invalid. +*/ + +ZEXTERN int ZEXPORT uncompress OF((Bytef *dest, uLongf *destLen, + const Bytef *source, uLong sourceLen)); +/* + Decompresses the source buffer into the destination buffer. sourceLen is + the byte length of the source buffer. Upon entry, destLen is the total + size of the destination buffer, which must be large enough to hold the + entire uncompressed data. (The size of the uncompressed data must have + been saved previously by the compressor and transmitted to the decompressor + by some mechanism outside the scope of this compression library.) + Upon exit, destLen is the actual size of the compressed buffer. + This function can be used to decompress a whole file at once if the + input file is mmap'ed. + + uncompress returns Z_OK if success, Z_MEM_ERROR if there was not + enough memory, Z_BUF_ERROR if there was not enough room in the output + buffer, or Z_DATA_ERROR if the input data was corrupted. +*/ + + +typedef voidp gzFile; + +ZEXTERN gzFile ZEXPORT gzopen OF((const char *path, const char *mode)); +/* + Opens a gzip (.gz) file for reading or writing. The mode parameter + is as in fopen ("rb" or "wb") but can also include a compression level + ("wb9") or a strategy: 'f' for filtered data as in "wb6f", 'h' for + Huffman only compression as in "wb1h". (See the description + of deflateInit2 for more information about the strategy parameter.) + + gzopen can be used to read a file which is not in gzip format; in this + case gzread will directly read from the file without decompression. + + gzopen returns NULL if the file could not be opened or if there was + insufficient memory to allocate the (de)compression state; errno + can be checked to distinguish the two cases (if errno is zero, the + zlib error is Z_MEM_ERROR). */ + +ZEXTERN gzFile ZEXPORT gzdopen OF((int fd, const char *mode)); +/* + gzdopen() associates a gzFile with the file descriptor fd. File + descriptors are obtained from calls like open, dup, creat, pipe or + fileno (in the file has been previously opened with fopen). + The mode parameter is as in gzopen. + The next call of gzclose on the returned gzFile will also close the + file descriptor fd, just like fclose(fdopen(fd), mode) closes the file + descriptor fd. If you want to keep fd open, use gzdopen(dup(fd), mode). + gzdopen returns NULL if there was insufficient memory to allocate + the (de)compression state. +*/ + +ZEXTERN int ZEXPORT gzsetparams OF((gzFile file, int level, int strategy)); +/* + Dynamically update the compression level or strategy. See the description + of deflateInit2 for the meaning of these parameters. + gzsetparams returns Z_OK if success, or Z_STREAM_ERROR if the file was not + opened for writing. +*/ + +ZEXTERN int ZEXPORT gzread OF((gzFile file, voidp buf, unsigned len)); +/* + Reads the given number of uncompressed bytes from the compressed file. + If the input file was not in gzip format, gzread copies the given number + of bytes into the buffer. + gzread returns the number of uncompressed bytes actually read (0 for + end of file, -1 for error). */ + +ZEXTERN int ZEXPORT gzwrite OF((gzFile file, + const voidp buf, unsigned len)); +/* + Writes the given number of uncompressed bytes into the compressed file. + gzwrite returns the number of uncompressed bytes actually written + (0 in case of error). +*/ + +ZEXTERN int ZEXPORTVA gzprintf OF((gzFile file, const char *format, ...)); +/* + Converts, formats, and writes the args to the compressed file under + control of the format string, as in fprintf. gzprintf returns the number of + uncompressed bytes actually written (0 in case of error). +*/ + +ZEXTERN int ZEXPORT gzputs OF((gzFile file, const char *s)); +/* + Writes the given null-terminated string to the compressed file, excluding + the terminating null character. + gzputs returns the number of characters written, or -1 in case of error. +*/ + +ZEXTERN char * ZEXPORT gzgets OF((gzFile file, char *buf, int len)); +/* + Reads bytes from the compressed file until len-1 characters are read, or + a newline character is read and transferred to buf, or an end-of-file + condition is encountered. The string is then terminated with a null + character. + gzgets returns buf, or Z_NULL in case of error. +*/ + +ZEXTERN int ZEXPORT gzputc OF((gzFile file, int c)); +/* + Writes c, converted to an unsigned char, into the compressed file. + gzputc returns the value that was written, or -1 in case of error. +*/ + +ZEXTERN int ZEXPORT gzgetc OF((gzFile file)); +/* + Reads one byte from the compressed file. gzgetc returns this byte + or -1 in case of end of file or error. +*/ + +ZEXTERN int ZEXPORT gzflush OF((gzFile file, int flush)); +/* + Flushes all pending output into the compressed file. The parameter + flush is as in the deflate() function. The return value is the zlib + error number (see function gzerror below). gzflush returns Z_OK if + the flush parameter is Z_FINISH and all output could be flushed. + gzflush should be called only when strictly necessary because it can + degrade compression. +*/ + +ZEXTERN z_off_t ZEXPORT gzseek OF((gzFile file, + z_off_t offset, int whence)); +/* + Sets the starting position for the next gzread or gzwrite on the + given compressed file. The offset represents a number of bytes in the + uncompressed data stream. The whence parameter is defined as in lseek(2); + the value SEEK_END is not supported. + If the file is opened for reading, this function is emulated but can be + extremely slow. If the file is opened for writing, only forward seeks are + supported; gzseek then compresses a sequence of zeroes up to the new + starting position. + + gzseek returns the resulting offset location as measured in bytes from + the beginning of the uncompressed stream, or -1 in case of error, in + particular if the file is opened for writing and the new starting position + would be before the current position. +*/ + +ZEXTERN int ZEXPORT gzrewind OF((gzFile file)); +/* + Rewinds the given file. This function is supported only for reading. + + gzrewind(file) is equivalent to (int)gzseek(file, 0L, SEEK_SET) +*/ + +ZEXTERN z_off_t ZEXPORT gztell OF((gzFile file)); +/* + Returns the starting position for the next gzread or gzwrite on the + given compressed file. This position represents a number of bytes in the + uncompressed data stream. + + gztell(file) is equivalent to gzseek(file, 0L, SEEK_CUR) +*/ + +ZEXTERN int ZEXPORT gzeof OF((gzFile file)); +/* + Returns 1 when EOF has previously been detected reading the given + input stream, otherwise zero. +*/ + +ZEXTERN int ZEXPORT gzclose OF((gzFile file)); +/* + Flushes all pending output if necessary, closes the compressed file + and deallocates all the (de)compression state. The return value is the zlib + error number (see function gzerror below). +*/ + +ZEXTERN const char * ZEXPORT gzerror OF((gzFile file, int *errnum)); +/* + Returns the error message for the last error which occurred on the + given compressed file. errnum is set to zlib error number. If an + error occurred in the file system and not in the compression library, + errnum is set to Z_ERRNO and the application may consult errno + to get the exact error code. +*/ + + /* checksum functions */ + +/* + These functions are not related to compression but are exported + anyway because they might be useful in applications using the + compression library. +*/ + +ZEXTERN uLong ZEXPORT adler32 OF((uLong adler, const Bytef *buf, uInt len)); + +/* + Update a running Adler-32 checksum with the bytes buf[0..len-1] and + return the updated checksum. If buf is NULL, this function returns + the required initial value for the checksum. + An Adler-32 checksum is almost as reliable as a CRC32 but can be computed + much faster. Usage example: + + uLong adler = adler32(0L, Z_NULL, 0); + + while (read_buffer(buffer, length) != EOF) { + adler = adler32(adler, buffer, length); + } + if (adler != original_adler) error(); +*/ + +ZEXTERN uLong ZEXPORT crc32 OF((uLong crc, const Bytef *buf, uInt len)); +/* + Update a running crc with the bytes buf[0..len-1] and return the updated + crc. If buf is NULL, this function returns the required initial value + for the crc. Pre- and post-conditioning (one's complement) is performed + within this function so it shouldn't be done by the application. + Usage example: + + uLong crc = crc32(0L, Z_NULL, 0); + + while (read_buffer(buffer, length) != EOF) { + crc = crc32(crc, buffer, length); + } + if (crc != original_crc) error(); +*/ + + + /* various hacks, don't look :) */ + +/* deflateInit and inflateInit are macros to allow checking the zlib version + * and the compiler's view of z_stream: + */ +ZEXTERN int ZEXPORT deflateInit_ OF((z_streamp strm, int level, + const char *version, int stream_size)); +ZEXTERN int ZEXPORT inflateInit_ OF((z_streamp strm, + const char *version, int stream_size)); +ZEXTERN int ZEXPORT deflateInit2_ OF((z_streamp strm, int level, int method, + int windowBits, int memLevel, + int strategy, const char *version, + int stream_size)); +ZEXTERN int ZEXPORT inflateInit2_ OF((z_streamp strm, int windowBits, + const char *version, int stream_size)); +#define deflateInit(strm, level) \ + deflateInit_((strm), (level), ZLIB_VERSION, sizeof(z_stream)) +#define inflateInit(strm) \ + inflateInit_((strm), ZLIB_VERSION, sizeof(z_stream)) +#define deflateInit2(strm, level, method, windowBits, memLevel, strategy) \ + deflateInit2_((strm),(level),(method),(windowBits),(memLevel),\ + (strategy), ZLIB_VERSION, sizeof(z_stream)) +#define inflateInit2(strm, windowBits) \ + inflateInit2_((strm), (windowBits), ZLIB_VERSION, sizeof(z_stream)) + + +#if !defined(_Z_UTIL_H) && !defined(NO_DUMMY_DECL) + struct internal_state {int dummy;}; /* hack for buggy compilers */ +#endif + +ZEXTERN const char * ZEXPORT zError OF((int err)); +ZEXTERN int ZEXPORT inflateSyncPoint OF((z_streamp z)); +ZEXTERN const uLongf * ZEXPORT get_crc_table OF((void)); + +#ifdef __cplusplus +} +#endif + +#endif /* _ZLIB_H */ diff --git a/channel/wiiload/win32/lib/libz.a b/channel/wiiload/win32/lib/libz.a new file mode 100644 index 0000000..12117e0 Binary files /dev/null and b/channel/wiiload/win32/lib/libz.a differ diff --git a/common.mk b/common.mk new file mode 100644 index 0000000..cd9fdcb --- /dev/null +++ b/common.mk @@ -0,0 +1,60 @@ +AR = $(PREFIX)ar +AS = $(PREFIX)as +CC = $(PREFIX)gcc +CXX = $(PREFIX)g++ +LD = $(PREFIX)ld +OBJCOPY = $(PREFIX)objcopy +RANLIB = $(PREFIX)ranlib +STRIP = $(PREFIX)strip + +BIN2S = $(DEVKITPPC)/bin/bin2s + +ifeq ($(NOMAPFILE),) +LDFLAGS += -Wl,-Map,$(TARGET).map +endif + +ifneq ($(LDSCRIPT),) +LDFLAGS += -Wl,-T$(LDSCRIPT) +endif + +DEPDIR = .deps + +all: $(TARGET) + +$(TARGET): $(OBJS) + @echo " LINK $@" + @$(CC) $(LDFLAGS) $(OBJS) $(LIBS) -o $@ + +ifneq ($(LDSCRIPT),) +$(TARGET): $(LDSCRIPT) +endif + +%.o: %.c + @echo " COMPILE $<" + @mkdir -p $(DEPDIR) + @$(CC) $(CFLAGS) $(DEFINES) -Wp,-MMD,$(DEPDIR)/$(*F).d,-MQ,"$@",-MP -c $< -o $@ + +%.o: %.s + @echo " ASSEMBLE $<" + @$(CC) $(CFLAGS) $(DEFINES) $(ASFLAGS) -c $< -o $@ + +%.o: %.S + @echo " ASSEMBLE $<" + @$(CC) $(CFLAGS) $(DEFINES) $(ASFLAGS) -c $< -o $@ + +clean: + rm -rf $(DEPDIR) + rm -f $(TARGET) $(TARGET).map $(OBJS) + +define bin2o + @echo " BIN2S $(notdir $<)" + @$(BIN2S) -a 32 $< | $(AS) -o $(@) + @echo "extern const u8" `(echo $( `(echo $(> `(echo $(> `(echo $(> 4) + 3 + inp += 1 + repOff = ord(data[inp]) | ((rep & 0x0F) << 8) + inp += 1 + + assert repOff <= len(ret) + + while repLength > 0: + ret.append(ret[-repOff - 1]) + repLength -= 1 + else: + ret.append(data[inp]) + inp += 1 + + bitmask <<= 1 + + return ''.join(ret) + +class U8(object): + class U8Header(Struct): + __endian__ = Struct.BE + def __format__(self): + self.Tag = Struct.string(4) + self.RootNode = Struct.uint32 + self.HeaderSize = Struct.uint32 + self.DataOffset = Struct.uint32 + self.Zeroes = Struct.uint8[0x10] + + class U8Node(Struct): + __endian__ = Struct.BE + def __format__(self): + self.Type = Struct.uint16 + self.NameOffset = Struct.uint16 + self.DataOffset = Struct.uint32 + self.Size = Struct.uint32 + + def __init__(self, data=None): + self.Files = {} + + if data != None: + self.Unpack(data) + + def Unpack(self, data): + pos = 0 + u8 = self.U8Header() + u8.unpack(data[pos:pos+len(u8)]) + pos += len(u8) + + assert u8.Tag == 'U\xAA8-' + pos += u8.RootNode - 0x20 + + root = self.U8Node() + root.unpack(data[pos:pos+len(root)]) + pos += len(root) + + children = [] + for i in xrange(root.Size - 1): + child = self.U8Node() + child.unpack(data[pos:pos+len(child)]) + pos += len(child) + children.append(child) + + stringTable = data[pos:pos + u8.DataOffset - len(u8) - root.Size * len(root)] + pos += len(stringTable) + + currentOffset = u8.DataOffset + + path = ['.'] + pathDepth = [root.Size - 1] + for offset,child in enumerate(children): + name = stringTable[child.NameOffset:].split('\0', 1)[0] + if child.Type == 0x0100: + path.append(name) + pathDepth.append(child.Size-offset-1) + elif child.Type == 0: + name = '/'.join(path + [name]) + + if currentOffset < child.DataOffset: + diff = child.DataOffset - currentOffset + assert diff <= 32 + + pos += diff + currentOffset += diff + + currentOffset += child.Size + + self.Files[name.lower()] = data[pos:pos+child.Size] + pos += child.Size + + pathDepth[-1] -= 1 + if pathDepth[-1] == 0: + path = path[:-1] + pathDepth = pathDepth[:-1] + +class IMD5Header(Struct): + __endian__ = Struct.BE + def __format__(self): + self.Tag = Struct.string(4) + self.Size = Struct.uint32 + self.Zeroes = Struct.uint8[8] + self.MD5 = Struct.uint8[0x10] + +def IMD5(data): + imd5 = IMD5Header() + imd5.unpack(data[:len(imd5)]) + assert imd5.Tag == 'IMD5' + pos = len(imd5) + + if data[pos:pos+4] == 'LZ77': + return LZ77Decompress(data[pos+8:]) + else: + return data[pos:] + +class TPL(object): + class TPLHeader(Struct): + __endian__ = Struct.BE + def __format__(self): + self.Magic = Struct.string(4) + self.Count = Struct.uint32 + self.Size = Struct.uint32 + + class TexOffsets(Struct): + __endian__ = Struct.BE + def __format__(self): + self.HeaderOff = Struct.uint32 + self.PaletteOff = Struct.uint32 + + class TexHeader(Struct): + __endian__ = Struct.BE + def __format__(self): + self.Size = Struct.uint16[2] + self.Format = Struct.uint32 + self.DataOff = Struct.uint32 + self.Wrap = Struct.uint32[2] + self.Filter = Struct.uint32[2] + self.LODBias = Struct.float + self.EdgeLOD = Struct.uint8 + self.LOD = Struct.uint8[2] + self.Unpacked = Struct.uint8 + + class PalHeader(Struct): + __endian__ = Struct.BE + def __format__(self): + self.Count = Struct.uint16 + self.Unpacked = Struct.uint8 + self.Pad = Struct.uint8 + self.Format = Struct.uint32 + self.DataOff = Struct.uint32 + + def __init__(self, data=None): + self.Textures = [] + + if data != None: + self.Unpack(data) + + def Unpack(self, data): + header = self.TPLHeader() + header.unpack(data[:len(header)]) + pos = len(header) + + assert header.Magic == '\x00\x20\xAF\x30' + assert header.Size == 0xc + + for i in xrange(header.Count): + offs = self.TexOffsets() + offs.unpack(data[pos:pos+len(offs)]) + pos += len(offs) + + self.Textures.append(self.ParseTexture(data, offs)) + + def ParseTexture(self, data, offs): + texHeader = self.TexHeader() + texHeader.unpack(data[offs.HeaderOff:offs.HeaderOff+len(texHeader)]) + format = texHeader.Format + self.format = format + rgba = None + if format == 0: + rgba = self.I4(data[texHeader.DataOff:], texHeader.Size) + elif format == 1: + rgba = self.I8(data[texHeader.DataOff:], texHeader.Size) + elif format == 2: + rgba = self.IA4(data[texHeader.DataOff:], texHeader.Size) + elif format == 3: + rgba = self.IA8(data[texHeader.DataOff:], texHeader.Size) + elif format == 4: + rgba = self.RGB565(data[texHeader.DataOff:], texHeader.Size) + elif format == 5: + rgba = self.RGB5A3(data[texHeader.DataOff:], texHeader.Size) + elif format == 6: + rgba = self.RGBA8(data[texHeader.DataOff:], texHeader.Size) + elif format == 14: + rgba = self.S3TC(data[texHeader.DataOff:], texHeader.Size) + else: + print 'Unknown texture format', format + + if rgba == None: + rgba = '\0\0\0\0' * texHeader.Size[0] * texHeader.Size[1] + + image = ImageData(texHeader.Size[1], texHeader.Size[0], 'RGBA', rgba) + print format + return image + + def I4(self, data, (y, x)): + out = [0 for i in xrange(x * y)] + outp = 0 + inp = 0 + for i in xrange(0, y, 8): + for j in xrange(0, x, 8): + ofs = 0 + for k in xrange(8): + off = min(x - j, 8) + for sub in xrange(0, off, 2): + texel = ord(data[inp]) + high, low = texel >> 4, texel & 0xF + if (outp + ofs + sub) < (x*y): + out[outp + ofs + sub] = (high << 4) | (high << 20) | (high << 12) | 0xFF<<24 + + if sub + 1 < off: + if (outp + ofs + sub + 1) < (x*y): + out[outp + ofs + sub + 1] = (low << 4) | (low << 20) | (low << 12) | 0xFF<<24 + + inp += 1 + + ofs += x + inp += (8 - off) / 2 + outp += off + outp += x * 7 + + return ''.join(Struct.uint32(p) for p in out) + + def I8(self, data, (y, x)): + out = [0 for i in xrange(x * y*2)] + outp = 0 + inp = 0 + for i in xrange(0, y, 4): + for j in xrange(0, x, 4): + ofs = 0 + for k in xrange(4): + off = min(x - j, 4) + for sub in xrange(off): + texel = ord(data[inp]) + out[outp + ofs + sub] = (texel << 24) | (texel << 16) | (texel << 8) | 0xFF + inp += 1 + + ofs += x + inp += 4 - off * 2 + outp += off + outp += x * 3 + + return ''.join(Struct.uint32(p) for p in out) + + def IA4(self, data, (y, x)): + out = [0 for i in xrange(x * y)] + outp = 0 + inp = 0 + for i in xrange(0, y, 4): + for j in xrange(0, x, 8): + ofs = 0 + for k in xrange(4): + off = min(x - j, 8) + for sub in xrange(off): + texel = ord(data[inp]) + alpha, inte = texel >> 4, texel & 0xF + if (outp + ofs + sub) < (x*y): + out[outp + ofs + sub] = (inte << 4) | (inte << 12) | (inte << 20) | (alpha << 28) + inp += 1 + + ofs += x + inp += 8 - off + outp += off + outp += x * 3 + + return ''.join(Struct.uint32(p) for p in out) + + def IA8(self, data, (y, x)): + out = [0 for i in xrange(x * y)] + outp = 0 + inp = 0 + for i in xrange(0, y, 4): + for j in xrange(0, x, 4): + ofs = 0 + for k in xrange(4): + off = min(x - j, 4) + for sub in xrange(off): + if (outp + ofs + sub) < (x*y): + texel = Struct.uint16(data[inp:inp + 2], endian='>') + p = (texel & 0xFF) + p |= (texel & 0xFF) << 8 + p |= (texel & 0xFF) << 16 + p |= (texel >> 8) << 24 + out[outp + ofs + sub] = p + inp += 2 + + ofs += x + inp += (4 - off) * 2 + outp += off + outp += x * 3 + + return ''.join(Struct.uint32(p) for p in out) + + def RGB565(self, data, (y, x)): + out = [0 for i in xrange(x * y)] + outp = 0 + inp = 0 + for i in xrange(0, y, 4): + for j in xrange(0, x, 4): + ofs = 0 + for k in xrange(4): + off = min(x - j, 4) + for sub in xrange(off): + if (outp + ofs + sub) < (x*y): + texel = Struct.uint16(data[inp:inp + 2], endian='>') + p = ((texel >> 11) & 0x1F) << 3 + p |= ((texel >> 5) & 0x3F) << 10 + p |= ( texel & 0x1F) << 19 + p |= 0xFF<<24 + out[outp + ofs + sub] = p + inp += 2 + + ofs += x + inp += (4 - off) * 2 + outp += off + outp += x * 3 + + return ''.join(Struct.uint32(p) for p in out) + + def RGB5A3(self, data, (y, x)): + out = [0 for i in xrange(x * y)] + outp = 0 + inp = 0 + for i in xrange(0, y, 4): + for j in xrange(0, x, 4): + ofs = 0 + for k in xrange(4): + off = min(x - j, 4) + for sub in xrange(off): + texel = Struct.uint16(data[inp:inp + 2], endian='>') + if texel & 0x8000: + p = ((texel >> 10) & 0x1F) << 3 + p |= ((texel >> 5) & 0x1F) << 11 + p |= ( texel & 0x1F) << 19 + p |= 0xFF<<24 + else: + p = ((texel >> 12) & 0x07) << 29 + p |= ((texel >> 8) & 0x0F) << 4 + p |= ((texel >> 4) & 0x0F) << 12 + p |= (texel & 0x0F) << 20 + if (outp + ofs + sub) < (x*y): + out[outp + ofs + sub] = p + inp += 2 + + ofs += x + inp += (4 - off) * 2 + outp += off + outp += x * 3 + + return ''.join(Struct.uint32(p) for p in out) + + def RGBA8(self, data, (y, x)): + out = [0 for i in xrange(x * y)] + outp = 0 + inp = 0 + for i in xrange(0, y, 4): + for j in xrange(0, x, 4): + ofs = 0 + for k in xrange(4): + off = min(x - j, 4) + for sub in xrange(off): + texel = Struct.uint16(data[inp:inp + 2], endian='>')<<16 + texel |= Struct.uint16(data[inp+32:inp + 34], endian='>') + if (outp + ofs + sub) < (x*y): + out[outp + ofs + sub] = texel&0xff000000 | ((texel>>16)&0xff) | (texel&0xff00) | ((texel<<16)&0xff0000) + inp += 2 + + ofs += x + inp += (4 - off) * 2 + outp += off + inp += 32 + outp += x * 3 + + return ''.join(Struct.uint32(p) for p in out) + + def unpack_rgb565(self,texel): + b = (texel&0x1f)<<3 + g = ((texel>>5)&0x3f)<<2 + r = ((texel>>11)&0x1f)<<3 + b |= b>>5 + g |= g>>6 + r |= r>>5 + return (0xff<<24) | (b<<16) | (g<<8) | r + def icolor(self,a,b,fa,fb,fc): + c = 0 + for i in xrange(0,32,8): + xa = (a>>i)&0xff + xb = (b>>i)&0xff + xc = min(255,max(0,int((xa*fa + xb*fb)/fc))) + c |= xc< texel2: + rgb[2] = self.icolor (rgb[0], rgb[1], 2, 1, 3) | 0xff000000 + rgb[3] = self.icolor (rgb[1], rgb[0], 2, 1, 3) | 0xff000000 + else: + rgb[2] = self.icolor (rgb[0], rgb[1], 0.5, 0.5, 1) | 0xff000000 + rgb[3] = 0 + + # color selection (00, 01, 10, 11) + cm = map(ord,data[inp+4:inp+8]) + ofs = l*4 + for n in range(4): + if (ofs + outp)<(x*y): + # one row (4 texels) + if maxw > (0 + l*4): + out[ofs + 0 + outp] = rgb[(cm[n] & 0xc0) >> 6]; + if maxw > (1 + l*4): + out[ofs + 1 + outp] = rgb[(cm[n] & 0x30) >> 4]; + if maxw > (2 + l*4): + out[ofs + 2 + outp] = rgb[(cm[n] & 0x0c) >> 2]; + if maxw > (3 + l*4): + out[ofs + 3 + outp] = rgb[(cm[n] & 0x03) >> 0]; + ofs += x + inp += 8 + outp += x * 4 + outp += maxw - x * 8 + outp += x * (TILE_HEIGHT - 1) + + return ''.join(Struct.uint32(p) for p in out) + +class Object(object): + def __init__(self, name): + self.Frame = 0 + self.Name = name + self.Alpha = 255 + self.Animations = 0 + def __str__(self): + return self.Name + +class Pane(Object): + def __init__(self, name, flags=0x104, alpha=255, coords=[0,0,0,0,0,0,1,1,0,0]): + Object.__init__(self, name) + self.Flags = flags + self.Alpha = alpha + self.Coords = list(coords) + self.Children = [] + self.Enabled = True + + def getX(self): return self.Coords[0] + def setX(self,v): self.Coords[0] = v + def getY(self): return self.Coords[1] + def setY(self,v): self.Coords[1] = v + def getMagX(self): return self.Coords[6] + def setMagX(self,v): self.Coords[6] = v + def getMagY(self): return self.Coords[7] + def setMagY(self,v): self.Coords[7] = v + def getW(self): return self.Coords[8] + def setW(self,v): self.Coords[8] = v + def getH(self): return self.Coords[9] + def setH(self,v): self.Coords[9] = v + def getRot(self): return self.Coords[5] + def setRot(self,v): self.Coords[5] = v + + X = property(getX,setX) + Y = property(getY,setY) + MagX = property(getMagX,setMagX) + MagY = property(getMagY,setMagY) + Width = property(getW,setW) + Height = property(getH,setH) + Rot = property(getRot,setRot) + + def Add(self, child): + self.Children.append(child) + + def AnimDone(self): + if not Object.AnimDone(self): + return False + + for child in self.Children: + if not child.AnimDone(): + return False + + return True + +class Picture(Pane): + def __init__(self, name, flags=0x504, alpha=255, coords=[0,0,0,0,0,0,1,1,0,0], unk=[0xff,0xff,0xff,0xff,0xff,0xff,0xff,0xff,0xff,0xff,0xff,0xff,0xff,0xff,0xff,0xff], mat=0, flags2=0x100, matcoord=[(0,0),(1,0),(0,1),(1,1)]): + Pane.__init__(self, name, flags, alpha, coords) + self.Unk = unk + self.Material = mat + self.Flags2 = flags2 + self.MaterialCoords = list(matcoord) + +class ItemList(object): + HDRLEN=4 + LSIZE=4 + OFFSET=0 + IS_ATOM=True + def __init__(self, data=None): + self.Items = [] + if data is not None: + self.unpack(data) + + def add(self,n): + self.Items.append(n) + + def unpack(self, data): + pos = 0 + if self.IS_ATOM: + pos = 8 + count = self.__unpkcnt__(data[pos:]) + pos = self.__getlistoff__(data[pos:]) + for i in xrange(count): + off = Struct.uint32(data[pos:pos+4], endian='>') + self.OFFSET + if i == (count-1): + next = len(data) + else: + next = Struct.uint32(data[pos+self.LSIZE:pos+self.LSIZE+4], endian='>') + self.OFFSET + pos += self.LSIZE + self.unpack_item(i, data[off:next]) + # sanity check + #dp = self.pack() + #assert dp == data + + def pack(self,extra=None): + + extradata = "" + if extra is not None: + extradata = extra.pack() + + data = "" + + offsets = [] + + for n,i in enumerate(self.Items): + offsets.append(len(data)) + data += self.pack_item(n,i) + + listlen = len(self.Items) * self.LSIZE + + head = self.__mkheader__() + outdata = "" + if self.IS_ATOM: + outdata += self.FOURCC + Struct.uint32((len(extradata) + listlen + len(data) + 8 + len(head) + 3) &(~3), endian='>') + outdata += head + + dataoff = len(outdata) + listlen - self.OFFSET + assert dataoff >= 0 + + for n in offsets: + outdata += Struct.uint32(n + dataoff, endian='>') + if self.LSIZE > 4: + outdata += (self.LSIZE-4)*"\x00" + + outdata += data + outdata += extradata + + if len(outdata)%4 != 0: + outdata += (4-len(outdata)%4)*"\x00" + + return outdata + + def __mkheader__(self): + return Struct.uint16(len(self.Items), endian='>') + "\x00\x00" + + def __unpkcnt__(self, data): + return Struct.uint16(data[0:2], endian='>') + + def __getlistoff__(self, data): + off=self.HDRLEN + if self.IS_ATOM: + off += 8 + return off + + def __getitem__(self, n): + return self.Items[n] + + def __len__(self): + return len(self.Items) + +class StdAtom(Struct): + __endian__ = Struct.BE + def __format__(self): + self.FourCC = Struct.string(4) + self.Size = Struct.uint32 + def pack(self): + self.FourCC = self.FOURCC + self.Size = len(self) + return Struct.pack(self) + +class Brlyt(object): + class BrlytHeader(Struct): + __endian__ = Struct.BE + def __format__(self): + self.Magic = Struct.string(4) + self.Unk = Struct.uint32 + self.Size = Struct.uint32 + self.UnkCount = Struct.uint16 + self.AtomCount = Struct.uint16 + + class BrlytLYT1(StdAtom): + FOURCC = "lyt1" + def __format__(self): + StdAtom.__format__(self) + self.Flag = Struct.uint8 + self.pad = Struct.uint8[3] + self.Width = Struct.float + self.Height = Struct.float + + class BrlytTexture(object): + def __init__(self, name, texture=None): + self.Name = name + self.Texture = texture + self.GLTexture = None + def create_texture(self): + self.GLTexture = self.Texture.create_texture(Texture) + + class BrlytTXL1(ItemList): + LSIZE = 8 + OFFSET = 12 + FOURCC = "txl1" + def __init__(self, archive=None, data=None): + self.Archive = archive + ItemList.__init__(self, data) + + def unpack_item(self, i, data): + fn = data.split('\0', 1)[0] + print fn + tex = TPL(self.Archive.Files['./arc/timg/' + fn.lower()]).Textures[0] + self.Items.append(Brlyt.BrlytTexture(fn, tex)) + def pack_item(self, i, item): + return item.Name + "\0" + + class BrlytMAT1(ItemList): + LSIZE = 4 + OFFSET = 0 + FOURCC = "mat1" + def __init__(self, data=None): + ItemList.__init__(self, data) + + def unpack_item(self, num, data): + mtl = Brlyt.BrlytMaterial() + mtl.unpack(data) + self.Items.append(mtl) + + def pack_item(self, i, item): + return item.pack() + + class BrlytMaterialFlags(object): + def __init__(self,value=None): + if value is not None: + self.unpack(value) + def unpack(self,value): + self.value = value + self.NumTextures = value & 15 + self.NumCoords = (value>>4) & 15 + self.m_2 = (value>>8) & 15 + self.m_4 = bool((value>>12) & 1) + self.m_6 = (value>>13) & 3 + self.m_5 = (value>>15) & 7 + self.m_3 = (value>>18) & 31 + self.m_9 = bool((value>>23) & 1) + self.m_10 = bool((value>>24) & 1) + self.m_7 = bool((value>>25) & 1) + self.m_8 = bool((value>>27) & 1) + + def show(self): + print "Flags: %08x"%self.value + print "ReserveGXMem(" + print " r4 =",self.NumTextures + print " r5 =",self.NumCoords + print " r6 =",self.m_2 + print " r7 =",self.m_3 + print " r8 =",self.m_4 + print " r9 =",self.m_5 + print " r10 =",self.m_6 + print " 0x8(%sp) =",self.m_7 + print " 0xC(%sp) =",self.m_8 + print " 0x10(%sp) =",self.m_9 + print " 0x14(%sp) =",self.m_10 + print ")" + + def pack(self): + val = 0 + val |= self.NumTextures + val |= self.NumCoords << 4 + val |= self.m_2 << 8 + val |= self.m_4 << 12 + val |= self.m_6 << 13 + val |= self.m_5 << 15 + val |= self.m_3 << 18 + val |= self.m_9 << 23 + val |= self.m_10 << 24 + val |= self.m_7 << 25 + val |= self.m_8 << 27 + return val + + class BrlytMatHeader(Struct): + __endian__ = Struct.BE + + def __format__(self): + self.Name = Struct.string(0x14) + self.Color1 = Struct.uint16[4] + self.Color2 = Struct.uint16[4] + self.Color3 = Struct.uint16[4] + self.Blah = Struct.uint32[4] + self.Flags = Struct.uint32 + + # 0-3 num 0-8 + # 4-7 num 0-10 + # 8-11 num 0-8 + # 12 bool + # 13-14 num 0-3 + # 15-17 num 0-4 + # 18-22 num 0-16 + # 23 bool + # 24 bool + # 25 bool a + # 27 bool + + + # if 25, xtra word + # if 27, xtra word + # if 12, xtra word + # 13-14 * 0x14 + # 15-17 * 4 + # 18-22 * 16 + # if 23, xtra word + # if 24, xtra word + + class BrlytMaterial(object): + + def __init__(self, name=None): + if name is not None: + self.Name = name + self.Color1 = [0,0,0,0] + self.Color2 = [255,255,255,255] + self.Color3 = [255,255,255,255] + self.Blah = [0xffffffff,0xffffffff,0xffffffff,0xffffffff] + self.Textures = [] + self.TextureCoords = [] + self.SthB = [] + self.SthI = None + self.SthJ = None + self.SthC = None + self.SthD = [] + self.SthE = [] + self.SthF = [] + self.SthG = 0x77000000 + self.SthH = None + + def unpack(self, data): + wii.chexdump(data) + hdr = Brlyt.BrlytMatHeader() + hdr.unpack(data) + self.FlagData = Brlyt.BrlytMaterialFlags(hdr.Flags) + self.FlagData.show() + + self.Name = hdr.Name.split("\0",1)[0] + self.Color1 = hdr.Color1 + self.Color2 = hdr.Color2 + self.Color3 = hdr.Color3 + self.Blah = hdr.Blah + + ptr = 0x40 + + self.Textures = [] + self.TextureCoords = [] + self.SthB = [] + self.SthI = None + self.SthJ = None + self.SthC = None + self.SthD = [] + self.SthE = [] + self.SthF = [] + self.SthG = None + self.SthH = None + + for i in range(self.FlagData.NumTextures): + texid = Struct.uint16(data[ptr:ptr+2], endian='>') + texcs = Struct.uint8(data[ptr+2], endian='>') + texct = Struct.uint8(data[ptr+3], endian='>') + print " * Texture: %04x %d %d"%(texid,texcs,texct) + self.Textures.append((texid,texcs,texct)) + ptr += 4 + for i in range(self.FlagData.NumCoords): + dat = [] + for j in range(5): + dat.append(Struct.float(data[ptr+j*4:ptr+j*4+4], endian='>')) + print " * Coords: [",', '.join(["%f"%x for x in dat]),"]" + ptr += 0x14 + self.TextureCoords.append(dat) + for i in range(self.FlagData.m_2): + dat = Struct.uint32(data[ptr:ptr+4], endian='>') + self.SthB.append(dat) + print " * SthB: %08x"%dat + ptr += 0x04 + if self.FlagData.m_7: + self.SthI = Struct.uint32(data[ptr:ptr+4], endian='>') + print " SthI: %08x"%self.SthI + ptr += 0x04 + if self.FlagData.m_8: + self.SthJ = Struct.uint32(data[ptr:ptr+4], endian='>') + print " SthJ: %08x"%self.SthJ + ptr += 0x04 + if self.FlagData.m_4: + self.SthC = Struct.uint32(data[ptr:ptr+4], endian='>') + print " SthC: %08x"%self.SthC + ptr += 0x04 + for i in range(self.FlagData.m_6): + dat = [] + for j in range(5): + dat.append(Struct.float(data[ptr+j*4:ptr+j*4+4], endian='>')) + self.SthD.append(dat) + print " * SthD: [",', '.join(["%f"%x for x in dat]),"]" + ptr += 0x14 + for i in range(self.FlagData.m_5): + dat = Struct.uint32(data[ptr:ptr+4], endian='>') + self.SthE.append(dat) + print " * SthE: %08x"%dat + ptr += 0x04 + for i in range(self.FlagData.m_3): + dat = [] + for j in range(4): + dat.append(Struct.uint32(data[ptr+j*4:ptr+j*4+4], endian='>')) + self.SthF.append(dat) + print " * SthF: [",', '.join(["%08x"%x for x in dat]),"]" + ptr += 0x10 + if self.FlagData.m_9: + dat = Struct.uint32(data[ptr:ptr+4], endian='>') + self.SthG = dat + print " SthG: %08x"%dat + ptr += 0x04 + if self.FlagData.m_10: + dat = Struct.uint32(data[ptr:ptr+4], endian='>') + self.SthH = dat + print " SthH: %08x"%dat + ptr += 0x04 + + #assert ptr == len(data) + + def pack(self): + + self.FlagData = Brlyt.BrlytMaterialFlags() + + self.FlagData.NumTextures = len(self.Textures) + self.FlagData.NumCoords = len(self.TextureCoords) + self.FlagData.m_2 = len(self.SthB) + self.FlagData.m_7 = self.SthI is not None + self.FlagData.m_8 = self.SthJ is not None + self.FlagData.m_4 = self.SthC is not None + self.FlagData.m_6 = len(self.SthD) + self.FlagData.m_5 = len(self.SthE) + self.FlagData.m_3 = len(self.SthF) + self.FlagData.m_9 = self.SthG is not None + self.FlagData.m_10 = self.SthH is not None + + self.Flags = self.FlagData.pack() + + hdr = Brlyt.BrlytMatHeader() + + hdr.Name = self.Name + "\x00"*(0x14-len(self.Name)) + hdr.Color1 = self.Color1 + hdr.Color2 = self.Color2 + hdr.Color3 = self.Color3 + hdr.Blah = self.Blah + hdr.Flags = self.Flags + + data = hdr.pack() + + for i in self.Textures: + data += Struct.uint16(i[0], endian='>') + data += Struct.uint8(i[1], endian='>') + data += Struct.uint8(i[2], endian='>') + for i in self.TextureCoords: + for j in i: + data += Struct.float(j, endian='>') + for i in self.SthB: + data += Struct.uint32(i, endian='>') + if self.SthI is not None: + data += Struct.uint32(self.SthI, endian='>') + if self.SthJ is not None: + data += Struct.uint32(self.SthJ, endian='>') + if self.SthC is not None: + data += Struct.uint32(self.SthC, endian='>') + for i in self.SthD: + for j in i: + data += Struct.float(j, endian='>') + for i in self.SthE: + data += Struct.uint32(i, endian='>') + for i in self.SthF: + for j in i: + data += Struct.uint32(j, endian='>') + if self.SthG is not None: + data += Struct.uint32(self.SthG, endian='>') + if self.SthH is not None: + data += Struct.uint32(self.SthH, endian='>') + return data + + class BrlytPAN1(StdAtom): + FOURCC = "pan1" + def __format__(self): + StdAtom.__format__(self) + self.Flags = Struct.uint16 + self.Alpha = Struct.uint16 + self.Name = Struct.string(0x18) + self.Coords = Struct.float[10] + + class BrlytPIC1(BrlytPAN1): + FOURCC = "pic1" + def __format__(self): + Brlyt.BrlytPAN1.__format__(self) + self.unk = Struct.uint8[16] + self.Material = Struct.uint16 + self.Flags2 = Struct.uint16 + self.MaterialCoords = Struct.float[8] + + def __init__(self, archive, data, renderer): + self.Archive = archive + self.Textures = Brlyt.BrlytTXL1() + self.Materials = Brlyt.BrlytMAT1() + self.LastPic = None + self.RootPane = None + self.RootPaneName = None + self.Objects = {} + self.PanePath = [] + self.PaneId = 0 + self.Language = "ENG" + self.Renderer = renderer + + if data != None: + self.Unpack(data) + + def Unpack(self, data): + pos = 0 + header = self.BrlytHeader() + header.unpack(data[:len(header)]) + print "BRLYT header:" + wii.chexdump(data[:len(header)]) + print " unk1: %08x"%header.Unk + print " unkc: %08x"%header.UnkCount + pos += len(header) + + print " %d atoms"%header.AtomCount + + assert header.Magic == 'RLYT' + + for i in xrange(header.AtomCount): + atom = StdAtom() + atom.unpack(data[pos:pos+len(atom)]) + + atomdata = data[pos:pos+atom.Size] + if atom.FourCC == 'txl1': + self.TXL1(atomdata) + elif atom.FourCC == 'lyt1': + self.LYT1(atomdata) + elif atom.FourCC == 'mat1': + self.MAT1(atomdata) + elif atom.FourCC == 'pan1': + self.PAN1(atomdata) + elif atom.FourCC == 'pas1': + self.PAS1(atomdata) + elif atom.FourCC == 'pae1': + self.PAE1(atomdata) + elif atom.FourCC == 'pic1': + self.PIC1(atomdata) + elif atom.FourCC == "grp1": + self.GRP1(atomdata) + else: + print "Unknown FOURCC:",atom.FourCC + wii.chexdump(atomdata) + + pos += atom.Size + + def _PackObject(self, object): + atoms = 0 + if isinstance(object,Pane): + atoms += 1 + if isinstance(object,Picture): + atom = Brlyt.BrlytPIC1() + else: + atom = Brlyt.BrlytPAN1() + atom.Name = object.Name + "\x00"*(0x18-len(object.Name)) + atom.Alpha = int(object.Alpha * 256) + atom.Flags = object.Flags + atom.Coords = object.Coords + if isinstance(object,Picture): + atom.Flags2 = object.Flags2 + atom.Material = object.Material + atom.unk = object.Unk + atom.MaterialCoords = sum(map(list,object.MaterialCoords),[]) + data = atom.pack() + + if len(object.Children) > 0: + atoms += 2 + data += "pas1\x00\x00\x00\x08" + for child in object.Children: + ac, dc = self._PackObject(child) + data += dc + atoms += ac + data += "pae1\x00\x00\x00\x08" + return atoms, data + else: + raise ValueError("Unknown object: "+repr(object)) + + def Pack(self, extra=None): + + header = self.BrlytHeader() + header.Unk = 0xfeff0008 + header.UnkCount = 0x10 + header.Magic = "RLYT" + header.AtomCount = 0 + + data = "" + data += self.mkLYT1() + header.AtomCount+=1 + + data += self.mkTXL1() + header.AtomCount+=1 + + data += self.mkMAT1(extra) + header.AtomCount+=1 + + atoms, ndata = self._PackObject(self.RootPane) + data += ndata + header.AtomCount += atoms + + #uuugly. TODO: fix this crap. + data += "grp1\x00\x00\x00\x1cRootGroup\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00" + header.AtomCount += 1 + + header.Size = len(data) + len(header) + data = header.pack() + data + + return data + + def mkLYT1(self): + lyt1 = self.BrlytLYT1() + lyt1.Width = self.Width + lyt1.Height = self.Height + lyt1.Flag = 1 + for n in range(3): + lyt1.pad[n] = 0 + return lyt1.pack() + + def mkTXL1(self): + return self.Textures.pack() + + def mkMAT1(self, extra=None): + return self.Materials.pack(extra) + + def LYT1(self, data): + lyt1 = self.BrlytLYT1() + lyt1.unpack(data) + self.Width = lyt1.Width + self.Height = lyt1.Height + print "LYT1: %f x %f, flag %d"%(self.Width, self.Height, lyt1.Flag) + self.Renderer.Create(int(self.Width), int(self.Height)) + + def TXL1(self, data): + self.Textures = self.BrlytTXL1(self.Archive, data) + for i in self.Textures: + i.create_texture() + + def ApplyMask(self, image, mask): + print "Making mask:",image,mask + print image.width,image.height,mask.width,mask.height + if image.height != mask.height or image.width != mask.width: + raise ValueError("Mask dimensions must be equal to mask dimensions") + newdata = [0 for x in xrange(image.height * image.width * 4)] + + for pix in xrange(image.height * image.width): + newdata[pix*4 + 0] = image.data[pix*4 + 0] + newdata[pix*4 + 1] = image.data[pix*4 + 1] + newdata[pix*4 + 2] = image.data[pix*4 + 2] + newdata[pix*4 + 3] = mask.data[pix*4 + 0] + + return ImageData(image.width, image.height, 'RGBA', ''.join(newdata)) + + def MAT1(self, data): + self.Materials = self.BrlytMAT1(data) + + def _addpane(self, p): + self.CurPane = p + self.Objects[p.Name] = p + if self.RootPane is None: + self.RootPane = p + else: + self.PanePath[-1].Add(p) + + def PAN1(self, data): + wii.chexdump(data) + pane = Brlyt.BrlytPAN1() + pane.unpack(data) + p = Pane(pane.Name.split('\0',1)[0], pane.Flags, pane.Alpha/256.0, pane.Coords) + print 'Pane %s (flags %04x, alpha %f): ' % (p.Name, pane.Flags, pane.Alpha),pane.Coords + self._addpane(p) + + def PAS1(self, data): + wii.chexdump(data) + if self.CurPane is None: + raise ValueError("No current pane!") + self.PanePath.append(self.CurPane) + print "Pane start:",'.'.join(map(str,self.PanePath)) + self.CurPane = None + + def PAE1(self, data): + print "Pane end:",'.'.join(map(str,self.PanePath)) + self.PanePath = self.PanePath[:-1] + + def PIC1(self, data): + wii.chexdump(data) + pic = Brlyt.BrlytPIC1() + pic.unpack(data) + mc = [] + for i in range(4): + mc.append(pic.MaterialCoords[i*2:i*2+2]) + print mc + p = Picture(pic.Name.split("\0",1)[0], pic.Flags, pic.Alpha/256.0, pic.Coords, pic.unk, pic.Material, pic.Flags2, mc) + print repr(p.Name) + mat = self.Materials[pic.Material] + if mat is not None: + self._addpane(p) + else: + print 'Picture %s with null material!' + + def GRP1(self, data): + wii.chexdump(data) + if len(data) < 0x1c: + pass + lang = data[0x8:0x18].split('\0', 1)[0] + nitems = Struct.uint16(data[0x18:0x1a], endian='>') + p = 0x1c + items = [] + for i in xrange(nitems): + items.append(data[p:].split('\0', 1)[0]) + p += 0x10 + for i in items: + try: + if lang != self.Language: + self.Objects[i].Enabled = False + else: + self.Objects[i].Enabled = True + except: + pass + +class Brlan(object): + A_COORD = "RLPA" + A_PARM = "RLVC" + C_X = 0 + C_Y = 1 + C_ANGLE = 5 + C_MAGX = 6 + C_MAGY = 7 + C_WIDTH = 8 + C_HEIGHT = 9 + P_ALPHA = 0x10 + class BrlanHeader(Struct): + __endian__ = Struct.BE + def __format__(self): + self.Magic = Struct.string(4) + self.Unk = Struct.uint32 + self.Size = Struct.uint32 + self.UnkCount = Struct.uint16 + self.AtomCount = Struct.uint16 + + def __init__(self, data=None): + self.Anim = None + if data != None: + self.Unpack(data) + else: + self.Anim = Brlan.BrlanPAI1() + + def Unpack(self, data): + header = self.BrlanHeader() + header.unpack(data[:len(header)]) + pos = len(header) + + assert header.Magic == 'RLAN' + + for i in xrange(header.AtomCount): + atom = StdAtom() + atom.unpack(data[pos:pos+len(atom)]) + atomdata = data[pos:pos+atom.Size] + pos += atom.Size + + if atom.FourCC == 'pai1': + self.PAI1(atomdata) + else: + print "Unknown animation atom: %s"%atom.FourCC + + class BrlanPAI1(ItemList): + LSIZE=4 + OFFSET=0 + FOURCC="pai1" + HDRLEN=12 + def unpack(self, data): + self.FrameCount = Struct.uint16(data[8:10], endian='>') + print self.FrameCount + ItemList.unpack(self, data) + def __mkheader__(self): + hdr = Struct.uint16(self.FrameCount, endian='>') + hdr += Struct.uint16(0x100, endian='>') + hdr += Struct.uint32(len(self.Items), endian='>') + hdr += Struct.uint32(0x14, endian='>') + return hdr + def __unpkcnt__(self, data): + return Struct.uint32(data[4:8], endian='>') + def __getlistoff__(self, data): + return Struct.uint32(data[8:12], endian='>') + def unpack_item (self, num, data): + self.Items.append(Brlan.BrlanAnimSet(data)) + def pack(self, offset, count): + self.FrameCount = count + self.Offset = offset + return ItemList.pack(self) + def pack_item (self, num, item): + return item.pack(self.Offset) + def __getitem__(self, n): + if isinstance(n,str): + for i in self.Items: + if i.Name == n: + return i + raise KeyError("Key %s not found"%n) + else: + return ItemList.__getitem__(self, n) + + def __contains__(self, n): + if isinstance(n,str): + for i in self.Items: + if i.Name == n: + return True + return False + else: + return n in self.Items + + + + class BrlanAnimSet(ItemList): + LSIZE=4 + OFFSET=0 + HDRLEN=0x18 + IS_ATOM=False + def __init__(self, data=None): + if data is not None and len(data)<0x14: + self.Name = data + ItemList.__init__(self, None) + else: + ItemList.__init__(self, data) + def unpack(self, data): + self.Name = data[:0x14].split("\0",1)[0] + print self.Name + ItemList.unpack(self, data) + def __mkheader__(self): + hdr = self.Name + "\x00" * (0x14-len(self.Name)) + hdr += Struct.uint8(len(self.Items), endian='>') + hdr += "\x00\x00\x00" + return hdr + def __unpkcnt__(self, data): + return Struct.uint8(data[0x14:0x15], endian='>') + def unpack_item (self, num, data): + self.Items.append(Brlan.BrlanAnimClass(data)) + def pack_item (self, num, item): + return item.pack(self.Offset) + def pack(self, offset): + self.Offset = offset + return ItemList.pack(self) + def __getitem__(self, n): + if isinstance(n,str): + for i in self.Items: + if i.Type == n: + return i + raise KeyError("Key %s not found"%n) + else: + return ItemList.__getitem__(self, n) + + class BrlanAnimClass(ItemList): + LSIZE=4 + OFFSET=0 + HDRLEN=8 + IS_ATOM=False + + class BrlanAnimClassIterator(object): + def __init__(self, cl): + self.cl = cl + self.item = 0 + def __iter__(self): + return self + def next(self): + if self.item == len(self.cl.Items): + raise StopIteration() + else: + self.item += 1 + return self.cl.Items[self.item-1] + + def __init__(self, data=None): + if data is not None and len(data) == 4: + self.Type = data + ItemList.__init__(self, None) + else: + ItemList.__init__(self, data) + def unpack(self, data): + self.Type = data[0:4] + print " ",self.Type + ItemList.unpack(self, data) + def __mkheader__(self): + hdr = self.Type + hdr += Struct.uint8(len(self.Items), endian='>') + hdr += "\x00\x00\x00" + return hdr + def __unpkcnt__(self, data): + return Struct.uint8(data[4:5], endian='>') + def unpack_item (self, num, data): + self.Items.append(Brlan.BrlanAnim(data)) + def pack(self, offset): + self.Offset = offset + return ItemList.pack(self) + def pack_item (self, num, item): + return item.pack(self.Offset) + def __getitem__(self, n): + for i in self.Items: + if i.Type == n: + return i + raise KeyError("Key %d not found"%n) + def __iter__(self): + return self.BrlanAnimClassIterator(self) + + class BrlanAnim(object): + def __init__(self, data=None): + self.Triplets = [] + self.Unk = 0x200 + self.Type = 0 + if data is not None and isinstance(data,int): + self.Type = data + else: + self.unpack(data) + def unpack(self, data): + self.Type = Struct.uint16(data[0:2], endian='>') + self.Unk = Struct.uint16(data[2:4], endian='>') + count = Struct.uint16(data[4:6], endian='>') + pos = Struct.uint32(data[8:12], endian='>') + print " ",self.Type + if self.Unk == 0x200: + print " Triplets:" + for i in range(count): + F = Struct.float(data[pos+0:pos+4], endian='>') + P = Struct.float(data[pos+4:pos+8], endian='>') + D = Struct.float(data[pos+8:pos+12], endian='>') + print " %11f %11f %11f"%(F,P,D) + self.Triplets.append((F,P,D)) + pos += 12 + else: + print " Unknown format: %04x"%self.Unk + def pack(self, offset): + self.Triplets.sort(key=lambda x: x[0]) + t = self.Triplets + self.Triplets = [] + lt = (None, None, None) + for i in t: + if i == lt: + pass + elif i[0] == lt[0]: + raise ValueError("Duplicate triplet frame numbers with different data") + else: + self.Triplets.append(i) + + out = Struct.uint16(self.Type, endian='>') + out += Struct.uint16(self.Unk, endian='>') + out += Struct.uint16(len(self.Triplets), endian='>') + out += "\x00\x00" + out += Struct.uint32(0xc, endian='>') + for F,P,D in self.Triplets: + out += Struct.float(F-offset, endian='>') + out += Struct.float(P, endian='>') + out += Struct.float(D, endian='>') + return out + def add(self, F, P=None, D=None): + if isinstance(F,tuple): + self.Triplets.append(F) + else: + self.Triplets.append((F,P,D)) + def rep(self, start, end, times): + replist = [] + for i in self.Triplets: + if i[0] >= start and i[0] <= end: + replist.append(i) + if len(replist) == 0: + return + off = end-start + for i in range(times-1): + for a,b,c in replist: + self.Triplets.append((a+off,b,c)) + off += end-start + a,b,c = replist[0] + if a == start: + self.Triplets.append((a+off,b,c)) + def repsimple(self, start, end, times, sp, sd, ep, ed): + step = float(end-start) / times + self.Triplets.append((start, sp, sd)) + self.Triplets.append((start+step/2.0, ep, ed)) + self.rep(start, start+step, times) + def calc(self, frame): + for tn in range(len(self.Triplets)): + if self.Triplets[tn][0] > frame: + if tn == 0: + return self.Triplets[0][1] + else: + start, value1, coef1 = self.Triplets[tn-1] + end, value2, coef2 = self.Triplets[tn] + t = (frame - start) / (end - start) + nf = end - start + return \ + coef1 * 1 * nf * (t + t**3 - 2*t**2) + \ + coef2 * 1 * nf * (t**3 - t**2) + \ + value1 * (1 + (2*t**3 - 3*t**2)) + \ + value2 * (-2*t**3 + 3*t**2) + else: + return self.Triplets[-1][1] + def __getitem__(self, n): + return self.Triplets[n] + def PAI1(self, data): + assert self.Anim is None + self.Anim = self.BrlanPAI1(data) + return + def Pack(self, a=None, b=None): + + if a is None and b is None: + frames = self.FrameCount + offset = 0 + elif a is not None and b is None: + frames = a + offset = 0 + elif a is not None and b is not None: + frames = b-a + offset = a + else: + raise ValueError("WTF are you doing") + + header = self.BrlanHeader() + header.Unk = 0xfeff0008 + header.UnkCount = 0x10 + header.Magic = "RLAN" + header.AtomCount = 1 + + data = self.Anim.pack(offset, frames) + + header.Size = len(data) + len(header) + data = header.pack() + data + + return data + +if have_pyglet: + class BannerWindow(window.Window): + def on_resize(self, width, height): + glViewport(0, 0, width, height) + glMatrixMode(GL_PROJECTION) + glLoadIdentity() + glOrtho(-width/2, width/2, -height/2, height/2, -1, 1) + glMatrixMode(GL_MODELVIEW) + + +class Renderer(object): + def __init__(self): + self.Brlyt = None + self.Brlan = None + + def Create(self, width, height): + self.Width = width + self.Height = height + print "Render: %f x %f"%(self.Width,self.Height) + self.Window = BannerWindow(self.Width, self.Height) + self.Window.set_exclusive_mouse(False) + + glClearColor(0.0, 0.0, 0.0, 0.0) + glClearDepth(1.0) + glDepthFunc(GL_LEQUAL) + glEnable(GL_DEPTH_TEST) + + glBlendFunc(GL_SRC_ALPHA,GL_ONE_MINUS_SRC_ALPHA) + glEnable(GL_BLEND) + glEnable(GL_TEXTURE_2D) + clock.set_fps_limit(60) + + + def Render(self, item, wireframe=False): + if not item.Enabled: + return + if isinstance(item, Picture): + + mat = self.Brlyt.Materials[item.Material] + texture = self.Brlyt.Textures[mat.Textures[0][0]].GLTexture + mtc = mat.TextureCoords[0] + x, y, a, b, c, rot, xsc, ysc, xs, ys = item.Coords[:10] + + xs *= xsc + ys *= ysc + #print item.Name,x,y,xs,ys + xc, yc = x-(xs/2),y-(ys/2) + + glPushMatrix() + glMatrixMode( GL_MODELVIEW ) + glTranslatef( x, y, 0) + glRotatef(rot,0,0,1) + glScalef(xs,ys,0) + + if not wireframe: + col = [x/255.0 for x in list(mat.Color2)] + col[3] = col[3] * item.Alpha / 255.0 + + tw = texture.tex_coords[6] + th = texture.tex_coords[7] + + mc = item.MaterialCoords + + glMatrixMode( GL_TEXTURE ) + glPushMatrix() + glTranslatef(0.5,0.5,0) + glTranslatef(mtc[0],mtc[1],0) + glScalef(mtc[3],mtc[4],0) + glTranslatef(-0.5,-0.5,0) + array = (GLfloat * 32)( + mc[0][0] * tw, mc[0][1] * th, 0, 1., + -0.5, 0.5, 0, 1., + mc[1][0] * tw, mc[1][1] * th, 0, 1., + 0.5, 0.5, 0, 1., + mc[3][0] * tw, mc[3][1] * th, 0, 1., + 0.5, -0.5, 0, 1., + mc[2][0] * tw, mc[2][1] * th, 0, 1., + -0.5, -0.5, 0, 1.) + + glColor4f(*col) + glPushAttrib(GL_ENABLE_BIT) + glEnable(texture.target) + glBindTexture(texture.target, texture.id) + if mat.Textures[0][1] == 0x0: + glTexParameteri(texture.target, GL_TEXTURE_WRAP_S, GL_CLAMP) + elif mat.Textures[0][1] == 0x1: + glTexParameteri(texture.target, GL_TEXTURE_WRAP_S, GL_REPEAT) + elif mat.Textures[0][1] == 0x2: + glTexParameteri(texture.target, GL_TEXTURE_WRAP_S, GL_MIRRORED_REPEAT) + else: + glTexParameteri(texture.target, GL_TEXTURE_WRAP_S, GL_CLAMP) + if mat.Textures[0][2] == 0x0: + glTexParameteri(texture.target, GL_TEXTURE_WRAP_T, GL_CLAMP) + elif mat.Textures[0][2] == 0x1: + glTexParameteri(texture.target, GL_TEXTURE_WRAP_T, GL_REPEAT) + elif mat.Textures[0][2] == 0x2: + glTexParameteri(texture.target, GL_TEXTURE_WRAP_T, GL_MIRRORED_REPEAT) + else: + glTexParameteri(texture.target, GL_TEXTURE_WRAP_T, GL_CLAMP) + + glPushClientAttrib(GL_CLIENT_VERTEX_ARRAY_BIT) + glInterleavedArrays(GL_T4F_V4F, 0, array) + glDrawArrays(GL_QUADS, 0, 4) + glPopClientAttrib() + glPopAttrib() + + glBindTexture(texture.target, 0) + glPopMatrix() + glMatrixMode( GL_MODELVIEW ) + + else: + + glColor3f(1,0,0) + glBegin(GL_LINE_STRIP) + glVertex2f(-0.5,-0.5) + glVertex2f(0.5,-0.5) + glVertex2f(0.5,0.5) + glVertex2f(-0.5,0.5) + glVertex2f(-0.5,-0.5) + glColor3f(1,1,1) + glEnd() + pass + + glPopMatrix() + if isinstance(item, Pane): + if item.Coords is not None: + x, y, a, b, c, rot, xsc, ysc, xs, ys = item.Coords[:10] + else: + x = y = 0 + xs = ys = 0 + xsc = 1 + ysc = 1 + glPushMatrix() + glTranslatef(x, y, 0) + glRotatef(rot,0,0,1) + glScalef(xsc,ysc,0) + #glScalef(xs,ys,1) + for child in item.Children: + self.Render(child,wireframe) + glPopMatrix() + + def Animate(self, frame): + for set in self.Brlan.Anim: + for clss in set: + for anim in clss: + #print set.Name, clss.Type, anim.Type, anim.calc(frame), frame + if clss.Type == Brlan.A_COORD: + self.Brlyt.Objects[set.Name].Coords[anim.Type] = anim.calc(frame) + elif clss.Type == Brlan.A_PARM: + if anim.Type == Brlan.P_ALPHA: + self.Brlyt.Objects[set.Name].Alpha = anim.calc(frame) + + def MainLoop(self, loop): + frame = 0 + print "Starting mainloop: loop =",loop + print "Length in frames:",self.Brlan.Anim.FrameCount + while not self.Window.has_exit: + self.Window.dispatch_events() + self.Window.clear() + + glClear(GL_COLOR_BUFFER_BIT | GL_DEPTH_BUFFER_BIT) + glLoadIdentity() + glColor4f(1.0, 1.0, 1.0, 1.0) + + self.Render(self.Brlyt.RootPane,False) + #self.Render(self.Brlyt.RootPane,True) + + #clock.tick() + + self.Window.flip() + if self.Brlan is not None: + self.Animate(frame) + if frame >= self.Brlan.Anim.FrameCount: + if not loop: + print "Animation done!" + return True + else: + print "Looping..." + frame = -1 + frame += 1 + +class Alameda(object): + class IMETHeader(Struct): + __endian__ = Struct.BE + def __format__(self): + self.Zeroes = Struct.uint8[0x40] + self.IMET = Struct.string(4) + self.Fixed = Struct.uint8[8] + self.Sizes = Struct.uint32[3] + self.Flag1 = Struct.uint32 + self.Names = Struct.string(0x2A<<1, encoding='utf-16-be', stripNulls=True)[7] + self.Zeroes = Struct.uint8[0x348] + self.MD5 = Struct.uint8[0x10] + + def __init__(self, fn, type='icon'): + renderer = Renderer() + + fp = file(fn, 'rb') + + imet = self.IMETHeader() + try: + imet.unpack(fp.read(len(imet))) + except: + imet = None + if imet is None or imet.IMET != 'IMET': + imet = self.IMETHeader() + fp.seek(0x40) + imet.unpack(fp.read(len(imet))) + assert imet.IMET == 'IMET' + + print 'English title: %s' % imet.Names[1] + + + root = U8(fp.read()) + if type == 'icon': + banner = U8(IMD5(root.Files['./meta/icon.bin'])) + renderer.Brlyt = Brlyt(banner, banner.Files['./arc/blyt/icon.brlyt'], renderer) + fd = None + try: + fd = banner.Files['./arc/anim/icon.brlan'] + except: + pass + if fd is not None: + renderer.Brlan = Brlan(fd) + + loop = True + else: + banner = U8(IMD5(root.Files['./meta/banner.bin'])) + renderer.Brlyt = Brlyt(banner, banner.Files['./arc/blyt/banner.brlyt'], renderer) + loop = not banner.Files.has_key('./arc/anim/banner_start.brlan') + if not loop: + renderer.Brlan = Brlan(banner.Files['./arc/anim/banner_start.brlan']) + else: + renderer.Brlan = Brlan(banner.Files['./arc/anim/banner.brlan']) + + if renderer.MainLoop(loop) and type == 'banner' and not loop: + renderer.Brlan = Brlan(banner.Files['./arc/anim/banner_loop.brlan']) + renderer.MainLoop(True) + +if __name__=='__main__': + Alameda(*sys.argv[1:]) diff --git a/pywii/Common/Struct.py b/pywii/Common/Struct.py new file mode 100644 index 0000000..4acb44f --- /dev/null +++ b/pywii/Common/Struct.py @@ -0,0 +1,335 @@ +import struct, sys + +class StructType(tuple): + def __getitem__(self, value): + return [self] * value + def __call__(self, value, endian='<'): + if isinstance(value, str): + return struct.unpack(endian + tuple.__getitem__(self, 0), value[:tuple.__getitem__(self, 1)])[0] + else: + return struct.pack(endian + tuple.__getitem__(self, 0), value) + +class StructException(Exception): + pass + +class Struct(object): + __slots__ = ('__attrs__', '__baked__', '__defs__', '__endian__', '__next__', '__sizes__', '__values__') + int8 = StructType(('b', 1)) + uint8 = StructType(('B', 1)) + + int16 = StructType(('h', 2)) + uint16 = StructType(('H', 2)) + + int32 = StructType(('l', 4)) + uint32 = StructType(('L', 4)) + + int64 = StructType(('q', 8)) + uint64 = StructType(('Q', 8)) + + float = StructType(('f', 4)) + + @classmethod + def string(cls, len, offset=0, encoding=None, stripNulls=False, value=''): + return StructType(('string', (len, offset, encoding, stripNulls, value))) + + LE = '<' + BE = '>' + __endian__ = '<' + + def __init__(self, func=None, unpack=None, **kwargs): + self.__defs__ = [] + self.__sizes__ = [] + self.__attrs__ = [] + self.__values__ = {} + self.__next__ = True + self.__baked__ = False + + if func == None: + self.__format__() + else: + sys.settrace(self.__trace__) + func() + for name in func.func_code.co_varnames: + value = self.__frame__.f_locals[name] + self.__setattr__(name, value) + + self.__baked__ = True + + if unpack != None: + if isinstance(unpack, tuple): + self.unpack(*unpack) + else: + self.unpack(unpack) + + if len(kwargs): + for name in kwargs: + self.__values__[name] = kwargs[name] + + def __trace__(self, frame, event, arg): + self.__frame__ = frame + sys.settrace(None) + + def __setattr__(self, name, value): + if name in self.__slots__: + return object.__setattr__(self, name, value) + + if self.__baked__ == False: + if not isinstance(value, list): + value = [value] + attrname = name + else: + attrname = '*' + name + + self.__values__[name] = None + + for sub in value: + if isinstance(sub, Struct): + sub = sub.__class__ + try: + if issubclass(sub, Struct): + sub = ('struct', sub) + except TypeError: + pass + type_, size = tuple(sub) + if type_ == 'string': + self.__defs__.append(Struct.string) + self.__sizes__.append(size) + self.__attrs__.append(attrname) + self.__next__ = True + + if attrname[0] != '*': + self.__values__[name] = size[3] + elif self.__values__[name] == None: + self.__values__[name] = [size[3] for val in value] + elif type_ == 'struct': + self.__defs__.append(Struct) + self.__sizes__.append(size) + self.__attrs__.append(attrname) + self.__next__ = True + + if attrname[0] != '*': + self.__values__[name] = size() + elif self.__values__[name] == None: + self.__values__[name] = [size() for val in value] + else: + if self.__next__: + self.__defs__.append('') + self.__sizes__.append(0) + self.__attrs__.append([]) + self.__next__ = False + + self.__defs__[-1] += type_ + self.__sizes__[-1] += size + self.__attrs__[-1].append(attrname) + + if attrname[0] != '*': + self.__values__[name] = 0 + elif self.__values__[name] == None: + self.__values__[name] = [0 for val in value] + else: + try: + self.__values__[name] = value + except KeyError: + raise AttributeError(name) + + def __getattr__(self, name): + if self.__baked__ == False: + return name + else: + try: + return self.__values__[name] + except KeyError: + raise AttributeError(name) + + def __len__(self): + ret = 0 + arraypos, arrayname = None, None + + for i in range(len(self.__defs__)): + sdef, size, attrs = self.__defs__[i], self.__sizes__[i], self.__attrs__[i] + + if sdef == Struct.string: + size, offset, encoding, stripNulls, value = size + if isinstance(size, str): + size = self.__values__[size] + offset + elif sdef == Struct: + if attrs[0] == '*': + if arrayname != attrs: + arrayname = attrs + arraypos = 0 + size = len(self.__values__[attrs[1:]][arraypos]) + size = len(self.__values__[attrs]) + + ret += size + + return ret + + def unpack(self, data, pos=0): + for name in self.__values__: + if not isinstance(self.__values__[name], Struct): + self.__values__[name] = None + elif self.__values__[name].__class__ == list and len(self.__values__[name]) != 0: + if not isinstance(self.__values__[name][0], Struct): + self.__values__[name] = None + + arraypos, arrayname = None, None + + for i in range(len(self.__defs__)): + sdef, size, attrs = self.__defs__[i], self.__sizes__[i], self.__attrs__[i] + + if sdef == Struct.string: + size, offset, encoding, stripNulls, value = size + if isinstance(size, str): + size = self.__values__[size] + offset + + temp = data[pos:pos+size] + if len(temp) != size: + raise StructException('Expected %i byte string, got %i' % (size, len(temp))) + + if encoding != None: + temp = temp.decode(encoding) + + if stripNulls: + temp = temp.rstrip('\0') + + if attrs[0] == '*': + name = attrs[1:] + if self.__values__[name] == None: + self.__values__[name] = [] + self.__values__[name].append(temp) + else: + self.__values__[attrs] = temp + pos += size + elif sdef == Struct: + if attrs[0] == '*': + if arrayname != attrs: + arrayname = attrs + arraypos = 0 + name = attrs[1:] + self.__values__[attrs][arraypos].unpack(data, pos) + pos += len(self.__values__[attrs][arraypos]) + arraypos += 1 + else: + self.__values__[attrs].unpack(data, pos) + pos += len(self.__values__[attrs]) + else: + values = struct.unpack(self.__endian__+sdef, data[pos:pos+size]) + pos += size + j = 0 + for name in attrs: + if name[0] == '*': + name = name[1:] + if self.__values__[name] == None: + self.__values__[name] = [] + self.__values__[name].append(values[j]) + else: + self.__values__[name] = values[j] + j += 1 + + return self + + def pack(self): + arraypos, arrayname = None, None + + ret = '' + for i in range(len(self.__defs__)): + sdef, size, attrs = self.__defs__[i], self.__sizes__[i], self.__attrs__[i] + + if sdef == Struct.string: + size, offset, encoding, stripNulls, value = size + if isinstance(size, str): + size = self.__values__[size]+offset + + if attrs[0] == '*': + if arrayname != attrs: + arraypos = 0 + arrayname = attrs + temp = self.__values__[attrs[1:]][arraypos] + arraypos += 1 + else: + temp = self.__values__[attrs] + + if encoding != None: + temp = temp.encode(encoding) + + temp = temp[:size] + ret += temp + ('\0' * (size - len(temp))) + elif sdef == Struct: + if attrs[0] == '*': + if arrayname != attrs: + arraypos = 0 + arrayname = attrs + ret += self.__values__[attrs[1:]][arraypos].pack() + arraypos += 1 + else: + ret += self.__values__[attrs].pack() + else: + values = [] + for name in attrs: + if name[0] == '*': + if arrayname != name: + arraypos = 0 + arrayname = name + values.append(self.__values__[name[1:]][arraypos]) + arraypos += 1 + else: + values.append(self.__values__[name]) + + ret += struct.pack(self.__endian__+sdef, *values) + return ret + + def __getitem__(self, value): + return [('struct', self.__class__)] * value + +if __name__=='__main__': + class TestStruct(Struct): + __endian__ = Struct.LE + def __format__(self): + self.foo, self.bar = Struct.uint32, Struct.float + self.baz = Struct.string(8) + + self.omg = Struct.uint32 + self.wtf = Struct.string(self.omg) + + class HaxStruct(Struct): + __endian__ = Struct.LE + def __format__(self): + self.thing1 = Struct.uint32 + self.thing2 = Struct.uint32 + self.hax = HaxStruct + + test = TestStruct() + test.unpack('\xEF\xBE\xAD\xDE\x00\x00\x80\x3Fdeadbeef\x04\x00\x00\x00test\xCA\xFE\xBA\xBE\xBE\xBA\xFE\xCA') + assert test.foo == 0xDEADBEEF + assert test.bar == 1.0 + assert test.baz == 'deadbeef' + assert test.omg == 4 + assert test.wtf == 'test' + assert test.hax.thing1 == 0xBEBAFECA + assert test.hax.thing2 == 0xCAFEBABE + + print 'Tests successful' + + """ + @Struct.LE + def TestStruct(): + foo, bar = Struct.uint32, Struct.float + baz = Struct.string(8) + + omg = Struct.uint32 + wtf = Struct.string(omg) + + @Struct.LE + def HaxStruct(): + thing1 = Struct.uint32 + thing2 = Struct.uint32 + hax = HaxStruct() + + test = TestStruct() + test.foo = 0xCAFEBABE + test.bar = 0.0 + thing = test.hax.thing1 + test.hax.thing1 = test.hax.thing2 + test.hax.thing2 = thing + assert test.pack() == '\xBE\xBA\xFE\xCA\0\0\0\0deadbeef\x04\x00\x00\x00test\xBE\xBA\xFE\xCA\xCA\xFE\xBA\xBE' + """ diff --git a/pywii/Common/pywii/__init__.py b/pywii/Common/pywii/__init__.py new file mode 100644 index 0000000..da598aa --- /dev/null +++ b/pywii/Common/pywii/__init__.py @@ -0,0 +1,3 @@ +#!/usr/bin/env python + +from wii import * diff --git a/pywii/Common/pywii/ec.py b/pywii/Common/pywii/ec.py new file mode 100644 index 0000000..d7058dd --- /dev/null +++ b/pywii/Common/pywii/ec.py @@ -0,0 +1,367 @@ +#!/usr/bin/python +# Copyright 2007,2008 Segher Boessenkool +# Copyright 2008 Hector Martin +# Licensed under the terms of the GNU GPL, version 2 +# http://www.gnu.org/licenses/old-licenses/gpl-2.0.txt + +from array import array +from struct import pack, unpack +from Crypto.Util.number import bytes_to_long, long_to_bytes + +# y**2 + x*y = x**3 + x + b +ec_b = "\x00\x66\x64\x7e\xde\x6c\x33\x2c\x7f\x8c\x09\x23\xbb\x58\x21"+\ + "\x3b\x33\x3b\x20\xe9\xce\x42\x81\xfe\x11\x5f\x7d\x8f\x90\xad" + +def hexdump(s,sep=""): + return sep.join(map(lambda x: "%02x"%ord(x),s)) + +def bhex(s,sep=""): + return hexdump(long_to_bytes(s,30),sep) + +fastelt = False +try: + import _ec + fastelt = True +except ImportError: + #print "C Elliptic Curve functions not available. EC certificate checking will be much slower." + pass + + +class ByteArray(array): + def __new__(cls, initializer=None): + return super(ByteArray, cls) .__new__(cls,'B',initializer) + def __init__(self,initializer=None): + array.__init__(self) + def __setitem__(self,item,value): + if isinstance(item, slice): + array.__setitem__(self, item, [x & 0xFF for x in value]) + else: + array.__setitem__(self, item, value & 0xFF) + def __long__(self): + return bytes_to_long(self.tostring()) + def __str__(self): + return ''.join(["%02x"%ord(x) for x in self.tostring()]) + def __repr__(self): + return "ByteArray('%s')"%''.join(["\\x%02x"%ord(x) for x in self.tostring()]) + +class ELT_PY: + SIZEBITS=233 + SIZE=(SIZEBITS+7)/8 + square = ByteArray("\x00\x01\x04\x05\x10\x11\x14\x15\x40\x41\x44\x45\x50\x51\x54\x55") + def __init__(self, initializer=None): + if isinstance(initializer, long) or isinstance(initializer, int): + self.d = ByteArray(long_to_bytes(initializer,self.SIZE)) + elif isinstance(initializer, str): + self.d = ByteArray(initializer) + elif isinstance(initializer, ByteArray): + self.d = ByteArray(initializer) + elif isinstance(initializer, array): + self.d = ByteArray(initializer) + elif isinstance(initializer, ELT): + self.d = ByteArray(initializer.d) + elif initializer is None: + self.d = ByteArray([0]*self.SIZE) + else: + raise TypeError("Invalid initializer type") + if len(self.d) != self.SIZE: + raise ValueError("ELT size must be 30") + + def __cmp__(self, other): + if other == 0: #exception + if self: + return 1 + else: + return 0 + if not isinstance(other,ELT): + return NotImplemented + return cmp(self.d,other.d) + + def __long__(self): + return long(self.d) + def __repr__(self): + return repr(self.d).replace("ByteArray","ELT") + def __str__(self): + return str(self.d) + def __nonzero__(self): + for x in self.d: + if x != 0: + return True + return False + def __len__(self): + return self.SIZE + def __add__(self,other): + if not isinstance(other,ELT): + return NotImplemented + new = ELT(self) + for x in range(self.SIZE): + new[x] ^= other[x] + return new + def _mul_x(self): + carry = self[0]&1 + x = 0 + d = ELT() + for i in range(self.SIZE-1): + y = self[i + 1] + d[i] = x ^ (y >> 7) + x = y << 1 + d[29] = x ^ carry + d[20] ^= carry << 2 + return d + def __mul__(self,other): + if not isinstance(other,ELT): + return NotImplemented + d = ELT() + i = 0 + mask = 1 + for n in range(self.SIZEBITS): + d = d._mul_x() + if (self[i] & mask) != 0: + d += other + mask >>= 1 + if mask == 0: + mask = 0x80 + i+=1 + return d + def __pow__(self,other): + if other == -1: + return 1/self + if other < 1: + return NotImplemented + if other % 2 == 0: + return self._square()**(other/2) + x = self + for i in range(other-1): + x *= self + return x + def _square(self): + wide = ByteArray([0]*self.SIZE*2) + for i in range(self.SIZE): + wide[2*i] = self.square[self[i] >> 4] + wide[2*i + 1] = self.square[self[i] & 0xf] + for i in range(self.SIZE): + x = wide[i] + + wide[i + 19] ^= x >> 7; + wide[i + 20] ^= x << 1; + + wide[i + 29] ^= x >> 1; + wide[i + 30] ^= x << 7; + x = wide[30] & 0xFE; + + wide[49] ^= x >> 7; + wide[50] ^= x << 1; + + wide[59] ^= x >> 1; + + wide[30] &= 1; + return ELT(wide[self.SIZE:]) + def _itoh_tsujii(self,b,j): + t = ELT(self) + return t**(2**j) * b + def __rdiv__(self,other): + if isinstance(other,ELT): + return 1/self * other + elif other == 1: + t = self._itoh_tsujii(self, 1) + s = t._itoh_tsujii(self, 1) + t = s._itoh_tsujii(s, 3) + s = t._itoh_tsujii(self, 1) + t = s._itoh_tsujii(s, 7) + s = t._itoh_tsujii(t, 14) + t = s._itoh_tsujii(self, 1) + s = t._itoh_tsujii(t, 29) + t = s._itoh_tsujii(s, 58) + s = t._itoh_tsujii(t, 116) + return s**2 + else: + return NotImplemented + + def __getitem__(self,item): + return self.d[item] + def __setitem__(self,item,value): + self.d[item] = value + def tobignum(self): + return bytes_to_long(self.d.tostring()) + def tobytes(self): + return self.d.tostring() + +class ELT_C(ELT_PY): + def __mul__(self,other): + if not isinstance(other,ELT): + return NotImplemented + return ELT(_ec.elt_mul(self.d.tostring(),other.d.tostring())) + def __rdiv__(self,other): + if other != 1: + return ELT_PY.__rdiv__(self,other) + return ELT(_ec.elt_inv(self.d.tostring())) + def _square(self): + return ELT(_ec.elt_square(self.d.tostring())) + +if fastelt: + ELT = ELT_C +else: + ELT = ELT_PY + +class Point: + def __init__(self,x,y=None): + if isinstance(x,str) and (y is None) and (len(x) == 60): + self.x = ELT(x[:30]) + self.y = ELT(x[30:]) + elif isinstance(x,Point): + self.x = ELT(x.x) + self.y = ELT(x.y) + else: + self.x = ELT(x) + self.y = ELT(y) + def on_curve(self): + return (self.x**3 + self.x**2 + self.y**2 + self.x*self.y + ELT(ec_b)) == 0 + def __cmp__(self, other): + if other == 0: + if self.x or self.y: + return 1 + else: + return 0 + elif isinstance(other, Point): + ca = cmp(self.x,other.x) + if ca != 0: + return ca + return cmp(self.y,other.y) + return NotImplemented + def _double(self): + if self.x == 0: + return Point(0,0) + + s = self.y/self.x + self.x + rx = s**2 + s + rx[29] ^= 1; + ry = s * rx + rx + self.x**2 + return Point(rx,ry) + def __add__(self, other): + if not isinstance(other,Point): + return NotImplemented + if self == 0: + return Point(other) + if other == 0: + return Point(self) + u = self.x + other.x + if u == 0: + u = self.y + other.y + if u == 0: + return self._double() + else: + return Point(0,0) + + s = (self.y + other.y) / u + t = s**2 + s + other.x + t[29] ^= 1 + + rx = t+self.x + ry = s*t+self.y+rx + return Point(rx,ry) + + def __mul__(self, other): + bts = long_to_bytes(other,30) + d = Point(0,0) + for i in range(30): + mask = 0x80 + while mask != 0: + d = d._double() + if ((ord(bts[i]) & mask) != 0): + d += self + mask >>=1 + return d + #def __mul__(self, other): + #if not (isinstance(other,long) or isinstance(other,int)): + #return NotImplemented + + #d = Point(0,0) + #s = Point(self) + + #while other != 0: + #if other & 1: + #d += s + #s = s._double() + #other >>= 1 + #return d + def __rmul__(self, other): + return self * other + def __str__(self): + return "(%s,%s)"%(str(self.x),str(self.y)) + def __repr__(self): + return "Point"+str(self) + def __nonzero__(self): + return self.x or self.y + def tobytes(self): + return self.x.tobytes() + self.y.tobytes() + +#only for prime N +#segher, your math makes my head hurt. But it works. +def bn_inv(a,N): + return pow(a,N-2,N) + + +# order of the addition group of points +ec_N = bytes_to_long( + "\x01\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00"+\ + "\x13\xe9\x74\xe7\x2f\x8a\x69\x22\x03\x1d\x26\x03\xcf\xe0\xd7") + +# base point +ec_G = Point( + "\x00\xfa\xc9\xdf\xcb\xac\x83\x13\xbb\x21\x39\xf1\xbb\x75\x5f"+ + "\xef\x65\xbc\x39\x1f\x8b\x36\xf8\xf8\xeb\x73\x71\xfd\x55\x8b"+ + "\x01\x00\x6a\x08\xa4\x19\x03\x35\x06\x78\xe5\x85\x28\xbe\xbf"+ + "\x8a\x0b\xef\xf8\x67\xa7\xca\x36\x71\x6f\x7e\x01\xf8\x10\x52") + +def generate_ecdsa(k, sha): + k = bytes_to_long(k) + + if k >= ec_N: + raise Exception("Invalid private key") + + e = bytes_to_long(sha) + + m = open("/dev/random","rb").read(30) + if len(m) != 30: + raise Exception("Failed to get random data") + m = bytes_to_long(m) % ec_N + + r = (m * ec_G).x.tobignum() % ec_N + + kk = ((r*k)+e)%ec_N + s = (bn_inv(m,ec_N) * kk)%ec_N + + r = long_to_bytes(r,30) + s = long_to_bytes(s,30) + return r,s + +def check_ecdsa(q,r,s,sha): + + q = Point(q) + r = bytes_to_long(r) + s = bytes_to_long(s) + e = bytes_to_long(sha) + + s_inv = bn_inv(s,ec_N) + + w1 = (e*s_inv)%ec_N + w2 = (r*s_inv)%ec_N + + r1 = w1 * ec_G + w2 * q + + rx = r1.x.tobignum()%ec_N + + return rx == r + +def priv_to_pub(k): + k = bytes_to_long(k) + q = k * ec_G + return q.tobytes() + +def gen_priv_key(): + k = open("/dev/random","rb").read(30) + if len(k) != 30: + raise Exception("Failed to get random data") + + k = bytes_to_long(k) + k = k % ec_N + return long_to_bytes(k,30) diff --git a/pywii/Common/pywii/wii.py b/pywii/Common/pywii/wii.py new file mode 100644 index 0000000..19ad366 --- /dev/null +++ b/pywii/Common/pywii/wii.py @@ -0,0 +1,1757 @@ +#!/usr/bin/env python +# Copyright 2008 Hector Martin +# Licensed under the terms of the GNU GPL, version 2 +# http://www.gnu.org/licenses/old-licenses/gpl-2.0.txt + +from struct import unpack, pack + +import os, os.path +import sys + +from Crypto.Cipher import AES +from Crypto.Hash import SHA +from Crypto.PublicKey import RSA +from Crypto.Util.number import bytes_to_long, long_to_bytes +from Crypto.Signature import pkcs1_15 + +import ec + +WII_RSA4096 = 0 +WII_RSA2048 = 1 +WII_ECDSA = 2 + +sigtypes = [ "RSA-4096", "RSA-2048", "EC-DSA" ] + +def load_rsa_key(issuer): + print "Loading private key for %s" % issuer + path = os.path.join(os.environ["HOME"], ".wii", "dpki", issuer + ".pem") + return RSA.importKey(open(path, "r").read()) + +signkeyfuncs = [ load_rsa_key, load_rsa_key, None ] + +NULL_IV = "\x00"*16 + +keylist = [ + "common-key", + "sd-key", + "sd-iv", + "md5-blanker", + "root-key", + "korean-key", +] + +keys = {} + +known_titles = { + 0x0000000100000001 : [ "boot2", "boot2" ], + 0x0000000100000002 : [ "System Menu", "SystemMenu" ], + 0x0000000100000100 : [ "BC", "BC" ], + 0x0000000100000101 : [ "MIOS", "MIOS" ], + 0x0001000148415858 : [ "The Homebrew Channel (HAXX)", "HBC1" ], + 0x000100014a4f4449 : [ "The Homebrew Channel (JODI)", "HBC2" ], + 0x00010001af1bf516 : [ "The Homebrew Channel (new)", "HBC3" ], + 0x000100014c554c5a : [ "The Homebrew Channel (LULZ)", "HBC4" ], + 0x0001000148424330 : [ "The Homebrew Channel OSS release (OHBC)", "OpenHBC" ], + 0x0001000844564458 : [ "DVDX (DVDX)", "DVDX1" ], + 0x0001000844495343 : [ "DVDX (DISC)", "DVDX2" ] +} + +known_titles_noregion = { + 0x0001000148414400 : [ "Internet Channel", "Internet" ], + 0x0001000148414a00 : [ "Everybody Votes Channel", "Votes" ], + 0x0001000148415000 : [ "Check Mii Out Channel", "CheckMii" ], + 0x0001000148415400 : [ "Nintendo Channel", "Nintendo" ], + 0x0001000148415600 : [ "Today and Tomorrow Channel", "Today" ], + 0x0001000148415700 : [ "Metroid Prime 3 Preview", "Metroid" ], + 0x0001000148434400 : [ "Digicam Print Channel", "Digicam" ], + 0x0001000148434600 : [ "Wii Speak Channel", "Speak", ], + + 0x0001000248414100 : [ "Photo Channel", "Photo" ], + 0x0001000248414200 : [ "Wii Shop Channel", "Shop" ], + 0x0001000248414300 : [ "Mii Channel", "Mii" ], + 0x0001000248414600 : [ "Weather Channel", "Weather" ], + 0x0001000248414700 : [ "News Channel", "News" ], + 0x0001000248415900 : [ "Photo Channel (1.1)", "Photo2" ], + + 0x0001000848414b00 : [ "EULA hidden channel", "EULA" ], + 0x0001000848414c00 : [ "Region Selection hidden channel", "Region" ], + 0x0001000848434300 : [ "Set Personal Data hidden channel", "Personal" ] +} + +def hexdump(s,sep=" "): + return sep.join(map(lambda x: "%02x"%ord(x),s)) + +def strcmp(s1,s2): + clen = min(len(s1),len(s2)) + + for i in range(clen): + if s1[i] == "\0" and s2[i] == "\0": + return True + if s1[i] != s2[i]: + return False + return False + +def ascii(s): + s2 = "" + for c in s: + if ord(c)<0x20 or ord(c)>0x7e: + s2 += "." + else: + s2 += c + return s2 + +def pad(s,c,l): + if len(s)= 0") + if size < 0: + raise ValueError("size must be >= 0") + if blocksize <= 0: + raise ValueError("blocksize must be > 0") + header = 0 + hdroffset = start % blocksize + startblock = start / blocksize + if hdroffset != 0: + startblock+=1 + header = blocksize - hdroffset + if size < header: + return (startblock, 0, hdroffset, size, 0) + start += header + size -= header + nblocks = size / blocksize + footer = size % blocksize + return (startblock, nblocks, hdroffset, header, footer) + +def get_readable_title(titleid, shortname = False): + if titleid in known_titles: + if shortname: + return known_titles[titleid][1] + return known_titles[titleid][0] + + if (titleid & ~0xFF) in known_titles_noregion: + if shortname: + return known_titles_noregion[titleid & ~0xFF][1] + return known_titles_noregion[titleid & ~0xFF][0] + + type = titleid >> 32 + id = titleid & 0xFFFFFFFF + if type == 0x00000001: # IOS + return "IOS%d" % id + + s = "%c%c%c%c" % (id >> 24, (id >> 16) & 0xFF, + (id >> 8) & 0xFF, id & 0xFF) + + if shortname: + return s + + if type == 0x00010002: # channel + return "Channel '%s'" % s + + if type == 0x00010008: # channel + return "System Channel '%s'" % s + + return "Unknown title type" + +def loadkeys(path = None): + keys.clear() + if path is None: + path = os.path.join(os.environ["HOME"], ".wii") + + for key in keylist: + try: + keys[key] = open(path + os.sep + key, "rb").read() + except: + print "Warning: failed to load key %s"%key + +def loadkeys_dpki(path = None): + if path is None: + path = os.path.join(os.environ["HOME"], ".wii", "dpki") + loadkeys(path) + +def parse_certs(blob): + certs = {} + certlist = [] + while blob != "": + cert = WiiCert(blob) + certs[cert.name] = cert + certlist.append(cert) + blob = blob[align(len(cert.data),0x40):] + + return (certs, certlist) + + +class HashError(RuntimeError): + def __init__(self, msg, expected, got): + RuntimeError.__init__(self, msg) + self.msg = msg + self.expected = expected + self.got = got + def __str__(self): + return "%s: expected %s, got %s"%(self.msg, hexdump(self.expected,''), hexdump(self.got,'')) + +class WiiPKAlgo: + def __init__(self,key): + self.key = key + + def get_digest(self, signature): + raise NotImplementedError("WiiPKAlgo is abstract") + + def check_digest(self, signature, expected): + return self.get_digest(signature) == expected + + def sign(self, data, key): + raise NotImplementedError("WiiPKAlgo is abstract") + + def bruteforce(self, signature, bytes, match): + codes = {1: "B", 2:"H", 4:"I", 8:"Q"} + + if bytes not in codes: + raise ValueError("Bytes must be 1,2,4, or 8") + + code = ">"+codes[bytes] + matchlen = len(match) + + for pad in xrange(0, 256**bytes): + pad += 0x4612512415125316 + pad %= 256**bytes + padsig = signature + pack(code, pad) + dig = self.get_digest(padsig) + if dig[:matchlen] == match: + return padsig + raise RuntimeError("Bruteforce failed") + +class WiiRSA(WiiPKAlgo): + def __init__(self,key): + WiiPKAlgo.__init__(self, key) + self.n = bytes_to_long(self.key[:-4]) + self.e = bytes_to_long(self.key[-4:]) + self.rsa = RSA.construct((self.n, self.e)) + self.can_get_digest = True + + def get_digest(self, signature): + lsig = bytes_to_long(signature) + if lsig >= self.n: + print "Warning: signature larger than modulus, using sig%modulus as signature" + ldec = pow(lsig, self.e, self.n) + dec = long_to_bytes(ldec) + pad = len(signature) - len(dec) + dec = "\x00"*pad+dec + return dec[-20:] + + def sign(self, data, key): + # Wrapper class to make PyCryptodome happy + class H(object): + oid = "1.3.14.3.2.26" + h = H() + h.digest = lambda: data + return pkcs1_15.new(key).sign(h) + +class WiiECDSA(WiiPKAlgo): + def __init__(self,key): + WiiPKAlgo.__init__(self, key) + self.can_get_digest = False + + def get_digest(self, signature): + raise NotImplementedError("EC-DSA cannot read message digest") + + def check_digest(self, signature, expected): + return ec.check_ecdsa(self.key,signature[0:30],signature[30:60],expected) + + def sign(self, data, key): + raise NotImplementedError("EC-DSA signing not implemented") + +class WiiDisc: + def __init__(self, iso, readonly=False): + self.fname = iso + self.readonly = readonly + self.f = open(self.fname, "rb") + + self.partitions = None + + self.read_header() + + def read_header(self): + self.f.seek(0) + self.gamecode = self.f.read(4) + self.makercode = self.f.read(2) + self.f.seek(0x18) + self.magic = unpack(">I",self.f.read(4))[0] + if self.magic != 0x5d1c9ea3: + raise RuntimeError("Not a Wii ISO!") + self.f.seek(0x20) + self.gamename = getcstring(self.f.read(0x60)) + + def read_partitions(self): + if self.partitions is not None: + return self.partitions + self.f.seek(0x40000) + pt_num, pt_start, p2_num, p2_start = unpack(">IIII",self.f.read(16)) + self.f.seek(pt_start<<2) + partitions = [] + for i in range(pt_num): + p_off, p_type = unpack(">II",self.f.read(8)) + partitions.append((p_off<<2, p_type)) + if p2_num > 0: + self.f.seek(p2_start<<2) + for i in range(p2_num): + p_off, p_type = unpack(">II",self.f.read(8)) + partitions.append((p_off<<2, p_type)) + + self.partitions = partitions + return self.partitions + + def showinfo(self): + print "Game %s, maker %s, magic %08x: %s"%(self.gamecode, self.makercode, self.magic, self.gamename) + self.read_partitions() + print "%d partitions in ISO:"%len(self.partitions) + for p_num,p_dat in enumerate(self.partitions): + print " [%2d] 0x%010x (%08x)"%(p_num,p_dat[0],p_dat[1]) + +class WiiSigned: + sigsizes = [512, 256, 60] + sigblocks = [0x240, 0x140, 0x80] + + def __init__(self, data): + self.type = "UNKNOWN" + if len(data) < 4: + raise ValueError("Data too short") + self.sigtype = unpack(">I",data[:4])[0] - 0x10000 + + if 0 <= self.sigtype < len(sigtypes): + self.signature = data[4:4+self.sigsizes[self.sigtype]] + self.body_offset = self.sigblocks[self.sigtype] + else: + raise ValueError("Unknown signature type %08x"%self.sigtype) + self.data = data + self.body = self.data[self.body_offset:] + self.data = data[:self._getbodysize() + self.body_offset] + self.body = self.body[:self._getbodysize()] + self.issuer = getcstring(self.body[:0x40]).split("-") + self.fillshort = None + + def update(self): + self.data = pack(">I",self.sigtype+0x10000) + self.signature + self.data += "\x00" * (self.body_offset - len(self.data)) + self.data += self.body + + def parse(self): + pass + + def _getbodysize(self): + return len(self.body) + + def get_hash(self): + return SHA.new(self.body).digest() + + def get_signature_hash(self,certs,): + cert = certs[self.issuer[-1]] + if cert.key_type != self.sigtype: + raise ValueError("Signature type %s does not match certificate type %s!"%(sigtypes[self.sigtype],sigtypes[cert.key_type])) + return cert.pkalgo.get_digest(self.sigtype, self.signature) + + def brute_sha(self, match = "\x00", fillshort = None): + l = len(match) + + if fillshort is None: + fillshort = self.fillshort + if fillshort is None: + raise ValueError("No fill short specified!") + + for cnt in range(65536): + self.body = self.body[:fillshort] + pack(">H",cnt) + self.body[fillshort+2:] + if self.get_hash()[:l] == match: + WiiSigned.update(self) + self.parse() + return + raise RuntimeError("SHA Bruteforce failed") + + def update_issuer(self, issuer): + if len(issuer) > 39: + raise ValueError("issuer name too long!") + self.issuer = issuer.split("-") + self.body = issuer + "\x00" * (0x40 - len(issuer)) + self.body[0x40:] + self.update() + + def update_signature(self, sig): + self.signature = sig + self.data = pack(">I",self.sigtype+0x10000) + self.signature + self.data[len(sig) + 4:] + + def null_signature(self): + self.signature = "\x00"*len(self.signature) + self.data = pack(">I",self.sigtype+0x10000) + self.signature + self.data[len(self.signature) + 4:] + + def sign(self,certs): + keyfunc = signkeyfuncs[self.sigtype] + if keyfunc is None: + raise ValueError("signing with %s not available" % sigtypes[self.sigtype]) + key = keyfunc(self.issuer[-1]) + if key is None: + raise ValueError("no key for %s available" % self.issuer[-1]) + cert = certs[self.issuer[-1]] + sig = cert.pkalgo.sign(self.get_hash(), key) + self.update_signature(sig) + return self.signcheck(certs) + + def signcheck(self,certs): + myhash = self.get_hash() + cert = certs[self.issuer[-1]] + return cert.pkalgo.check_digest(self.signature,myhash) + + def findcert(self, certs): + if self.issuer[-1] == "Root": + cert = WiiRootCert(keys['root-key']) + else: + cert = certs[self.issuer[-1]] + return cert + + def showsig(self,certs,it=""): + myhash = self.get_hash() + try: + if self.issuer[-1] == "Root": + cert = WiiRootCert(keys['root-key']) + else: + cert = certs[self.issuer[-1]] + if cert.pkalgo.can_get_digest: + signhash = cert.pkalgo.get_digest(self.signature) + if myhash == signhash: + print it+"%s signed by %s using %s: %s [OK]"%(self.type, "-".join(self.issuer), sigtypes[self.sigtype], hexdump(myhash)) + elif strcmp(myhash, signhash): + print it+"%s signed by %s using %s: %s [BUG]"%(self.type, "-".join(self.issuer), sigtypes[self.sigtype], hexdump(myhash)) + print it+" Signature hash: %s"%hexdump(signhash) + else: + print it+"%s signed by %s using %s: %s [FAIL]"%(self.type, "-".join(self.issuer), sigtypes[self.sigtype], hexdump(myhash)) + print it+" Signature hash: %s"%hexdump(signhash) + else: + sigok = cert.pkalgo.check_digest(self.signature,myhash) + if sigok: + print it+"%s signed by %s using %s: %s [OK]"%(self.type, "-".join(self.issuer), sigtypes[self.sigtype], hexdump(myhash)) + else: + print it+"%s signed by %s using %s: %s [FAIL]"%(self.type, "-".join(self.issuer), sigtypes[self.sigtype], hexdump(myhash)) + + except KeyError: + print it+"%s signed by %s using %s: %s [ISSUER NOT FOUND]"%(self.type, "-".join(self.issuer), sigtypes[self.sigtype], hexdump(myhash)) + +class WiiTik(WiiSigned): + def __init__(self, data): + WiiSigned.__init__(self, data) + self.type = "ETicket" + self.fillshort = 0xb2 + self.parse() + + def parse(self): + self.title_key_enc = self.body[0x7f:0x8f] + self.title_id = self.body[0x9c:0xa4] + self.title_key_iv = self.title_id + "\x00"*8 + self.common_key_index = ord(self.body[0xb1:0xb2]) + + try: + if self.common_key_index == 0: + key = keys["common-key"] + elif self.common_key_index == 1: + key = keys["korean-key"] + else: + print "WARNING: OLD FAKESIGNED TICKET WITH BAD KEY OFFSET, ASSUMING NORMAL COMMON KEY" + key = keys["common-key"] + aes = AES.new(key, AES.MODE_CBC, self.title_key_iv) + self.title_key = aes.decrypt(self.title_key_enc) + except: + self.title_key = None + + def update(self): + self.body = self.body[:0x9c] + self.title_id + self.body[0xa4:] + self.parse() + WiiSigned.update(self) + + def _getbodysize(self): + return 0x164 + + def showinfo(self, it=""): + print it+"ETicket: " + print it+" Title ID: "+repr(self.title_id) + print it+" Title key IV: "+hexdump(self.title_key_iv) + print it+" Title key (encrypted): "+hexdump(self.title_key_enc) + print it+" Common key index: %d" % self.common_key_index + if self.title_key is not None: + print it+" Title key (decrypted): "+hexdump(self.title_key) + +class WiiPartitionOffsets: + def __init__(self, data): + self.data = data + self.parse() + + def parse(self): + self.tmd_size = unpack(">I",self.data[0x0:0x4])[0] + self.tmd_offset = unpack(">I",self.data[0x4:0x8])[0]<<2 + self.cert_size = unpack(">I",self.data[0x8:0xc])[0] + self.cert_offset = unpack(">I",self.data[0xc:0x10])[0]<<2 + self.h3_offset = unpack(">I",self.data[0x10:0x14])[0]<<2 + self.data_offset = unpack(">I",self.data[0x14:0x18])[0]<<2 + self.data_size = unpack(">I",self.data[0x18:0x1c])[0]<<2 + + def showinfo(self, it=""): + print it+"TMD @ 0x%x [0x%x], Certs @ 0x%x [0x%x], H3 @ 0x%x, Data @ 0x%x [0x%x]"%( + self.tmd_offset, self.tmd_size, self.cert_offset, self.cert_size, + self.h3_offset, self.data_offset, self.data_size) + + def update(self): + self.data = pack(">II",self.tmd_size, self.tmd_offset>>2) + self.data += pack(">II",self.cert_size, self.cert_offset>>2) + self.data += pack(">I",self.h3_offset>>2) + self.data += pack(">II",self.data_offset>>2,self.data_size>>2) + +class WiiTmdContentRecord: + def __init__(self, data): + self.cid, self.index, self.ftype, self.size, self.sha = unpack(">IHHQ20s",data) + self.data = data + self.shared = (self.ftype & 0x8000) != 0 + + def update(self): + self.data = pack(">IHHQ20s", self.cid, self.index, self.ftype, self.size, self.sha) + +class WiiTmd(WiiSigned): + def __init__(self, data): + WiiSigned.__init__(self, data) + self.type = "TMD" + self.fillshort = 0x70 + self.parse() + + def parse(self): + self.version, self.ca_crl_version, self.signer_crl_version, \ + self.fill2, self.sys_version, self.title_id, self.title_type, \ + self.group_id, self.reserved, self.access_rights, \ + self.title_version, self.num_contents, self.boot_index = \ + unpack(">BBBBQ8sI2s62sIHHH",self.body[0x40:0xa2]) + + def _getbodysize(self): + self.parse() + return 0xa4+self.num_contents*36 + + def update(self): + hdr = pack(">BBBBQ8sI2s62sIHHH", \ + self.version, self.ca_crl_version, self.signer_crl_version, \ + self.fill2, self.sys_version, self.title_id, self.title_type, \ + self.group_id, self.reserved, self.access_rights, \ + self.title_version, self.num_contents, self.boot_index \ + ) + self.body = self.body[:0x40] + hdr + self.body[0xa2:] + WiiSigned.update(self) + + def get_content_records(self): + cts = [] + for i in range(self.num_contents): + ctr = WiiTmdContentRecord(self.body[0xa4+36*i:0xa4+36*(i+1)]) + cts.append(ctr) + return cts + + def find_cr_by_index(self, index): + for i,ct in enumerate(self.get_content_records()): + if ct.index == index: + return i + raise ValueError("Index %d not found"%index) + + def find_cr_by_cid(self, cid): + for i,ct in enumerate(self.get_content_records()): + if ct.cid == cid: + return i + raise ValueError("CID %08X not found"%cid) + + def update_content_record(self, num, cr): + cr.update() + self.body = self.body[:0xa4+36*num] + cr.data + self.body[0xa4+36*(num+1):] + self.update() + + def showinfo(self,it=""): + print it+"TMD: " + print it+" Versions: %d, CA CRL %d, Signer CRL %d, System %d-%d"%( + self.version,self.ca_crl_version,self.signer_crl_version,self.sys_version>>32,self.sys_version&0xffffffff) + print it+" Title ID: %s-%s (%s-%s)"%(hexdump(self.title_id[:4],''),hexdump(self.title_id[4:],''),repr(self.title_id[:4]),repr(self.title_id[4:])) + print it+" Title Type: %d"%self.title_type + print it+" Group ID: %s"%repr(self.group_id) + print it+" Access Rights: 0x%08x"%self.access_rights + print it+" Title Version: 0x%x"%self.title_version + print it+" Boot Index: %d"%self.boot_index + print it+" Contents:" + print it+" ID Index Type Size Hash" + for ct in self.get_content_records(): + + print it+" %08X %-5d 0x%-5x %-12s %s"%(ct.cid, ct.index, ct.ftype, "0x%x"%ct.size,hexdump(ct.sha)) + +class WiiCert(WiiSigned): + key_sizes = [516, 260, 60] + pk_types = [WiiRSA, WiiRSA, WiiECDSA] + + def __init__(self, data): + WiiSigned.__init__(self, data) + self.type = "Certificate" + self.parse() + + def _getbodysize(self): + keytype = unpack(">I",self.body[0x40:0x44])[0] + keysize = self.key_sizes[keytype] + return align(keysize + 0x8c,64) + + def parse(self): + self.key_type = unpack(">I",self.body[0x40:0x44])[0] + self.key_size = self.key_sizes[self.key_type] + self.name = getcstring(self.body[0x44:0x84]) + self.unk2 = self.body[0x84:0x88] + self.key = self.body[0x88:0x88 + self.key_size] + self.pkalgo = self.pk_types[self.key_type](self.key) + + def showinfo(self,it=""): + print it+"%s (%s)"%(self.name,sigtypes[self.key_type]) + +class WiiRootCert: + def __init__(self, data): + self.type = "Certificate" + self.key = data + self.pkalgo = WiiRSA(self.key) + self.name = "Root" + self.key_size = 516 + self.key_type = 0 + + def showinfo(self,it=""): + print it+"%s (%s)"%(self.name,sigtypes[self.key_type]) + +class WiiPartition: + BLOCKS_PER_SUBGROUP = 8 + SUBGROUPS_PER_GROUP = 8 + BLOCKS_PER_GROUP = BLOCKS_PER_SUBGROUP * SUBGROUPS_PER_GROUP + CIPHER_BLOCK_SIZE = 0x8000 + CIPHER_SUBGROUP_SIZE = BLOCKS_PER_SUBGROUP*CIPHER_BLOCK_SIZE + CIPHER_GROUP_SIZE = SUBGROUPS_PER_GROUP*CIPHER_SUBGROUP_SIZE + SHA_SIZE = 0x400 + DATA_SIZE = CIPHER_BLOCK_SIZE - SHA_SIZE + DATA_CHUNK_SIZE = 0x400 + DATA_CHUNKS_PER_BLOCK = DATA_SIZE / DATA_CHUNK_SIZE + PLAIN_BLOCK_SIZE = DATA_SIZE + PLAIN_SUBGROUP_SIZE = BLOCKS_PER_SUBGROUP*PLAIN_BLOCK_SIZE + PLAIN_GROUP_SIZE = SUBGROUPS_PER_GROUP*PLAIN_SUBGROUP_SIZE + NUM_H3,TAIL_H3 = divmod(0x18000,20) #the division has a remainder, so there is some dead space + + def __init__(self, disc, number, checkhash = True): + if disc.readonly: + self.f = open(disc.fname,"rb") + else: + self.f = open(disc.fname,"r+b") + self.offset = disc.read_partitions()[number][0] + + self.read_headers() + + self.checkhash = checkhash + + if checkhash and not self.checkh4hash(): + raise HashError("H4 hash check failed",self.tmd.get_content_records()[0].sha, self.geth4hash()) + + def _seek(self,n): + self.f.seek(self.offset + n) + + def read_headers(self): + + self._seek(0) + self.tik = WiiTik(self.f.read(0x2a4)) + self.offsets = WiiPartitionOffsets(self.f.read(0x1c)) + self._seek(self.offsets.tmd_offset) + self.tmd = WiiTmd(self.f.read(self.offsets.tmd_size)) + self.certs = {} + self._seek(self.offsets.cert_offset) + certdata = self.f.read(self.offsets.cert_size) + self.certlist = [] + while certdata != "": + cert = WiiCert(certdata) + self.certs[cert.name] = cert + self.certlist.append(cert) + certdata = certdata[align(len(cert.data),0x40):] + self._seek(self.offsets.h3_offset) + h3data = self.f.read(0x18000) + self.h3 = [] + for i in range(self.NUM_H3): + self.h3.append(h3data[20*i:20*(i+1)]) + self.data_offset = self.offsets.data_offset + self.offset + self.data_blocks = self.offsets.data_size / self.CIPHER_BLOCK_SIZE + if self.offsets.data_size % self.CIPHER_BLOCK_SIZE != 0: + raise ValueError("Data size (0x%x) not a multiple of block size (0x%x)"%( + self.offsets.data_size, self.CIPHER_BLOCK_SIZE)) + self.data_bytes = self.data_blocks * self.PLAIN_BLOCK_SIZE + self.data_subgroups = self.data_blocks / self.BLOCKS_PER_SUBGROUP + self.data_groups = self.data_subgroups / self.SUBGROUPS_PER_GROUP + self.extra_subgroup_blocks = self.data_blocks % self.BLOCKS_PER_SUBGROUP + self.extra_group_blocks = self.data_blocks % self.BLOCKS_PER_GROUP + self.partition_end = self.data_offset + self.data_blocks * self.CIPHER_BLOCK_SIZE + + def updatetmd(self): + self._seek(self.offsets.tmd_offset) + self.f.write(self.tmd.data) + + def updatetik(self): + self._seek(0) + self.f.write(self.tik.data) + + def updateoffsets(self): + self.offsets.update() + self._seek(0x2a4) + self.f.write(self.offsets.data) + + def showinfo(self,it=""): + print it+"Wii Partition at 0x%010x:"%(self.offset) + self.offsets.showinfo(" ") + self.tik.showinfo(it+" ") + self.tik.showsig(self.certs,it+" ") + self.tmd.showinfo(it+" ") + self.tmd.showsig(self.certs,it+" ") + if self.checkh4hash(): + print it+" H4 hash check passed" + else: + print it+" H4 check failed: SHA1(H3) = "+hexdump(self.geth4hash()) + print it+" Data:" + print it+" Blocks: %d"%self.data_blocks + print it+" Subgroups: %d (plus %d blocks)"%(self.data_subgroups,self.extra_subgroup_blocks) + print it+" Groups: %d (plus %d blocks)"%(self.data_groups,self.extra_group_blocks) + self.showcerts(it+" ") + + def showcerts(self,it=""): + print it+"Certificates: " + for cert in self.certlist: + cert.showinfo(it+" - ") + cert.showsig(self.certs,it+" ") + + def geth4hash(self): + return SHA.new(''.join(self.h3) + "\x00"*self.TAIL_H3).digest() + + def checkh4hash(self): + return self.geth4hash() == self.tmd.get_content_records()[0].sha + + def updateh3(self): + self._seek(self.offsets.h3_offset) + self.f.write(''.join(self.h3)) + + def updateh4(self): + cr = self.tmd.get_content_records()[0] + cr.sha = self.geth4hash() + self.tmd.update_content_record(0,cr) + self.updatetmd() + + def readc(self, start, length): + '''Read raw data from the partition''' + if (start + length) > self.offsets.data_size: + raise ValueError("Attempted to read past the end of the partition data") + if start < 0: + raise ValueError("start must be >= 0") + self.f.seek(self.data_offset + start) + return self.f.read(length) + + def readblockc(self, block): + '''Read a raw block (0x8000 bytes) from the partition''' + if block >= self.data_blocks: + raise ValueError("Attempted to read block past the end of the partition data") + if block < 0: + raise ValueError("block must be >= 0") + self.f.seek(self.data_offset + block * self.CIPHER_BLOCK_SIZE) + return self.f.read(self.CIPHER_BLOCK_SIZE) + + def readsubgroupc(self, subgroup): + '''Read a raw subgroup (0x40000 bytes) from the partition''' + if subgroup >= self.data_subgroups: + raise ValueError("Attempted to read subgroup past the end of the partition data") + if subgroup < 0: + raise ValueError("block must be >= 0") + self.f.seek(self.data_offset + subgroup * self.CIPHER_SUBGROUP_SIZE) + return self.f.read(self.CIPHER_SUBGROUP_SIZE) + + def readgroupc(self, group): + '''Read a raw group (0x200000 bytes) from the partition''' + if group >= self.data_groups: + raise ValueError("Attempted to read group past the end of the partition data") + if group < 0: + raise ValueError("block must be >= 0") + self.f.seek(self.data_offset + group * self.CIPHER_GROUP_SIZE) + return self.f.read(self.CIPHER_GROUP_SIZE) + + def read(self, start, length): + '''Read data from an arbitrary offset into the partition''' + if (start+length) > (self.data_blocks*self.PLAIN_BLOCK_SIZE): + raise ValueError("Attempted to read past the end of the partition data (0x%x + 0x%x)"%(start,length)) + if start < 0: + raise ValueError("start must be >= 0") + + bstart, bnum, hdroff, header, footer = toblocks(start, length, self.PLAIN_BLOCK_SIZE) + + if header: + data = self.readblock(bstart - 1)[hdroff:hdroff+header] + else: + data = "" + for block in xrangel(bstart, bnum): + data += self.readblock(block) + if footer: + data += self.readblock(bstart + bnum)[:footer] + return data + + def _check_hash_chain(self, blocknum, data, sha): + h0 = sha[:0x26c] + h1 = sha[0x280:0x320] + h2 = sha[0x340:0x3e0] + for i in range(self.DATA_CHUNKS_PER_BLOCK): + h0i = SHA.new(data[i*self.DATA_CHUNK_SIZE:(i+1)*self.DATA_CHUNK_SIZE]).digest() + if h0i != h0[i*20:(i+1)*20]: + raise HashError("Failed to verify data chunk %d against H0"%i,h0[i*20:(i+1)*20],h0i) + h1i = SHA.new(h0).digest() + rem,block = divmod(blocknum, self.BLOCKS_PER_SUBGROUP) + if h1i != h1[block*20:(block+1)*20]: + raise HashError("Failed to verify H0 %d against H1"%block,h1[block*20:(block+1)*20],h1i) + h2i = SHA.new(h1).digest() + group,subgroup = divmod(rem, self.SUBGROUPS_PER_GROUP) + if h2i != h2[subgroup*20:(subgroup+1)*20]: + raise HashError("Failed to verify H1 %d against H2"%subgroup,h2[subgroup*20:(subgroup+1)*20],h2i) + h3i = SHA.new(h2).digest() + if h3i != self.h3[group]: + raise HashError("Failed to verify H2 %d against H3"%group,self.h3[group],h3i) + + def _decrypt_block(self, block, blocknum): + if self.tik.title_key is None: + raise RuntimeError("Title key is missing (you probably don't have the common key)") + data_iv = block[0x3d0:0x3e0] + aes = AES.new(self.tik.title_key, AES.MODE_CBC, data_iv) + data = aes.decrypt(block[self.SHA_SIZE:]) + if self.checkhash: + aes = AES.new(self.tik.title_key, AES.MODE_CBC, NULL_IV) + shablock = aes.decrypt(block[:self.SHA_SIZE]) + self._check_hash_chain(blocknum, data, shablock) + return data + + def readblock(self, blocknum): + '''Read one block of data (0x7C00 bytes) from the partition''' + if blocknum >= self.data_blocks: + raise ValueError("Attempted to read block past the end of the partition data") + if blocknum < 0: + raise ValueError("blocknum must be >= 0") + + rawblock = self.readblockc(blocknum) + return self._decrypt_block(rawblock, blocknum) + + def readsubgroup(self, subgroupnum): + '''Read one subgroup of data (0x3E000 bytes) from the partition''' + if subgroupnum > self.data_subgroups : + raise ValueError("Attempted to read subgroup past the end of the partition data") + if subgroupnum < 0: + raise ValueError("subgroupnum must be >= 0") + + nblocks = self.BLOCKS_PER_SUBGROUP + blockoff = subgroupnum * self.BLOCKS_PER_SUBGROUP + if subgroupnum == self.data_subgroups: + if self.extra_subgroup_blocks != 0: + nblocks = self.extra_subgroup_blocks + blocknum = subgroupnum * self.BLOCKS_PER_SUBGROUP + else: + raise ValueError("Attempted to read subgroup past the end of the partition data") + + data = "" + for i in range(nblocks): + data += self.readblock(blockoff+i) + return data + + def readgroup(self, groupnum): + if groupnum > self.data_groups: + raise ValueError("Attempted to read group past the end of the partition data") + if groupnum < 0: + raise ValueError("groupnum must be >= 0") + + nblocks = self.BLOCKS_PER_GROUP + blockoff = groupnum * self.BLOCKS_PER_GROUP + if groupnum == self.data_groups: + if self.extra_group_blocks != 0: + nblocks = self.extra_group_blocks + blockoff = groupnum * self.BLOCKS_PER_GROUP + else: + raise ValueError("Attempted to read group past the end of the partition data") + + data = "" + for i in range(nblocks): + data += self.readblock(blockoff+i) + return data + + def writegroup(self, groupnum, data): + '''Write a group of data (0x1F0000 bytes) to the partition''' + + if self.tik.title_key is None: + raise RuntimeError("Title key is missing (you probably don't have the common key)") + + if groupnum >= self.data_groups: + # tolerate writing last incomplete group if data is of the right size + if groupnum == self.data_groups and self.extra_group_blocks > 0 and len(data) == (self.extra_group_blocks * self.PLAIN_BLOCK_SIZE): + blocks = self.extra_group_blocks + writesize = blocks * self.CIPHER_BLOCK_SIZE + data += "\x00" * (self.PLAIN_BLOCK_SIZE * self.BLOCKS_PER_GROUP - blocks) + else: + raise ValueError("Attempted to write group past the end of the partition data") + else: + blocks = self.BLOCKS_PER_GROUP + writesize = self.CIPHER_GROUP_SIZE + if len(data) != self.PLAIN_GROUP_SIZE: + raise ValueError("Data size must be equal to group size") + + h0 = [] + h1 = [] + h2 = "" + for subgroup in range(self.SUBGROUPS_PER_GROUP): + bh1 = "" + sh0 = [] + for block in range(self.BLOCKS_PER_SUBGROUP): + bh0 = "" + for chunk in range(self.DATA_CHUNKS_PER_BLOCK): + offset = subgroup * self.PLAIN_SUBGROUP_SIZE + block * self.PLAIN_BLOCK_SIZE + chunk * self.DATA_CHUNK_SIZE + bh0 += SHA.new(data[offset:offset+self.DATA_CHUNK_SIZE]).digest() + sh0.append(bh0) + bh1 += SHA.new(bh0).digest() + h0.append(sh0) + h1.append(bh1) + h2 += SHA.new(bh1).digest() + h3 = SHA.new(h2).digest() + + data_out = "" + for subgroup in range(self.SUBGROUPS_PER_GROUP): + for block in range(self.BLOCKS_PER_SUBGROUP): + shablock = "" + shablock += h0[subgroup][block] + shablock += "\x00"*20 + shablock += h1[subgroup] + shablock += "\x00"*32 + shablock += h2 + shablock += "\x00"*32 + assert len(shablock) == self.SHA_SIZE, "sha block size messed up" + aes = AES.new(self.tik.title_key, AES.MODE_CBC, NULL_IV) + shablock = aes.encrypt(shablock) + data_out += shablock + aes = AES.new(self.tik.title_key, AES.MODE_CBC, shablock[0x3d0:0x3e0]) + offset = subgroup * self.PLAIN_SUBGROUP_SIZE + block * self.PLAIN_BLOCK_SIZE + data_out += aes.encrypt(data[offset:offset+self.PLAIN_BLOCK_SIZE]) + + assert len(data_out) == self.CIPHER_GROUP_SIZE, "group size messed up" + + self.f.seek(self.data_offset + groupnum * self.CIPHER_GROUP_SIZE) + self.f.write(data_out[:writesize]) + + self.h3[groupnum] = h3 + + def write(self, start, data): + if (start+len(data)) > self.data_blocks*self.PLAIN_BLOCK_SIZE: + raise ValueError("Attempted to write past the end of the partition data") + if start < 0: + raise ValueError("start must be >= 0") + + gstart, gnum, hdroff, header, footer = toblocks(start, len(data), self.PLAIN_GROUP_SIZE) + + if header != 0: + g = self.readgroup(gstart-1) + g2 = g[:hdroff] + data[:header] + g[hdroff+header:] + assert len(g) == len(g2), "group length mismatch in header" + self.writegroup(gstart-1,g2) + for group in xrangel(gstart,gnum): + self.writegroup(group,data[header+self.PLAIN_GROUP_SIZE*group:header+self.PLAIN_GROUP_SIZE*(group+1)]) + if footer != 0: + g = self.readgroup(gstart+gnum) + g2 = data[header+self.PLAIN_GROUP_SIZE*gnum:] + g[footer:] + assert len(g) == len(g2), "group length mismatch in footer" + self.writegroup(gstart+gnum,g2) + + def update(self): + self.updateh3() + self.updateh4() + self.updatetmd() + self.updatetik() + +class CacheEntry: + def __init__(self, data, lastuse, dirty=False): + self.data = data + self.lastuse = lastuse + self.dirty = dirty + + def __repr__(self): + return "CacheEntry([0x%x bytes], %d, %s)"%(len(self.data),self.lastuse,repr(self.dirty)) + def __str__(self): + return repr(self) + +class WiiCachedPartition(WiiPartition): + def __init__(self, disc, number, checkhash = True, cachesize=16, debug=False): + WiiPartition.__init__(self, disc, number, checkhash) + self.cachesectors = cachesize * 32 + self.cache = {} + self.opnum = 0 + self.debug = debug + + def _dprint(self, s, *args): + if self.debug: + print s%tuple(args) + + def _readblock(self, blocknum): + self._dprint("_readblock(0x%x)",blocknum) + d=WiiPartition.readblock(self, blocknum) + self._dprint(" -> [0x%x]",len(d)) + return d + + def _flushgroup(self, groupno): + self._dprint("_flushgroup(0x%x)",groupno) + groupdata = "" + startblock = groupno * self.BLOCKS_PER_GROUP + if groupno == self.data_groups and self.extra_group_blocks > 0: + self._dprint(" Last group") + numblocks = self.extra_group_blocks + else: + numblocks = self.BLOCKS_PER_GROUP + self._dprint(" numblocks = 0x%x",numblocks) + for block in range(numblocks): + self._dprint(" block 0x%x",block+startblock) + if (block+startblock) in self.cache: + self._dprint(" in cache: %s",repr(self.cache[block+startblock])) + groupdata += self.cache[block+startblock].data + else: + self._dprint(" not in cache") + groupdata += self._readblock(block+startblock) + + self._dprint(" writegroup(0x%x, [0x%x])",groupno,len(groupdata)) + WiiPartition.writegroup(self, groupno, groupdata) + + for block in range(self.BLOCKS_PER_GROUP): + if (block+startblock) in self.cache: + self._dprint(" cache undirty: 0x%x",block+startblock) + self.cache[block+startblock].dirty = False + + def _freecache(self, num): + self._dprint("_freecache(%d)",num) + while (len(self.cache)+num) > self.cachesectors: + self._dprint(" want %d, have %d, freeing",num,self.cachesectors-len(self.cache)) + # dirty group lastops + groups = {} + for key in self.cache: + if self.cache[key].dirty: + grnum = key/self.BLOCKS_PER_GROUP + if grnum in groups: + groups[grnum] = max(groups[grnum], self.cache[key].lastuse) + else: + groups[grnum] = self.cache[key].lastuse + + lruop = self.opnum + lru = -1 + lrugroup = None + for key in self.cache: + # if dirty, return the lastop of the entire group + if self.cache[key].dirty: + groupno = key/self.BLOCKS_PER_GROUP + if groups[groupno] < lruop: + lru = key + lruop = groups[groupno] + lrugroup = groupno + # otherwise return the lastop of the sector only + else: + if self.cache[key].lastuse < lruop: + lrugroup = None + lru = key + lruop = self.cache[key].lastuse + self._dprint(" LRU cache entry: [0x%x, %d]: %s",lru,lruop,self.cache[lru]) + if lrugroup is not None: + self._dprint(" dirty, in group 0x%x",lrugroup) + # if not dirty, delete it and continue + if lrugroup is None: + del self.cache[lru] + continue + # otherwise flush entire group and try again + else: + self._flushgroup(lrugroup) + + def readblock(self, blocknum): + '''Read one block of data (0x7C00 bytes) from the partition''' + self._dprint("readblock(0x%x)",blocknum) + if blocknum >= self.data_blocks: + raise ValueError("Attempted to read block past the end of the partition data") + if blocknum < 0: + raise ValueError("blocknum must be >= 0") + + self.opnum += 1 + if blocknum in self.cache: + self._dprint(" cached: %s",self.cache[blocknum]) + self.cache[blocknum].lastuse = self.opnum + return self.cache[blocknum].data + else: + self._dprint(" not cached") + self._freecache(1) + decblock = self._readblock(blocknum) + self.cache[blocknum] = CacheEntry(decblock, self.opnum, False) + return decblock + + def writeblock(self, blocknum, data): + self._dprint("writeblock(0x%x,[0x%x])",blocknum, len(data)) + if self.tik.title_key is None: + raise RuntimeError("Title key is missing (you probably don't have the common key)") + + if blocknum >= self.data_blocks: + raise ValueError("Attempted to write block past the end of the partition data") + if blocknum < 0: + raise ValueError("blocknum must be >= 0") + if len(data) != self.PLAIN_BLOCK_SIZE: + raise ValueError("Data size must be equal to block size") + + self.opnum += 1 + + if blocknum not in self.cache: + self._dprint(" not in cache, freeing") + self._freecache(1) + self.cache[blocknum]=CacheEntry(data,self.opnum,True) + + # writegroup is not cached, since we already have the entire block + # but we update the cache, for stuff already in cache + def writegroup(self, groupnum, data): + self._dprint("writegroup(0x%x,[0x%x])",groupnum, len(data)) + self.opnum += 1 + WiiPartition.writegroup(self, groupnum, data) + + if groupnum == self.data_groups: + nblocks = self.extra_group_blocks + else: + nblocks = self.BLOCKS_PER_GROUP + + blockoff = groupnum * self.BLOCKS_PER_GROUP + self._dprint(" nblocks=0x%x",nblocks) + self._dprint(" blockoff=0x%x",blockoff) + for blockno in range(nblocks): + if (blockno+blockoff) in self.cache: + self._dprint(" updating cache entry for block 0x%x",blockno+blockoff) + self.cache[blockno+blockoff].data = data[blockno*self.PLAIN_BLOCK_SIZE:(1+blockno)*self.PLAIN_BLOCK_SIZE:] + self.cache[blockno+blockoff].lastuse = self.opnum + self.cache[blockno+blockoff].dirty = False + self._dprint(" --> %s",self.cache[blockno+blockoff]) + + def write(self, start, data): + '''Write data to an arbitrary offset into the partition''' + self._dprint("write(0x%x,[0x%x])",start, len(data)) + if (start+len(data)) > self.data_blocks*self.PLAIN_BLOCK_SIZE: + raise ValueError("Attempted to write past the end of the partition data") + if start < 0: + raise ValueError("start must be >= 0") + + bstart, bnum, hdroff, header, footer = toblocks(start, len(data), self.PLAIN_BLOCK_SIZE) + + self._dprint(" bstart=0x%x bnum=0x%x hdroff=0x%x header=0x%x footer=0x%x",bstart,bnum,hdroff,header,footer) + + if header: + hb = self.readblock(bstart - 1) + hb = hb[:hdroff] + data[:header] + hb[hdroff+header:] + self.writeblock(bstart - 1, hb) + for block in xrange(bnum): + self.writeblock(block+bstart, data[header+block*self.PLAIN_BLOCK_SIZE:header+(block+1)*self.PLAIN_BLOCK_SIZE]) + if footer: + fb = self.readblock(bstart+bnum) + df = data[header+bnum*self.PLAIN_BLOCK_SIZE:] + self._dprint(" df: [0x%x]",len(df)) + fb = df + fb[footer:] + self.writeblock(bstart + bnum, fb) + + def flush(self): + self._dprint("flush()") + # dirty groups + groups = [] + for key in self.cache: + if self.cache[key].dirty: + grnum = key/self.BLOCKS_PER_GROUP + if grnum not in groups: + groups.append(grnum) + for group in groups: + self._flushgroup(group) + +class WiiPartitionFile: + def __init__(self, part, offset=0, size=None): + self.part = part + self.pointer = 0 + self.offset = offset + if size is None: + self.size = part.data_bytes - offset + else: + self.size = size + def read(self, n=None): + leftn = self.size - self.pointer + if n is None: + n = leftn + if n > leftn: + n = leftn + if n < 0: + return "" + d = self.part.read(self.pointer + self.offset, n) + self.pointer += len(d) + return d + + def write(self, d): + self.part.write(self.pointer + self.offset,d) + self.pointer += len(d) + + def tell(self): + return self.pointer + + def seek(self, where, whence=0): + if whence == 1: + where += self.pointer + elif whence == 2: + where += self.size + elif whence == 0: + pass + else: + raise ValueError("Invalid value for whence: %d"%whence) + self.pointer = where + + def close(self): + self.part = None + +class WiiApploader: + def __init__(self, data): + self.date = getcstring(data[:0x10]) + self.entry, self.textsize, self.trailersize = unpack(">III",data[0x10:0x1c]) + self.text = data[0x20:0x20+self.textsize] + self.trailer = data[0x20+self.textsize:0x20+self.textsize+self.trailersize] + self.extrafooter = data[0x20+self.textsize+self.trailersize:] + + def showinfo(self, it=""): + print it+"Apploader:" + print it+" Date: %s"%self.date + print it+" Entrypoint: 0x%08x"%self.entry + print it+" Text size: 0x%x"%self.textsize + print it+" Trailer size: 0x%x"%self.trailersize + +class WiiPartitionData: + def __init__(self, partition): + self.part = partition + self.hdr = self.part.read(0, 0x440) + self.bi2 = self.part.read(0x440, 0x20) + + self.gamecode, self.maker, self.diskid, self.version, self.streaming, self.strbufsize = unpack(">4s2sBBBB",self.hdr[:10]) + self.magic = unpack(">I",self.hdr[0x18:0x1c])[0] + if self.magic != 0x5d1c9ea3: + raise RuntimeError("Not a Wii partition!") + + self.gamename = getcstring(self.part.read(0x20,0x60)) + + self.dbgoff, self.dbgaddr, self.rsvd1, self.doloff, self.fstoff, self.fstsize, self.fstmax, self.usrpos, self.usrlen, self.unk1, self.rsvd2 = unpack(">II24sIIIIIIII", self.hdr[0x400:0x440]) + self.fstoff *= 4 + self.fstsize *= 4 + self.doloff *= 4 + self.dbgoff *= 4 + + apphdr = self.part.read(0x2440,32) + appsiz, apptr = unpack(">II",apphdr[0x14:0x1c]) + self.apploadersize = align(appsiz + apptr,32) + 32 + self.apploader = WiiApploader(self.part.read(0x2440, self.apploadersize)) + + self.dolsize = 0x100 + dolhdr = self.part.read(self.doloff, 0x100) + for i in range(0,0x48,4): + self.dolsize += unpack(">I",dolhdr[0x90+i:0x94+i])[0] + self.dol = self.part.read(self.doloff, self.dolsize) + + fstd = self.part.read(self.fstoff,self.fstsize) + self.fst = WiiFST(fstd, wiigcm=True) + + def replacedol(self, dol): + if len(dol) > self.dolsize: + raise ValueError("dol must be smaller than preexisting dol") + self.dol = dol + self.part.write(self.doloff, dol) + def showinfo(self,it=""): + print it+"Partition data:" + print it+" Game Name: %s"%self.gamename + print it+" Offsets: DOL @ 0x%x [0x%x], Apploader @ 0x%x [0x%x], FST @ 0x%x [0x%x]"%(self.doloff, self.dolsize, 0x2440, self.apploadersize, self.fstoff, self.fstsize) + self.apploader.showinfo(it+" ") + print it+"FST:" + self.fst.show(it+" ") + +class FakeFile: + def __init__(self, data): + self.data = data + self.offset = 0 + def close(self): + pass + def flush(self): + pass + def write(self, data): + raise NotImplementedError("Writes not implemented") + def read(self, size=None): + if size is None: + return self.data[self.offset:] + r = self.data[self.offset:self.offset+size] + self.offset=min(len(self.data),self.offset+size) + def tell(self): + return self.offset + def seek(self, offset, whence=0): + if whence == 0: + pass + elif whence == 1: + offset += self.offset + elif whence == 2: + offset += len(self.data) + else: + raise ValueError("Invalid whence") + self.offset=max(0,min(len(self.data),offset)) + +class WiiWad: + def __init__(self, file, readonly = True): + self.ALIGNMENT = 0x40 + if readonly: + self.f = open(file, "rb") + else: + self.f = open(file, "r+b") + self.read_headers() + + def read_hdr(self): + + self.f.seek(0) + + hdr = unpack(">I2sHIIIIII",self.f.read(0x20)) + if hdr[0] != 0x20: + raise ValueError("Invalid header length!") + + self.hdr_len = hdr[0] + self.wadtype = hdr[1] + self.cert_len = hdr[3] + self.tik_len = hdr[5] + self.tmd_len = hdr[6] + self.data_len = hdr[7] + self.footer_len = hdr[8] + + falign(self.f, self.ALIGNMENT) + + def read_boot2hdr(self): + + self.ALIGNMENT = 0 + self.f.seek(0) + + hdr = unpack(">IHHIIIIII",self.f.read(0x20)) + if hdr[0] != 0x20: + raise ValueError("Invalid header length!") + + self.hdr_len = hdr[0] + self.wadtype = hdr[1] + self.cert_len = hdr[3] + self.tik_len = hdr[4] + self.tmd_len = hdr[5] + + self.f.seek(0,os.SEEK_END) + size = self.f.tell() + self.f.seek(0x20) + + self.data_len = size - 0x20 - self.tmd_len - self.tik_len - self.cert_len + self.data_off = align(0x20 + self.tmd_len + self.tik_len + self.cert_len, 0x40) + self.footer_len = 0 + + def read_headers(self): + self.data_off = None + + self.f.seek(4) + wt = self.f.read(2) + if wt == "\x00\x00": + self.read_boot2hdr() + else: + self.read_hdr() + + certdata = self.f.read(self.cert_len) + self.certlist = [] + self.certs = {} + while certdata != "": + cert = WiiCert(certdata) + self.certs[cert.name] = cert + self.certlist.append(cert) + certdata = certdata[align(len(cert.data),0x40):] + + falign(self.f, self.ALIGNMENT) + self.tik_offset = self.f.tell() + self.tik = WiiTik(self.f.read(self.tik_len)) + falign(self.f, self.ALIGNMENT) + self.tmd_offset = self.f.tell() + self.tmd = WiiTmd(self.f.read(self.tmd_len)) + falign(self.f, 0x40) + self.data_off = self.f.tell() + self.f.seek(self.data_len,1) + falign(self.f, self.ALIGNMENT) + self.footer = self.f.read(self.footer_len) + + def updatetmd(self): + self.f.seek(self.tmd_offset) + self.f.write(self.tmd.data) + def updatetik(self): + self.f.seek(self.tik_offset) + self.f.write(self.tik.data) + + def showinfo(self,it=""): + print it+"Wii Wad:" + print it+" Header 0x%x Type %s Certs 0x%x Tik 0x%x TMD 0x%x Data 0x%x @ 0x%x Footer 0x%x"%(self.hdr_len, repr(self.wadtype), self.cert_len, self.tik_len, self.tmd_len, self.data_len, self.data_off, self.footer_len) + self.tik.showinfo(it+" ") + self.tik.showsig(self.certs,it+" ") + self.tmd.showinfo(it+" ") + self.tmd.showsig(self.certs,it+" ") + allok=True + for ct in self.tmd.get_content_records(): + d = self.getcontent(ct.index) + sha = SHA.new(d).digest() + if sha != ct.sha: + print it+" SHA-1 for content %08x is invalid:"%ct.cid, hexdump(sha) + print it+" Expected:",hexdump(ct.sha) + allok=False + if allok: + print it+" All content SHA-1 hashes are valid" + self.showcerts(it+" ") + + def showcerts(self,it=""): + print it+"Certificates: " + for cert in self.certlist: + cert.showinfo(it+" - ") + cert.showsig(self.certs,it+" ") + + def getcontent(self, index, encrypted=False): + self.f.seek(self.data_off) + for ct in self.tmd.get_content_records(): + if ct.index == index: + data = self.f.read(align(ct.size,0x10)) + if encrypted: + return data + + iv = pack(">H",index)+"\x00"*14 + aes = AES.new(self.tik.title_key, AES.MODE_CBC, iv) + return aes.decrypt(data)[:ct.size] + + self.f.seek(ct.size,1) + falign(self.f, 0x40) + +class WiiWadMaker(WiiWad): + def __init__(self, file, tmd, tik, certs, footer, nandwad=False): + self.ALIGNMENT = 0x40 + self.tmd = tmd + self.tik = tik + self.certlist = certs + self.certs = {} + for cert in certs: + self.certs[cert.name] = cert + self.f = open(file, "wb") + self.hdr_len = 0x20 + self.cert_len = sum([len(c.data) for c in self.certlist]) + self.tmd_len = len(self.tmd.data) + self.tik_len = len(self.tik.data) + + # if boot2, set type to "ib" + if self.tmd.title_id == "\x00\x00\x00\x01\x00\x00\x00\x01": + if nandwad: + self.wadtype = 0 + self.ALIGNMENT = 0 + else: + self.wadtype = "ib" + else: + self.wadtype = "Is" + + self.data_len = 0 + self.footer = footer + self.footer_len = len(footer) + if self.wadtype == 0: + hdr = pack(">IHHIII12x",self.hdr_len, self.wadtype, 0xf00, self.cert_len, self.tik_len, self.tmd_len) + self.f.write(hdr) + else: + hdr = pack(">I2sHIIIIII",self.hdr_len, self.wadtype, 0, self.cert_len, 0, self.tik_len, self.tmd_len, self.data_len, self.footer_len) + self.f.write(hdr) + falign(self.f,self.ALIGNMENT) + for cert in self.certlist: + self.f.write(cert.data) + falign(self.f,self.ALIGNMENT) + self.tik_offset = self.f.tell() + self.f.write(self.tik.data) + falign(self.f,self.ALIGNMENT) + self.tmd_offset = self.f.tell() + self.f.write(self.tmd.data) + falign(self.f,0x40) + self.data_off = self.f.tell() + + def adddata(self, data, cid): + i = self.tmd.find_cr_by_cid(cid) + cr = self.tmd.get_content_records()[i] + cr.sha = SHA.new(data).digest() + cr.size = len(data) + self.tmd.update_content_record(i,cr) + if len(data)%16 != 0: + data += "\x00"*(16-len(data)%16) + falign(self.f,0x40) + iv = pack(">H",cr.index)+"\x00"*14 + aes = AES.new(self.tik.title_key, AES.MODE_CBC, iv) + self.f.write(aes.encrypt(data)) + falign(self.f,0x40) + + def adddata_encrypted(self, data): + if len(data)%16 != 0: + data += "\x00"*(16-len(data)%16) + falign(self.f,0x40) + self.f.write(data) + falign(self.f,0x40) + + def finish(self,pad=False): + self.data_end = self.f.tell() + self.data_len = self.data_end - self.data_off + falign(self.f,self.ALIGNMENT) + self.f.write(self.footer) + falign(self.f,self.ALIGNMENT) + self.f.truncate() + if pad: + self.f.write("\x00"*0x40) + self.updatetmd() + self.f.seek(0) + if self.wadtype == 0: + hdr = pack(">IHHIII12x",self.hdr_len, self.wadtype, 0xf00, self.cert_len, self.tik_len, self.tmd_len) + self.f.write(hdr) + else: + hdr = pack(">I2sHIIIIII",self.hdr_len, self.wadtype, 0, self.cert_len, 0, self.tik_len, self.tmd_len, self.data_len, self.footer_len) + self.f.write(hdr) + +class WiiLZSS: + TYPE_LZSS = 1 + def __init__(self, file, offset): + self.file = file + self.offset = offset + + self.file.seek(offset) + + hdr = unpack(">8 + self.compression_type = hdr>4 & 0xF + + if self.compression_type != self.TYPE_LZSS: + raise ValueError("Unsupported compression method %d"%self.compression_type) + + def uncompress(self): + dout = "" + + self.file.seek(self.offset + 0x4) + while len(dout) < self.uncompressed_length: + flags = unpack(">B",self.file.read(1))[0] + + for i in range(8): + if flags & 0x80: + info = unpack(">B",self.file.read(1))[0] + if (info>>4) == 1: + self.file.seek(-1, 1) + info = unpack(">L",self.file.read(4))[0] + num = ((info>>12)&0xffff) + 0x111 + elif (info>>4) == 0: + info = (info<<16) + unpack(">H", self.file.read(2))[0] + num = ((info>>12)&0xff) + 0x11 + else: + self.file.seek(-1, 1) + info = unpack(">H", self.file.read(2))[0] + num = ((info>>12)&0xf) + 0x1 + ptr = len(dout) - (info&0xfff) - 1 + for i in range(num): + dout += dout[ptr] + ptr+=1 + if len(dout) >= self.uncompressed_length: + break + else: + dout += self.file.read(1) + flags <<= 1 + if len(dout) >= self.uncompressed_length: + break + + self.data = dout + return self.data + +class WiiLZ77: + TYPE_LZ77 = 1 + def __init__(self, file, offset): + self.file = file + self.offset = offset + + self.file.seek(offset) + + hdr = unpack(">8 + self.compression_type = hdr>4 & 0xF + + if self.compression_type != self.TYPE_LZ77: + raise ValueError("Unsupported compression method %d"%self.compression_type) + + def uncompress(self): + dout = "" + + self.file.seek(self.offset + 0x4) + + while len(dout) < self.uncompressed_length: + flags = unpack(">B",self.file.read(1))[0] + + for i in range(8): + if flags & 0x80: + info = unpack(">H",self.file.read(2))[0] + num = 3 + ((info>>12)&0xF) + disp = info & 0xFFF + ptr = len(dout) - (info & 0xFFF) - 1 + for i in range(num): + dout += dout[ptr] + ptr+=1 + if len(dout) >= self.uncompressed_length: + break + else: + dout += self.file.read(1) + flags <<= 1 + if len(dout) >= self.uncompressed_length: + break + + self.data = dout + return self.data + +class WiiFSTFile: + def __init__(self, name=None, off=None, size=None): + self.name = name + self.off = off + self.size = size + def show(self, it=""): + print "%s%s @ 0x%x [0x%x]"%(it,self.name,self.off,self.size) + def generate(self, offset, stringoff, parent, dataoff, wiigcm=False): + stringdata = self.name + "\x00" + off = self.off+dataoff + if wiigcm: + off >>= 2 + entry = pack(">III", stringoff, off, self.size) + return (entry,stringdata) + +class WiiFSTDir: + def __init__(self, name=None, offset=0, data=None, stringtable=None, wiigcm=False): + if (data is None) != (stringtable is None): + raise ValueError("data and stringtable go together") + if data is not None: + self.load(data, stringtable, offset, wiigcm) + else: + self.entries = [] + if name is not None: + self.name = name + if self.name is None: + raise ValueError("No name specified") + def load(self, data, stringtable, offset, wiigcm=False): + etype, poff, nextoff = unpack(">III",data[:12]) + if (etype>>24) != 1: + raise ValueError("Entry is not a dir") + self.name = getcstring(stringtable[etype&0xFFFFFF:]) + if nextoff < offset: + raise ValueError("Negative directory size") + size = nextoff - offset + off = 1 + self.entries = [] + while off < size: + etype, a, b = unpack(">III",data[off*12:off*12+12]) + if (etype>>24) == 1: + self.entries.append(WiiFSTDir(None, off+offset, data[off*12:], stringtable, wiigcm)) + off = b-offset + elif (etype>>24) == 0: + if wiigcm: + a <<= 2 + self.entries.append(WiiFSTFile(getcstring(stringtable[etype&0xFFFFFF:]), a, b)) + off += 1 + else: + raise ValueError("Bad file/dir type: %d"%etype>>24) + if off != size: + raise ValueError("WTF") + def show(self, it=""): + if self.name == "": + print it+"/" + else: + print it+self.name+"/" + for i in self.entries: + i.show(it+self.name+"/") + def dump(self): + pass + def add(self,x): + self.entries.append(x) + def generate(self, offset, stringoff, parent, dataoff, wiigcm=False): + stringdata = self.name + "\x00" + myoff = offset + mysoff = stringoff + stringoff+=len(stringdata) + offset += 1 + subdata="" + for e in self.entries: + d, s = e.generate(offset, stringoff, myoff, dataoff, wiigcm) + offset += len(d)/12 + stringoff += len(s) + stringdata += s + subdata += d + entry = pack(">III", mysoff+0x1000000, parent, offset) + return (entry+subdata, stringdata) + +class WiiFST: + def __init__(self, data=None, wiigcm=False): + if data is not None: + self.load(data, wiigcm) + else: + self.root = WiiFSTDir('') + + def load(self, data, wiigcm=False): + size = unpack(">I",data[8:12])[0] + stringtable = data[size*12:] + self.root = WiiFSTDir('', 0, data, stringtable, wiigcm) + + def generate(self, dataoff=0, wiigcm=False): + d,s = self.root.generate(0,0,0,dataoff, wiigcm) + return d+s + + def show(self,it=""): + self.root.show(it) + +class WiiFSTBuilder: + def __init__(self,alignment=0,offset=0): + self.align=alignment + self.offset=offset + self.files = [] + self.offset = 0 + self.fst = WiiFST() + + def addfrom(self, path, fstdir=None): + if fstdir is None: + fstdir = self.fst.root + names = os.listdir(path) + names.sort() + for n in names: + self.add(fstdir, os.path.join(path,n)) + + def add(self, fstdir, path): + if os.path.isfile(path): + size = os.stat(path).st_size + if size != 0: + fstdir.add(WiiFSTFile(os.path.basename(path),self.offset,size)) + self.files.append(path) + else: + fstdir.add(WiiFSTFile(os.path.basename(path),0,0)) + self.offset = align(self.offset+size, self.align) + elif os.path.isdir(path): + d = WiiFSTDir(os.path.basename(path)) + self.addfrom(path,d) + fstdir.add(d) + else: + raise ValueError("Bad file: %s"%path) + diff --git a/pywii/README b/pywii/README new file mode 100644 index 0000000..34f5cb7 --- /dev/null +++ b/pywii/README @@ -0,0 +1,10 @@ +Python library to deal with Wii formats. + +Currently has decent support for discs, partitions (including +accessing the partition data using read() and write(), with +caching), wads, and the structures contained within (certs, +tiks, tmds, etc) + +Stuff may be broken. Some things are unfinished. + +It also makes breakfast. diff --git a/pywii/breakfast.py b/pywii/breakfast.py new file mode 100644 index 0000000..b072f3f --- /dev/null +++ b/pywii/breakfast.py @@ -0,0 +1,94 @@ +import zlib, time, base64, sys + +class WaffleBatter: + def __init__(self): + self.cooking_time = 100 + self.batter = "eNp9VbGu3DgM7PMVhBs2sq4VDnHKwxUu1DLehQhBxeukD7iPvyEl78tLXuJisbbJ4Qw5lIn+dJUfri+/i+n92Lbr70siblreWFLejtE/zQhHZpW4C+OGW0pKoWuOUvVb6VlG/DUtJAsO8tC3SGFkpvKUy19xyO0ynlHl54LhYEIpteTnyOmiIcwa9BHLGIzHWvFSN0D+lFwaFzyM2ks4NjaKdb04NujEPWe8NTV4SPohvSWvSe3IUsn4kkQ5V3DRt+JtQyNq7GiEUpT+nt84SnSZqBOtq+SqpZsAoq1K97fPEbWWCcUSXwjJyBvNoR7+rN3lht1nRNDEF2r7DIRTR3+RsT/PBRFsini0VHsPEL9bUSXAOTeBqO59RrDWv1yz1IkRKHXtVhVqJt0ydoz5VV9i9oxHTIYXEGz9tri8QKSZV8gfmcLggEqcJoO2nXcGXyR9ekL4uymiuyM50QNsDnYOdU0SrMHhnBy2iQgcmEPOd1GReXG5xB/JuQ9vG8LmiNpe50SDVZA+Z6/fIOsfG5K4jeRGuWwO8SbRsC3nPkkILVkHyySxLHzu8335niZKdeOidXST6IvDXI+bBHo/IdH6OjtpJNN/q720zAVjypwuZTcqhMK3j5ni7QWLr+zCzU4Mh9E9JarAcYc1GLKvPQHkGZ2crdlj/rUdfsyazZcTOXQtGI0ko3icBvh+/i19sUBOzbYxE/PUat1BEEV9a9xu+yN6Ns/W/sgJ22pGz07NpZUR7wjQ0OprQsoNp8lricDGqfpUL7o5KHc7SYrJNa9Emizkkr62l95ByOas/16rh+8zIHqpIiCCzuDbVrFl+oHIlGTxy97LJjgL8vJRGPOIKjciEEDkw3E4+649OhcIOpdRw8TzJt0qnOFHDi8IMMFpqTDUrFeC7ebJd+2Y5tQB+OsHQPrkyPaB6OksLkWEzb6lhIH5ONLIn309dBt31whH+WbXvtsv803g07orZaNeyudfMRx2v81bk9d9H/vr6mNX5i//A5YHk94=" + +class Waffle: + def __init__(self, waffle): + self.waffle = waffle + def display(self, file=sys.stdout): + file.write(self.waffle) + +class WaffleIron: + def __init__(self): + self.power = False + self.time = 0 + self.full = False + self.contents = False + pass + def switchPower(self, power): + self.power = power + def fill(self, contents): + if not self.power: + raise RuntimeError("Turn on the iron first!") + if not isinstance(contents, WaffleBatter): + raise ValueError("Iron can only be filled with batter!") + self.contents = contents + self.time = time.time() + return self.contents.cooking_time + def contentsAreCooked(self): + return time.time() > (self.time+self.contents.cooking_time) + def getTimeLeft(self): + return max(0,(self.time+self.contents.cooking_time) - time.time()) + def getContents(self): + if self.contentsAreCooked(): + batter = self.contents.batter + cookedbatter = zlib.decompress(base64.b64decode(batter)) + self.contents = Waffle(cookedbatter) + else: + raise RuntimeError("Waffle is not yet cooked!") + return self.contents + +class BreakfastType: + def __init__(self): + raise NotImplementedError("BreakfastType is abstract") + +class Waffles(BreakfastType): + def __init__(self): + pass + def make(self): + batter = WaffleBatter() + iron = WaffleIron() + iron.switchPower(True) + cooktime = iron.fill(batter) + cm, cs = divmod(cooktime,60) + if cm > 0: + print "Cooking time will be approximately %d minute%s and %d second%s"%(cm, 's'*(cm!=1), cs, 's'*(cs!=1)) + else: + print "Cooking time will be approximately %d second%s"%(cs, 's'*(cs!=1)) + while not iron.contentsAreCooked(): + left = iron.getTimeLeft() + m,s = divmod(left+0.99,60) + sys.stdout.write("%02d:%02d"%(m,s)) + sys.stdout.flush() + time.sleep(0.5) + sys.stdout.write("\x08"*5) + sys.stdout.flush() + print + waffle = iron.getContents() + iron.switchPower(False) + return waffle + +class BreakfastMaker: + preferredBreakfasts = {'bushing':Waffles} + def __init__(self): + pass + def makeBreakfastFor(self, user): + if not user in self.preferredBreakfasts: + raise ValueError("I don't know how to make breakfast for %s!"%user) + maker = self.preferredBreakfasts[user] + breakfast = maker().make() + return breakfast + +print "Breakfast Maker v0.2" +user = raw_input("Please enter your username: ") +maker = BreakfastMaker() +print "Making breakfast for %s..."%user +breakfast = maker.makeBreakfastFor(user) +print +print "Your breakfast is ready!" +print +breakfast.display() +print "\a" diff --git a/pywii/ecmodule/_ecmodule.c b/pywii/ecmodule/_ecmodule.c new file mode 100644 index 0000000..6294ee9 --- /dev/null +++ b/pywii/ecmodule/_ecmodule.c @@ -0,0 +1,248 @@ +// Copyright 2007,2008 Segher Boessenkool +// Copyright 2008 Hector Martin +// Licensed under the terms of the GNU GPL, version 2 +// http://www.gnu.org/licenses/old-licenses/gpl-2.0.txt +#include "Python.h" +#include + +typedef uint8_t u8; +typedef uint32_t u32; + +/* ----------------------------------------------------- */ + +static void elt_copy(u8 *d, const u8 *a) +{ + memcpy(d, a, 30); +} + +static void elt_zero(u8 *d) +{ + memset(d, 0, 30); +} + +static int elt_is_zero(const u8 *d) +{ + u32 i; + + for (i = 0; i < 30; i++) + if (d[i] != 0) + return 0; + + return 1; +} + +static void elt_add(u8 *d, const u8 *a, const u8 *b) +{ + u32 i; + + for (i = 0; i < 30; i++) + d[i] = a[i] ^ b[i]; +} + +static void elt_mul_x(u8 *d, const u8 *a) +{ + u8 carry, x, y; + u32 i; + + carry = a[0] & 1; + + x = 0; + for (i = 0; i < 29; i++) { + y = a[i + 1]; + d[i] = x ^ (y >> 7); + x = y << 1; + } + d[29] = x ^ carry; + + d[20] ^= carry << 2; +} + +static void elt_mul(u8 *d, const u8 *a, const u8 *b) +{ + u32 i, n; + u8 mask; + + elt_zero(d); + + i = 0; + mask = 1; + for (n = 0; n < 233; n++) { + elt_mul_x(d, d); + + if ((a[i] & mask) != 0) + elt_add(d, d, b); + + mask >>= 1; + if (mask == 0) { + mask = 0x80; + i++; + } + } +} + +static const u8 square[16] = + "\x00\x01\x04\x05\x10\x11\x14\x15\x40\x41\x44\x45\x50\x51\x54\x55"; + +static void elt_square_to_wide(u8 *d, const u8 *a) +{ + u32 i; + + for (i = 0; i < 30; i++) { + d[2*i] = square[a[i] >> 4]; + d[2*i + 1] = square[a[i] & 15]; + } +} + +static void wide_reduce(u8 *d) +{ + u32 i; + u8 x; + + for (i = 0; i < 30; i++) { + x = d[i]; + + d[i + 19] ^= x >> 7; + d[i + 20] ^= x << 1; + + d[i + 29] ^= x >> 1; + d[i + 30] ^= x << 7; + } + + x = d[30] & ~1; + + d[49] ^= x >> 7; + d[50] ^= x << 1; + + d[59] ^= x >> 1; + + d[30] &= 1; +} + +static void elt_square(u8 *d, const u8 *a) +{ + u8 wide[60]; + + elt_square_to_wide(wide, a); + wide_reduce(wide); + + elt_copy(d, wide + 30); +} + +static void itoh_tsujii(u8 *d, const u8 *a, const u8 *b, u32 j) +{ + u8 t[30]; + + elt_copy(t, a); + while (j--) { + elt_square(d, t); + elt_copy(t, d); + } + + elt_mul(d, t, b); +} + +static void elt_inv(u8 *d, const u8 *a) +{ + u8 t[30]; + u8 s[30]; + + itoh_tsujii(t, a, a, 1); + itoh_tsujii(s, t, a, 1); + itoh_tsujii(t, s, s, 3); + itoh_tsujii(s, t, a, 1); + itoh_tsujii(t, s, s, 7); + itoh_tsujii(s, t, t, 14); + itoh_tsujii(t, s, a, 1); + itoh_tsujii(s, t, t, 29); + itoh_tsujii(t, s, s, 58); + itoh_tsujii(s, t, t, 116); + elt_square(d, s); +} + +/* ----------------------------------------------------- */ + +static PyObject * +_ec_elt_mul(PyObject *self /* Not used */, PyObject *args) +{ + const char *a, *b; + char d[30]; + int la, lb; + if (!PyArg_ParseTuple(args, "s#s#", &a, &la, &b, &lb)) + return NULL; + + if(la != 30 || lb != 30) { + PyErr_SetString(PyExc_ValueError, "ELT lengths must be 30"); + return NULL; + } + elt_mul(d,a,b); + return Py_BuildValue("s#", d, 30); +} + +static PyObject * +_ec_elt_inv(PyObject *self /* Not used */, PyObject *args) +{ + const char *a; + char d[30]; + int la; + + if (!PyArg_ParseTuple(args, "s#", &a, &la)) + return NULL; + + if(la != 30) { + PyErr_SetString(PyExc_ValueError, "ELT length must be 30"); + return NULL; + } + elt_inv(d,a); + return Py_BuildValue("s#", d, 30); +} + +static PyObject * +_ec_elt_square(PyObject *self /* Not used */, PyObject *args) +{ + const char *a; + char d[30]; + int la; + + if (!PyArg_ParseTuple(args, "s#", &a, &la)) + return NULL; + + if(la != 30) { + PyErr_SetString(PyExc_ValueError, "ELT length must be 30"); + return NULL; + } + elt_square(d,a); + return Py_BuildValue("s#", d, 30); +} + +/* List of methods defined in the module */ + +static struct PyMethodDef _ec_methods[] = { + {"elt_mul", (PyCFunction)_ec_elt_mul, METH_VARARGS, "Multiply two ELTs"}, + {"elt_inv", (PyCFunction)_ec_elt_inv, METH_VARARGS, "Take the inverse of an ELT"}, + {"elt_square", (PyCFunction)_ec_elt_square, METH_VARARGS, "Efficiently square an ELT"}, + + {NULL, (PyCFunction)NULL, 0, NULL} /* sentinel */ +}; + + +/* Initialization function for the module (*must* be called init_ec) */ + +static char _ec_module_documentation[] = +"Faster C versions of some ELT functions" +; + +void +init_ec() +{ + PyObject *m, *d; + + /* Create the module and add the functions */ + m = Py_InitModule4("_ec", _ec_methods, + _ec_module_documentation, + (PyObject*)NULL,PYTHON_API_VERSION); + + /* Check for errors */ + if (PyErr_Occurred()) + Py_FatalError("can't initialize module _ec"); +} + diff --git a/pywii/ecmodule/setup.py b/pywii/ecmodule/setup.py new file mode 100644 index 0000000..2896642 --- /dev/null +++ b/pywii/ecmodule/setup.py @@ -0,0 +1,9 @@ +from distutils.core import setup, Extension + +module1 = Extension('_ec', + sources = ['_ecmodule.c']) + +setup (name = 'ELT_C', + version = '0.1', + description = 'C ELT functions', + ext_modules = [module1]) \ No newline at end of file diff --git a/pywii/pywii-tools/addhash.py b/pywii/pywii-tools/addhash.py new file mode 100755 index 0000000..1c41b44 --- /dev/null +++ b/pywii/pywii-tools/addhash.py @@ -0,0 +1,11 @@ +#!/usr/bin/env python +import sys +import re +import pywii as wii + +hash = wii.SHA.new(open(sys.argv[2]).read()).digest().encode("hex") +f = open(sys.argv[1], "r") +data = f.read() +f.close() +data = re.sub('@SHA1SUM@', hash, data) +open(sys.argv[3], "w").write(data) diff --git a/pywii/pywii-tools/arclist.py b/pywii/pywii-tools/arclist.py new file mode 100755 index 0000000..e9330d3 --- /dev/null +++ b/pywii/pywii-tools/arclist.py @@ -0,0 +1,15 @@ +#!/usr/bin/env python + +import sys, os, os.path, struct +import pywii as wii + +arc = open(sys.argv[1], "rb") + +tag, fstoff, fstsize, dataoff = struct.unpack(">IIII16x",arc.read(0x20)) + +arc.seek(fstoff) +fst = arc.read(fstsize) + +fst = wii.WiiFST(fst) + +fst.show() diff --git a/pywii/pywii-tools/arcpack.py b/pywii/pywii-tools/arcpack.py new file mode 100755 index 0000000..a28803e --- /dev/null +++ b/pywii/pywii-tools/arcpack.py @@ -0,0 +1,26 @@ +#!/usr/bin/env python + +import sys, os, os.path, struct +import pywii as wii + +fstb = wii.WiiFSTBuilder(0x20) + +fstb.addfrom(sys.argv[2]) + +arc = open(sys.argv[1],"wb") +# dummy generate to get length +fstlen = len(fstb.fst.generate()) +dataoff = wii.align(0x20+fstlen,0x20) +fst = fstb.fst.generate(dataoff) + +hdr = struct.pack(">IIII16x",0x55AA382d,0x20,fstlen,dataoff) +arc.write(hdr) + +arc.write(fst) +wii.falign(arc,0x20) +for f in fstb.files: + data = open(f, "rb").read() + arc.write(data) + wii.falign(arc,0x20) + +arc.close() diff --git a/pywii/pywii-tools/certinfo.py b/pywii/pywii-tools/certinfo.py new file mode 100755 index 0000000..3d93844 --- /dev/null +++ b/pywii/pywii-tools/certinfo.py @@ -0,0 +1,26 @@ +#!/usr/bin/env python + +import sys, os, os.path +import pywii as wii + +args = sys.argv[1:] + +if args[0] == "-dpki": + wii.loadkeys_dpki() + args.pop(0) +else: + wii.loadkeys() + +certfile = args.pop(0) + +certs, certlist = wii.parse_certs(open(args.pop(0), "rb").read()) + +print "Certification file %s: " % certfile +cert = wii.WiiCert(open(certfile, "rb").read()) +cert.showinfo(" ") +cert.showsig(certs," ") + +print "Certificates:" +for cert in certlist: + cert.showinfo(" - ") + cert.showsig(certs," ") diff --git a/pywii/pywii-tools/discinfo.py b/pywii/pywii-tools/discinfo.py new file mode 100755 index 0000000..ae447e3 --- /dev/null +++ b/pywii/pywii-tools/discinfo.py @@ -0,0 +1,28 @@ +#!/usr/bin/env python + +import sys, os, os.path +import pywii as wii + +wii.loadkeys() + +isofile = sys.argv[1] +disc = wii.WiiDisc(isofile,readonly=True) +disc.showinfo() + +partitions = disc.read_partitions() + +parts = range(len(partitions)) + +try: + pnum = int(sys.argv[2]) + partitions[pnum] + parts = [pnum] +except: + pass + +for partno in parts: + part = wii.WiiCachedPartition(disc,partno) + part.showinfo() + pdat = wii.WiiPartitionData(part) + pdat.showinfo() + diff --git a/pywii/pywii-tools/dpkisign.py b/pywii/pywii-tools/dpkisign.py new file mode 100755 index 0000000..2c0a2b8 --- /dev/null +++ b/pywii/pywii-tools/dpkisign.py @@ -0,0 +1,35 @@ +#!/usr/bin/env python + +import sys, os, os.path +import pywii + +pywii.loadkeys_dpki() + +args = sys.argv[1:] +mode = args.pop(0) +infile = args.pop(0) +outfile = args.pop(0) +certfile = args.pop(0) +issuer = args.pop(0) + +if sys.argv[1] == "-cetk": + signed = pywii.WiiTik(open(infile, "rb").read()) +elif sys.argv[1] == "-tmd": + signed = pywii.WiiTmd(open(infile, "rb").read()) +else: + print "EYOUFAILIT" + sys.exit(1) + +certs, certlist = pywii.parse_certs(open(certfile).read()) + +signed.update_issuer(issuer) + +if not signed.sign(certs): + print "dpki signing failed" + sys.exit(1) + +open(outfile, "wb").write(signed.data) + +print "successfully signed %s" % outfile +sys.exit(0) + diff --git a/pywii/pywii-tools/ecchecksig.py b/pywii/pywii-tools/ecchecksig.py new file mode 100755 index 0000000..4fe78f2 --- /dev/null +++ b/pywii/pywii-tools/ecchecksig.py @@ -0,0 +1,57 @@ +#!/usr/bin/env python + +import sys +import pywii as wii + +if len(sys.argv) != 3: + print "Usage: %s keyfile.[priv|pub] infile"%sys.argv[0] + sys.exit(1) + +if sys.argv[1] == "-": + k = sys.stdin.read() +else: + k = open(sys.argv[1],"rb").read() +if len(k) not in (30,60): + print "Failed to read key" + sys.exit(2) + +if len(k) == 30: + print "Key is a private key, generating public key..." + q = wii.ec.priv_to_pub(k) +else: + q = k + +print "Public key:" +pq = q.encode('hex') +print "X =",pq[:30] +print " ",pq[30:60] +print "Y =",pq[60:90] +print " ",pq[90:] +print + +indata = open(sys.argv[2],"rb").read() + +if len(indata) < 64 or indata[:4] != "SIG0": + print "Invalid header" + sys.exit(3) + +r = indata[4:34] +s = indata[34:64] + +sha = wii.SHA.new(indata[64:]).digest() +print "SHA1: %s"%sha.encode('hex') + +print +print "Signature:" +print "R =",r[:15].encode('hex') +print " ",r[15:].encode('hex') +print "S =",s[:15].encode('hex') +print " ",s[15:].encode('hex') +print + +if wii.ec.check_ecdsa(q,r,s,sha): + print "Signature is VALID" +else: + print "Signature is INVALID" + sys.exit(4) + diff --git a/pywii/pywii-tools/ecgenpriv.py b/pywii/pywii-tools/ecgenpriv.py new file mode 100755 index 0000000..b4d9dbf --- /dev/null +++ b/pywii/pywii-tools/ecgenpriv.py @@ -0,0 +1,33 @@ +#!/usr/bin/env python + +import sys, os +import pywii as wii + +if len(sys.argv) != 2: + print "Usage: %s keyfile.priv"%sys.argv[0] + sys.exit(1) + +print "Generating private key..." + +k = wii.ec.gen_priv_key() + +print "Private key:" +pk = k.encode('hex') +print "K =",pk[:30] +print " ",pk[30:] + +print +print "Corresponding public key:" +q = wii.ec.priv_to_pub(k) +pq = q.encode('hex') +print "X =",pq[:30] +print " ",pq[30:60] +print "Y =",pq[60:90] +print " ",pq[90:] + +fd = open(sys.argv[1],"wb") +os.fchmod(fd.fileno(), 0o600) +fd.write(k) +fd.close() + +print "Saved private key to %s"%sys.argv[1] diff --git a/pywii/pywii-tools/ecpriv2pub.py b/pywii/pywii-tools/ecpriv2pub.py new file mode 100755 index 0000000..8981b47 --- /dev/null +++ b/pywii/pywii-tools/ecpriv2pub.py @@ -0,0 +1,30 @@ +#!/usr/bin/env python + +import sys +import pywii as wii + +if len(sys.argv) not in (2,3): + print "Usage: %s keyfile.priv [keyfile.pub]"%sys.argv[0] + sys.exit(1) + +if sys.argv[1] == "-": + k = sys.stdin.read() +else: + k = open(sys.argv[1],"rb").read() +if len(k) != 30: + print "Failed to read private key" + sys.exit(2) + +print "Public key:" +q = wii.ec.priv_to_pub(k) +pq = q.encode('hex') +print "X =",pq[:30] +print " ",pq[30:60] +print "Y =",pq[60:90] +print " ",pq[90:] + +if len(sys.argv) == 3: + fd = open(sys.argv[2],"wb") + fd.write(q) + fd.close() + print "Saved public key to %s"%sys.argv[2] diff --git a/pywii/pywii-tools/ecsign.py b/pywii/pywii-tools/ecsign.py new file mode 100755 index 0000000..1209dfd --- /dev/null +++ b/pywii/pywii-tools/ecsign.py @@ -0,0 +1,35 @@ +#!/usr/bin/env python + +import sys +import pywii as wii + +if len(sys.argv) != 4: + print "Usage: %s keyfile.priv infile outfile"%sys.argv[0] + sys.exit(1) + +if sys.argv[1] == "-": + k = sys.stdin.read() +else: + k = open(sys.argv[1],"rb").read() + +if len(k) != 30: + print "Failed to read private key" + sys.exit(2) + +indata = open(sys.argv[2],"rb").read() +sha = wii.SHA.new(indata).digest() + +print "SHA1: %s"%sha.encode('hex') +print +print "Signature:" +r,s = wii.ec.generate_ecdsa(k,sha) +print "R =",r[:15].encode('hex') +print " ",r[15:].encode('hex') +print "S =",s[:15].encode('hex') +print " ",s[15:].encode('hex') + +outdata = "SIG0" + r + s + indata + +fd = open(sys.argv[3],"wb") +fd.write(outdata) +fd.close() diff --git a/pywii/pywii-tools/extract.py b/pywii/pywii-tools/extract.py new file mode 100755 index 0000000..7944007 --- /dev/null +++ b/pywii/pywii-tools/extract.py @@ -0,0 +1,59 @@ +#!/usr/bin/env python + +import sys, os, os.path +import pywii as wii + +wii.loadkeys() + +def parseint(d): + if len(d) > 2 and d[0:2].lower()=='0x': + return int(d,16) + return int(d) + +if len(sys.argv) < 4 or len(sys.argv) > 7: + print "Usage:" + print " python %s [Partition offset] [length]"%sys.argv[0] + sys.exit(1) + +iso_name, partno, data_name = sys.argv[1:4] +partno = int(partno) +part_offset = 0 +data_offset = 0 +copy_length = None + +if len(sys.argv) >= 5: + part_offset = parseint(sys.argv[4]) +if len(sys.argv) == 6: + copy_length = parseint(sys.argv[5]) + +if copy_length is not None and copy_length < 0: + print "Error: negative copy length" + sys.exit(1) + +disc = wii.WiiDisc(iso_name) +disc.showinfo() +part = wii.WiiCachedPartition(disc, partno, cachesize=32, debug=False, checkhash=False) +if part_offset >= part.data_bytes: + print "Error: Offset past end of partition" + sys.exit(1) +if copy_length is None: + copy_length = part.data_bytes - part_offset +if copy_length > (part.data_bytes - part_offset): + print "Error: Length too large" + sys.exit(1) + +dataf = open(data_name, "wb") +left = copy_length +offset = part_offset +while left > 0: + blocklen = min(left, 4*1024*1024) + d = part.read(offset, blocklen) + if len(d) != blocklen: + print "Part EOF reached!" + sys.exit(1) + dataf.write(d) + offset += blocklen + left -= blocklen + +dataf.close() + diff --git a/pywii/pywii-tools/extractdol.py b/pywii/pywii-tools/extractdol.py new file mode 100755 index 0000000..b7b7c4f --- /dev/null +++ b/pywii/pywii-tools/extractdol.py @@ -0,0 +1,26 @@ +#!/usr/bin/python + +import sys, os, os.path +sys.path.append(os.path.realpath(os.path.dirname(sys.argv[0]))+"/../Common") +import pywii as wii + +wii.loadkeys(os.environ["HOME"]+os.sep+".wii") + +if len(sys.argv) != 4: + print "Usage:" + print " python %s "%sys.argv[0] + sys.exit(1) + +iso_name, partno, dol_name = sys.argv[1:4] +partno = int(partno) + +disc = wii.WiiDisc(iso_name) +disc.showinfo() +part = wii.WiiCachedPartition(disc, partno, cachesize=32, debug=False) +partdata = wii.WiiPartitionData(part) + +dolf = open(dol_name, "wb") +dolf.write(partdata.dol) +dolf.close() + + diff --git a/pywii/pywii-tools/extractfiles.py b/pywii/pywii-tools/extractfiles.py new file mode 100644 index 0000000..f7d8f73 --- /dev/null +++ b/pywii/pywii-tools/extractfiles.py @@ -0,0 +1,59 @@ +#!/usr/bin/env python + +import sys, os, os.path +import pywii as wii + +wii.loadkeys() + +def parseint(d): + if len(d) > 2 and d[0:2].lower()=='0x': + return int(d,16) + return int(d) + +if len(sys.argv) < 4 or len(sys.argv) > 7: + print "Usage:" + print " python %s "%sys.argv[0] + sys.exit(1) + +iso_name, partno, data_name = sys.argv[1:4] +partno = int(partno) +part_offset = 0 +data_offset = 0 +copy_length = None + +if len(sys.argv) >= 5: + part_offset = parseint(sys.argv[4]) +if len(sys.argv) == 6: + copy_length = parseint(sys.argv[5]) + +if copy_length is not None and copy_length < 0: + print "Error: negative copy length" + sys.exit(1) + +disc = wii.WiiDisc(iso_name) +disc.showinfo() +part = wii.WiiCachedPartition(disc, partno, cachesize=32, debug=False, checkhash=False) +if part_offset >= part.data_bytes: + print "Error: Offset past end of partition" + sys.exit(1) +if copy_length is None: + copy_length = part.data_bytes - part_offset +if copy_length > (part.data_bytes - part_offset): + print "Error: Length too large" + sys.exit(1) + +dataf = open(data_name, "wb") +left = copy_length +offset = part_offset +while left > 0: + blocklen = min(left, 4*1024*1024) + d = part.read(offset, blocklen) + if len(d) != blocklen: + print "Part EOF reached!" + sys.exit(1) + dataf.write(d) + offset += blocklen + left -= blocklen + +dataf.close() + diff --git a/pywii/pywii-tools/getappldr.py b/pywii/pywii-tools/getappldr.py new file mode 100755 index 0000000..602abf4 --- /dev/null +++ b/pywii/pywii-tools/getappldr.py @@ -0,0 +1,24 @@ +#!/usr/bin/env python + +import sys, os, os.path +import pywii as wii + +wii.loadkeys() + +if len(sys.argv) != 5: + print "Usage:" + print " python %s "%sys.argv[0] + sys.exit(1) + +iso_name, partno, app_name, trail_name = sys.argv[1:5] +partno = int(partno) + +disc = wii.WiiDisc(iso_name) +disc.showinfo() +part = wii.WiiCachedPartition(disc, partno, cachesize=32, debug=False) +partdata = wii.WiiPartitionData(part) + +apploader = partdata.apploader +apploader.showinfo() +open(app_name,"wb").write(apploader.text) +open(trail_name,"wb").write(apploader.trailer) diff --git a/pywii/pywii-tools/inject.py b/pywii/pywii-tools/inject.py new file mode 100755 index 0000000..d489e30 --- /dev/null +++ b/pywii/pywii-tools/inject.py @@ -0,0 +1,68 @@ +#!/usr/bin/env python + +import sys, os, os.path +import pywii as wii + +wii.loadkeys() + +def parseint(d): + if len(d) > 2 and d[0:2].lower()=='0x': + return int(d,16) + return int(d) + +if len(sys.argv) < 4 or len(sys.argv) > 7: + print "Usage:" + print " python %s [Partition offset] [data offset] [length]"%sys.argv[0] + sys.exit(1) + +iso_name, partno, data_name = sys.argv[1:4] +partno = int(partno) +part_offset = 0 +data_offset = 0 +copy_length = None + +if len(sys.argv) >= 5: + part_offset = parseint(sys.argv[4]) +if len(sys.argv) >= 6: + data_offset = parseint(sys.argv[5]) +if len(sys.argv) == 7: + copy_length = parseint(sys.argv[6]) + +data_len = os.stat(data_name).st_size + +if copy_length == None: + copy_length = data_len - data_offset +copy_end = data_offset + copy_length +if copy_length < 0: + print "Error: negative copy length" + sys.exit(1) +if copy_end > data_len: + print "Error: data file is too small" + sys.exit(1) + +disc = wii.WiiDisc(iso_name) +disc.showinfo() +part = wii.WiiCachedPartition(disc, partno, cachesize=32, debug=False, checkhash=False) + +dataf = open(data_name, "rb") +dataf.seek(data_offset) +left = copy_length +offset = part_offset +while left > 0: + blocklen = min(left, 4*1024*1024) + d = dataf.read(blocklen) + if len(d) != blocklen: + print "File EOF reached!" + sys.exit(1) + part.write(offset, d) + offset += blocklen + left -= blocklen + +part.flush() +part.update() +part.tmd.null_signature() +part.tmd.brute_sha() +part.updatetmd() + + + diff --git a/pywii/pywii-tools/injectdol.py b/pywii/pywii-tools/injectdol.py new file mode 100755 index 0000000..d06ebde --- /dev/null +++ b/pywii/pywii-tools/injectdol.py @@ -0,0 +1,31 @@ +#!/usr/bin/env python + +import sys, os, os.path +import pywii as wii + +wii.loadkeys() + +if len(sys.argv) != 4: + print "Usage:" + print " python %s "%sys.argv[0] + sys.exit(1) + +iso_name, partno, dol_name = sys.argv[1:4] +partno = int(partno) + +doldata = open(dol_name, "rb").read() + +disc = wii.WiiDisc(iso_name) +disc.showinfo() +part = wii.WiiCachedPartition(disc, partno, cachesize=32, debug=False) +partdata = wii.WiiPartitionData(part) +partdata.replacedol(doldata) + +part.flush() +part.update() +part.tmd.null_signature() +part.tmd.brute_sha() +part.updatetmd() +part.showinfo() + + diff --git a/pywii/pywii-tools/partsetios.py b/pywii/pywii-tools/partsetios.py new file mode 100755 index 0000000..f2c81dd --- /dev/null +++ b/pywii/pywii-tools/partsetios.py @@ -0,0 +1,28 @@ +#!/usr/bin/env python + +import sys, os, os.path +import pywii as wii + +wii.loadkeys() + +if len(sys.argv) != 4: + print "Usage:" + print " python %s "%sys.argv[0] + print " IOS version should be just the minor number (16, 33, etc) in decimal" + sys.exit(1) + +iso_name, partno, ios = sys.argv[1:4] +partno = int(partno) +iosno = int(ios) + +disc = wii.WiiDisc(iso_name) +disc.showinfo() +part = wii.WiiCachedPartition(disc, partno, cachesize=32, debug=False) +part.tmd.sys_version = 0x100000000 + iosno +part.tmd.update() +part.tmd.null_signature() +part.tmd.brute_sha() +part.updatetmd() +part.showinfo() + + diff --git a/pywii/pywii-tools/pywii b/pywii/pywii-tools/pywii new file mode 120000 index 0000000..51fbba6 --- /dev/null +++ b/pywii/pywii-tools/pywii @@ -0,0 +1 @@ +../Common/pywii \ No newline at end of file diff --git a/pywii/pywii-tools/rsapatch.py b/pywii/pywii-tools/rsapatch.py new file mode 100755 index 0000000..75a9a6c --- /dev/null +++ b/pywii/pywii-tools/rsapatch.py @@ -0,0 +1,20 @@ +#!/usr/bin/env python + +import sys, os, os.path +import pywii as wii + +def hexdump(s): + return ' '.join(map(lambda x: "%02x"%x,map(ord,s))) + +isofile = sys.argv[1] +disc = WiiDisc(isofile) +disc.showinfo() +part = WiiPartition(disc,int(sys.argv[2])) +part.showinfo() + +part.tmd.update_signature(file("signthree.bin").read()) +part.tmd.brute_sha() +part.updatetmd() + +part.showinfo() + diff --git a/pywii/pywii-tools/saveinfo.py b/pywii/pywii-tools/saveinfo.py new file mode 100755 index 0000000..ba64b30 --- /dev/null +++ b/pywii/pywii-tools/saveinfo.py @@ -0,0 +1,10 @@ +#!/usr/bin/env python + +import sys, os, os.path +import pywii as wii + +wii.loadkeys() + +savefile = sys.argv[1] +save = wii.WiiSave(savefile) +save.showcerts() diff --git a/pywii/pywii-tools/tikfix.py b/pywii/pywii-tools/tikfix.py new file mode 100755 index 0000000..e790efe --- /dev/null +++ b/pywii/pywii-tools/tikfix.py @@ -0,0 +1,17 @@ +#!/usr/bin/env python + +import sys, os, os.path +import pywii as wii + +wii.loadkeys() + +tikfile = sys.argv[1] +print "fixing Tik file %s " % tikfile +tik = wii.WiiTik(open(tikfile, "rb").read()) +tik.null_signature() +tik.brute_sha() +tik.update() +f = open(tikfile,"wb") +f.write(tik.data) +f.close() + diff --git a/pywii/pywii-tools/tikinfo.py b/pywii/pywii-tools/tikinfo.py new file mode 100755 index 0000000..8fe7ac3 --- /dev/null +++ b/pywii/pywii-tools/tikinfo.py @@ -0,0 +1,28 @@ +#!/usr/bin/env python + +import sys, os, os.path +import pywii as wii + +args = sys.argv[1:] + +if args[0] == "-dpki": + wii.loadkeys_dpki() + args.pop(0) +else: + wii.loadkeys() + +tikfile = args.pop(0) + +certs = None +if len(args) > 0: + certs, certlist = wii.parse_certs(open(args.pop(0), "rb").read()) + +print "ETicket file %s:"%tikfile +tik = wii.WiiTik(open(tikfile, "rb").read()) +tik.showinfo(" ") +if certs is not None: + tik.showsig(certs," ") + print "Certificates:" + for cert in certlist: + cert.showinfo(" - ") + cert.showsig(certs," ") diff --git a/pywii/pywii-tools/tmdfix.py b/pywii/pywii-tools/tmdfix.py new file mode 100755 index 0000000..0258f5f --- /dev/null +++ b/pywii/pywii-tools/tmdfix.py @@ -0,0 +1,18 @@ +#!/usr/bin/env python + +import sys, os, os.path +import pywii as wii + +wii.loadkeys() + +tmdfile = sys.argv[1] +print "TMD file %s:"%tmdfile +tmd = wii.WiiTmd(open(tmdfile, "rb").read()) +tmd.null_signature() +tmd.brute_sha() +tmd.update() +tmd.parse() +tmd.showinfo(" ") +f = open(tmdfile,"wb") +f.write(tmd.data) +f.close() diff --git a/pywii/pywii-tools/tmdinfo.py b/pywii/pywii-tools/tmdinfo.py new file mode 100755 index 0000000..abcc30b --- /dev/null +++ b/pywii/pywii-tools/tmdinfo.py @@ -0,0 +1,28 @@ +#!/usr/bin/env python + +import sys, os, os.path +import pywii as wii + +args = sys.argv[1:] + +if args[0] == "-dpki": + wii.loadkeys_dpki() + args.pop(0) +else: + wii.loadkeys() + +tmdfile = args.pop(0) + +certs = None +if len(args) > 0: + certs, certlist = wii.parse_certs(open(args.pop(0), "rb").read()) + +print "TMD file %s:"%tmdfile +tmd = wii.WiiTmd(open(tmdfile, "rb").read()) +tmd.showinfo(" ") +if certs is not None: + tmd.showsig(certs," ") + print "Certificates:" + for cert in certlist: + cert.showinfo(" - ") + cert.showsig(certs," ") diff --git a/pywii/pywii-tools/tmdupdatecr.py b/pywii/pywii-tools/tmdupdatecr.py new file mode 100755 index 0000000..3359d45 --- /dev/null +++ b/pywii/pywii-tools/tmdupdatecr.py @@ -0,0 +1,29 @@ +#!/usr/bin/env python + +import sys, os, os.path +import pywii as wii +from Crypto.Hash import SHA + +wii.loadkeys() + +args = sys.argv[1:] + +tmdfile = args.pop(0) +indir = args.pop(0) + +print "updating content records of TMD file %s" % tmdfile +tmd = wii.WiiTmd(open(tmdfile, "rb").read()) + +for i, cr in enumerate(tmd.get_content_records()): + data = open(os.path.join(indir, "%08X" % cr.cid), "rb").read() + cr.sha = SHA.new(data).digest() + cr.size = len(data) + tmd.update_content_record(i, cr) + +tmd.null_signature() +tmd.brute_sha() +tmd.update() +f = open(tmdfile, "wb") +f.write(tmd.data) +f.close() + diff --git a/pywii/pywii-tools/tmdvers.py b/pywii/pywii-tools/tmdvers.py new file mode 100755 index 0000000..9863b6a --- /dev/null +++ b/pywii/pywii-tools/tmdvers.py @@ -0,0 +1,23 @@ +#!/usr/bin/env python + +import sys, os, os.path +import pywii as wii + +wii.loadkeys() + +args = sys.argv[1:] + +tmdfile = args.pop(0) + +if len(args) == 2: + newvers = int(args.pop(0)) << 8 | int(args.pop(0)) +else: + newvers = int(args.pop(0), 16) + +print "setting version of TMD file %s to 0x%04x" % (tmdfile, newvers) +tmd = wii.WiiTmd(open(tmdfile, "rb").read()) +tmd.title_version = newvers +tmd.update() +f = open(tmdfile,"wb") +f.write(tmd.data) +f.close() diff --git a/pywii/pywii-tools/wadinfo.py b/pywii/pywii-tools/wadinfo.py new file mode 100755 index 0000000..3eb16f9 --- /dev/null +++ b/pywii/pywii-tools/wadinfo.py @@ -0,0 +1,16 @@ +#!/usr/bin/env python + +import sys, os, os.path +import pywii as wii + +args = sys.argv[1:] + +if args[0] == "-dpki": + wii.loadkeys_dpki() + args.pop(0) +else: + wii.loadkeys() + +wadfile = args.pop(0) +wad = wii.WiiWad(wadfile) +wad.showinfo() diff --git a/pywii/pywii-tools/wadpack-boot2.py b/pywii/pywii-tools/wadpack-boot2.py new file mode 100755 index 0000000..94fb95e --- /dev/null +++ b/pywii/pywii-tools/wadpack-boot2.py @@ -0,0 +1,34 @@ +#!/usr/bin/env python + +import sys, os, os.path +import pywii as wii + +wii.loadkeys() + +wadfile = sys.argv[1] +indir = sys.argv[2] + +tmd = wii.WiiTmd(open(indir+os.sep+"tmd", "rb").read()) +tik = wii.WiiTik(open(indir+os.sep+"cetk", "rb").read()) + +certs, certlist = wii.parse_certs(open(os.path.join(indir, "certs"), "rb").read()) + +footer = open(indir+os.sep+"footer", "rb").read() + +wad = wii.WiiWadMaker(wadfile, tmd, tik, certlist, footer, nandwad=True) + +for i,ct in enumerate(tmd.get_content_records()): + data = open(indir+os.sep+"%08X"%ct.cid, "rb").read() + wad.adddata(data,ct.cid) + +wad.finish() + +if not wad.tik.signcheck(wad.certs): + wad.tik.null_signature() + wad.tik.brute_sha() + wad.updatetik() + +if not wad.tmd.signcheck(wad.certs): + wad.tmd.null_signature() + wad.tmd.brute_sha() + wad.updatetmd() diff --git a/pywii/pywii-tools/wadpack.py b/pywii/pywii-tools/wadpack.py new file mode 100755 index 0000000..0f34182 --- /dev/null +++ b/pywii/pywii-tools/wadpack.py @@ -0,0 +1,40 @@ +#!/usr/bin/env python + +import sys, os, os.path +import pywii as wii + +args = sys.argv[1:] + +if args[0] == "-dpki": + wii.loadkeys_dpki() + args.pop(0) +else: + wii.loadkeys() + +wadfile = args.pop(0) +indir = args.pop(0) + +tmd = wii.WiiTmd(open(os.path.join(indir, "tmd"), "rb").read()) +tik = wii.WiiTik(open(os.path.join(indir, "cetk"), "rb").read()) + +certs, certlist = wii.parse_certs(open(os.path.join(indir, "certs"), "rb").read()) + +footer = open(os.path.join(indir, "footer"), "rb").read() + +wad = wii.WiiWadMaker(wadfile, tmd, tik, certlist, footer) + +for i,ct in enumerate(tmd.get_content_records()): + data = open(os.path.join(indir, "%08X" % ct.cid), "rb").read() + wad.adddata(data,ct.cid) + +wad.finish() + +if not wad.tik.signcheck(wad.certs): + wad.tik.null_signature() + wad.tik.brute_sha() + wad.updatetik() + +if not wad.tmd.signcheck(wad.certs): + wad.tmd.null_signature() + wad.tmd.brute_sha() + wad.updatetmd() diff --git a/pywii/pywii-tools/wadunpack.py b/pywii/pywii-tools/wadunpack.py new file mode 100755 index 0000000..f1a40c9 --- /dev/null +++ b/pywii/pywii-tools/wadunpack.py @@ -0,0 +1,44 @@ +#!/usr/bin/env python + +import sys, os, os.path +import pywii as wii + +args = sys.argv[1:] + +if args[0] == "-dpki": + wii.loadkeys_dpki() + args.pop(0) +else: + wii.loadkeys() + +wadfile = args.pop(0) +outdir = args.pop(0) +wad = wii.WiiWad(wadfile) +wad.showinfo() + +if not os.path.isdir(outdir): + os.mkdir(outdir) + +for ct in wad.tmd.get_content_records(): + data = wad.getcontent(ct.index) + f = open(os.path.join(outdir, "%08X" % ct.cid),"wb") + f.write(data) + f.close() + +f = open(os.path.join(outdir, "cetk"),"wb") +f.write(wad.tik.data) +f.close() + +f = open(os.path.join(outdir, "tmd"),"wb") +f.write(wad.tmd.data) +f.close() + +f = open(os.path.join(outdir, "certs"),"wb") +for cert in wad.certlist: + f.write(cert.data) + wii.falign(f,0x40) +f.close() + +f = open(os.path.join(outdir, "footer"),"wb") +f.write(wad.footer) +f.close() diff --git a/wiipax/.gitignore b/wiipax/.gitignore new file mode 100644 index 0000000..9251773 --- /dev/null +++ b/wiipax/.gitignore @@ -0,0 +1,2 @@ +*.map + diff --git a/wiipax/Makefile b/wiipax/Makefile new file mode 100644 index 0000000..6d69506 --- /dev/null +++ b/wiipax/Makefile @@ -0,0 +1,11 @@ +all: + $(MAKE) -C stub release + $(MAKE) -C client + +clean distclean: + $(MAKE) -C stub distclean + $(MAKE) -C client clean + +install: + $(MAKE) -C client install + diff --git a/wiipax/client/.gitignore b/wiipax/client/.gitignore new file mode 100644 index 0000000..774e86f --- /dev/null +++ b/wiipax/client/.gitignore @@ -0,0 +1,4 @@ +wiipax +out.elf +x.lzma + diff --git a/wiipax/client/LzFind.c b/wiipax/client/LzFind.c new file mode 100644 index 0000000..e0ebe62 --- /dev/null +++ b/wiipax/client/LzFind.c @@ -0,0 +1,751 @@ +/* LzFind.c -- Match finder for LZ algorithms +2008-10-04 : Igor Pavlov : Public domain */ + +#include + +#include "LzFind.h" +#include "LzHash.h" + +#define kEmptyHashValue 0 +#define kMaxValForNormalize ((UInt32)0xFFFFFFFF) +#define kNormalizeStepMin (1 << 10) /* it must be power of 2 */ +#define kNormalizeMask (~(kNormalizeStepMin - 1)) +#define kMaxHistorySize ((UInt32)3 << 30) + +#define kStartMaxLen 3 + +static void LzInWindow_Free(CMatchFinder *p, ISzAlloc *alloc) +{ + if (!p->directInput) + { + alloc->Free(alloc, p->bufferBase); + p->bufferBase = 0; + } +} + +/* keepSizeBefore + keepSizeAfter + keepSizeReserv must be < 4G) */ + +static int LzInWindow_Create(CMatchFinder *p, UInt32 keepSizeReserv, ISzAlloc *alloc) +{ + UInt32 blockSize = p->keepSizeBefore + p->keepSizeAfter + keepSizeReserv; + if (p->directInput) + { + p->blockSize = blockSize; + return 1; + } + if (p->bufferBase == 0 || p->blockSize != blockSize) + { + LzInWindow_Free(p, alloc); + p->blockSize = blockSize; + p->bufferBase = (Byte *)alloc->Alloc(alloc, (size_t)blockSize); + } + return (p->bufferBase != 0); +} + +Byte *MatchFinder_GetPointerToCurrentPos(CMatchFinder *p) { return p->buffer; } +Byte MatchFinder_GetIndexByte(CMatchFinder *p, Int32 index) { return p->buffer[index]; } + +UInt32 MatchFinder_GetNumAvailableBytes(CMatchFinder *p) { return p->streamPos - p->pos; } + +void MatchFinder_ReduceOffsets(CMatchFinder *p, UInt32 subValue) +{ + p->posLimit -= subValue; + p->pos -= subValue; + p->streamPos -= subValue; +} + +static void MatchFinder_ReadBlock(CMatchFinder *p) +{ + if (p->streamEndWasReached || p->result != SZ_OK) + return; + for (;;) + { + Byte *dest = p->buffer + (p->streamPos - p->pos); + size_t size = (p->bufferBase + p->blockSize - dest); + if (size == 0) + return; + p->result = p->stream->Read(p->stream, dest, &size); + if (p->result != SZ_OK) + return; + if (size == 0) + { + p->streamEndWasReached = 1; + return; + } + p->streamPos += (UInt32)size; + if (p->streamPos - p->pos > p->keepSizeAfter) + return; + } +} + +void MatchFinder_MoveBlock(CMatchFinder *p) +{ + memmove(p->bufferBase, + p->buffer - p->keepSizeBefore, + (size_t)(p->streamPos - p->pos + p->keepSizeBefore)); + p->buffer = p->bufferBase + p->keepSizeBefore; +} + +int MatchFinder_NeedMove(CMatchFinder *p) +{ + /* if (p->streamEndWasReached) return 0; */ + return ((size_t)(p->bufferBase + p->blockSize - p->buffer) <= p->keepSizeAfter); +} + +void MatchFinder_ReadIfRequired(CMatchFinder *p) +{ + if (p->streamEndWasReached) + return; + if (p->keepSizeAfter >= p->streamPos - p->pos) + MatchFinder_ReadBlock(p); +} + +static void MatchFinder_CheckAndMoveAndRead(CMatchFinder *p) +{ + if (MatchFinder_NeedMove(p)) + MatchFinder_MoveBlock(p); + MatchFinder_ReadBlock(p); +} + +static void MatchFinder_SetDefaultSettings(CMatchFinder *p) +{ + p->cutValue = 32; + p->btMode = 1; + p->numHashBytes = 4; + /* p->skipModeBits = 0; */ + p->directInput = 0; + p->bigHash = 0; +} + +#define kCrcPoly 0xEDB88320 + +void MatchFinder_Construct(CMatchFinder *p) +{ + UInt32 i; + p->bufferBase = 0; + p->directInput = 0; + p->hash = 0; + MatchFinder_SetDefaultSettings(p); + + for (i = 0; i < 256; i++) + { + UInt32 r = i; + int j; + for (j = 0; j < 8; j++) + r = (r >> 1) ^ (kCrcPoly & ~((r & 1) - 1)); + p->crc[i] = r; + } +} + +static void MatchFinder_FreeThisClassMemory(CMatchFinder *p, ISzAlloc *alloc) +{ + alloc->Free(alloc, p->hash); + p->hash = 0; +} + +void MatchFinder_Free(CMatchFinder *p, ISzAlloc *alloc) +{ + MatchFinder_FreeThisClassMemory(p, alloc); + LzInWindow_Free(p, alloc); +} + +static CLzRef* AllocRefs(UInt32 num, ISzAlloc *alloc) +{ + size_t sizeInBytes = (size_t)num * sizeof(CLzRef); + if (sizeInBytes / sizeof(CLzRef) != num) + return 0; + return (CLzRef *)alloc->Alloc(alloc, sizeInBytes); +} + +int MatchFinder_Create(CMatchFinder *p, UInt32 historySize, + UInt32 keepAddBufferBefore, UInt32 matchMaxLen, UInt32 keepAddBufferAfter, + ISzAlloc *alloc) +{ + UInt32 sizeReserv; + if (historySize > kMaxHistorySize) + { + MatchFinder_Free(p, alloc); + return 0; + } + sizeReserv = historySize >> 1; + if (historySize > ((UInt32)2 << 30)) + sizeReserv = historySize >> 2; + sizeReserv += (keepAddBufferBefore + matchMaxLen + keepAddBufferAfter) / 2 + (1 << 19); + + p->keepSizeBefore = historySize + keepAddBufferBefore + 1; + p->keepSizeAfter = matchMaxLen + keepAddBufferAfter; + /* we need one additional byte, since we use MoveBlock after pos++ and before dictionary using */ + if (LzInWindow_Create(p, sizeReserv, alloc)) + { + UInt32 newCyclicBufferSize = (historySize /* >> p->skipModeBits */) + 1; + UInt32 hs; + p->matchMaxLen = matchMaxLen; + { + p->fixedHashSize = 0; + if (p->numHashBytes == 2) + hs = (1 << 16) - 1; + else + { + hs = historySize - 1; + hs |= (hs >> 1); + hs |= (hs >> 2); + hs |= (hs >> 4); + hs |= (hs >> 8); + hs >>= 1; + /* hs >>= p->skipModeBits; */ + hs |= 0xFFFF; /* don't change it! It's required for Deflate */ + if (hs > (1 << 24)) + { + if (p->numHashBytes == 3) + hs = (1 << 24) - 1; + else + hs >>= 1; + } + } + p->hashMask = hs; + hs++; + if (p->numHashBytes > 2) p->fixedHashSize += kHash2Size; + if (p->numHashBytes > 3) p->fixedHashSize += kHash3Size; + if (p->numHashBytes > 4) p->fixedHashSize += kHash4Size; + hs += p->fixedHashSize; + } + + { + UInt32 prevSize = p->hashSizeSum + p->numSons; + UInt32 newSize; + p->historySize = historySize; + p->hashSizeSum = hs; + p->cyclicBufferSize = newCyclicBufferSize; + p->numSons = (p->btMode ? newCyclicBufferSize * 2 : newCyclicBufferSize); + newSize = p->hashSizeSum + p->numSons; + if (p->hash != 0 && prevSize == newSize) + return 1; + MatchFinder_FreeThisClassMemory(p, alloc); + p->hash = AllocRefs(newSize, alloc); + if (p->hash != 0) + { + p->son = p->hash + p->hashSizeSum; + return 1; + } + } + } + MatchFinder_Free(p, alloc); + return 0; +} + +static void MatchFinder_SetLimits(CMatchFinder *p) +{ + UInt32 limit = kMaxValForNormalize - p->pos; + UInt32 limit2 = p->cyclicBufferSize - p->cyclicBufferPos; + if (limit2 < limit) + limit = limit2; + limit2 = p->streamPos - p->pos; + if (limit2 <= p->keepSizeAfter) + { + if (limit2 > 0) + limit2 = 1; + } + else + limit2 -= p->keepSizeAfter; + if (limit2 < limit) + limit = limit2; + { + UInt32 lenLimit = p->streamPos - p->pos; + if (lenLimit > p->matchMaxLen) + lenLimit = p->matchMaxLen; + p->lenLimit = lenLimit; + } + p->posLimit = p->pos + limit; +} + +void MatchFinder_Init(CMatchFinder *p) +{ + UInt32 i; + for (i = 0; i < p->hashSizeSum; i++) + p->hash[i] = kEmptyHashValue; + p->cyclicBufferPos = 0; + p->buffer = p->bufferBase; + p->pos = p->streamPos = p->cyclicBufferSize; + p->result = SZ_OK; + p->streamEndWasReached = 0; + MatchFinder_ReadBlock(p); + MatchFinder_SetLimits(p); +} + +static UInt32 MatchFinder_GetSubValue(CMatchFinder *p) +{ + return (p->pos - p->historySize - 1) & kNormalizeMask; +} + +void MatchFinder_Normalize3(UInt32 subValue, CLzRef *items, UInt32 numItems) +{ + UInt32 i; + for (i = 0; i < numItems; i++) + { + UInt32 value = items[i]; + if (value <= subValue) + value = kEmptyHashValue; + else + value -= subValue; + items[i] = value; + } +} + +static void MatchFinder_Normalize(CMatchFinder *p) +{ + UInt32 subValue = MatchFinder_GetSubValue(p); + MatchFinder_Normalize3(subValue, p->hash, p->hashSizeSum + p->numSons); + MatchFinder_ReduceOffsets(p, subValue); +} + +static void MatchFinder_CheckLimits(CMatchFinder *p) +{ + if (p->pos == kMaxValForNormalize) + MatchFinder_Normalize(p); + if (!p->streamEndWasReached && p->keepSizeAfter == p->streamPos - p->pos) + MatchFinder_CheckAndMoveAndRead(p); + if (p->cyclicBufferPos == p->cyclicBufferSize) + p->cyclicBufferPos = 0; + MatchFinder_SetLimits(p); +} + +static UInt32 * Hc_GetMatchesSpec(UInt32 lenLimit, UInt32 curMatch, UInt32 pos, const Byte *cur, CLzRef *son, + UInt32 _cyclicBufferPos, UInt32 _cyclicBufferSize, UInt32 cutValue, + UInt32 *distances, UInt32 maxLen) +{ + son[_cyclicBufferPos] = curMatch; + for (;;) + { + UInt32 delta = pos - curMatch; + if (cutValue-- == 0 || delta >= _cyclicBufferSize) + return distances; + { + const Byte *pb = cur - delta; + curMatch = son[_cyclicBufferPos - delta + ((delta > _cyclicBufferPos) ? _cyclicBufferSize : 0)]; + if (pb[maxLen] == cur[maxLen] && *pb == *cur) + { + UInt32 len = 0; + while (++len != lenLimit) + if (pb[len] != cur[len]) + break; + if (maxLen < len) + { + *distances++ = maxLen = len; + *distances++ = delta - 1; + if (len == lenLimit) + return distances; + } + } + } + } +} + +UInt32 * GetMatchesSpec1(UInt32 lenLimit, UInt32 curMatch, UInt32 pos, const Byte *cur, CLzRef *son, + UInt32 _cyclicBufferPos, UInt32 _cyclicBufferSize, UInt32 cutValue, + UInt32 *distances, UInt32 maxLen) +{ + CLzRef *ptr0 = son + (_cyclicBufferPos << 1) + 1; + CLzRef *ptr1 = son + (_cyclicBufferPos << 1); + UInt32 len0 = 0, len1 = 0; + for (;;) + { + UInt32 delta = pos - curMatch; + if (cutValue-- == 0 || delta >= _cyclicBufferSize) + { + *ptr0 = *ptr1 = kEmptyHashValue; + return distances; + } + { + CLzRef *pair = son + ((_cyclicBufferPos - delta + ((delta > _cyclicBufferPos) ? _cyclicBufferSize : 0)) << 1); + const Byte *pb = cur - delta; + UInt32 len = (len0 < len1 ? len0 : len1); + if (pb[len] == cur[len]) + { + if (++len != lenLimit && pb[len] == cur[len]) + while (++len != lenLimit) + if (pb[len] != cur[len]) + break; + if (maxLen < len) + { + *distances++ = maxLen = len; + *distances++ = delta - 1; + if (len == lenLimit) + { + *ptr1 = pair[0]; + *ptr0 = pair[1]; + return distances; + } + } + } + if (pb[len] < cur[len]) + { + *ptr1 = curMatch; + ptr1 = pair + 1; + curMatch = *ptr1; + len1 = len; + } + else + { + *ptr0 = curMatch; + ptr0 = pair; + curMatch = *ptr0; + len0 = len; + } + } + } +} + +static void SkipMatchesSpec(UInt32 lenLimit, UInt32 curMatch, UInt32 pos, const Byte *cur, CLzRef *son, + UInt32 _cyclicBufferPos, UInt32 _cyclicBufferSize, UInt32 cutValue) +{ + CLzRef *ptr0 = son + (_cyclicBufferPos << 1) + 1; + CLzRef *ptr1 = son + (_cyclicBufferPos << 1); + UInt32 len0 = 0, len1 = 0; + for (;;) + { + UInt32 delta = pos - curMatch; + if (cutValue-- == 0 || delta >= _cyclicBufferSize) + { + *ptr0 = *ptr1 = kEmptyHashValue; + return; + } + { + CLzRef *pair = son + ((_cyclicBufferPos - delta + ((delta > _cyclicBufferPos) ? _cyclicBufferSize : 0)) << 1); + const Byte *pb = cur - delta; + UInt32 len = (len0 < len1 ? len0 : len1); + if (pb[len] == cur[len]) + { + while (++len != lenLimit) + if (pb[len] != cur[len]) + break; + { + if (len == lenLimit) + { + *ptr1 = pair[0]; + *ptr0 = pair[1]; + return; + } + } + } + if (pb[len] < cur[len]) + { + *ptr1 = curMatch; + ptr1 = pair + 1; + curMatch = *ptr1; + len1 = len; + } + else + { + *ptr0 = curMatch; + ptr0 = pair; + curMatch = *ptr0; + len0 = len; + } + } + } +} + +#define MOVE_POS \ + ++p->cyclicBufferPos; \ + p->buffer++; \ + if (++p->pos == p->posLimit) MatchFinder_CheckLimits(p); + +#define MOVE_POS_RET MOVE_POS return offset; + +static void MatchFinder_MovePos(CMatchFinder *p) { MOVE_POS; } + +#define GET_MATCHES_HEADER2(minLen, ret_op) \ + UInt32 lenLimit; UInt32 hashValue; const Byte *cur; UInt32 curMatch; \ + lenLimit = p->lenLimit; { if (lenLimit < minLen) { MatchFinder_MovePos(p); ret_op; }} \ + cur = p->buffer; + +#define GET_MATCHES_HEADER(minLen) GET_MATCHES_HEADER2(minLen, return 0) +#define SKIP_HEADER(minLen) GET_MATCHES_HEADER2(minLen, continue) + +#define MF_PARAMS(p) p->pos, p->buffer, p->son, p->cyclicBufferPos, p->cyclicBufferSize, p->cutValue + +#define GET_MATCHES_FOOTER(offset, maxLen) \ + offset = (UInt32)(GetMatchesSpec1(lenLimit, curMatch, MF_PARAMS(p), \ + distances + offset, maxLen) - distances); MOVE_POS_RET; + +#define SKIP_FOOTER \ + SkipMatchesSpec(lenLimit, curMatch, MF_PARAMS(p)); MOVE_POS; + +static UInt32 Bt2_MatchFinder_GetMatches(CMatchFinder *p, UInt32 *distances) +{ + UInt32 offset; + GET_MATCHES_HEADER(2) + HASH2_CALC; + curMatch = p->hash[hashValue]; + p->hash[hashValue] = p->pos; + offset = 0; + GET_MATCHES_FOOTER(offset, 1) +} + +UInt32 Bt3Zip_MatchFinder_GetMatches(CMatchFinder *p, UInt32 *distances) +{ + UInt32 offset; + GET_MATCHES_HEADER(3) + HASH_ZIP_CALC; + curMatch = p->hash[hashValue]; + p->hash[hashValue] = p->pos; + offset = 0; + GET_MATCHES_FOOTER(offset, 2) +} + +static UInt32 Bt3_MatchFinder_GetMatches(CMatchFinder *p, UInt32 *distances) +{ + UInt32 hash2Value, delta2, maxLen, offset; + GET_MATCHES_HEADER(3) + + HASH3_CALC; + + delta2 = p->pos - p->hash[hash2Value]; + curMatch = p->hash[kFix3HashSize + hashValue]; + + p->hash[hash2Value] = + p->hash[kFix3HashSize + hashValue] = p->pos; + + + maxLen = 2; + offset = 0; + if (delta2 < p->cyclicBufferSize && *(cur - delta2) == *cur) + { + for (; maxLen != lenLimit; maxLen++) + if (cur[(ptrdiff_t)maxLen - delta2] != cur[maxLen]) + break; + distances[0] = maxLen; + distances[1] = delta2 - 1; + offset = 2; + if (maxLen == lenLimit) + { + SkipMatchesSpec(lenLimit, curMatch, MF_PARAMS(p)); + MOVE_POS_RET; + } + } + GET_MATCHES_FOOTER(offset, maxLen) +} + +static UInt32 Bt4_MatchFinder_GetMatches(CMatchFinder *p, UInt32 *distances) +{ + UInt32 hash2Value, hash3Value, delta2, delta3, maxLen, offset; + GET_MATCHES_HEADER(4) + + HASH4_CALC; + + delta2 = p->pos - p->hash[ hash2Value]; + delta3 = p->pos - p->hash[kFix3HashSize + hash3Value]; + curMatch = p->hash[kFix4HashSize + hashValue]; + + p->hash[ hash2Value] = + p->hash[kFix3HashSize + hash3Value] = + p->hash[kFix4HashSize + hashValue] = p->pos; + + maxLen = 1; + offset = 0; + if (delta2 < p->cyclicBufferSize && *(cur - delta2) == *cur) + { + distances[0] = maxLen = 2; + distances[1] = delta2 - 1; + offset = 2; + } + if (delta2 != delta3 && delta3 < p->cyclicBufferSize && *(cur - delta3) == *cur) + { + maxLen = 3; + distances[offset + 1] = delta3 - 1; + offset += 2; + delta2 = delta3; + } + if (offset != 0) + { + for (; maxLen != lenLimit; maxLen++) + if (cur[(ptrdiff_t)maxLen - delta2] != cur[maxLen]) + break; + distances[offset - 2] = maxLen; + if (maxLen == lenLimit) + { + SkipMatchesSpec(lenLimit, curMatch, MF_PARAMS(p)); + MOVE_POS_RET; + } + } + if (maxLen < 3) + maxLen = 3; + GET_MATCHES_FOOTER(offset, maxLen) +} + +static UInt32 Hc4_MatchFinder_GetMatches(CMatchFinder *p, UInt32 *distances) +{ + UInt32 hash2Value, hash3Value, delta2, delta3, maxLen, offset; + GET_MATCHES_HEADER(4) + + HASH4_CALC; + + delta2 = p->pos - p->hash[ hash2Value]; + delta3 = p->pos - p->hash[kFix3HashSize + hash3Value]; + curMatch = p->hash[kFix4HashSize + hashValue]; + + p->hash[ hash2Value] = + p->hash[kFix3HashSize + hash3Value] = + p->hash[kFix4HashSize + hashValue] = p->pos; + + maxLen = 1; + offset = 0; + if (delta2 < p->cyclicBufferSize && *(cur - delta2) == *cur) + { + distances[0] = maxLen = 2; + distances[1] = delta2 - 1; + offset = 2; + } + if (delta2 != delta3 && delta3 < p->cyclicBufferSize && *(cur - delta3) == *cur) + { + maxLen = 3; + distances[offset + 1] = delta3 - 1; + offset += 2; + delta2 = delta3; + } + if (offset != 0) + { + for (; maxLen != lenLimit; maxLen++) + if (cur[(ptrdiff_t)maxLen - delta2] != cur[maxLen]) + break; + distances[offset - 2] = maxLen; + if (maxLen == lenLimit) + { + p->son[p->cyclicBufferPos] = curMatch; + MOVE_POS_RET; + } + } + if (maxLen < 3) + maxLen = 3; + offset = (UInt32)(Hc_GetMatchesSpec(lenLimit, curMatch, MF_PARAMS(p), + distances + offset, maxLen) - (distances)); + MOVE_POS_RET +} + +UInt32 Hc3Zip_MatchFinder_GetMatches(CMatchFinder *p, UInt32 *distances) +{ + UInt32 offset; + GET_MATCHES_HEADER(3) + HASH_ZIP_CALC; + curMatch = p->hash[hashValue]; + p->hash[hashValue] = p->pos; + offset = (UInt32)(Hc_GetMatchesSpec(lenLimit, curMatch, MF_PARAMS(p), + distances, 2) - (distances)); + MOVE_POS_RET +} + +static void Bt2_MatchFinder_Skip(CMatchFinder *p, UInt32 num) +{ + do + { + SKIP_HEADER(2) + HASH2_CALC; + curMatch = p->hash[hashValue]; + p->hash[hashValue] = p->pos; + SKIP_FOOTER + } + while (--num != 0); +} + +void Bt3Zip_MatchFinder_Skip(CMatchFinder *p, UInt32 num) +{ + do + { + SKIP_HEADER(3) + HASH_ZIP_CALC; + curMatch = p->hash[hashValue]; + p->hash[hashValue] = p->pos; + SKIP_FOOTER + } + while (--num != 0); +} + +static void Bt3_MatchFinder_Skip(CMatchFinder *p, UInt32 num) +{ + do + { + UInt32 hash2Value; + SKIP_HEADER(3) + HASH3_CALC; + curMatch = p->hash[kFix3HashSize + hashValue]; + p->hash[hash2Value] = + p->hash[kFix3HashSize + hashValue] = p->pos; + SKIP_FOOTER + } + while (--num != 0); +} + +static void Bt4_MatchFinder_Skip(CMatchFinder *p, UInt32 num) +{ + do + { + UInt32 hash2Value, hash3Value; + SKIP_HEADER(4) + HASH4_CALC; + curMatch = p->hash[kFix4HashSize + hashValue]; + p->hash[ hash2Value] = + p->hash[kFix3HashSize + hash3Value] = p->pos; + p->hash[kFix4HashSize + hashValue] = p->pos; + SKIP_FOOTER + } + while (--num != 0); +} + +static void Hc4_MatchFinder_Skip(CMatchFinder *p, UInt32 num) +{ + do + { + UInt32 hash2Value, hash3Value; + SKIP_HEADER(4) + HASH4_CALC; + curMatch = p->hash[kFix4HashSize + hashValue]; + p->hash[ hash2Value] = + p->hash[kFix3HashSize + hash3Value] = + p->hash[kFix4HashSize + hashValue] = p->pos; + p->son[p->cyclicBufferPos] = curMatch; + MOVE_POS + } + while (--num != 0); +} + +void Hc3Zip_MatchFinder_Skip(CMatchFinder *p, UInt32 num) +{ + do + { + SKIP_HEADER(3) + HASH_ZIP_CALC; + curMatch = p->hash[hashValue]; + p->hash[hashValue] = p->pos; + p->son[p->cyclicBufferPos] = curMatch; + MOVE_POS + } + while (--num != 0); +} + +void MatchFinder_CreateVTable(CMatchFinder *p, IMatchFinder *vTable) +{ + vTable->Init = (Mf_Init_Func)MatchFinder_Init; + vTable->GetIndexByte = (Mf_GetIndexByte_Func)MatchFinder_GetIndexByte; + vTable->GetNumAvailableBytes = (Mf_GetNumAvailableBytes_Func)MatchFinder_GetNumAvailableBytes; + vTable->GetPointerToCurrentPos = (Mf_GetPointerToCurrentPos_Func)MatchFinder_GetPointerToCurrentPos; + if (!p->btMode) + { + vTable->GetMatches = (Mf_GetMatches_Func)Hc4_MatchFinder_GetMatches; + vTable->Skip = (Mf_Skip_Func)Hc4_MatchFinder_Skip; + } + else if (p->numHashBytes == 2) + { + vTable->GetMatches = (Mf_GetMatches_Func)Bt2_MatchFinder_GetMatches; + vTable->Skip = (Mf_Skip_Func)Bt2_MatchFinder_Skip; + } + else if (p->numHashBytes == 3) + { + vTable->GetMatches = (Mf_GetMatches_Func)Bt3_MatchFinder_GetMatches; + vTable->Skip = (Mf_Skip_Func)Bt3_MatchFinder_Skip; + } + else + { + vTable->GetMatches = (Mf_GetMatches_Func)Bt4_MatchFinder_GetMatches; + vTable->Skip = (Mf_Skip_Func)Bt4_MatchFinder_Skip; + } +} diff --git a/wiipax/client/LzFind.h b/wiipax/client/LzFind.h new file mode 100644 index 0000000..423d67e --- /dev/null +++ b/wiipax/client/LzFind.h @@ -0,0 +1,107 @@ +/* LzFind.h -- Match finder for LZ algorithms +2008-10-04 : Igor Pavlov : Public domain */ + +#ifndef __LZFIND_H +#define __LZFIND_H + +#include "Types.h" + +typedef UInt32 CLzRef; + +typedef struct _CMatchFinder +{ + Byte *buffer; + UInt32 pos; + UInt32 posLimit; + UInt32 streamPos; + UInt32 lenLimit; + + UInt32 cyclicBufferPos; + UInt32 cyclicBufferSize; /* it must be = (historySize + 1) */ + + UInt32 matchMaxLen; + CLzRef *hash; + CLzRef *son; + UInt32 hashMask; + UInt32 cutValue; + + Byte *bufferBase; + ISeqInStream *stream; + int streamEndWasReached; + + UInt32 blockSize; + UInt32 keepSizeBefore; + UInt32 keepSizeAfter; + + UInt32 numHashBytes; + int directInput; + int btMode; + /* int skipModeBits; */ + int bigHash; + UInt32 historySize; + UInt32 fixedHashSize; + UInt32 hashSizeSum; + UInt32 numSons; + SRes result; + UInt32 crc[256]; +} CMatchFinder; + +#define Inline_MatchFinder_GetPointerToCurrentPos(p) ((p)->buffer) +#define Inline_MatchFinder_GetIndexByte(p, index) ((p)->buffer[(Int32)(index)]) + +#define Inline_MatchFinder_GetNumAvailableBytes(p) ((p)->streamPos - (p)->pos) + +int MatchFinder_NeedMove(CMatchFinder *p); +Byte *MatchFinder_GetPointerToCurrentPos(CMatchFinder *p); +void MatchFinder_MoveBlock(CMatchFinder *p); +void MatchFinder_ReadIfRequired(CMatchFinder *p); + +void MatchFinder_Construct(CMatchFinder *p); + +/* Conditions: + historySize <= 3 GB + keepAddBufferBefore + matchMaxLen + keepAddBufferAfter < 511MB +*/ +int MatchFinder_Create(CMatchFinder *p, UInt32 historySize, + UInt32 keepAddBufferBefore, UInt32 matchMaxLen, UInt32 keepAddBufferAfter, + ISzAlloc *alloc); +void MatchFinder_Free(CMatchFinder *p, ISzAlloc *alloc); +void MatchFinder_Normalize3(UInt32 subValue, CLzRef *items, UInt32 numItems); +void MatchFinder_ReduceOffsets(CMatchFinder *p, UInt32 subValue); + +UInt32 * GetMatchesSpec1(UInt32 lenLimit, UInt32 curMatch, UInt32 pos, const Byte *buffer, CLzRef *son, + UInt32 _cyclicBufferPos, UInt32 _cyclicBufferSize, UInt32 _cutValue, + UInt32 *distances, UInt32 maxLen); + +/* +Conditions: + Mf_GetNumAvailableBytes_Func must be called before each Mf_GetMatchLen_Func. + Mf_GetPointerToCurrentPos_Func's result must be used only before any other function +*/ + +typedef void (*Mf_Init_Func)(void *object); +typedef Byte (*Mf_GetIndexByte_Func)(void *object, Int32 index); +typedef UInt32 (*Mf_GetNumAvailableBytes_Func)(void *object); +typedef const Byte * (*Mf_GetPointerToCurrentPos_Func)(void *object); +typedef UInt32 (*Mf_GetMatches_Func)(void *object, UInt32 *distances); +typedef void (*Mf_Skip_Func)(void *object, UInt32); + +typedef struct _IMatchFinder +{ + Mf_Init_Func Init; + Mf_GetIndexByte_Func GetIndexByte; + Mf_GetNumAvailableBytes_Func GetNumAvailableBytes; + Mf_GetPointerToCurrentPos_Func GetPointerToCurrentPos; + Mf_GetMatches_Func GetMatches; + Mf_Skip_Func Skip; +} IMatchFinder; + +void MatchFinder_CreateVTable(CMatchFinder *p, IMatchFinder *vTable); + +void MatchFinder_Init(CMatchFinder *p); +UInt32 Bt3Zip_MatchFinder_GetMatches(CMatchFinder *p, UInt32 *distances); +UInt32 Hc3Zip_MatchFinder_GetMatches(CMatchFinder *p, UInt32 *distances); +void Bt3Zip_MatchFinder_Skip(CMatchFinder *p, UInt32 num); +void Hc3Zip_MatchFinder_Skip(CMatchFinder *p, UInt32 num); + +#endif diff --git a/wiipax/client/LzHash.h b/wiipax/client/LzHash.h new file mode 100644 index 0000000..c923417 --- /dev/null +++ b/wiipax/client/LzHash.h @@ -0,0 +1,54 @@ +/* LzHash.h -- HASH functions for LZ algorithms +2008-10-04 : Igor Pavlov : Public domain */ + +#ifndef __LZHASH_H +#define __LZHASH_H + +#define kHash2Size (1 << 10) +#define kHash3Size (1 << 16) +#define kHash4Size (1 << 20) + +#define kFix3HashSize (kHash2Size) +#define kFix4HashSize (kHash2Size + kHash3Size) +#define kFix5HashSize (kHash2Size + kHash3Size + kHash4Size) + +#define HASH2_CALC hashValue = cur[0] | ((UInt32)cur[1] << 8); + +#define HASH3_CALC { \ + UInt32 temp = p->crc[cur[0]] ^ cur[1]; \ + hash2Value = temp & (kHash2Size - 1); \ + hashValue = (temp ^ ((UInt32)cur[2] << 8)) & p->hashMask; } + +#define HASH4_CALC { \ + UInt32 temp = p->crc[cur[0]] ^ cur[1]; \ + hash2Value = temp & (kHash2Size - 1); \ + hash3Value = (temp ^ ((UInt32)cur[2] << 8)) & (kHash3Size - 1); \ + hashValue = (temp ^ ((UInt32)cur[2] << 8) ^ (p->crc[cur[3]] << 5)) & p->hashMask; } + +#define HASH5_CALC { \ + UInt32 temp = p->crc[cur[0]] ^ cur[1]; \ + hash2Value = temp & (kHash2Size - 1); \ + hash3Value = (temp ^ ((UInt32)cur[2] << 8)) & (kHash3Size - 1); \ + hash4Value = (temp ^ ((UInt32)cur[2] << 8) ^ (p->crc[cur[3]] << 5)); \ + hashValue = (hash4Value ^ (p->crc[cur[4]] << 3)) & p->hashMask; \ + hash4Value &= (kHash4Size - 1); } + +/* #define HASH_ZIP_CALC hashValue = ((cur[0] | ((UInt32)cur[1] << 8)) ^ p->crc[cur[2]]) & 0xFFFF; */ +#define HASH_ZIP_CALC hashValue = ((cur[2] | ((UInt32)cur[0] << 8)) ^ p->crc[cur[1]]) & 0xFFFF; + + +#define MT_HASH2_CALC \ + hash2Value = (p->crc[cur[0]] ^ cur[1]) & (kHash2Size - 1); + +#define MT_HASH3_CALC { \ + UInt32 temp = p->crc[cur[0]] ^ cur[1]; \ + hash2Value = temp & (kHash2Size - 1); \ + hash3Value = (temp ^ ((UInt32)cur[2] << 8)) & (kHash3Size - 1); } + +#define MT_HASH4_CALC { \ + UInt32 temp = p->crc[cur[0]] ^ cur[1]; \ + hash2Value = temp & (kHash2Size - 1); \ + hash3Value = (temp ^ ((UInt32)cur[2] << 8)) & (kHash3Size - 1); \ + hash4Value = (temp ^ ((UInt32)cur[2] << 8) ^ (p->crc[cur[3]] << 5)) & (kHash4Size - 1); } + +#endif diff --git a/wiipax/client/LzmaDec.c b/wiipax/client/LzmaDec.c new file mode 100644 index 0000000..eb52501 --- /dev/null +++ b/wiipax/client/LzmaDec.c @@ -0,0 +1,1007 @@ +/* LzmaDec.c -- LZMA Decoder +2008-11-06 : Igor Pavlov : Public domain */ + +#include "LzmaDec.h" + +#include + +#define kNumTopBits 24 +#define kTopValue ((UInt32)1 << kNumTopBits) + +#define kNumBitModelTotalBits 11 +#define kBitModelTotal (1 << kNumBitModelTotalBits) +#define kNumMoveBits 5 + +#define RC_INIT_SIZE 5 + +#define NORMALIZE if (range < kTopValue) { range <<= 8; code = (code << 8) | (*buf++); } + +#define IF_BIT_0(p) ttt = *(p); NORMALIZE; bound = (range >> kNumBitModelTotalBits) * ttt; if (code < bound) +#define UPDATE_0(p) range = bound; *(p) = (CLzmaProb)(ttt + ((kBitModelTotal - ttt) >> kNumMoveBits)); +#define UPDATE_1(p) range -= bound; code -= bound; *(p) = (CLzmaProb)(ttt - (ttt >> kNumMoveBits)); +#define GET_BIT2(p, i, A0, A1) IF_BIT_0(p) \ + { UPDATE_0(p); i = (i + i); A0; } else \ + { UPDATE_1(p); i = (i + i) + 1; A1; } +#define GET_BIT(p, i) GET_BIT2(p, i, ; , ;) + +#define TREE_GET_BIT(probs, i) { GET_BIT((probs + i), i); } +#define TREE_DECODE(probs, limit, i) \ + { i = 1; do { TREE_GET_BIT(probs, i); } while (i < limit); i -= limit; } + +/* #define _LZMA_SIZE_OPT */ + +#ifdef _LZMA_SIZE_OPT +#define TREE_6_DECODE(probs, i) TREE_DECODE(probs, (1 << 6), i) +#else +#define TREE_6_DECODE(probs, i) \ + { i = 1; \ + TREE_GET_BIT(probs, i); \ + TREE_GET_BIT(probs, i); \ + TREE_GET_BIT(probs, i); \ + TREE_GET_BIT(probs, i); \ + TREE_GET_BIT(probs, i); \ + TREE_GET_BIT(probs, i); \ + i -= 0x40; } +#endif + +#define NORMALIZE_CHECK if (range < kTopValue) { if (buf >= bufLimit) return DUMMY_ERROR; range <<= 8; code = (code << 8) | (*buf++); } + +#define IF_BIT_0_CHECK(p) ttt = *(p); NORMALIZE_CHECK; bound = (range >> kNumBitModelTotalBits) * ttt; if (code < bound) +#define UPDATE_0_CHECK range = bound; +#define UPDATE_1_CHECK range -= bound; code -= bound; +#define GET_BIT2_CHECK(p, i, A0, A1) IF_BIT_0_CHECK(p) \ + { UPDATE_0_CHECK; i = (i + i); A0; } else \ + { UPDATE_1_CHECK; i = (i + i) + 1; A1; } +#define GET_BIT_CHECK(p, i) GET_BIT2_CHECK(p, i, ; , ;) +#define TREE_DECODE_CHECK(probs, limit, i) \ + { i = 1; do { GET_BIT_CHECK(probs + i, i) } while (i < limit); i -= limit; } + + +#define kNumPosBitsMax 4 +#define kNumPosStatesMax (1 << kNumPosBitsMax) + +#define kLenNumLowBits 3 +#define kLenNumLowSymbols (1 << kLenNumLowBits) +#define kLenNumMidBits 3 +#define kLenNumMidSymbols (1 << kLenNumMidBits) +#define kLenNumHighBits 8 +#define kLenNumHighSymbols (1 << kLenNumHighBits) + +#define LenChoice 0 +#define LenChoice2 (LenChoice + 1) +#define LenLow (LenChoice2 + 1) +#define LenMid (LenLow + (kNumPosStatesMax << kLenNumLowBits)) +#define LenHigh (LenMid + (kNumPosStatesMax << kLenNumMidBits)) +#define kNumLenProbs (LenHigh + kLenNumHighSymbols) + + +#define kNumStates 12 +#define kNumLitStates 7 + +#define kStartPosModelIndex 4 +#define kEndPosModelIndex 14 +#define kNumFullDistances (1 << (kEndPosModelIndex >> 1)) + +#define kNumPosSlotBits 6 +#define kNumLenToPosStates 4 + +#define kNumAlignBits 4 +#define kAlignTableSize (1 << kNumAlignBits) + +#define kMatchMinLen 2 +#define kMatchSpecLenStart (kMatchMinLen + kLenNumLowSymbols + kLenNumMidSymbols + kLenNumHighSymbols) + +#define IsMatch 0 +#define IsRep (IsMatch + (kNumStates << kNumPosBitsMax)) +#define IsRepG0 (IsRep + kNumStates) +#define IsRepG1 (IsRepG0 + kNumStates) +#define IsRepG2 (IsRepG1 + kNumStates) +#define IsRep0Long (IsRepG2 + kNumStates) +#define PosSlot (IsRep0Long + (kNumStates << kNumPosBitsMax)) +#define SpecPos (PosSlot + (kNumLenToPosStates << kNumPosSlotBits)) +#define Align (SpecPos + kNumFullDistances - kEndPosModelIndex) +#define LenCoder (Align + kAlignTableSize) +#define RepLenCoder (LenCoder + kNumLenProbs) +#define Literal (RepLenCoder + kNumLenProbs) + +#define LZMA_BASE_SIZE 1846 +#define LZMA_LIT_SIZE 768 + +#define LzmaProps_GetNumProbs(p) ((UInt32)LZMA_BASE_SIZE + (LZMA_LIT_SIZE << ((p)->lc + (p)->lp))) + +#if Literal != LZMA_BASE_SIZE +StopCompilingDueBUG +#endif + +static const Byte kLiteralNextStates[kNumStates * 2] = +{ + 0, 0, 0, 0, 1, 2, 3, 4, 5, 6, 4, 5, + 7, 7, 7, 7, 7, 7, 7, 10, 10, 10, 10, 10 +}; + +#define LZMA_DIC_MIN (1 << 12) + +/* First LZMA-symbol is always decoded. +And it decodes new LZMA-symbols while (buf < bufLimit), but "buf" is without last normalization +Out: + Result: + SZ_OK - OK + SZ_ERROR_DATA - Error + p->remainLen: + < kMatchSpecLenStart : normal remain + = kMatchSpecLenStart : finished + = kMatchSpecLenStart + 1 : Flush marker + = kMatchSpecLenStart + 2 : State Init Marker +*/ + +static int MY_FAST_CALL LzmaDec_DecodeReal(CLzmaDec *p, SizeT limit, const Byte *bufLimit) +{ + CLzmaProb *probs = p->probs; + + unsigned state = p->state; + UInt32 rep0 = p->reps[0], rep1 = p->reps[1], rep2 = p->reps[2], rep3 = p->reps[3]; + unsigned pbMask = ((unsigned)1 << (p->prop.pb)) - 1; + unsigned lpMask = ((unsigned)1 << (p->prop.lp)) - 1; + unsigned lc = p->prop.lc; + + Byte *dic = p->dic; + SizeT dicBufSize = p->dicBufSize; + SizeT dicPos = p->dicPos; + + UInt32 processedPos = p->processedPos; + UInt32 checkDicSize = p->checkDicSize; + unsigned len = 0; + + const Byte *buf = p->buf; + UInt32 range = p->range; + UInt32 code = p->code; + + do + { + CLzmaProb *prob; + UInt32 bound; + unsigned ttt; + unsigned posState = processedPos & pbMask; + + prob = probs + IsMatch + (state << kNumPosBitsMax) + posState; + IF_BIT_0(prob) + { + unsigned symbol; + UPDATE_0(prob); + prob = probs + Literal; + if (checkDicSize != 0 || processedPos != 0) + prob += (LZMA_LIT_SIZE * (((processedPos & lpMask) << lc) + + (dic[(dicPos == 0 ? dicBufSize : dicPos) - 1] >> (8 - lc)))); + + if (state < kNumLitStates) + { + symbol = 1; + do { GET_BIT(prob + symbol, symbol) } while (symbol < 0x100); + } + else + { + unsigned matchByte = p->dic[(dicPos - rep0) + ((dicPos < rep0) ? dicBufSize : 0)]; + unsigned offs = 0x100; + symbol = 1; + do + { + unsigned bit; + CLzmaProb *probLit; + matchByte <<= 1; + bit = (matchByte & offs); + probLit = prob + offs + bit + symbol; + GET_BIT2(probLit, symbol, offs &= ~bit, offs &= bit) + } + while (symbol < 0x100); + } + dic[dicPos++] = (Byte)symbol; + processedPos++; + + state = kLiteralNextStates[state]; + /* if (state < 4) state = 0; else if (state < 10) state -= 3; else state -= 6; */ + continue; + } + else + { + UPDATE_1(prob); + prob = probs + IsRep + state; + IF_BIT_0(prob) + { + UPDATE_0(prob); + state += kNumStates; + prob = probs + LenCoder; + } + else + { + UPDATE_1(prob); + if (checkDicSize == 0 && processedPos == 0) + return SZ_ERROR_DATA; + prob = probs + IsRepG0 + state; + IF_BIT_0(prob) + { + UPDATE_0(prob); + prob = probs + IsRep0Long + (state << kNumPosBitsMax) + posState; + IF_BIT_0(prob) + { + UPDATE_0(prob); + dic[dicPos] = dic[(dicPos - rep0) + ((dicPos < rep0) ? dicBufSize : 0)]; + dicPos++; + processedPos++; + state = state < kNumLitStates ? 9 : 11; + continue; + } + UPDATE_1(prob); + } + else + { + UInt32 distance; + UPDATE_1(prob); + prob = probs + IsRepG1 + state; + IF_BIT_0(prob) + { + UPDATE_0(prob); + distance = rep1; + } + else + { + UPDATE_1(prob); + prob = probs + IsRepG2 + state; + IF_BIT_0(prob) + { + UPDATE_0(prob); + distance = rep2; + } + else + { + UPDATE_1(prob); + distance = rep3; + rep3 = rep2; + } + rep2 = rep1; + } + rep1 = rep0; + rep0 = distance; + } + state = state < kNumLitStates ? 8 : 11; + prob = probs + RepLenCoder; + } + { + unsigned limit, offset; + CLzmaProb *probLen = prob + LenChoice; + IF_BIT_0(probLen) + { + UPDATE_0(probLen); + probLen = prob + LenLow + (posState << kLenNumLowBits); + offset = 0; + limit = (1 << kLenNumLowBits); + } + else + { + UPDATE_1(probLen); + probLen = prob + LenChoice2; + IF_BIT_0(probLen) + { + UPDATE_0(probLen); + probLen = prob + LenMid + (posState << kLenNumMidBits); + offset = kLenNumLowSymbols; + limit = (1 << kLenNumMidBits); + } + else + { + UPDATE_1(probLen); + probLen = prob + LenHigh; + offset = kLenNumLowSymbols + kLenNumMidSymbols; + limit = (1 << kLenNumHighBits); + } + } + TREE_DECODE(probLen, limit, len); + len += offset; + } + + if (state >= kNumStates) + { + UInt32 distance; + prob = probs + PosSlot + + ((len < kNumLenToPosStates ? len : kNumLenToPosStates - 1) << kNumPosSlotBits); + TREE_6_DECODE(prob, distance); + if (distance >= kStartPosModelIndex) + { + unsigned posSlot = (unsigned)distance; + int numDirectBits = (int)(((distance >> 1) - 1)); + distance = (2 | (distance & 1)); + if (posSlot < kEndPosModelIndex) + { + distance <<= numDirectBits; + prob = probs + SpecPos + distance - posSlot - 1; + { + UInt32 mask = 1; + unsigned i = 1; + do + { + GET_BIT2(prob + i, i, ; , distance |= mask); + mask <<= 1; + } + while (--numDirectBits != 0); + } + } + else + { + numDirectBits -= kNumAlignBits; + do + { + NORMALIZE + range >>= 1; + + { + UInt32 t; + code -= range; + t = (0 - ((UInt32)code >> 31)); /* (UInt32)((Int32)code >> 31) */ + distance = (distance << 1) + (t + 1); + code += range & t; + } + /* + distance <<= 1; + if (code >= range) + { + code -= range; + distance |= 1; + } + */ + } + while (--numDirectBits != 0); + prob = probs + Align; + distance <<= kNumAlignBits; + { + unsigned i = 1; + GET_BIT2(prob + i, i, ; , distance |= 1); + GET_BIT2(prob + i, i, ; , distance |= 2); + GET_BIT2(prob + i, i, ; , distance |= 4); + GET_BIT2(prob + i, i, ; , distance |= 8); + } + if (distance == (UInt32)0xFFFFFFFF) + { + len += kMatchSpecLenStart; + state -= kNumStates; + break; + } + } + } + rep3 = rep2; + rep2 = rep1; + rep1 = rep0; + rep0 = distance + 1; + if (checkDicSize == 0) + { + if (distance >= processedPos) + return SZ_ERROR_DATA; + } + else if (distance >= checkDicSize) + return SZ_ERROR_DATA; + state = (state < kNumStates + kNumLitStates) ? kNumLitStates : kNumLitStates + 3; + /* state = kLiteralNextStates[state]; */ + } + + len += kMatchMinLen; + + if (limit == dicPos) + return SZ_ERROR_DATA; + { + SizeT rem = limit - dicPos; + unsigned curLen = ((rem < len) ? (unsigned)rem : len); + SizeT pos = (dicPos - rep0) + ((dicPos < rep0) ? dicBufSize : 0); + + processedPos += curLen; + + len -= curLen; + if (pos + curLen <= dicBufSize) + { + Byte *dest = dic + dicPos; + ptrdiff_t src = (ptrdiff_t)pos - (ptrdiff_t)dicPos; + const Byte *lim = dest + curLen; + dicPos += curLen; + do + *(dest) = (Byte)*(dest + src); + while (++dest != lim); + } + else + { + do + { + dic[dicPos++] = dic[pos]; + if (++pos == dicBufSize) + pos = 0; + } + while (--curLen != 0); + } + } + } + } + while (dicPos < limit && buf < bufLimit); + NORMALIZE; + p->buf = buf; + p->range = range; + p->code = code; + p->remainLen = len; + p->dicPos = dicPos; + p->processedPos = processedPos; + p->reps[0] = rep0; + p->reps[1] = rep1; + p->reps[2] = rep2; + p->reps[3] = rep3; + p->state = state; + + return SZ_OK; +} + +static void MY_FAST_CALL LzmaDec_WriteRem(CLzmaDec *p, SizeT limit) +{ + if (p->remainLen != 0 && p->remainLen < kMatchSpecLenStart) + { + Byte *dic = p->dic; + SizeT dicPos = p->dicPos; + SizeT dicBufSize = p->dicBufSize; + unsigned len = p->remainLen; + UInt32 rep0 = p->reps[0]; + if (limit - dicPos < len) + len = (unsigned)(limit - dicPos); + + if (p->checkDicSize == 0 && p->prop.dicSize - p->processedPos <= len) + p->checkDicSize = p->prop.dicSize; + + p->processedPos += len; + p->remainLen -= len; + while (len-- != 0) + { + dic[dicPos] = dic[(dicPos - rep0) + ((dicPos < rep0) ? dicBufSize : 0)]; + dicPos++; + } + p->dicPos = dicPos; + } +} + +static int MY_FAST_CALL LzmaDec_DecodeReal2(CLzmaDec *p, SizeT limit, const Byte *bufLimit) +{ + do + { + SizeT limit2 = limit; + if (p->checkDicSize == 0) + { + UInt32 rem = p->prop.dicSize - p->processedPos; + if (limit - p->dicPos > rem) + limit2 = p->dicPos + rem; + } + RINOK(LzmaDec_DecodeReal(p, limit2, bufLimit)); + if (p->processedPos >= p->prop.dicSize) + p->checkDicSize = p->prop.dicSize; + LzmaDec_WriteRem(p, limit); + } + while (p->dicPos < limit && p->buf < bufLimit && p->remainLen < kMatchSpecLenStart); + + if (p->remainLen > kMatchSpecLenStart) + { + p->remainLen = kMatchSpecLenStart; + } + return 0; +} + +typedef enum +{ + DUMMY_ERROR, /* unexpected end of input stream */ + DUMMY_LIT, + DUMMY_MATCH, + DUMMY_REP +} ELzmaDummy; + +static ELzmaDummy LzmaDec_TryDummy(const CLzmaDec *p, const Byte *buf, SizeT inSize) +{ + UInt32 range = p->range; + UInt32 code = p->code; + const Byte *bufLimit = buf + inSize; + CLzmaProb *probs = p->probs; + unsigned state = p->state; + ELzmaDummy res; + + { + CLzmaProb *prob; + UInt32 bound; + unsigned ttt; + unsigned posState = (p->processedPos) & ((1 << p->prop.pb) - 1); + + prob = probs + IsMatch + (state << kNumPosBitsMax) + posState; + IF_BIT_0_CHECK(prob) + { + UPDATE_0_CHECK + + /* if (bufLimit - buf >= 7) return DUMMY_LIT; */ + + prob = probs + Literal; + if (p->checkDicSize != 0 || p->processedPos != 0) + prob += (LZMA_LIT_SIZE * + ((((p->processedPos) & ((1 << (p->prop.lp)) - 1)) << p->prop.lc) + + (p->dic[(p->dicPos == 0 ? p->dicBufSize : p->dicPos) - 1] >> (8 - p->prop.lc)))); + + if (state < kNumLitStates) + { + unsigned symbol = 1; + do { GET_BIT_CHECK(prob + symbol, symbol) } while (symbol < 0x100); + } + else + { + unsigned matchByte = p->dic[p->dicPos - p->reps[0] + + ((p->dicPos < p->reps[0]) ? p->dicBufSize : 0)]; + unsigned offs = 0x100; + unsigned symbol = 1; + do + { + unsigned bit; + CLzmaProb *probLit; + matchByte <<= 1; + bit = (matchByte & offs); + probLit = prob + offs + bit + symbol; + GET_BIT2_CHECK(probLit, symbol, offs &= ~bit, offs &= bit) + } + while (symbol < 0x100); + } + res = DUMMY_LIT; + } + else + { + unsigned len; + UPDATE_1_CHECK; + + prob = probs + IsRep + state; + IF_BIT_0_CHECK(prob) + { + UPDATE_0_CHECK; + state = 0; + prob = probs + LenCoder; + res = DUMMY_MATCH; + } + else + { + UPDATE_1_CHECK; + res = DUMMY_REP; + prob = probs + IsRepG0 + state; + IF_BIT_0_CHECK(prob) + { + UPDATE_0_CHECK; + prob = probs + IsRep0Long + (state << kNumPosBitsMax) + posState; + IF_BIT_0_CHECK(prob) + { + UPDATE_0_CHECK; + NORMALIZE_CHECK; + return DUMMY_REP; + } + else + { + UPDATE_1_CHECK; + } + } + else + { + UPDATE_1_CHECK; + prob = probs + IsRepG1 + state; + IF_BIT_0_CHECK(prob) + { + UPDATE_0_CHECK; + } + else + { + UPDATE_1_CHECK; + prob = probs + IsRepG2 + state; + IF_BIT_0_CHECK(prob) + { + UPDATE_0_CHECK; + } + else + { + UPDATE_1_CHECK; + } + } + } + state = kNumStates; + prob = probs + RepLenCoder; + } + { + unsigned limit, offset; + CLzmaProb *probLen = prob + LenChoice; + IF_BIT_0_CHECK(probLen) + { + UPDATE_0_CHECK; + probLen = prob + LenLow + (posState << kLenNumLowBits); + offset = 0; + limit = 1 << kLenNumLowBits; + } + else + { + UPDATE_1_CHECK; + probLen = prob + LenChoice2; + IF_BIT_0_CHECK(probLen) + { + UPDATE_0_CHECK; + probLen = prob + LenMid + (posState << kLenNumMidBits); + offset = kLenNumLowSymbols; + limit = 1 << kLenNumMidBits; + } + else + { + UPDATE_1_CHECK; + probLen = prob + LenHigh; + offset = kLenNumLowSymbols + kLenNumMidSymbols; + limit = 1 << kLenNumHighBits; + } + } + TREE_DECODE_CHECK(probLen, limit, len); + len += offset; + } + + if (state < 4) + { + unsigned posSlot; + prob = probs + PosSlot + + ((len < kNumLenToPosStates ? len : kNumLenToPosStates - 1) << + kNumPosSlotBits); + TREE_DECODE_CHECK(prob, 1 << kNumPosSlotBits, posSlot); + if (posSlot >= kStartPosModelIndex) + { + int numDirectBits = ((posSlot >> 1) - 1); + + /* if (bufLimit - buf >= 8) return DUMMY_MATCH; */ + + if (posSlot < kEndPosModelIndex) + { + prob = probs + SpecPos + ((2 | (posSlot & 1)) << numDirectBits) - posSlot - 1; + } + else + { + numDirectBits -= kNumAlignBits; + do + { + NORMALIZE_CHECK + range >>= 1; + code -= range & (((code - range) >> 31) - 1); + /* if (code >= range) code -= range; */ + } + while (--numDirectBits != 0); + prob = probs + Align; + numDirectBits = kNumAlignBits; + } + { + unsigned i = 1; + do + { + GET_BIT_CHECK(prob + i, i); + } + while (--numDirectBits != 0); + } + } + } + } + } + NORMALIZE_CHECK; + return res; +} + + +static void LzmaDec_InitRc(CLzmaDec *p, const Byte *data) +{ + p->code = ((UInt32)data[1] << 24) | ((UInt32)data[2] << 16) | ((UInt32)data[3] << 8) | ((UInt32)data[4]); + p->range = 0xFFFFFFFF; + p->needFlush = 0; +} + +void LzmaDec_InitDicAndState(CLzmaDec *p, Bool initDic, Bool initState) +{ + p->needFlush = 1; + p->remainLen = 0; + p->tempBufSize = 0; + + if (initDic) + { + p->processedPos = 0; + p->checkDicSize = 0; + p->needInitState = 1; + } + if (initState) + p->needInitState = 1; +} + +void LzmaDec_Init(CLzmaDec *p) +{ + p->dicPos = 0; + LzmaDec_InitDicAndState(p, True, True); +} + +static void LzmaDec_InitStateReal(CLzmaDec *p) +{ + UInt32 numProbs = Literal + ((UInt32)LZMA_LIT_SIZE << (p->prop.lc + p->prop.lp)); + UInt32 i; + CLzmaProb *probs = p->probs; + for (i = 0; i < numProbs; i++) + probs[i] = kBitModelTotal >> 1; + p->reps[0] = p->reps[1] = p->reps[2] = p->reps[3] = 1; + p->state = 0; + p->needInitState = 0; +} + +SRes LzmaDec_DecodeToDic(CLzmaDec *p, SizeT dicLimit, const Byte *src, SizeT *srcLen, + ELzmaFinishMode finishMode, ELzmaStatus *status) +{ + SizeT inSize = *srcLen; + (*srcLen) = 0; + LzmaDec_WriteRem(p, dicLimit); + + *status = LZMA_STATUS_NOT_SPECIFIED; + + while (p->remainLen != kMatchSpecLenStart) + { + int checkEndMarkNow; + + if (p->needFlush != 0) + { + for (; inSize > 0 && p->tempBufSize < RC_INIT_SIZE; (*srcLen)++, inSize--) + p->tempBuf[p->tempBufSize++] = *src++; + if (p->tempBufSize < RC_INIT_SIZE) + { + *status = LZMA_STATUS_NEEDS_MORE_INPUT; + return SZ_OK; + } + if (p->tempBuf[0] != 0) + return SZ_ERROR_DATA; + + LzmaDec_InitRc(p, p->tempBuf); + p->tempBufSize = 0; + } + + checkEndMarkNow = 0; + if (p->dicPos >= dicLimit) + { + if (p->remainLen == 0 && p->code == 0) + { + *status = LZMA_STATUS_MAYBE_FINISHED_WITHOUT_MARK; + return SZ_OK; + } + if (finishMode == LZMA_FINISH_ANY) + { + *status = LZMA_STATUS_NOT_FINISHED; + return SZ_OK; + } + if (p->remainLen != 0) + { + *status = LZMA_STATUS_NOT_FINISHED; + return SZ_ERROR_DATA; + } + checkEndMarkNow = 1; + } + + if (p->needInitState) + LzmaDec_InitStateReal(p); + + if (p->tempBufSize == 0) + { + SizeT processed; + const Byte *bufLimit; + if (inSize < LZMA_REQUIRED_INPUT_MAX || checkEndMarkNow) + { + int dummyRes = LzmaDec_TryDummy(p, src, inSize); + if (dummyRes == DUMMY_ERROR) + { + memcpy(p->tempBuf, src, inSize); + p->tempBufSize = (unsigned)inSize; + (*srcLen) += inSize; + *status = LZMA_STATUS_NEEDS_MORE_INPUT; + return SZ_OK; + } + if (checkEndMarkNow && dummyRes != DUMMY_MATCH) + { + *status = LZMA_STATUS_NOT_FINISHED; + return SZ_ERROR_DATA; + } + bufLimit = src; + } + else + bufLimit = src + inSize - LZMA_REQUIRED_INPUT_MAX; + p->buf = src; + if (LzmaDec_DecodeReal2(p, dicLimit, bufLimit) != 0) + return SZ_ERROR_DATA; + processed = (SizeT)(p->buf - src); + (*srcLen) += processed; + src += processed; + inSize -= processed; + } + else + { + unsigned rem = p->tempBufSize, lookAhead = 0; + while (rem < LZMA_REQUIRED_INPUT_MAX && lookAhead < inSize) + p->tempBuf[rem++] = src[lookAhead++]; + p->tempBufSize = rem; + if (rem < LZMA_REQUIRED_INPUT_MAX || checkEndMarkNow) + { + int dummyRes = LzmaDec_TryDummy(p, p->tempBuf, rem); + if (dummyRes == DUMMY_ERROR) + { + (*srcLen) += lookAhead; + *status = LZMA_STATUS_NEEDS_MORE_INPUT; + return SZ_OK; + } + if (checkEndMarkNow && dummyRes != DUMMY_MATCH) + { + *status = LZMA_STATUS_NOT_FINISHED; + return SZ_ERROR_DATA; + } + } + p->buf = p->tempBuf; + if (LzmaDec_DecodeReal2(p, dicLimit, p->buf) != 0) + return SZ_ERROR_DATA; + lookAhead -= (rem - (unsigned)(p->buf - p->tempBuf)); + (*srcLen) += lookAhead; + src += lookAhead; + inSize -= lookAhead; + p->tempBufSize = 0; + } + } + if (p->code == 0) + *status = LZMA_STATUS_FINISHED_WITH_MARK; + return (p->code == 0) ? SZ_OK : SZ_ERROR_DATA; +} + +SRes LzmaDec_DecodeToBuf(CLzmaDec *p, Byte *dest, SizeT *destLen, const Byte *src, SizeT *srcLen, ELzmaFinishMode finishMode, ELzmaStatus *status) +{ + SizeT outSize = *destLen; + SizeT inSize = *srcLen; + *srcLen = *destLen = 0; + for (;;) + { + SizeT inSizeCur = inSize, outSizeCur, dicPos; + ELzmaFinishMode curFinishMode; + SRes res; + if (p->dicPos == p->dicBufSize) + p->dicPos = 0; + dicPos = p->dicPos; + if (outSize > p->dicBufSize - dicPos) + { + outSizeCur = p->dicBufSize; + curFinishMode = LZMA_FINISH_ANY; + } + else + { + outSizeCur = dicPos + outSize; + curFinishMode = finishMode; + } + + res = LzmaDec_DecodeToDic(p, outSizeCur, src, &inSizeCur, curFinishMode, status); + src += inSizeCur; + inSize -= inSizeCur; + *srcLen += inSizeCur; + outSizeCur = p->dicPos - dicPos; + memcpy(dest, p->dic + dicPos, outSizeCur); + dest += outSizeCur; + outSize -= outSizeCur; + *destLen += outSizeCur; + if (res != 0) + return res; + if (outSizeCur == 0 || outSize == 0) + return SZ_OK; + } +} + +void LzmaDec_FreeProbs(CLzmaDec *p, ISzAlloc *alloc) +{ + alloc->Free(alloc, p->probs); + p->probs = 0; +} + +static void LzmaDec_FreeDict(CLzmaDec *p, ISzAlloc *alloc) +{ + alloc->Free(alloc, p->dic); + p->dic = 0; +} + +void LzmaDec_Free(CLzmaDec *p, ISzAlloc *alloc) +{ + LzmaDec_FreeProbs(p, alloc); + LzmaDec_FreeDict(p, alloc); +} + +SRes LzmaProps_Decode(CLzmaProps *p, const Byte *data, unsigned size) +{ + UInt32 dicSize; + Byte d; + + if (size < LZMA_PROPS_SIZE) + return SZ_ERROR_UNSUPPORTED; + else + dicSize = data[1] | ((UInt32)data[2] << 8) | ((UInt32)data[3] << 16) | ((UInt32)data[4] << 24); + + if (dicSize < LZMA_DIC_MIN) + dicSize = LZMA_DIC_MIN; + p->dicSize = dicSize; + + d = data[0]; + if (d >= (9 * 5 * 5)) + return SZ_ERROR_UNSUPPORTED; + + p->lc = d % 9; + d /= 9; + p->pb = d / 5; + p->lp = d % 5; + + return SZ_OK; +} + +static SRes LzmaDec_AllocateProbs2(CLzmaDec *p, const CLzmaProps *propNew, ISzAlloc *alloc) +{ + UInt32 numProbs = LzmaProps_GetNumProbs(propNew); + if (p->probs == 0 || numProbs != p->numProbs) + { + LzmaDec_FreeProbs(p, alloc); + p->probs = (CLzmaProb *)alloc->Alloc(alloc, numProbs * sizeof(CLzmaProb)); + p->numProbs = numProbs; + if (p->probs == 0) + return SZ_ERROR_MEM; + } + return SZ_OK; +} + +SRes LzmaDec_AllocateProbs(CLzmaDec *p, const Byte *props, unsigned propsSize, ISzAlloc *alloc) +{ + CLzmaProps propNew; + RINOK(LzmaProps_Decode(&propNew, props, propsSize)); + RINOK(LzmaDec_AllocateProbs2(p, &propNew, alloc)); + p->prop = propNew; + return SZ_OK; +} + +SRes LzmaDec_Allocate(CLzmaDec *p, const Byte *props, unsigned propsSize, ISzAlloc *alloc) +{ + CLzmaProps propNew; + SizeT dicBufSize; + RINOK(LzmaProps_Decode(&propNew, props, propsSize)); + RINOK(LzmaDec_AllocateProbs2(p, &propNew, alloc)); + dicBufSize = propNew.dicSize; + if (p->dic == 0 || dicBufSize != p->dicBufSize) + { + LzmaDec_FreeDict(p, alloc); + p->dic = (Byte *)alloc->Alloc(alloc, dicBufSize); + if (p->dic == 0) + { + LzmaDec_FreeProbs(p, alloc); + return SZ_ERROR_MEM; + } + } + p->dicBufSize = dicBufSize; + p->prop = propNew; + return SZ_OK; +} + +SRes LzmaDecode(Byte *dest, SizeT *destLen, const Byte *src, SizeT *srcLen, + const Byte *propData, unsigned propSize, ELzmaFinishMode finishMode, + ELzmaStatus *status, ISzAlloc *alloc) +{ + CLzmaDec p; + SRes res; + SizeT inSize = *srcLen; + SizeT outSize = *destLen; + *srcLen = *destLen = 0; + if (inSize < RC_INIT_SIZE) + return SZ_ERROR_INPUT_EOF; + + LzmaDec_Construct(&p); + res = LzmaDec_AllocateProbs(&p, propData, propSize, alloc); + if (res != 0) + return res; + p.dic = dest; + p.dicBufSize = outSize; + + LzmaDec_Init(&p); + + *srcLen = inSize; + res = LzmaDec_DecodeToDic(&p, outSize, src, srcLen, finishMode, status); + + if (res == SZ_OK && *status == LZMA_STATUS_NEEDS_MORE_INPUT) + res = SZ_ERROR_INPUT_EOF; + + (*destLen) = p.dicPos; + LzmaDec_FreeProbs(&p, alloc); + return res; +} diff --git a/wiipax/client/LzmaDec.h b/wiipax/client/LzmaDec.h new file mode 100644 index 0000000..ad7d705 --- /dev/null +++ b/wiipax/client/LzmaDec.h @@ -0,0 +1,223 @@ +/* LzmaDec.h -- LZMA Decoder +2008-10-04 : Igor Pavlov : Public domain */ + +#ifndef __LZMADEC_H +#define __LZMADEC_H + +#include "Types.h" + +/* #define _LZMA_PROB32 */ +/* _LZMA_PROB32 can increase the speed on some CPUs, + but memory usage for CLzmaDec::probs will be doubled in that case */ + +#ifdef _LZMA_PROB32 +#define CLzmaProb UInt32 +#else +#define CLzmaProb UInt16 +#endif + + +/* ---------- LZMA Properties ---------- */ + +#define LZMA_PROPS_SIZE 5 + +typedef struct _CLzmaProps +{ + unsigned lc, lp, pb; + UInt32 dicSize; +} CLzmaProps; + +/* LzmaProps_Decode - decodes properties +Returns: + SZ_OK + SZ_ERROR_UNSUPPORTED - Unsupported properties +*/ + +SRes LzmaProps_Decode(CLzmaProps *p, const Byte *data, unsigned size); + + +/* ---------- LZMA Decoder state ---------- */ + +/* LZMA_REQUIRED_INPUT_MAX = number of required input bytes for worst case. + Num bits = log2((2^11 / 31) ^ 22) + 26 < 134 + 26 = 160; */ + +#define LZMA_REQUIRED_INPUT_MAX 20 + +typedef struct +{ + CLzmaProps prop; + CLzmaProb *probs; + Byte *dic; + const Byte *buf; + UInt32 range, code; + SizeT dicPos; + SizeT dicBufSize; + UInt32 processedPos; + UInt32 checkDicSize; + unsigned state; + UInt32 reps[4]; + unsigned remainLen; + int needFlush; + int needInitState; + UInt32 numProbs; + unsigned tempBufSize; + Byte tempBuf[LZMA_REQUIRED_INPUT_MAX]; +} CLzmaDec; + +#define LzmaDec_Construct(p) { (p)->dic = 0; (p)->probs = 0; } + +void LzmaDec_Init(CLzmaDec *p); + +/* There are two types of LZMA streams: + 0) Stream with end mark. That end mark adds about 6 bytes to compressed size. + 1) Stream without end mark. You must know exact uncompressed size to decompress such stream. */ + +typedef enum +{ + LZMA_FINISH_ANY, /* finish at any point */ + LZMA_FINISH_END /* block must be finished at the end */ +} ELzmaFinishMode; + +/* ELzmaFinishMode has meaning only if the decoding reaches output limit !!! + + You must use LZMA_FINISH_END, when you know that current output buffer + covers last bytes of block. In other cases you must use LZMA_FINISH_ANY. + + If LZMA decoder sees end marker before reaching output limit, it returns SZ_OK, + and output value of destLen will be less than output buffer size limit. + You can check status result also. + + You can use multiple checks to test data integrity after full decompression: + 1) Check Result and "status" variable. + 2) Check that output(destLen) = uncompressedSize, if you know real uncompressedSize. + 3) Check that output(srcLen) = compressedSize, if you know real compressedSize. + You must use correct finish mode in that case. */ + +typedef enum +{ + LZMA_STATUS_NOT_SPECIFIED, /* use main error code instead */ + LZMA_STATUS_FINISHED_WITH_MARK, /* stream was finished with end mark. */ + LZMA_STATUS_NOT_FINISHED, /* stream was not finished */ + LZMA_STATUS_NEEDS_MORE_INPUT, /* you must provide more input bytes */ + LZMA_STATUS_MAYBE_FINISHED_WITHOUT_MARK /* there is probability that stream was finished without end mark */ +} ELzmaStatus; + +/* ELzmaStatus is used only as output value for function call */ + + +/* ---------- Interfaces ---------- */ + +/* There are 3 levels of interfaces: + 1) Dictionary Interface + 2) Buffer Interface + 3) One Call Interface + You can select any of these interfaces, but don't mix functions from different + groups for same object. */ + + +/* There are two variants to allocate state for Dictionary Interface: + 1) LzmaDec_Allocate / LzmaDec_Free + 2) LzmaDec_AllocateProbs / LzmaDec_FreeProbs + You can use variant 2, if you set dictionary buffer manually. + For Buffer Interface you must always use variant 1. + +LzmaDec_Allocate* can return: + SZ_OK + SZ_ERROR_MEM - Memory allocation error + SZ_ERROR_UNSUPPORTED - Unsupported properties +*/ + +SRes LzmaDec_AllocateProbs(CLzmaDec *p, const Byte *props, unsigned propsSize, ISzAlloc *alloc); +void LzmaDec_FreeProbs(CLzmaDec *p, ISzAlloc *alloc); + +SRes LzmaDec_Allocate(CLzmaDec *state, const Byte *prop, unsigned propsSize, ISzAlloc *alloc); +void LzmaDec_Free(CLzmaDec *state, ISzAlloc *alloc); + +/* ---------- Dictionary Interface ---------- */ + +/* You can use it, if you want to eliminate the overhead for data copying from + dictionary to some other external buffer. + You must work with CLzmaDec variables directly in this interface. + + STEPS: + LzmaDec_Constr() + LzmaDec_Allocate() + for (each new stream) + { + LzmaDec_Init() + while (it needs more decompression) + { + LzmaDec_DecodeToDic() + use data from CLzmaDec::dic and update CLzmaDec::dicPos + } + } + LzmaDec_Free() +*/ + +/* LzmaDec_DecodeToDic + + The decoding to internal dictionary buffer (CLzmaDec::dic). + You must manually update CLzmaDec::dicPos, if it reaches CLzmaDec::dicBufSize !!! + +finishMode: + It has meaning only if the decoding reaches output limit (dicLimit). + LZMA_FINISH_ANY - Decode just dicLimit bytes. + LZMA_FINISH_END - Stream must be finished after dicLimit. + +Returns: + SZ_OK + status: + LZMA_STATUS_FINISHED_WITH_MARK + LZMA_STATUS_NOT_FINISHED + LZMA_STATUS_NEEDS_MORE_INPUT + LZMA_STATUS_MAYBE_FINISHED_WITHOUT_MARK + SZ_ERROR_DATA - Data error +*/ + +SRes LzmaDec_DecodeToDic(CLzmaDec *p, SizeT dicLimit, + const Byte *src, SizeT *srcLen, ELzmaFinishMode finishMode, ELzmaStatus *status); + + +/* ---------- Buffer Interface ---------- */ + +/* It's zlib-like interface. + See LzmaDec_DecodeToDic description for information about STEPS and return results, + but you must use LzmaDec_DecodeToBuf instead of LzmaDec_DecodeToDic and you don't need + to work with CLzmaDec variables manually. + +finishMode: + It has meaning only if the decoding reaches output limit (*destLen). + LZMA_FINISH_ANY - Decode just destLen bytes. + LZMA_FINISH_END - Stream must be finished after (*destLen). +*/ + +SRes LzmaDec_DecodeToBuf(CLzmaDec *p, Byte *dest, SizeT *destLen, + const Byte *src, SizeT *srcLen, ELzmaFinishMode finishMode, ELzmaStatus *status); + + +/* ---------- One Call Interface ---------- */ + +/* LzmaDecode + +finishMode: + It has meaning only if the decoding reaches output limit (*destLen). + LZMA_FINISH_ANY - Decode just destLen bytes. + LZMA_FINISH_END - Stream must be finished after (*destLen). + +Returns: + SZ_OK + status: + LZMA_STATUS_FINISHED_WITH_MARK + LZMA_STATUS_NOT_FINISHED + LZMA_STATUS_MAYBE_FINISHED_WITHOUT_MARK + SZ_ERROR_DATA - Data error + SZ_ERROR_MEM - Memory allocation error + SZ_ERROR_UNSUPPORTED - Unsupported properties + SZ_ERROR_INPUT_EOF - It needs more bytes in input buffer (src). +*/ + +SRes LzmaDecode(Byte *dest, SizeT *destLen, const Byte *src, SizeT *srcLen, + const Byte *propData, unsigned propSize, ELzmaFinishMode finishMode, + ELzmaStatus *status, ISzAlloc *alloc); + +#endif diff --git a/wiipax/client/LzmaEnc.c b/wiipax/client/LzmaEnc.c new file mode 100644 index 0000000..529fd98 --- /dev/null +++ b/wiipax/client/LzmaEnc.c @@ -0,0 +1,2281 @@ +/* LzmaEnc.c -- LZMA Encoder +2009-02-02 : Igor Pavlov : Public domain */ + +#include + +/* #define SHOW_STAT */ +/* #define SHOW_STAT2 */ + +#if defined(SHOW_STAT) || defined(SHOW_STAT2) +#include +#endif + +#include "LzmaEnc.h" + +#include "LzFind.h" +#ifdef COMPRESS_MF_MT +#include "LzFindMt.h" +#endif + +#ifdef SHOW_STAT +static int ttt = 0; +#endif + +#define kBlockSizeMax ((1 << LZMA_NUM_BLOCK_SIZE_BITS) - 1) + +#define kBlockSize (9 << 10) +#define kUnpackBlockSize (1 << 18) +#define kMatchArraySize (1 << 21) +#define kMatchRecordMaxSize ((LZMA_MATCH_LEN_MAX * 2 + 3) * LZMA_MATCH_LEN_MAX) + +#define kNumMaxDirectBits (31) + +#define kNumTopBits 24 +#define kTopValue ((UInt32)1 << kNumTopBits) + +#define kNumBitModelTotalBits 11 +#define kBitModelTotal (1 << kNumBitModelTotalBits) +#define kNumMoveBits 5 +#define kProbInitValue (kBitModelTotal >> 1) + +#define kNumMoveReducingBits 4 +#define kNumBitPriceShiftBits 4 +#define kBitPrice (1 << kNumBitPriceShiftBits) + +void LzmaEncProps_Init(CLzmaEncProps *p) +{ + p->level = 5; + p->dictSize = p->mc = 0; + p->lc = p->lp = p->pb = p->algo = p->fb = p->btMode = p->numHashBytes = p->numThreads = -1; + p->writeEndMark = 0; +} + +void LzmaEncProps_Normalize(CLzmaEncProps *p) +{ + int level = p->level; + if (level < 0) level = 5; + p->level = level; + if (p->dictSize == 0) p->dictSize = (level <= 5 ? (1 << (level * 2 + 14)) : (level == 6 ? (1 << 25) : (1 << 26))); + if (p->lc < 0) p->lc = 3; + if (p->lp < 0) p->lp = 0; + if (p->pb < 0) p->pb = 2; + if (p->algo < 0) p->algo = (level < 5 ? 0 : 1); + if (p->fb < 0) p->fb = (level < 7 ? 32 : 64); + if (p->btMode < 0) p->btMode = (p->algo == 0 ? 0 : 1); + if (p->numHashBytes < 0) p->numHashBytes = 4; + if (p->mc == 0) p->mc = (16 + (p->fb >> 1)) >> (p->btMode ? 0 : 1); + if (p->numThreads < 0) + p->numThreads = + #ifdef COMPRESS_MF_MT + ((p->btMode && p->algo) ? 2 : 1); + #else + 1; + #endif +} + +UInt32 LzmaEncProps_GetDictSize(const CLzmaEncProps *props2) +{ + CLzmaEncProps props = *props2; + LzmaEncProps_Normalize(&props); + return props.dictSize; +} + +/* #define LZMA_LOG_BSR */ +/* Define it for Intel's CPU */ + + +#ifdef LZMA_LOG_BSR + +#define kDicLogSizeMaxCompress 30 + +#define BSR2_RET(pos, res) { unsigned long i; _BitScanReverse(&i, (pos)); res = (i + i) + ((pos >> (i - 1)) & 1); } + +UInt32 GetPosSlot1(UInt32 pos) +{ + UInt32 res; + BSR2_RET(pos, res); + return res; +} +#define GetPosSlot2(pos, res) { BSR2_RET(pos, res); } +#define GetPosSlot(pos, res) { if (pos < 2) res = pos; else BSR2_RET(pos, res); } + +#else + +#define kNumLogBits (9 + (int)sizeof(size_t) / 2) +#define kDicLogSizeMaxCompress ((kNumLogBits - 1) * 2 + 7) + +void LzmaEnc_FastPosInit(Byte *g_FastPos) +{ + int c = 2, slotFast; + g_FastPos[0] = 0; + g_FastPos[1] = 1; + + for (slotFast = 2; slotFast < kNumLogBits * 2; slotFast++) + { + UInt32 k = (1 << ((slotFast >> 1) - 1)); + UInt32 j; + for (j = 0; j < k; j++, c++) + g_FastPos[c] = (Byte)slotFast; + } +} + +#define BSR2_RET(pos, res) { UInt32 i = 6 + ((kNumLogBits - 1) & \ + (0 - (((((UInt32)1 << (kNumLogBits + 6)) - 1) - pos) >> 31))); \ + res = p->g_FastPos[pos >> i] + (i * 2); } +/* +#define BSR2_RET(pos, res) { res = (pos < (1 << (kNumLogBits + 6))) ? \ + p->g_FastPos[pos >> 6] + 12 : \ + p->g_FastPos[pos >> (6 + kNumLogBits - 1)] + (6 + (kNumLogBits - 1)) * 2; } +*/ + +#define GetPosSlot1(pos) p->g_FastPos[pos] +#define GetPosSlot2(pos, res) { BSR2_RET(pos, res); } +#define GetPosSlot(pos, res) { if (pos < kNumFullDistances) res = p->g_FastPos[pos]; else BSR2_RET(pos, res); } + +#endif + + +#define LZMA_NUM_REPS 4 + +typedef unsigned CState; + +typedef struct _COptimal +{ + UInt32 price; + + CState state; + int prev1IsChar; + int prev2; + + UInt32 posPrev2; + UInt32 backPrev2; + + UInt32 posPrev; + UInt32 backPrev; + UInt32 backs[LZMA_NUM_REPS]; +} COptimal; + +#define kNumOpts (1 << 12) + +#define kNumLenToPosStates 4 +#define kNumPosSlotBits 6 +#define kDicLogSizeMin 0 +#define kDicLogSizeMax 32 +#define kDistTableSizeMax (kDicLogSizeMax * 2) + + +#define kNumAlignBits 4 +#define kAlignTableSize (1 << kNumAlignBits) +#define kAlignMask (kAlignTableSize - 1) + +#define kStartPosModelIndex 4 +#define kEndPosModelIndex 14 +#define kNumPosModels (kEndPosModelIndex - kStartPosModelIndex) + +#define kNumFullDistances (1 << (kEndPosModelIndex / 2)) + +#ifdef _LZMA_PROB32 +#define CLzmaProb UInt32 +#else +#define CLzmaProb UInt16 +#endif + +#define LZMA_PB_MAX 4 +#define LZMA_LC_MAX 8 +#define LZMA_LP_MAX 4 + +#define LZMA_NUM_PB_STATES_MAX (1 << LZMA_PB_MAX) + + +#define kLenNumLowBits 3 +#define kLenNumLowSymbols (1 << kLenNumLowBits) +#define kLenNumMidBits 3 +#define kLenNumMidSymbols (1 << kLenNumMidBits) +#define kLenNumHighBits 8 +#define kLenNumHighSymbols (1 << kLenNumHighBits) + +#define kLenNumSymbolsTotal (kLenNumLowSymbols + kLenNumMidSymbols + kLenNumHighSymbols) + +#define LZMA_MATCH_LEN_MIN 2 +#define LZMA_MATCH_LEN_MAX (LZMA_MATCH_LEN_MIN + kLenNumSymbolsTotal - 1) + +#define kNumStates 12 + +typedef struct +{ + CLzmaProb choice; + CLzmaProb choice2; + CLzmaProb low[LZMA_NUM_PB_STATES_MAX << kLenNumLowBits]; + CLzmaProb mid[LZMA_NUM_PB_STATES_MAX << kLenNumMidBits]; + CLzmaProb high[kLenNumHighSymbols]; +} CLenEnc; + +typedef struct +{ + CLenEnc p; + UInt32 prices[LZMA_NUM_PB_STATES_MAX][kLenNumSymbolsTotal]; + UInt32 tableSize; + UInt32 counters[LZMA_NUM_PB_STATES_MAX]; +} CLenPriceEnc; + +typedef struct _CRangeEnc +{ + UInt32 range; + Byte cache; + UInt64 low; + UInt64 cacheSize; + Byte *buf; + Byte *bufLim; + Byte *bufBase; + ISeqOutStream *outStream; + UInt64 processed; + SRes res; +} CRangeEnc; + +typedef struct _CSeqInStreamBuf +{ + ISeqInStream funcTable; + const Byte *data; + SizeT rem; +} CSeqInStreamBuf; + +static SRes MyRead(void *pp, void *data, size_t *size) +{ + size_t curSize = *size; + CSeqInStreamBuf *p = (CSeqInStreamBuf *)pp; + if (p->rem < curSize) + curSize = p->rem; + memcpy(data, p->data, curSize); + p->rem -= curSize; + p->data += curSize; + *size = curSize; + return SZ_OK; +} + +typedef struct +{ + CLzmaProb *litProbs; + + CLzmaProb isMatch[kNumStates][LZMA_NUM_PB_STATES_MAX]; + CLzmaProb isRep[kNumStates]; + CLzmaProb isRepG0[kNumStates]; + CLzmaProb isRepG1[kNumStates]; + CLzmaProb isRepG2[kNumStates]; + CLzmaProb isRep0Long[kNumStates][LZMA_NUM_PB_STATES_MAX]; + + CLzmaProb posSlotEncoder[kNumLenToPosStates][1 << kNumPosSlotBits]; + CLzmaProb posEncoders[kNumFullDistances - kEndPosModelIndex]; + CLzmaProb posAlignEncoder[1 << kNumAlignBits]; + + CLenPriceEnc lenEnc; + CLenPriceEnc repLenEnc; + + UInt32 reps[LZMA_NUM_REPS]; + UInt32 state; +} CSaveState; + +typedef struct _CLzmaEnc +{ + IMatchFinder matchFinder; + void *matchFinderObj; + + #ifdef COMPRESS_MF_MT + Bool mtMode; + CMatchFinderMt matchFinderMt; + #endif + + CMatchFinder matchFinderBase; + + #ifdef COMPRESS_MF_MT + Byte pad[128]; + #endif + + UInt32 optimumEndIndex; + UInt32 optimumCurrentIndex; + + UInt32 longestMatchLength; + UInt32 numPairs; + UInt32 numAvail; + COptimal opt[kNumOpts]; + + #ifndef LZMA_LOG_BSR + Byte g_FastPos[1 << kNumLogBits]; + #endif + + UInt32 ProbPrices[kBitModelTotal >> kNumMoveReducingBits]; + UInt32 matches[LZMA_MATCH_LEN_MAX * 2 + 2 + 1]; + UInt32 numFastBytes; + UInt32 additionalOffset; + UInt32 reps[LZMA_NUM_REPS]; + UInt32 state; + + UInt32 posSlotPrices[kNumLenToPosStates][kDistTableSizeMax]; + UInt32 distancesPrices[kNumLenToPosStates][kNumFullDistances]; + UInt32 alignPrices[kAlignTableSize]; + UInt32 alignPriceCount; + + UInt32 distTableSize; + + unsigned lc, lp, pb; + unsigned lpMask, pbMask; + + CLzmaProb *litProbs; + + CLzmaProb isMatch[kNumStates][LZMA_NUM_PB_STATES_MAX]; + CLzmaProb isRep[kNumStates]; + CLzmaProb isRepG0[kNumStates]; + CLzmaProb isRepG1[kNumStates]; + CLzmaProb isRepG2[kNumStates]; + CLzmaProb isRep0Long[kNumStates][LZMA_NUM_PB_STATES_MAX]; + + CLzmaProb posSlotEncoder[kNumLenToPosStates][1 << kNumPosSlotBits]; + CLzmaProb posEncoders[kNumFullDistances - kEndPosModelIndex]; + CLzmaProb posAlignEncoder[1 << kNumAlignBits]; + + CLenPriceEnc lenEnc; + CLenPriceEnc repLenEnc; + + unsigned lclp; + + Bool fastMode; + + CRangeEnc rc; + + Bool writeEndMark; + UInt64 nowPos64; + UInt32 matchPriceCount; + Bool finished; + Bool multiThread; + + SRes result; + UInt32 dictSize; + UInt32 matchFinderCycles; + + ISeqInStream *inStream; + CSeqInStreamBuf seqBufInStream; + + CSaveState saveState; +} CLzmaEnc; + +void LzmaEnc_SaveState(CLzmaEncHandle pp) +{ + CLzmaEnc *p = (CLzmaEnc *)pp; + CSaveState *dest = &p->saveState; + int i; + dest->lenEnc = p->lenEnc; + dest->repLenEnc = p->repLenEnc; + dest->state = p->state; + + for (i = 0; i < kNumStates; i++) + { + memcpy(dest->isMatch[i], p->isMatch[i], sizeof(p->isMatch[i])); + memcpy(dest->isRep0Long[i], p->isRep0Long[i], sizeof(p->isRep0Long[i])); + } + for (i = 0; i < kNumLenToPosStates; i++) + memcpy(dest->posSlotEncoder[i], p->posSlotEncoder[i], sizeof(p->posSlotEncoder[i])); + memcpy(dest->isRep, p->isRep, sizeof(p->isRep)); + memcpy(dest->isRepG0, p->isRepG0, sizeof(p->isRepG0)); + memcpy(dest->isRepG1, p->isRepG1, sizeof(p->isRepG1)); + memcpy(dest->isRepG2, p->isRepG2, sizeof(p->isRepG2)); + memcpy(dest->posEncoders, p->posEncoders, sizeof(p->posEncoders)); + memcpy(dest->posAlignEncoder, p->posAlignEncoder, sizeof(p->posAlignEncoder)); + memcpy(dest->reps, p->reps, sizeof(p->reps)); + memcpy(dest->litProbs, p->litProbs, (0x300 << p->lclp) * sizeof(CLzmaProb)); +} + +void LzmaEnc_RestoreState(CLzmaEncHandle pp) +{ + CLzmaEnc *dest = (CLzmaEnc *)pp; + const CSaveState *p = &dest->saveState; + int i; + dest->lenEnc = p->lenEnc; + dest->repLenEnc = p->repLenEnc; + dest->state = p->state; + + for (i = 0; i < kNumStates; i++) + { + memcpy(dest->isMatch[i], p->isMatch[i], sizeof(p->isMatch[i])); + memcpy(dest->isRep0Long[i], p->isRep0Long[i], sizeof(p->isRep0Long[i])); + } + for (i = 0; i < kNumLenToPosStates; i++) + memcpy(dest->posSlotEncoder[i], p->posSlotEncoder[i], sizeof(p->posSlotEncoder[i])); + memcpy(dest->isRep, p->isRep, sizeof(p->isRep)); + memcpy(dest->isRepG0, p->isRepG0, sizeof(p->isRepG0)); + memcpy(dest->isRepG1, p->isRepG1, sizeof(p->isRepG1)); + memcpy(dest->isRepG2, p->isRepG2, sizeof(p->isRepG2)); + memcpy(dest->posEncoders, p->posEncoders, sizeof(p->posEncoders)); + memcpy(dest->posAlignEncoder, p->posAlignEncoder, sizeof(p->posAlignEncoder)); + memcpy(dest->reps, p->reps, sizeof(p->reps)); + memcpy(dest->litProbs, p->litProbs, (0x300 << dest->lclp) * sizeof(CLzmaProb)); +} + +SRes LzmaEnc_SetProps(CLzmaEncHandle pp, const CLzmaEncProps *props2) +{ + CLzmaEnc *p = (CLzmaEnc *)pp; + CLzmaEncProps props = *props2; + LzmaEncProps_Normalize(&props); + + if (props.lc > LZMA_LC_MAX || props.lp > LZMA_LP_MAX || props.pb > LZMA_PB_MAX || + props.dictSize > (1 << kDicLogSizeMaxCompress) || props.dictSize > (1 << 30)) + return SZ_ERROR_PARAM; + p->dictSize = props.dictSize; + p->matchFinderCycles = props.mc; + { + unsigned fb = props.fb; + if (fb < 5) + fb = 5; + if (fb > LZMA_MATCH_LEN_MAX) + fb = LZMA_MATCH_LEN_MAX; + p->numFastBytes = fb; + } + p->lc = props.lc; + p->lp = props.lp; + p->pb = props.pb; + p->fastMode = (props.algo == 0); + p->matchFinderBase.btMode = props.btMode; + { + UInt32 numHashBytes = 4; + if (props.btMode) + { + if (props.numHashBytes < 2) + numHashBytes = 2; + else if (props.numHashBytes < 4) + numHashBytes = props.numHashBytes; + } + p->matchFinderBase.numHashBytes = numHashBytes; + } + + p->matchFinderBase.cutValue = props.mc; + + p->writeEndMark = props.writeEndMark; + + #ifdef COMPRESS_MF_MT + /* + if (newMultiThread != _multiThread) + { + ReleaseMatchFinder(); + _multiThread = newMultiThread; + } + */ + p->multiThread = (props.numThreads > 1); + #endif + + return SZ_OK; +} + +static const int kLiteralNextStates[kNumStates] = {0, 0, 0, 0, 1, 2, 3, 4, 5, 6, 4, 5}; +static const int kMatchNextStates[kNumStates] = {7, 7, 7, 7, 7, 7, 7, 10, 10, 10, 10, 10}; +static const int kRepNextStates[kNumStates] = {8, 8, 8, 8, 8, 8, 8, 11, 11, 11, 11, 11}; +static const int kShortRepNextStates[kNumStates]= {9, 9, 9, 9, 9, 9, 9, 11, 11, 11, 11, 11}; + +#define IsCharState(s) ((s) < 7) + +#define GetLenToPosState(len) (((len) < kNumLenToPosStates + 1) ? (len) - 2 : kNumLenToPosStates - 1) + +#define kInfinityPrice (1 << 30) + +static void RangeEnc_Construct(CRangeEnc *p) +{ + p->outStream = 0; + p->bufBase = 0; +} + +#define RangeEnc_GetProcessed(p) ((p)->processed + ((p)->buf - (p)->bufBase) + (p)->cacheSize) + +#define RC_BUF_SIZE (1 << 16) +static int RangeEnc_Alloc(CRangeEnc *p, ISzAlloc *alloc) +{ + if (p->bufBase == 0) + { + p->bufBase = (Byte *)alloc->Alloc(alloc, RC_BUF_SIZE); + if (p->bufBase == 0) + return 0; + p->bufLim = p->bufBase + RC_BUF_SIZE; + } + return 1; +} + +static void RangeEnc_Free(CRangeEnc *p, ISzAlloc *alloc) +{ + alloc->Free(alloc, p->bufBase); + p->bufBase = 0; +} + +static void RangeEnc_Init(CRangeEnc *p) +{ + /* Stream.Init(); */ + p->low = 0; + p->range = 0xFFFFFFFF; + p->cacheSize = 1; + p->cache = 0; + + p->buf = p->bufBase; + + p->processed = 0; + p->res = SZ_OK; +} + +static void RangeEnc_FlushStream(CRangeEnc *p) +{ + size_t num; + if (p->res != SZ_OK) + return; + num = p->buf - p->bufBase; + if (num != p->outStream->Write(p->outStream, p->bufBase, num)) + p->res = SZ_ERROR_WRITE; + p->processed += num; + p->buf = p->bufBase; +} + +static void MY_FAST_CALL RangeEnc_ShiftLow(CRangeEnc *p) +{ + if ((UInt32)p->low < (UInt32)0xFF000000 || (int)(p->low >> 32) != 0) + { + Byte temp = p->cache; + do + { + Byte *buf = p->buf; + *buf++ = (Byte)(temp + (Byte)(p->low >> 32)); + p->buf = buf; + if (buf == p->bufLim) + RangeEnc_FlushStream(p); + temp = 0xFF; + } + while (--p->cacheSize != 0); + p->cache = (Byte)((UInt32)p->low >> 24); + } + p->cacheSize++; + p->low = (UInt32)p->low << 8; +} + +static void RangeEnc_FlushData(CRangeEnc *p) +{ + int i; + for (i = 0; i < 5; i++) + RangeEnc_ShiftLow(p); +} + +static void RangeEnc_EncodeDirectBits(CRangeEnc *p, UInt32 value, int numBits) +{ + do + { + p->range >>= 1; + p->low += p->range & (0 - ((value >> --numBits) & 1)); + if (p->range < kTopValue) + { + p->range <<= 8; + RangeEnc_ShiftLow(p); + } + } + while (numBits != 0); +} + +static void RangeEnc_EncodeBit(CRangeEnc *p, CLzmaProb *prob, UInt32 symbol) +{ + UInt32 ttt = *prob; + UInt32 newBound = (p->range >> kNumBitModelTotalBits) * ttt; + if (symbol == 0) + { + p->range = newBound; + ttt += (kBitModelTotal - ttt) >> kNumMoveBits; + } + else + { + p->low += newBound; + p->range -= newBound; + ttt -= ttt >> kNumMoveBits; + } + *prob = (CLzmaProb)ttt; + if (p->range < kTopValue) + { + p->range <<= 8; + RangeEnc_ShiftLow(p); + } +} + +static void LitEnc_Encode(CRangeEnc *p, CLzmaProb *probs, UInt32 symbol) +{ + symbol |= 0x100; + do + { + RangeEnc_EncodeBit(p, probs + (symbol >> 8), (symbol >> 7) & 1); + symbol <<= 1; + } + while (symbol < 0x10000); +} + +static void LitEnc_EncodeMatched(CRangeEnc *p, CLzmaProb *probs, UInt32 symbol, UInt32 matchByte) +{ + UInt32 offs = 0x100; + symbol |= 0x100; + do + { + matchByte <<= 1; + RangeEnc_EncodeBit(p, probs + (offs + (matchByte & offs) + (symbol >> 8)), (symbol >> 7) & 1); + symbol <<= 1; + offs &= ~(matchByte ^ symbol); + } + while (symbol < 0x10000); +} + +void LzmaEnc_InitPriceTables(UInt32 *ProbPrices) +{ + UInt32 i; + for (i = (1 << kNumMoveReducingBits) / 2; i < kBitModelTotal; i += (1 << kNumMoveReducingBits)) + { + const int kCyclesBits = kNumBitPriceShiftBits; + UInt32 w = i; + UInt32 bitCount = 0; + int j; + for (j = 0; j < kCyclesBits; j++) + { + w = w * w; + bitCount <<= 1; + while (w >= ((UInt32)1 << 16)) + { + w >>= 1; + bitCount++; + } + } + ProbPrices[i >> kNumMoveReducingBits] = ((kNumBitModelTotalBits << kCyclesBits) - 15 - bitCount); + } +} + + +#define GET_PRICE(prob, symbol) \ + p->ProbPrices[((prob) ^ (((-(int)(symbol))) & (kBitModelTotal - 1))) >> kNumMoveReducingBits]; + +#define GET_PRICEa(prob, symbol) \ + ProbPrices[((prob) ^ ((-((int)(symbol))) & (kBitModelTotal - 1))) >> kNumMoveReducingBits]; + +#define GET_PRICE_0(prob) p->ProbPrices[(prob) >> kNumMoveReducingBits] +#define GET_PRICE_1(prob) p->ProbPrices[((prob) ^ (kBitModelTotal - 1)) >> kNumMoveReducingBits] + +#define GET_PRICE_0a(prob) ProbPrices[(prob) >> kNumMoveReducingBits] +#define GET_PRICE_1a(prob) ProbPrices[((prob) ^ (kBitModelTotal - 1)) >> kNumMoveReducingBits] + +static UInt32 LitEnc_GetPrice(const CLzmaProb *probs, UInt32 symbol, UInt32 *ProbPrices) +{ + UInt32 price = 0; + symbol |= 0x100; + do + { + price += GET_PRICEa(probs[symbol >> 8], (symbol >> 7) & 1); + symbol <<= 1; + } + while (symbol < 0x10000); + return price; +} + +static UInt32 LitEnc_GetPriceMatched(const CLzmaProb *probs, UInt32 symbol, UInt32 matchByte, UInt32 *ProbPrices) +{ + UInt32 price = 0; + UInt32 offs = 0x100; + symbol |= 0x100; + do + { + matchByte <<= 1; + price += GET_PRICEa(probs[offs + (matchByte & offs) + (symbol >> 8)], (symbol >> 7) & 1); + symbol <<= 1; + offs &= ~(matchByte ^ symbol); + } + while (symbol < 0x10000); + return price; +} + + +static void RcTree_Encode(CRangeEnc *rc, CLzmaProb *probs, int numBitLevels, UInt32 symbol) +{ + UInt32 m = 1; + int i; + for (i = numBitLevels; i != 0;) + { + UInt32 bit; + i--; + bit = (symbol >> i) & 1; + RangeEnc_EncodeBit(rc, probs + m, bit); + m = (m << 1) | bit; + } +} + +static void RcTree_ReverseEncode(CRangeEnc *rc, CLzmaProb *probs, int numBitLevels, UInt32 symbol) +{ + UInt32 m = 1; + int i; + for (i = 0; i < numBitLevels; i++) + { + UInt32 bit = symbol & 1; + RangeEnc_EncodeBit(rc, probs + m, bit); + m = (m << 1) | bit; + symbol >>= 1; + } +} + +static UInt32 RcTree_GetPrice(const CLzmaProb *probs, int numBitLevels, UInt32 symbol, UInt32 *ProbPrices) +{ + UInt32 price = 0; + symbol |= (1 << numBitLevels); + while (symbol != 1) + { + price += GET_PRICEa(probs[symbol >> 1], symbol & 1); + symbol >>= 1; + } + return price; +} + +static UInt32 RcTree_ReverseGetPrice(const CLzmaProb *probs, int numBitLevels, UInt32 symbol, UInt32 *ProbPrices) +{ + UInt32 price = 0; + UInt32 m = 1; + int i; + for (i = numBitLevels; i != 0; i--) + { + UInt32 bit = symbol & 1; + symbol >>= 1; + price += GET_PRICEa(probs[m], bit); + m = (m << 1) | bit; + } + return price; +} + + +static void LenEnc_Init(CLenEnc *p) +{ + unsigned i; + p->choice = p->choice2 = kProbInitValue; + for (i = 0; i < (LZMA_NUM_PB_STATES_MAX << kLenNumLowBits); i++) + p->low[i] = kProbInitValue; + for (i = 0; i < (LZMA_NUM_PB_STATES_MAX << kLenNumMidBits); i++) + p->mid[i] = kProbInitValue; + for (i = 0; i < kLenNumHighSymbols; i++) + p->high[i] = kProbInitValue; +} + +static void LenEnc_Encode(CLenEnc *p, CRangeEnc *rc, UInt32 symbol, UInt32 posState) +{ + if (symbol < kLenNumLowSymbols) + { + RangeEnc_EncodeBit(rc, &p->choice, 0); + RcTree_Encode(rc, p->low + (posState << kLenNumLowBits), kLenNumLowBits, symbol); + } + else + { + RangeEnc_EncodeBit(rc, &p->choice, 1); + if (symbol < kLenNumLowSymbols + kLenNumMidSymbols) + { + RangeEnc_EncodeBit(rc, &p->choice2, 0); + RcTree_Encode(rc, p->mid + (posState << kLenNumMidBits), kLenNumMidBits, symbol - kLenNumLowSymbols); + } + else + { + RangeEnc_EncodeBit(rc, &p->choice2, 1); + RcTree_Encode(rc, p->high, kLenNumHighBits, symbol - kLenNumLowSymbols - kLenNumMidSymbols); + } + } +} + +static void LenEnc_SetPrices(CLenEnc *p, UInt32 posState, UInt32 numSymbols, UInt32 *prices, UInt32 *ProbPrices) +{ + UInt32 a0 = GET_PRICE_0a(p->choice); + UInt32 a1 = GET_PRICE_1a(p->choice); + UInt32 b0 = a1 + GET_PRICE_0a(p->choice2); + UInt32 b1 = a1 + GET_PRICE_1a(p->choice2); + UInt32 i = 0; + for (i = 0; i < kLenNumLowSymbols; i++) + { + if (i >= numSymbols) + return; + prices[i] = a0 + RcTree_GetPrice(p->low + (posState << kLenNumLowBits), kLenNumLowBits, i, ProbPrices); + } + for (; i < kLenNumLowSymbols + kLenNumMidSymbols; i++) + { + if (i >= numSymbols) + return; + prices[i] = b0 + RcTree_GetPrice(p->mid + (posState << kLenNumMidBits), kLenNumMidBits, i - kLenNumLowSymbols, ProbPrices); + } + for (; i < numSymbols; i++) + prices[i] = b1 + RcTree_GetPrice(p->high, kLenNumHighBits, i - kLenNumLowSymbols - kLenNumMidSymbols, ProbPrices); +} + +static void MY_FAST_CALL LenPriceEnc_UpdateTable(CLenPriceEnc *p, UInt32 posState, UInt32 *ProbPrices) +{ + LenEnc_SetPrices(&p->p, posState, p->tableSize, p->prices[posState], ProbPrices); + p->counters[posState] = p->tableSize; +} + +static void LenPriceEnc_UpdateTables(CLenPriceEnc *p, UInt32 numPosStates, UInt32 *ProbPrices) +{ + UInt32 posState; + for (posState = 0; posState < numPosStates; posState++) + LenPriceEnc_UpdateTable(p, posState, ProbPrices); +} + +static void LenEnc_Encode2(CLenPriceEnc *p, CRangeEnc *rc, UInt32 symbol, UInt32 posState, Bool updatePrice, UInt32 *ProbPrices) +{ + LenEnc_Encode(&p->p, rc, symbol, posState); + if (updatePrice) + if (--p->counters[posState] == 0) + LenPriceEnc_UpdateTable(p, posState, ProbPrices); +} + + + + +static void MovePos(CLzmaEnc *p, UInt32 num) +{ + #ifdef SHOW_STAT + ttt += num; + printf("\n MovePos %d", num); + #endif + if (num != 0) + { + p->additionalOffset += num; + p->matchFinder.Skip(p->matchFinderObj, num); + } +} + +static UInt32 ReadMatchDistances(CLzmaEnc *p, UInt32 *numDistancePairsRes) +{ + UInt32 lenRes = 0, numPairs; + p->numAvail = p->matchFinder.GetNumAvailableBytes(p->matchFinderObj); + numPairs = p->matchFinder.GetMatches(p->matchFinderObj, p->matches); + #ifdef SHOW_STAT + printf("\n i = %d numPairs = %d ", ttt, numPairs / 2); + ttt++; + { + UInt32 i; + for (i = 0; i < numPairs; i += 2) + printf("%2d %6d | ", p->matches[i], p->matches[i + 1]); + } + #endif + if (numPairs > 0) + { + lenRes = p->matches[numPairs - 2]; + if (lenRes == p->numFastBytes) + { + const Byte *pby = p->matchFinder.GetPointerToCurrentPos(p->matchFinderObj) - 1; + UInt32 distance = p->matches[numPairs - 1] + 1; + UInt32 numAvail = p->numAvail; + if (numAvail > LZMA_MATCH_LEN_MAX) + numAvail = LZMA_MATCH_LEN_MAX; + { + const Byte *pby2 = pby - distance; + for (; lenRes < numAvail && pby[lenRes] == pby2[lenRes]; lenRes++); + } + } + } + p->additionalOffset++; + *numDistancePairsRes = numPairs; + return lenRes; +} + + +#define MakeAsChar(p) (p)->backPrev = (UInt32)(-1); (p)->prev1IsChar = False; +#define MakeAsShortRep(p) (p)->backPrev = 0; (p)->prev1IsChar = False; +#define IsShortRep(p) ((p)->backPrev == 0) + +static UInt32 GetRepLen1Price(CLzmaEnc *p, UInt32 state, UInt32 posState) +{ + return + GET_PRICE_0(p->isRepG0[state]) + + GET_PRICE_0(p->isRep0Long[state][posState]); +} + +static UInt32 GetPureRepPrice(CLzmaEnc *p, UInt32 repIndex, UInt32 state, UInt32 posState) +{ + UInt32 price; + if (repIndex == 0) + { + price = GET_PRICE_0(p->isRepG0[state]); + price += GET_PRICE_1(p->isRep0Long[state][posState]); + } + else + { + price = GET_PRICE_1(p->isRepG0[state]); + if (repIndex == 1) + price += GET_PRICE_0(p->isRepG1[state]); + else + { + price += GET_PRICE_1(p->isRepG1[state]); + price += GET_PRICE(p->isRepG2[state], repIndex - 2); + } + } + return price; +} + +static UInt32 GetRepPrice(CLzmaEnc *p, UInt32 repIndex, UInt32 len, UInt32 state, UInt32 posState) +{ + return p->repLenEnc.prices[posState][len - LZMA_MATCH_LEN_MIN] + + GetPureRepPrice(p, repIndex, state, posState); +} + +static UInt32 Backward(CLzmaEnc *p, UInt32 *backRes, UInt32 cur) +{ + UInt32 posMem = p->opt[cur].posPrev; + UInt32 backMem = p->opt[cur].backPrev; + p->optimumEndIndex = cur; + do + { + if (p->opt[cur].prev1IsChar) + { + MakeAsChar(&p->opt[posMem]) + p->opt[posMem].posPrev = posMem - 1; + if (p->opt[cur].prev2) + { + p->opt[posMem - 1].prev1IsChar = False; + p->opt[posMem - 1].posPrev = p->opt[cur].posPrev2; + p->opt[posMem - 1].backPrev = p->opt[cur].backPrev2; + } + } + { + UInt32 posPrev = posMem; + UInt32 backCur = backMem; + + backMem = p->opt[posPrev].backPrev; + posMem = p->opt[posPrev].posPrev; + + p->opt[posPrev].backPrev = backCur; + p->opt[posPrev].posPrev = cur; + cur = posPrev; + } + } + while (cur != 0); + *backRes = p->opt[0].backPrev; + p->optimumCurrentIndex = p->opt[0].posPrev; + return p->optimumCurrentIndex; +} + +#define LIT_PROBS(pos, prevByte) (p->litProbs + ((((pos) & p->lpMask) << p->lc) + ((prevByte) >> (8 - p->lc))) * 0x300) + +static UInt32 GetOptimum(CLzmaEnc *p, UInt32 position, UInt32 *backRes) +{ + UInt32 numAvail, mainLen, numPairs, repMaxIndex, i, posState, lenEnd, len, cur; + UInt32 matchPrice, repMatchPrice, normalMatchPrice; + UInt32 reps[LZMA_NUM_REPS], repLens[LZMA_NUM_REPS]; + UInt32 *matches; + const Byte *data; + Byte curByte, matchByte; + if (p->optimumEndIndex != p->optimumCurrentIndex) + { + const COptimal *opt = &p->opt[p->optimumCurrentIndex]; + UInt32 lenRes = opt->posPrev - p->optimumCurrentIndex; + *backRes = opt->backPrev; + p->optimumCurrentIndex = opt->posPrev; + return lenRes; + } + p->optimumCurrentIndex = p->optimumEndIndex = 0; + + if (p->additionalOffset == 0) + mainLen = ReadMatchDistances(p, &numPairs); + else + { + mainLen = p->longestMatchLength; + numPairs = p->numPairs; + } + + numAvail = p->numAvail; + if (numAvail < 2) + { + *backRes = (UInt32)(-1); + return 1; + } + if (numAvail > LZMA_MATCH_LEN_MAX) + numAvail = LZMA_MATCH_LEN_MAX; + + data = p->matchFinder.GetPointerToCurrentPos(p->matchFinderObj) - 1; + repMaxIndex = 0; + for (i = 0; i < LZMA_NUM_REPS; i++) + { + UInt32 lenTest; + const Byte *data2; + reps[i] = p->reps[i]; + data2 = data - (reps[i] + 1); + if (data[0] != data2[0] || data[1] != data2[1]) + { + repLens[i] = 0; + continue; + } + for (lenTest = 2; lenTest < numAvail && data[lenTest] == data2[lenTest]; lenTest++); + repLens[i] = lenTest; + if (lenTest > repLens[repMaxIndex]) + repMaxIndex = i; + } + if (repLens[repMaxIndex] >= p->numFastBytes) + { + UInt32 lenRes; + *backRes = repMaxIndex; + lenRes = repLens[repMaxIndex]; + MovePos(p, lenRes - 1); + return lenRes; + } + + matches = p->matches; + if (mainLen >= p->numFastBytes) + { + *backRes = matches[numPairs - 1] + LZMA_NUM_REPS; + MovePos(p, mainLen - 1); + return mainLen; + } + curByte = *data; + matchByte = *(data - (reps[0] + 1)); + + if (mainLen < 2 && curByte != matchByte && repLens[repMaxIndex] < 2) + { + *backRes = (UInt32)-1; + return 1; + } + + p->opt[0].state = (CState)p->state; + + posState = (position & p->pbMask); + + { + const CLzmaProb *probs = LIT_PROBS(position, *(data - 1)); + p->opt[1].price = GET_PRICE_0(p->isMatch[p->state][posState]) + + (!IsCharState(p->state) ? + LitEnc_GetPriceMatched(probs, curByte, matchByte, p->ProbPrices) : + LitEnc_GetPrice(probs, curByte, p->ProbPrices)); + } + + MakeAsChar(&p->opt[1]); + + matchPrice = GET_PRICE_1(p->isMatch[p->state][posState]); + repMatchPrice = matchPrice + GET_PRICE_1(p->isRep[p->state]); + + if (matchByte == curByte) + { + UInt32 shortRepPrice = repMatchPrice + GetRepLen1Price(p, p->state, posState); + if (shortRepPrice < p->opt[1].price) + { + p->opt[1].price = shortRepPrice; + MakeAsShortRep(&p->opt[1]); + } + } + lenEnd = ((mainLen >= repLens[repMaxIndex]) ? mainLen : repLens[repMaxIndex]); + + if (lenEnd < 2) + { + *backRes = p->opt[1].backPrev; + return 1; + } + + p->opt[1].posPrev = 0; + for (i = 0; i < LZMA_NUM_REPS; i++) + p->opt[0].backs[i] = reps[i]; + + len = lenEnd; + do + p->opt[len--].price = kInfinityPrice; + while (len >= 2); + + for (i = 0; i < LZMA_NUM_REPS; i++) + { + UInt32 repLen = repLens[i]; + UInt32 price; + if (repLen < 2) + continue; + price = repMatchPrice + GetPureRepPrice(p, i, p->state, posState); + do + { + UInt32 curAndLenPrice = price + p->repLenEnc.prices[posState][repLen - 2]; + COptimal *opt = &p->opt[repLen]; + if (curAndLenPrice < opt->price) + { + opt->price = curAndLenPrice; + opt->posPrev = 0; + opt->backPrev = i; + opt->prev1IsChar = False; + } + } + while (--repLen >= 2); + } + + normalMatchPrice = matchPrice + GET_PRICE_0(p->isRep[p->state]); + + len = ((repLens[0] >= 2) ? repLens[0] + 1 : 2); + if (len <= mainLen) + { + UInt32 offs = 0; + while (len > matches[offs]) + offs += 2; + for (; ; len++) + { + COptimal *opt; + UInt32 distance = matches[offs + 1]; + + UInt32 curAndLenPrice = normalMatchPrice + p->lenEnc.prices[posState][len - LZMA_MATCH_LEN_MIN]; + UInt32 lenToPosState = GetLenToPosState(len); + if (distance < kNumFullDistances) + curAndLenPrice += p->distancesPrices[lenToPosState][distance]; + else + { + UInt32 slot; + GetPosSlot2(distance, slot); + curAndLenPrice += p->alignPrices[distance & kAlignMask] + p->posSlotPrices[lenToPosState][slot]; + } + opt = &p->opt[len]; + if (curAndLenPrice < opt->price) + { + opt->price = curAndLenPrice; + opt->posPrev = 0; + opt->backPrev = distance + LZMA_NUM_REPS; + opt->prev1IsChar = False; + } + if (len == matches[offs]) + { + offs += 2; + if (offs == numPairs) + break; + } + } + } + + cur = 0; + + #ifdef SHOW_STAT2 + if (position >= 0) + { + unsigned i; + printf("\n pos = %4X", position); + for (i = cur; i <= lenEnd; i++) + printf("\nprice[%4X] = %d", position - cur + i, p->opt[i].price); + } + #endif + + for (;;) + { + UInt32 numAvailFull, newLen, numPairs, posPrev, state, posState, startLen; + UInt32 curPrice, curAnd1Price, matchPrice, repMatchPrice; + Bool nextIsChar; + Byte curByte, matchByte; + const Byte *data; + COptimal *curOpt; + COptimal *nextOpt; + + cur++; + if (cur == lenEnd) + return Backward(p, backRes, cur); + + newLen = ReadMatchDistances(p, &numPairs); + if (newLen >= p->numFastBytes) + { + p->numPairs = numPairs; + p->longestMatchLength = newLen; + return Backward(p, backRes, cur); + } + position++; + curOpt = &p->opt[cur]; + posPrev = curOpt->posPrev; + if (curOpt->prev1IsChar) + { + posPrev--; + if (curOpt->prev2) + { + state = p->opt[curOpt->posPrev2].state; + if (curOpt->backPrev2 < LZMA_NUM_REPS) + state = kRepNextStates[state]; + else + state = kMatchNextStates[state]; + } + else + state = p->opt[posPrev].state; + state = kLiteralNextStates[state]; + } + else + state = p->opt[posPrev].state; + if (posPrev == cur - 1) + { + if (IsShortRep(curOpt)) + state = kShortRepNextStates[state]; + else + state = kLiteralNextStates[state]; + } + else + { + UInt32 pos; + const COptimal *prevOpt; + if (curOpt->prev1IsChar && curOpt->prev2) + { + posPrev = curOpt->posPrev2; + pos = curOpt->backPrev2; + state = kRepNextStates[state]; + } + else + { + pos = curOpt->backPrev; + if (pos < LZMA_NUM_REPS) + state = kRepNextStates[state]; + else + state = kMatchNextStates[state]; + } + prevOpt = &p->opt[posPrev]; + if (pos < LZMA_NUM_REPS) + { + UInt32 i; + reps[0] = prevOpt->backs[pos]; + for (i = 1; i <= pos; i++) + reps[i] = prevOpt->backs[i - 1]; + for (; i < LZMA_NUM_REPS; i++) + reps[i] = prevOpt->backs[i]; + } + else + { + UInt32 i; + reps[0] = (pos - LZMA_NUM_REPS); + for (i = 1; i < LZMA_NUM_REPS; i++) + reps[i] = prevOpt->backs[i - 1]; + } + } + curOpt->state = (CState)state; + + curOpt->backs[0] = reps[0]; + curOpt->backs[1] = reps[1]; + curOpt->backs[2] = reps[2]; + curOpt->backs[3] = reps[3]; + + curPrice = curOpt->price; + nextIsChar = False; + data = p->matchFinder.GetPointerToCurrentPos(p->matchFinderObj) - 1; + curByte = *data; + matchByte = *(data - (reps[0] + 1)); + + posState = (position & p->pbMask); + + curAnd1Price = curPrice + GET_PRICE_0(p->isMatch[state][posState]); + { + const CLzmaProb *probs = LIT_PROBS(position, *(data - 1)); + curAnd1Price += + (!IsCharState(state) ? + LitEnc_GetPriceMatched(probs, curByte, matchByte, p->ProbPrices) : + LitEnc_GetPrice(probs, curByte, p->ProbPrices)); + } + + nextOpt = &p->opt[cur + 1]; + + if (curAnd1Price < nextOpt->price) + { + nextOpt->price = curAnd1Price; + nextOpt->posPrev = cur; + MakeAsChar(nextOpt); + nextIsChar = True; + } + + matchPrice = curPrice + GET_PRICE_1(p->isMatch[state][posState]); + repMatchPrice = matchPrice + GET_PRICE_1(p->isRep[state]); + + if (matchByte == curByte && !(nextOpt->posPrev < cur && nextOpt->backPrev == 0)) + { + UInt32 shortRepPrice = repMatchPrice + GetRepLen1Price(p, state, posState); + if (shortRepPrice <= nextOpt->price) + { + nextOpt->price = shortRepPrice; + nextOpt->posPrev = cur; + MakeAsShortRep(nextOpt); + nextIsChar = True; + } + } + numAvailFull = p->numAvail; + { + UInt32 temp = kNumOpts - 1 - cur; + if (temp < numAvailFull) + numAvailFull = temp; + } + + if (numAvailFull < 2) + continue; + numAvail = (numAvailFull <= p->numFastBytes ? numAvailFull : p->numFastBytes); + + if (!nextIsChar && matchByte != curByte) /* speed optimization */ + { + /* try Literal + rep0 */ + UInt32 temp; + UInt32 lenTest2; + const Byte *data2 = data - (reps[0] + 1); + UInt32 limit = p->numFastBytes + 1; + if (limit > numAvailFull) + limit = numAvailFull; + + for (temp = 1; temp < limit && data[temp] == data2[temp]; temp++); + lenTest2 = temp - 1; + if (lenTest2 >= 2) + { + UInt32 state2 = kLiteralNextStates[state]; + UInt32 posStateNext = (position + 1) & p->pbMask; + UInt32 nextRepMatchPrice = curAnd1Price + + GET_PRICE_1(p->isMatch[state2][posStateNext]) + + GET_PRICE_1(p->isRep[state2]); + /* for (; lenTest2 >= 2; lenTest2--) */ + { + UInt32 curAndLenPrice; + COptimal *opt; + UInt32 offset = cur + 1 + lenTest2; + while (lenEnd < offset) + p->opt[++lenEnd].price = kInfinityPrice; + curAndLenPrice = nextRepMatchPrice + GetRepPrice(p, 0, lenTest2, state2, posStateNext); + opt = &p->opt[offset]; + if (curAndLenPrice < opt->price) + { + opt->price = curAndLenPrice; + opt->posPrev = cur + 1; + opt->backPrev = 0; + opt->prev1IsChar = True; + opt->prev2 = False; + } + } + } + } + + startLen = 2; /* speed optimization */ + { + UInt32 repIndex; + for (repIndex = 0; repIndex < LZMA_NUM_REPS; repIndex++) + { + UInt32 lenTest; + UInt32 lenTestTemp; + UInt32 price; + const Byte *data2 = data - (reps[repIndex] + 1); + if (data[0] != data2[0] || data[1] != data2[1]) + continue; + for (lenTest = 2; lenTest < numAvail && data[lenTest] == data2[lenTest]; lenTest++); + while (lenEnd < cur + lenTest) + p->opt[++lenEnd].price = kInfinityPrice; + lenTestTemp = lenTest; + price = repMatchPrice + GetPureRepPrice(p, repIndex, state, posState); + do + { + UInt32 curAndLenPrice = price + p->repLenEnc.prices[posState][lenTest - 2]; + COptimal *opt = &p->opt[cur + lenTest]; + if (curAndLenPrice < opt->price) + { + opt->price = curAndLenPrice; + opt->posPrev = cur; + opt->backPrev = repIndex; + opt->prev1IsChar = False; + } + } + while (--lenTest >= 2); + lenTest = lenTestTemp; + + if (repIndex == 0) + startLen = lenTest + 1; + + /* if (_maxMode) */ + { + UInt32 lenTest2 = lenTest + 1; + UInt32 limit = lenTest2 + p->numFastBytes; + UInt32 nextRepMatchPrice; + if (limit > numAvailFull) + limit = numAvailFull; + for (; lenTest2 < limit && data[lenTest2] == data2[lenTest2]; lenTest2++); + lenTest2 -= lenTest + 1; + if (lenTest2 >= 2) + { + UInt32 state2 = kRepNextStates[state]; + UInt32 posStateNext = (position + lenTest) & p->pbMask; + UInt32 curAndLenCharPrice = + price + p->repLenEnc.prices[posState][lenTest - 2] + + GET_PRICE_0(p->isMatch[state2][posStateNext]) + + LitEnc_GetPriceMatched(LIT_PROBS(position + lenTest, data[lenTest - 1]), + data[lenTest], data2[lenTest], p->ProbPrices); + state2 = kLiteralNextStates[state2]; + posStateNext = (position + lenTest + 1) & p->pbMask; + nextRepMatchPrice = curAndLenCharPrice + + GET_PRICE_1(p->isMatch[state2][posStateNext]) + + GET_PRICE_1(p->isRep[state2]); + + /* for (; lenTest2 >= 2; lenTest2--) */ + { + UInt32 curAndLenPrice; + COptimal *opt; + UInt32 offset = cur + lenTest + 1 + lenTest2; + while (lenEnd < offset) + p->opt[++lenEnd].price = kInfinityPrice; + curAndLenPrice = nextRepMatchPrice + GetRepPrice(p, 0, lenTest2, state2, posStateNext); + opt = &p->opt[offset]; + if (curAndLenPrice < opt->price) + { + opt->price = curAndLenPrice; + opt->posPrev = cur + lenTest + 1; + opt->backPrev = 0; + opt->prev1IsChar = True; + opt->prev2 = True; + opt->posPrev2 = cur; + opt->backPrev2 = repIndex; + } + } + } + } + } + } + /* for (UInt32 lenTest = 2; lenTest <= newLen; lenTest++) */ + if (newLen > numAvail) + { + newLen = numAvail; + for (numPairs = 0; newLen > matches[numPairs]; numPairs += 2); + matches[numPairs] = newLen; + numPairs += 2; + } + if (newLen >= startLen) + { + UInt32 normalMatchPrice = matchPrice + GET_PRICE_0(p->isRep[state]); + UInt32 offs, curBack, posSlot; + UInt32 lenTest; + while (lenEnd < cur + newLen) + p->opt[++lenEnd].price = kInfinityPrice; + + offs = 0; + while (startLen > matches[offs]) + offs += 2; + curBack = matches[offs + 1]; + GetPosSlot2(curBack, posSlot); + for (lenTest = /*2*/ startLen; ; lenTest++) + { + UInt32 curAndLenPrice = normalMatchPrice + p->lenEnc.prices[posState][lenTest - LZMA_MATCH_LEN_MIN]; + UInt32 lenToPosState = GetLenToPosState(lenTest); + COptimal *opt; + if (curBack < kNumFullDistances) + curAndLenPrice += p->distancesPrices[lenToPosState][curBack]; + else + curAndLenPrice += p->posSlotPrices[lenToPosState][posSlot] + p->alignPrices[curBack & kAlignMask]; + + opt = &p->opt[cur + lenTest]; + if (curAndLenPrice < opt->price) + { + opt->price = curAndLenPrice; + opt->posPrev = cur; + opt->backPrev = curBack + LZMA_NUM_REPS; + opt->prev1IsChar = False; + } + + if (/*_maxMode && */lenTest == matches[offs]) + { + /* Try Match + Literal + Rep0 */ + const Byte *data2 = data - (curBack + 1); + UInt32 lenTest2 = lenTest + 1; + UInt32 limit = lenTest2 + p->numFastBytes; + UInt32 nextRepMatchPrice; + if (limit > numAvailFull) + limit = numAvailFull; + for (; lenTest2 < limit && data[lenTest2] == data2[lenTest2]; lenTest2++); + lenTest2 -= lenTest + 1; + if (lenTest2 >= 2) + { + UInt32 state2 = kMatchNextStates[state]; + UInt32 posStateNext = (position + lenTest) & p->pbMask; + UInt32 curAndLenCharPrice = curAndLenPrice + + GET_PRICE_0(p->isMatch[state2][posStateNext]) + + LitEnc_GetPriceMatched(LIT_PROBS(position + lenTest, data[lenTest - 1]), + data[lenTest], data2[lenTest], p->ProbPrices); + state2 = kLiteralNextStates[state2]; + posStateNext = (posStateNext + 1) & p->pbMask; + nextRepMatchPrice = curAndLenCharPrice + + GET_PRICE_1(p->isMatch[state2][posStateNext]) + + GET_PRICE_1(p->isRep[state2]); + + /* for (; lenTest2 >= 2; lenTest2--) */ + { + UInt32 offset = cur + lenTest + 1 + lenTest2; + UInt32 curAndLenPrice; + COptimal *opt; + while (lenEnd < offset) + p->opt[++lenEnd].price = kInfinityPrice; + curAndLenPrice = nextRepMatchPrice + GetRepPrice(p, 0, lenTest2, state2, posStateNext); + opt = &p->opt[offset]; + if (curAndLenPrice < opt->price) + { + opt->price = curAndLenPrice; + opt->posPrev = cur + lenTest + 1; + opt->backPrev = 0; + opt->prev1IsChar = True; + opt->prev2 = True; + opt->posPrev2 = cur; + opt->backPrev2 = curBack + LZMA_NUM_REPS; + } + } + } + offs += 2; + if (offs == numPairs) + break; + curBack = matches[offs + 1]; + if (curBack >= kNumFullDistances) + GetPosSlot2(curBack, posSlot); + } + } + } + } +} + +#define ChangePair(smallDist, bigDist) (((bigDist) >> 7) > (smallDist)) + +static UInt32 GetOptimumFast(CLzmaEnc *p, UInt32 *backRes) +{ + UInt32 numAvail, mainLen, mainDist, numPairs, repIndex, repLen, i; + const Byte *data; + const UInt32 *matches; + + if (p->additionalOffset == 0) + mainLen = ReadMatchDistances(p, &numPairs); + else + { + mainLen = p->longestMatchLength; + numPairs = p->numPairs; + } + + numAvail = p->numAvail; + *backRes = (UInt32)-1; + if (numAvail < 2) + return 1; + if (numAvail > LZMA_MATCH_LEN_MAX) + numAvail = LZMA_MATCH_LEN_MAX; + data = p->matchFinder.GetPointerToCurrentPos(p->matchFinderObj) - 1; + + repLen = repIndex = 0; + for (i = 0; i < LZMA_NUM_REPS; i++) + { + UInt32 len; + const Byte *data2 = data - (p->reps[i] + 1); + if (data[0] != data2[0] || data[1] != data2[1]) + continue; + for (len = 2; len < numAvail && data[len] == data2[len]; len++); + if (len >= p->numFastBytes) + { + *backRes = i; + MovePos(p, len - 1); + return len; + } + if (len > repLen) + { + repIndex = i; + repLen = len; + } + } + + matches = p->matches; + if (mainLen >= p->numFastBytes) + { + *backRes = matches[numPairs - 1] + LZMA_NUM_REPS; + MovePos(p, mainLen - 1); + return mainLen; + } + + mainDist = 0; /* for GCC */ + if (mainLen >= 2) + { + mainDist = matches[numPairs - 1]; + while (numPairs > 2 && mainLen == matches[numPairs - 4] + 1) + { + if (!ChangePair(matches[numPairs - 3], mainDist)) + break; + numPairs -= 2; + mainLen = matches[numPairs - 2]; + mainDist = matches[numPairs - 1]; + } + if (mainLen == 2 && mainDist >= 0x80) + mainLen = 1; + } + + if (repLen >= 2 && ( + (repLen + 1 >= mainLen) || + (repLen + 2 >= mainLen && mainDist >= (1 << 9)) || + (repLen + 3 >= mainLen && mainDist >= (1 << 15)))) + { + *backRes = repIndex; + MovePos(p, repLen - 1); + return repLen; + } + + if (mainLen < 2 || numAvail <= 2) + return 1; + + p->longestMatchLength = ReadMatchDistances(p, &p->numPairs); + if (p->longestMatchLength >= 2) + { + UInt32 newDistance = matches[p->numPairs - 1]; + if ((p->longestMatchLength >= mainLen && newDistance < mainDist) || + (p->longestMatchLength == mainLen + 1 && !ChangePair(mainDist, newDistance)) || + (p->longestMatchLength > mainLen + 1) || + (p->longestMatchLength + 1 >= mainLen && mainLen >= 3 && ChangePair(newDistance, mainDist))) + return 1; + } + + data = p->matchFinder.GetPointerToCurrentPos(p->matchFinderObj) - 1; + for (i = 0; i < LZMA_NUM_REPS; i++) + { + UInt32 len, limit; + const Byte *data2 = data - (p->reps[i] + 1); + if (data[0] != data2[0] || data[1] != data2[1]) + continue; + limit = mainLen - 1; + for (len = 2; len < limit && data[len] == data2[len]; len++); + if (len >= limit) + return 1; + } + *backRes = mainDist + LZMA_NUM_REPS; + MovePos(p, mainLen - 2); + return mainLen; +} + +static void WriteEndMarker(CLzmaEnc *p, UInt32 posState) +{ + UInt32 len; + RangeEnc_EncodeBit(&p->rc, &p->isMatch[p->state][posState], 1); + RangeEnc_EncodeBit(&p->rc, &p->isRep[p->state], 0); + p->state = kMatchNextStates[p->state]; + len = LZMA_MATCH_LEN_MIN; + LenEnc_Encode2(&p->lenEnc, &p->rc, len - LZMA_MATCH_LEN_MIN, posState, !p->fastMode, p->ProbPrices); + RcTree_Encode(&p->rc, p->posSlotEncoder[GetLenToPosState(len)], kNumPosSlotBits, (1 << kNumPosSlotBits) - 1); + RangeEnc_EncodeDirectBits(&p->rc, (((UInt32)1 << 30) - 1) >> kNumAlignBits, 30 - kNumAlignBits); + RcTree_ReverseEncode(&p->rc, p->posAlignEncoder, kNumAlignBits, kAlignMask); +} + +static SRes CheckErrors(CLzmaEnc *p) +{ + if (p->result != SZ_OK) + return p->result; + if (p->rc.res != SZ_OK) + p->result = SZ_ERROR_WRITE; + if (p->matchFinderBase.result != SZ_OK) + p->result = SZ_ERROR_READ; + if (p->result != SZ_OK) + p->finished = True; + return p->result; +} + +static SRes Flush(CLzmaEnc *p, UInt32 nowPos) +{ + /* ReleaseMFStream(); */ + p->finished = True; + if (p->writeEndMark) + WriteEndMarker(p, nowPos & p->pbMask); + RangeEnc_FlushData(&p->rc); + RangeEnc_FlushStream(&p->rc); + return CheckErrors(p); +} + +static void FillAlignPrices(CLzmaEnc *p) +{ + UInt32 i; + for (i = 0; i < kAlignTableSize; i++) + p->alignPrices[i] = RcTree_ReverseGetPrice(p->posAlignEncoder, kNumAlignBits, i, p->ProbPrices); + p->alignPriceCount = 0; +} + +static void FillDistancesPrices(CLzmaEnc *p) +{ + UInt32 tempPrices[kNumFullDistances]; + UInt32 i, lenToPosState; + for (i = kStartPosModelIndex; i < kNumFullDistances; i++) + { + UInt32 posSlot = GetPosSlot1(i); + UInt32 footerBits = ((posSlot >> 1) - 1); + UInt32 base = ((2 | (posSlot & 1)) << footerBits); + tempPrices[i] = RcTree_ReverseGetPrice(p->posEncoders + base - posSlot - 1, footerBits, i - base, p->ProbPrices); + } + + for (lenToPosState = 0; lenToPosState < kNumLenToPosStates; lenToPosState++) + { + UInt32 posSlot; + const CLzmaProb *encoder = p->posSlotEncoder[lenToPosState]; + UInt32 *posSlotPrices = p->posSlotPrices[lenToPosState]; + for (posSlot = 0; posSlot < p->distTableSize; posSlot++) + posSlotPrices[posSlot] = RcTree_GetPrice(encoder, kNumPosSlotBits, posSlot, p->ProbPrices); + for (posSlot = kEndPosModelIndex; posSlot < p->distTableSize; posSlot++) + posSlotPrices[posSlot] += ((((posSlot >> 1) - 1) - kNumAlignBits) << kNumBitPriceShiftBits); + + { + UInt32 *distancesPrices = p->distancesPrices[lenToPosState]; + UInt32 i; + for (i = 0; i < kStartPosModelIndex; i++) + distancesPrices[i] = posSlotPrices[i]; + for (; i < kNumFullDistances; i++) + distancesPrices[i] = posSlotPrices[GetPosSlot1(i)] + tempPrices[i]; + } + } + p->matchPriceCount = 0; +} + +void LzmaEnc_Construct(CLzmaEnc *p) +{ + RangeEnc_Construct(&p->rc); + MatchFinder_Construct(&p->matchFinderBase); + #ifdef COMPRESS_MF_MT + MatchFinderMt_Construct(&p->matchFinderMt); + p->matchFinderMt.MatchFinder = &p->matchFinderBase; + #endif + + { + CLzmaEncProps props; + LzmaEncProps_Init(&props); + LzmaEnc_SetProps(p, &props); + } + + #ifndef LZMA_LOG_BSR + LzmaEnc_FastPosInit(p->g_FastPos); + #endif + + LzmaEnc_InitPriceTables(p->ProbPrices); + p->litProbs = 0; + p->saveState.litProbs = 0; +} + +CLzmaEncHandle LzmaEnc_Create(ISzAlloc *alloc) +{ + void *p; + p = alloc->Alloc(alloc, sizeof(CLzmaEnc)); + if (p != 0) + LzmaEnc_Construct((CLzmaEnc *)p); + return p; +} + +void LzmaEnc_FreeLits(CLzmaEnc *p, ISzAlloc *alloc) +{ + alloc->Free(alloc, p->litProbs); + alloc->Free(alloc, p->saveState.litProbs); + p->litProbs = 0; + p->saveState.litProbs = 0; +} + +void LzmaEnc_Destruct(CLzmaEnc *p, ISzAlloc *alloc, ISzAlloc *allocBig) +{ + #ifdef COMPRESS_MF_MT + MatchFinderMt_Destruct(&p->matchFinderMt, allocBig); + #endif + MatchFinder_Free(&p->matchFinderBase, allocBig); + LzmaEnc_FreeLits(p, alloc); + RangeEnc_Free(&p->rc, alloc); +} + +void LzmaEnc_Destroy(CLzmaEncHandle p, ISzAlloc *alloc, ISzAlloc *allocBig) +{ + LzmaEnc_Destruct((CLzmaEnc *)p, alloc, allocBig); + alloc->Free(alloc, p); +} + +static SRes LzmaEnc_CodeOneBlock(CLzmaEnc *p, Bool useLimits, UInt32 maxPackSize, UInt32 maxUnpackSize) +{ + UInt32 nowPos32, startPos32; + if (p->inStream != 0) + { + p->matchFinderBase.stream = p->inStream; + p->matchFinder.Init(p->matchFinderObj); + p->inStream = 0; + } + + if (p->finished) + return p->result; + RINOK(CheckErrors(p)); + + nowPos32 = (UInt32)p->nowPos64; + startPos32 = nowPos32; + + if (p->nowPos64 == 0) + { + UInt32 numPairs; + Byte curByte; + if (p->matchFinder.GetNumAvailableBytes(p->matchFinderObj) == 0) + return Flush(p, nowPos32); + ReadMatchDistances(p, &numPairs); + RangeEnc_EncodeBit(&p->rc, &p->isMatch[p->state][0], 0); + p->state = kLiteralNextStates[p->state]; + curByte = p->matchFinder.GetIndexByte(p->matchFinderObj, 0 - p->additionalOffset); + LitEnc_Encode(&p->rc, p->litProbs, curByte); + p->additionalOffset--; + nowPos32++; + } + + if (p->matchFinder.GetNumAvailableBytes(p->matchFinderObj) != 0) + for (;;) + { + UInt32 pos, len, posState; + + if (p->fastMode) + len = GetOptimumFast(p, &pos); + else + len = GetOptimum(p, nowPos32, &pos); + + #ifdef SHOW_STAT2 + printf("\n pos = %4X, len = %d pos = %d", nowPos32, len, pos); + #endif + + posState = nowPos32 & p->pbMask; + if (len == 1 && pos == (UInt32)-1) + { + Byte curByte; + CLzmaProb *probs; + const Byte *data; + + RangeEnc_EncodeBit(&p->rc, &p->isMatch[p->state][posState], 0); + data = p->matchFinder.GetPointerToCurrentPos(p->matchFinderObj) - p->additionalOffset; + curByte = *data; + probs = LIT_PROBS(nowPos32, *(data - 1)); + if (IsCharState(p->state)) + LitEnc_Encode(&p->rc, probs, curByte); + else + LitEnc_EncodeMatched(&p->rc, probs, curByte, *(data - p->reps[0] - 1)); + p->state = kLiteralNextStates[p->state]; + } + else + { + RangeEnc_EncodeBit(&p->rc, &p->isMatch[p->state][posState], 1); + if (pos < LZMA_NUM_REPS) + { + RangeEnc_EncodeBit(&p->rc, &p->isRep[p->state], 1); + if (pos == 0) + { + RangeEnc_EncodeBit(&p->rc, &p->isRepG0[p->state], 0); + RangeEnc_EncodeBit(&p->rc, &p->isRep0Long[p->state][posState], ((len == 1) ? 0 : 1)); + } + else + { + UInt32 distance = p->reps[pos]; + RangeEnc_EncodeBit(&p->rc, &p->isRepG0[p->state], 1); + if (pos == 1) + RangeEnc_EncodeBit(&p->rc, &p->isRepG1[p->state], 0); + else + { + RangeEnc_EncodeBit(&p->rc, &p->isRepG1[p->state], 1); + RangeEnc_EncodeBit(&p->rc, &p->isRepG2[p->state], pos - 2); + if (pos == 3) + p->reps[3] = p->reps[2]; + p->reps[2] = p->reps[1]; + } + p->reps[1] = p->reps[0]; + p->reps[0] = distance; + } + if (len == 1) + p->state = kShortRepNextStates[p->state]; + else + { + LenEnc_Encode2(&p->repLenEnc, &p->rc, len - LZMA_MATCH_LEN_MIN, posState, !p->fastMode, p->ProbPrices); + p->state = kRepNextStates[p->state]; + } + } + else + { + UInt32 posSlot; + RangeEnc_EncodeBit(&p->rc, &p->isRep[p->state], 0); + p->state = kMatchNextStates[p->state]; + LenEnc_Encode2(&p->lenEnc, &p->rc, len - LZMA_MATCH_LEN_MIN, posState, !p->fastMode, p->ProbPrices); + pos -= LZMA_NUM_REPS; + GetPosSlot(pos, posSlot); + RcTree_Encode(&p->rc, p->posSlotEncoder[GetLenToPosState(len)], kNumPosSlotBits, posSlot); + + if (posSlot >= kStartPosModelIndex) + { + UInt32 footerBits = ((posSlot >> 1) - 1); + UInt32 base = ((2 | (posSlot & 1)) << footerBits); + UInt32 posReduced = pos - base; + + if (posSlot < kEndPosModelIndex) + RcTree_ReverseEncode(&p->rc, p->posEncoders + base - posSlot - 1, footerBits, posReduced); + else + { + RangeEnc_EncodeDirectBits(&p->rc, posReduced >> kNumAlignBits, footerBits - kNumAlignBits); + RcTree_ReverseEncode(&p->rc, p->posAlignEncoder, kNumAlignBits, posReduced & kAlignMask); + p->alignPriceCount++; + } + } + p->reps[3] = p->reps[2]; + p->reps[2] = p->reps[1]; + p->reps[1] = p->reps[0]; + p->reps[0] = pos; + p->matchPriceCount++; + } + } + p->additionalOffset -= len; + nowPos32 += len; + if (p->additionalOffset == 0) + { + UInt32 processed; + if (!p->fastMode) + { + if (p->matchPriceCount >= (1 << 7)) + FillDistancesPrices(p); + if (p->alignPriceCount >= kAlignTableSize) + FillAlignPrices(p); + } + if (p->matchFinder.GetNumAvailableBytes(p->matchFinderObj) == 0) + break; + processed = nowPos32 - startPos32; + if (useLimits) + { + if (processed + kNumOpts + 300 >= maxUnpackSize || + RangeEnc_GetProcessed(&p->rc) + kNumOpts * 2 >= maxPackSize) + break; + } + else if (processed >= (1 << 15)) + { + p->nowPos64 += nowPos32 - startPos32; + return CheckErrors(p); + } + } + } + p->nowPos64 += nowPos32 - startPos32; + return Flush(p, nowPos32); +} + +#define kBigHashDicLimit ((UInt32)1 << 24) + +static SRes LzmaEnc_Alloc(CLzmaEnc *p, UInt32 keepWindowSize, ISzAlloc *alloc, ISzAlloc *allocBig) +{ + UInt32 beforeSize = kNumOpts; + Bool btMode; + if (!RangeEnc_Alloc(&p->rc, alloc)) + return SZ_ERROR_MEM; + btMode = (p->matchFinderBase.btMode != 0); + #ifdef COMPRESS_MF_MT + p->mtMode = (p->multiThread && !p->fastMode && btMode); + #endif + + { + unsigned lclp = p->lc + p->lp; + if (p->litProbs == 0 || p->saveState.litProbs == 0 || p->lclp != lclp) + { + LzmaEnc_FreeLits(p, alloc); + p->litProbs = (CLzmaProb *)alloc->Alloc(alloc, (0x300 << lclp) * sizeof(CLzmaProb)); + p->saveState.litProbs = (CLzmaProb *)alloc->Alloc(alloc, (0x300 << lclp) * sizeof(CLzmaProb)); + if (p->litProbs == 0 || p->saveState.litProbs == 0) + { + LzmaEnc_FreeLits(p, alloc); + return SZ_ERROR_MEM; + } + p->lclp = lclp; + } + } + + p->matchFinderBase.bigHash = (p->dictSize > kBigHashDicLimit); + + if (beforeSize + p->dictSize < keepWindowSize) + beforeSize = keepWindowSize - p->dictSize; + + #ifdef COMPRESS_MF_MT + if (p->mtMode) + { + RINOK(MatchFinderMt_Create(&p->matchFinderMt, p->dictSize, beforeSize, p->numFastBytes, LZMA_MATCH_LEN_MAX, allocBig)); + p->matchFinderObj = &p->matchFinderMt; + MatchFinderMt_CreateVTable(&p->matchFinderMt, &p->matchFinder); + } + else + #endif + { + if (!MatchFinder_Create(&p->matchFinderBase, p->dictSize, beforeSize, p->numFastBytes, LZMA_MATCH_LEN_MAX, allocBig)) + return SZ_ERROR_MEM; + p->matchFinderObj = &p->matchFinderBase; + MatchFinder_CreateVTable(&p->matchFinderBase, &p->matchFinder); + } + return SZ_OK; +} + +void LzmaEnc_Init(CLzmaEnc *p) +{ + UInt32 i; + p->state = 0; + for (i = 0 ; i < LZMA_NUM_REPS; i++) + p->reps[i] = 0; + + RangeEnc_Init(&p->rc); + + + for (i = 0; i < kNumStates; i++) + { + UInt32 j; + for (j = 0; j < LZMA_NUM_PB_STATES_MAX; j++) + { + p->isMatch[i][j] = kProbInitValue; + p->isRep0Long[i][j] = kProbInitValue; + } + p->isRep[i] = kProbInitValue; + p->isRepG0[i] = kProbInitValue; + p->isRepG1[i] = kProbInitValue; + p->isRepG2[i] = kProbInitValue; + } + + { + UInt32 num = 0x300 << (p->lp + p->lc); + for (i = 0; i < num; i++) + p->litProbs[i] = kProbInitValue; + } + + { + for (i = 0; i < kNumLenToPosStates; i++) + { + CLzmaProb *probs = p->posSlotEncoder[i]; + UInt32 j; + for (j = 0; j < (1 << kNumPosSlotBits); j++) + probs[j] = kProbInitValue; + } + } + { + for (i = 0; i < kNumFullDistances - kEndPosModelIndex; i++) + p->posEncoders[i] = kProbInitValue; + } + + LenEnc_Init(&p->lenEnc.p); + LenEnc_Init(&p->repLenEnc.p); + + for (i = 0; i < (1 << kNumAlignBits); i++) + p->posAlignEncoder[i] = kProbInitValue; + + p->optimumEndIndex = 0; + p->optimumCurrentIndex = 0; + p->additionalOffset = 0; + + p->pbMask = (1 << p->pb) - 1; + p->lpMask = (1 << p->lp) - 1; +} + +void LzmaEnc_InitPrices(CLzmaEnc *p) +{ + if (!p->fastMode) + { + FillDistancesPrices(p); + FillAlignPrices(p); + } + + p->lenEnc.tableSize = + p->repLenEnc.tableSize = + p->numFastBytes + 1 - LZMA_MATCH_LEN_MIN; + LenPriceEnc_UpdateTables(&p->lenEnc, 1 << p->pb, p->ProbPrices); + LenPriceEnc_UpdateTables(&p->repLenEnc, 1 << p->pb, p->ProbPrices); +} + +static SRes LzmaEnc_AllocAndInit(CLzmaEnc *p, UInt32 keepWindowSize, ISzAlloc *alloc, ISzAlloc *allocBig) +{ + UInt32 i; + for (i = 0; i < (UInt32)kDicLogSizeMaxCompress; i++) + if (p->dictSize <= ((UInt32)1 << i)) + break; + p->distTableSize = i * 2; + + p->finished = False; + p->result = SZ_OK; + RINOK(LzmaEnc_Alloc(p, keepWindowSize, alloc, allocBig)); + LzmaEnc_Init(p); + LzmaEnc_InitPrices(p); + p->nowPos64 = 0; + return SZ_OK; +} + +static SRes LzmaEnc_Prepare(CLzmaEncHandle pp, ISeqInStream *inStream, ISeqOutStream *outStream, + ISzAlloc *alloc, ISzAlloc *allocBig) +{ + CLzmaEnc *p = (CLzmaEnc *)pp; + p->inStream = inStream; + p->rc.outStream = outStream; + return LzmaEnc_AllocAndInit(p, 0, alloc, allocBig); +} + +SRes LzmaEnc_PrepareForLzma2(CLzmaEncHandle pp, + ISeqInStream *inStream, UInt32 keepWindowSize, + ISzAlloc *alloc, ISzAlloc *allocBig) +{ + CLzmaEnc *p = (CLzmaEnc *)pp; + p->inStream = inStream; + return LzmaEnc_AllocAndInit(p, keepWindowSize, alloc, allocBig); +} + +static void LzmaEnc_SetInputBuf(CLzmaEnc *p, const Byte *src, SizeT srcLen) +{ + p->seqBufInStream.funcTable.Read = MyRead; + p->seqBufInStream.data = src; + p->seqBufInStream.rem = srcLen; +} + +SRes LzmaEnc_MemPrepare(CLzmaEncHandle pp, const Byte *src, SizeT srcLen, + UInt32 keepWindowSize, ISzAlloc *alloc, ISzAlloc *allocBig) +{ + CLzmaEnc *p = (CLzmaEnc *)pp; + LzmaEnc_SetInputBuf(p, src, srcLen); + p->inStream = &p->seqBufInStream.funcTable; + return LzmaEnc_AllocAndInit(p, keepWindowSize, alloc, allocBig); +} + +void LzmaEnc_Finish(CLzmaEncHandle pp) +{ + #ifdef COMPRESS_MF_MT + CLzmaEnc *p = (CLzmaEnc *)pp; + if (p->mtMode) + MatchFinderMt_ReleaseStream(&p->matchFinderMt); + #else + pp = pp; + #endif +} + +typedef struct _CSeqOutStreamBuf +{ + ISeqOutStream funcTable; + Byte *data; + SizeT rem; + Bool overflow; +} CSeqOutStreamBuf; + +static size_t MyWrite(void *pp, const void *data, size_t size) +{ + CSeqOutStreamBuf *p = (CSeqOutStreamBuf *)pp; + if (p->rem < size) + { + size = p->rem; + p->overflow = True; + } + memcpy(p->data, data, size); + p->rem -= size; + p->data += size; + return size; +} + + +UInt32 LzmaEnc_GetNumAvailableBytes(CLzmaEncHandle pp) +{ + const CLzmaEnc *p = (CLzmaEnc *)pp; + return p->matchFinder.GetNumAvailableBytes(p->matchFinderObj); +} + +const Byte *LzmaEnc_GetCurBuf(CLzmaEncHandle pp) +{ + const CLzmaEnc *p = (CLzmaEnc *)pp; + return p->matchFinder.GetPointerToCurrentPos(p->matchFinderObj) - p->additionalOffset; +} + +SRes LzmaEnc_CodeOneMemBlock(CLzmaEncHandle pp, Bool reInit, + Byte *dest, size_t *destLen, UInt32 desiredPackSize, UInt32 *unpackSize) +{ + CLzmaEnc *p = (CLzmaEnc *)pp; + UInt64 nowPos64; + SRes res; + CSeqOutStreamBuf outStream; + + outStream.funcTable.Write = MyWrite; + outStream.data = dest; + outStream.rem = *destLen; + outStream.overflow = False; + + p->writeEndMark = False; + p->finished = False; + p->result = SZ_OK; + + if (reInit) + LzmaEnc_Init(p); + LzmaEnc_InitPrices(p); + nowPos64 = p->nowPos64; + RangeEnc_Init(&p->rc); + p->rc.outStream = &outStream.funcTable; + + res = LzmaEnc_CodeOneBlock(p, True, desiredPackSize, *unpackSize); + + *unpackSize = (UInt32)(p->nowPos64 - nowPos64); + *destLen -= outStream.rem; + if (outStream.overflow) + return SZ_ERROR_OUTPUT_EOF; + + return res; +} + +SRes LzmaEnc_Encode(CLzmaEncHandle pp, ISeqOutStream *outStream, ISeqInStream *inStream, ICompressProgress *progress, + ISzAlloc *alloc, ISzAlloc *allocBig) +{ + CLzmaEnc *p = (CLzmaEnc *)pp; + SRes res = SZ_OK; + + #ifdef COMPRESS_MF_MT + Byte allocaDummy[0x300]; + int i = 0; + for (i = 0; i < 16; i++) + allocaDummy[i] = (Byte)i; + #endif + + RINOK(LzmaEnc_Prepare(pp, inStream, outStream, alloc, allocBig)); + + for (;;) + { + res = LzmaEnc_CodeOneBlock(p, False, 0, 0); + if (res != SZ_OK || p->finished != 0) + break; + if (progress != 0) + { + res = progress->Progress(progress, p->nowPos64, RangeEnc_GetProcessed(&p->rc)); + if (res != SZ_OK) + { + res = SZ_ERROR_PROGRESS; + break; + } + } + } + LzmaEnc_Finish(pp); + return res; +} + +SRes LzmaEnc_WriteProperties(CLzmaEncHandle pp, Byte *props, SizeT *size) +{ + CLzmaEnc *p = (CLzmaEnc *)pp; + int i; + UInt32 dictSize = p->dictSize; + if (*size < LZMA_PROPS_SIZE) + return SZ_ERROR_PARAM; + *size = LZMA_PROPS_SIZE; + props[0] = (Byte)((p->pb * 5 + p->lp) * 9 + p->lc); + + for (i = 11; i <= 30; i++) + { + if (dictSize <= ((UInt32)2 << i)) + { + dictSize = (2 << i); + break; + } + if (dictSize <= ((UInt32)3 << i)) + { + dictSize = (3 << i); + break; + } + } + + for (i = 0; i < 4; i++) + props[1 + i] = (Byte)(dictSize >> (8 * i)); + return SZ_OK; +} + +SRes LzmaEnc_MemEncode(CLzmaEncHandle pp, Byte *dest, SizeT *destLen, const Byte *src, SizeT srcLen, + int writeEndMark, ICompressProgress *progress, ISzAlloc *alloc, ISzAlloc *allocBig) +{ + SRes res; + CLzmaEnc *p = (CLzmaEnc *)pp; + + CSeqOutStreamBuf outStream; + + LzmaEnc_SetInputBuf(p, src, srcLen); + + outStream.funcTable.Write = MyWrite; + outStream.data = dest; + outStream.rem = *destLen; + outStream.overflow = False; + + p->writeEndMark = writeEndMark; + res = LzmaEnc_Encode(pp, &outStream.funcTable, &p->seqBufInStream.funcTable, + progress, alloc, allocBig); + + *destLen -= outStream.rem; + if (outStream.overflow) + return SZ_ERROR_OUTPUT_EOF; + return res; +} + +SRes LzmaEncode(Byte *dest, SizeT *destLen, const Byte *src, SizeT srcLen, + const CLzmaEncProps *props, Byte *propsEncoded, SizeT *propsSize, int writeEndMark, + ICompressProgress *progress, ISzAlloc *alloc, ISzAlloc *allocBig) +{ + CLzmaEnc *p = (CLzmaEnc *)LzmaEnc_Create(alloc); + SRes res; + if (p == 0) + return SZ_ERROR_MEM; + + res = LzmaEnc_SetProps(p, props); + if (res == SZ_OK) + { + res = LzmaEnc_WriteProperties(p, propsEncoded, propsSize); + if (res == SZ_OK) + res = LzmaEnc_MemEncode(p, dest, destLen, src, srcLen, + writeEndMark, progress, alloc, allocBig); + } + + LzmaEnc_Destroy(p, alloc, allocBig); + return res; +} diff --git a/wiipax/client/LzmaEnc.h b/wiipax/client/LzmaEnc.h new file mode 100644 index 0000000..e3d84fa --- /dev/null +++ b/wiipax/client/LzmaEnc.h @@ -0,0 +1,72 @@ +/* LzmaEnc.h -- LZMA Encoder +2008-10-04 : Igor Pavlov : Public domain */ + +#ifndef __LZMAENC_H +#define __LZMAENC_H + +#include "Types.h" + +#define LZMA_PROPS_SIZE 5 + +typedef struct _CLzmaEncProps +{ + int level; /* 0 <= level <= 9 */ + UInt32 dictSize; /* (1 << 12) <= dictSize <= (1 << 27) for 32-bit version + (1 << 12) <= dictSize <= (1 << 30) for 64-bit version + default = (1 << 24) */ + int lc; /* 0 <= lc <= 8, default = 3 */ + int lp; /* 0 <= lp <= 4, default = 0 */ + int pb; /* 0 <= pb <= 4, default = 2 */ + int algo; /* 0 - fast, 1 - normal, default = 1 */ + int fb; /* 5 <= fb <= 273, default = 32 */ + int btMode; /* 0 - hashChain Mode, 1 - binTree mode - normal, default = 1 */ + int numHashBytes; /* 2, 3 or 4, default = 4 */ + UInt32 mc; /* 1 <= mc <= (1 << 30), default = 32 */ + unsigned writeEndMark; /* 0 - do not write EOPM, 1 - write EOPM, default = 0 */ + int numThreads; /* 1 or 2, default = 2 */ +} CLzmaEncProps; + +void LzmaEncProps_Init(CLzmaEncProps *p); +void LzmaEncProps_Normalize(CLzmaEncProps *p); +UInt32 LzmaEncProps_GetDictSize(const CLzmaEncProps *props2); + + +/* ---------- CLzmaEncHandle Interface ---------- */ + +/* LzmaEnc_* functions can return the following exit codes: +Returns: + SZ_OK - OK + SZ_ERROR_MEM - Memory allocation error + SZ_ERROR_PARAM - Incorrect paramater in props + SZ_ERROR_WRITE - Write callback error. + SZ_ERROR_PROGRESS - some break from progress callback + SZ_ERROR_THREAD - errors in multithreading functions (only for Mt version) +*/ + +typedef void * CLzmaEncHandle; + +CLzmaEncHandle LzmaEnc_Create(ISzAlloc *alloc); +void LzmaEnc_Destroy(CLzmaEncHandle p, ISzAlloc *alloc, ISzAlloc *allocBig); +SRes LzmaEnc_SetProps(CLzmaEncHandle p, const CLzmaEncProps *props); +SRes LzmaEnc_WriteProperties(CLzmaEncHandle p, Byte *properties, SizeT *size); +SRes LzmaEnc_Encode(CLzmaEncHandle p, ISeqOutStream *outStream, ISeqInStream *inStream, + ICompressProgress *progress, ISzAlloc *alloc, ISzAlloc *allocBig); +SRes LzmaEnc_MemEncode(CLzmaEncHandle p, Byte *dest, SizeT *destLen, const Byte *src, SizeT srcLen, + int writeEndMark, ICompressProgress *progress, ISzAlloc *alloc, ISzAlloc *allocBig); + +/* ---------- One Call Interface ---------- */ + +/* LzmaEncode +Return code: + SZ_OK - OK + SZ_ERROR_MEM - Memory allocation error + SZ_ERROR_PARAM - Incorrect paramater + SZ_ERROR_OUTPUT_EOF - output buffer overflow + SZ_ERROR_THREAD - errors in multithreading functions (only for Mt version) +*/ + +SRes LzmaEncode(Byte *dest, SizeT *destLen, const Byte *src, SizeT srcLen, + const CLzmaEncProps *props, Byte *propsEncoded, SizeT *propsSize, int writeEndMark, + ICompressProgress *progress, ISzAlloc *alloc, ISzAlloc *allocBig); + +#endif diff --git a/wiipax/client/Makefile b/wiipax/client/Makefile new file mode 100644 index 0000000..1f3ecfc --- /dev/null +++ b/wiipax/client/Makefile @@ -0,0 +1,14 @@ +CFLAGS = -Wall -W -Os -g +TARGET = wiipax +OBJS = LzFind.o LzmaEnc.o LzmaDec.o lzma.o main.o +OBJS += stub_mini.o stub_mini_debug.o +OBJS += stub_dkf.o stub_dkf_debug.o +OBJS += stub_dkfc.o stub_dkfc_debug.o + +NOMAPFILE = 1 + +include ../../common.mk + +install: all + install -m 755 $(TARGET) $(WIIDEV)/bin + diff --git a/wiipax/client/Types.h b/wiipax/client/Types.h new file mode 100644 index 0000000..2638196 --- /dev/null +++ b/wiipax/client/Types.h @@ -0,0 +1,208 @@ +/* Types.h -- Basic types +2008-11-23 : Igor Pavlov : Public domain */ + +#ifndef __7Z_TYPES_H +#define __7Z_TYPES_H + +#include + +#ifdef _WIN32 +#include +#endif + +#define SZ_OK 0 + +#define SZ_ERROR_DATA 1 +#define SZ_ERROR_MEM 2 +#define SZ_ERROR_CRC 3 +#define SZ_ERROR_UNSUPPORTED 4 +#define SZ_ERROR_PARAM 5 +#define SZ_ERROR_INPUT_EOF 6 +#define SZ_ERROR_OUTPUT_EOF 7 +#define SZ_ERROR_READ 8 +#define SZ_ERROR_WRITE 9 +#define SZ_ERROR_PROGRESS 10 +#define SZ_ERROR_FAIL 11 +#define SZ_ERROR_THREAD 12 + +#define SZ_ERROR_ARCHIVE 16 +#define SZ_ERROR_NO_ARCHIVE 17 + +typedef int SRes; + +#ifdef _WIN32 +typedef DWORD WRes; +#else +typedef int WRes; +#endif + +#ifndef RINOK +#define RINOK(x) { int __result__ = (x); if (__result__ != 0) return __result__; } +#endif + +typedef unsigned char Byte; +typedef short Int16; +typedef unsigned short UInt16; + +#ifdef _LZMA_UINT32_IS_ULONG +typedef long Int32; +typedef unsigned long UInt32; +#else +typedef int Int32; +typedef unsigned int UInt32; +#endif + +#ifdef _SZ_NO_INT_64 + +/* define _SZ_NO_INT_64, if your compiler doesn't support 64-bit integers. + NOTES: Some code will work incorrectly in that case! */ + +typedef long Int64; +typedef unsigned long UInt64; + +#else + +#if defined(_MSC_VER) || defined(__BORLANDC__) +typedef __int64 Int64; +typedef unsigned __int64 UInt64; +#else +typedef long long int Int64; +typedef unsigned long long int UInt64; +#endif + +#endif + +#ifdef _LZMA_NO_SYSTEM_SIZE_T +typedef UInt32 SizeT; +#else +typedef size_t SizeT; +#endif + +typedef int Bool; +#define True 1 +#define False 0 + + +#ifdef _MSC_VER + +#if _MSC_VER >= 1300 +#define MY_NO_INLINE __declspec(noinline) +#else +#define MY_NO_INLINE +#endif + +#define MY_CDECL __cdecl +#define MY_STD_CALL __stdcall +#define MY_FAST_CALL MY_NO_INLINE __fastcall + +#else + +#define MY_CDECL +#define MY_STD_CALL +#define MY_FAST_CALL + +#endif + + +/* The following interfaces use first parameter as pointer to structure */ + +typedef struct +{ + SRes (*Read)(void *p, void *buf, size_t *size); + /* if (input(*size) != 0 && output(*size) == 0) means end_of_stream. + (output(*size) < input(*size)) is allowed */ +} ISeqInStream; + +/* it can return SZ_ERROR_INPUT_EOF */ +SRes SeqInStream_Read(ISeqInStream *stream, void *buf, size_t size); +SRes SeqInStream_Read2(ISeqInStream *stream, void *buf, size_t size, SRes errorType); +SRes SeqInStream_ReadByte(ISeqInStream *stream, Byte *buf); + +typedef struct +{ + size_t (*Write)(void *p, const void *buf, size_t size); + /* Returns: result - the number of actually written bytes. + (result < size) means error */ +} ISeqOutStream; + +typedef enum +{ + SZ_SEEK_SET = 0, + SZ_SEEK_CUR = 1, + SZ_SEEK_END = 2 +} ESzSeek; + +typedef struct +{ + SRes (*Read)(void *p, void *buf, size_t *size); /* same as ISeqInStream::Read */ + SRes (*Seek)(void *p, Int64 *pos, ESzSeek origin); +} ISeekInStream; + +typedef struct +{ + SRes (*Look)(void *p, void **buf, size_t *size); + /* if (input(*size) != 0 && output(*size) == 0) means end_of_stream. + (output(*size) > input(*size)) is not allowed + (output(*size) < input(*size)) is allowed */ + SRes (*Skip)(void *p, size_t offset); + /* offset must be <= output(*size) of Look */ + + SRes (*Read)(void *p, void *buf, size_t *size); + /* reads directly (without buffer). It's same as ISeqInStream::Read */ + SRes (*Seek)(void *p, Int64 *pos, ESzSeek origin); +} ILookInStream; + +SRes LookInStream_LookRead(ILookInStream *stream, void *buf, size_t *size); +SRes LookInStream_SeekTo(ILookInStream *stream, UInt64 offset); + +/* reads via ILookInStream::Read */ +SRes LookInStream_Read2(ILookInStream *stream, void *buf, size_t size, SRes errorType); +SRes LookInStream_Read(ILookInStream *stream, void *buf, size_t size); + +#define LookToRead_BUF_SIZE (1 << 14) + +typedef struct +{ + ILookInStream s; + ISeekInStream *realStream; + size_t pos; + size_t size; + Byte buf[LookToRead_BUF_SIZE]; +} CLookToRead; + +void LookToRead_CreateVTable(CLookToRead *p, int lookahead); +void LookToRead_Init(CLookToRead *p); + +typedef struct +{ + ISeqInStream s; + ILookInStream *realStream; +} CSecToLook; + +void SecToLook_CreateVTable(CSecToLook *p); + +typedef struct +{ + ISeqInStream s; + ILookInStream *realStream; +} CSecToRead; + +void SecToRead_CreateVTable(CSecToRead *p); + +typedef struct +{ + SRes (*Progress)(void *p, UInt64 inSize, UInt64 outSize); + /* Returns: result. (result != SZ_OK) means break. + Value (UInt64)(Int64)-1 for size means unknown value. */ +} ICompressProgress; + +typedef struct +{ + void *(*Alloc)(void *p, size_t size); + void (*Free)(void *p, void *address); /* address can be 0 */ +} ISzAlloc; + +#define IAlloc_Alloc(p, size) (p)->Alloc((p), size) +#define IAlloc_Free(p, a) (p)->Free((p), a) + +#endif diff --git a/wiipax/client/common.h b/wiipax/client/common.h new file mode 100644 index 0000000..18eadd8 --- /dev/null +++ b/wiipax/client/common.h @@ -0,0 +1,43 @@ +#ifndef _COMMON_H_ +#define _COMMON_H_ + +typedef unsigned char u8; +typedef signed char s8; +typedef unsigned short u16; +typedef signed short s16; +typedef unsigned int u32; +typedef signed int s32; +typedef unsigned long long u64; +typedef signed long long s64; + +#define round_up(x,n) (-(-(x) & -(n))) + +#define die(...) { \ + fprintf(stderr, __VA_ARGS__); \ + fprintf(stderr, "\n"); exit(1); \ +} + +#define perrordie(x) { perror(x); exit(1); } + +#if BYTE_ORDER == BIG_ENDIAN + +#define be32(x) (x) +#define be16(x) (x) + +#else + +static inline u32 be32(const u32 v) { + return (v >> 24) | + ((v >> 8) & 0x0000FF00) | + ((v << 8) & 0x00FF0000) | + (v << 24); +} + +static inline u16 be16(const u16 v) { + return (v >> 8) | (v << 8); +} + +#endif /* BIG_ENDIAN */ + +#endif /* _COMMON_H_ */ + diff --git a/wiipax/client/elf_abi.h b/wiipax/client/elf_abi.h new file mode 100644 index 0000000..379355c --- /dev/null +++ b/wiipax/client/elf_abi.h @@ -0,0 +1,594 @@ +/* + * Copyright (c) 1995, 1996, 2001, 2002 + * Erik Theisen. All rights reserved. + * + * Redistribution and use in source and binary forms, with or without + * modification, are permitted provided that the following conditions + * are met: + * 1. Redistributions of source code must retain the above copyright + * notice, this list of conditions and the following disclaimer. + * 2. Redistributions in binary form must reproduce the above copyright + * notice, this list of conditions and the following disclaimer in the + * documentation and/or other materials provided with the distribution. + * 3. The name of the author may not be used to endorse or promote products + * derived from this software without specific prior written permission + * + * THIS SOFTWARE IS PROVIDED BY THE AUTHOR ``AS IS'' AND ANY EXPRESS OR + * IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE IMPLIED WARRANTIES + * OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE ARE DISCLAIMED. + * IN NO EVENT SHALL THE AUTHOR BE LIABLE FOR ANY DIRECT, INDIRECT, + * INCIDENTAL, SPECIAL, EXEMPLARY, OR CONSEQUENTIAL DAMAGES (INCLUDING, BUT + * NOT LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS OR SERVICES; LOSS OF USE, + * DATA, OR PROFITS; OR BUSINESS INTERRUPTION) HOWEVER CAUSED AND ON ANY + * THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT LIABILITY, OR TORT + * (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY OUT OF THE USE OF + * THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF SUCH DAMAGE. + */ + +/* + * This is the ELF ABI header file + * formerly known as "elf_abi.h". + */ + +#ifndef _ELF_ABI_H +#define _ELF_ABI_H + +#include "common.h" + +/* + * This version doesn't work for 64-bit ABIs - Erik. + */ + +/* + * These typedefs need to be handled better. + */ +typedef u32 Elf32_Addr; /* Unsigned program address */ +typedef u32 Elf32_Off; /* Unsigned file offset */ +typedef s32 Elf32_Sword; /* Signed large integer */ +typedef u32 Elf32_Word; /* Unsigned large integer */ +typedef u16 Elf32_Half; /* Unsigned medium integer */ + +/* e_ident[] identification indexes */ +#define EI_MAG0 0 /* file ID */ +#define EI_MAG1 1 /* file ID */ +#define EI_MAG2 2 /* file ID */ +#define EI_MAG3 3 /* file ID */ +#define EI_CLASS 4 /* file class */ +#define EI_DATA 5 /* data encoding */ +#define EI_VERSION 6 /* ELF header version */ +#define EI_OSABI 7 /* OS/ABI specific ELF extensions */ +#define EI_ABIVERSION 8 /* ABI target version */ +#define EI_PAD 9 /* start of pad bytes */ +#define EI_NIDENT 16 /* Size of e_ident[] */ + +/* e_ident[] magic number */ +#define ELFMAG0 0x7f /* e_ident[EI_MAG0] */ +#define ELFMAG1 'E' /* e_ident[EI_MAG1] */ +#define ELFMAG2 'L' /* e_ident[EI_MAG2] */ +#define ELFMAG3 'F' /* e_ident[EI_MAG3] */ +#define ELFMAG "\177ELF" /* magic */ +#define SELFMAG 4 /* size of magic */ + +/* e_ident[] file class */ +#define ELFCLASSNONE 0 /* invalid */ +#define ELFCLASS32 1 /* 32-bit objs */ +#define ELFCLASS64 2 /* 64-bit objs */ +#define ELFCLASSNUM 3 /* number of classes */ + +/* e_ident[] data encoding */ +#define ELFDATANONE 0 /* invalid */ +#define ELFDATA2LSB 1 /* Little-Endian */ +#define ELFDATA2MSB 2 /* Big-Endian */ +#define ELFDATANUM 3 /* number of data encode defines */ + +/* e_ident[] OS/ABI specific ELF extensions */ +#define ELFOSABI_NONE 0 /* No extension specified */ +#define ELFOSABI_HPUX 1 /* Hewlett-Packard HP-UX */ +#define ELFOSABI_NETBSD 2 /* NetBSD */ +#define ELFOSABI_LINUX 3 /* Linux */ +#define ELFOSABI_SOLARIS 6 /* Sun Solaris */ +#define ELFOSABI_AIX 7 /* AIX */ +#define ELFOSABI_IRIX 8 /* IRIX */ +#define ELFOSABI_FREEBSD 9 /* FreeBSD */ +#define ELFOSABI_TRU64 10 /* Compaq TRU64 UNIX */ +#define ELFOSABI_MODESTO 11 /* Novell Modesto */ +#define ELFOSABI_OPENBSD 12 /* OpenBSD */ +/* 64-255 Architecture-specific value range */ + +/* e_ident[] ABI Version */ +#define ELFABIVERSION 0 + +/* e_ident */ +#define IS_ELF(ehdr) ((ehdr).e_ident[EI_MAG0] == ELFMAG0 && \ + (ehdr).e_ident[EI_MAG1] == ELFMAG1 && \ + (ehdr).e_ident[EI_MAG2] == ELFMAG2 && \ + (ehdr).e_ident[EI_MAG3] == ELFMAG3) + +/* ELF Header */ +typedef struct elfhdr{ + unsigned char e_ident[EI_NIDENT]; /* ELF Identification */ + Elf32_Half e_type; /* object file type */ + Elf32_Half e_machine; /* machine */ + Elf32_Word e_version; /* object file version */ + Elf32_Addr e_entry; /* virtual entry point */ + Elf32_Off e_phoff; /* program header table offset */ + Elf32_Off e_shoff; /* section header table offset */ + Elf32_Word e_flags; /* processor-specific flags */ + Elf32_Half e_ehsize; /* ELF header size */ + Elf32_Half e_phentsize; /* program header entry size */ + Elf32_Half e_phnum; /* number of program header entries */ + Elf32_Half e_shentsize; /* section header entry size */ + Elf32_Half e_shnum; /* number of section header entries */ + Elf32_Half e_shstrndx; /* section header table's "section + header string table" entry offset */ +} Elf32_Ehdr; + +/* e_type */ +#define ET_NONE 0 /* No file type */ +#define ET_REL 1 /* relocatable file */ +#define ET_EXEC 2 /* executable file */ +#define ET_DYN 3 /* shared object file */ +#define ET_CORE 4 /* core file */ +#define ET_NUM 5 /* number of types */ +#define ET_LOOS 0xfe00 /* reserved range for operating */ +#define ET_HIOS 0xfeff /* system specific e_type */ +#define ET_LOPROC 0xff00 /* reserved range for processor */ +#define ET_HIPROC 0xffff /* specific e_type */ + +/* e_machine */ +#define EM_NONE 0 /* No Machine */ +#define EM_M32 1 /* AT&T WE 32100 */ +#define EM_SPARC 2 /* SPARC */ +#define EM_386 3 /* Intel 80386 */ +#define EM_68K 4 /* Motorola 68000 */ +#define EM_88K 5 /* Motorola 88000 */ +#if 0 +#define EM_486 6 /* RESERVED - was Intel 80486 */ +#endif +#define EM_860 7 /* Intel 80860 */ +#define EM_MIPS 8 /* MIPS R3000 Big-Endian only */ +#define EM_S370 9 /* IBM System/370 Processor */ +#define EM_MIPS_RS4_BE 10 /* MIPS R4000 Big-Endian */ +#if 0 +#define EM_SPARC64 11 /* RESERVED - was SPARC v9 + 64-bit unoffical */ +#endif +/* RESERVED 11-14 for future use */ +#define EM_PARISC 15 /* HPPA */ +/* RESERVED 16 for future use */ +#define EM_VPP500 17 /* Fujitsu VPP500 */ +#define EM_SPARC32PLUS 18 /* Enhanced instruction set SPARC */ +#define EM_960 19 /* Intel 80960 */ +#define EM_PPC 20 /* PowerPC */ +#define EM_PPC64 21 /* 64-bit PowerPC */ +#define EM_S390 22 /* IBM System/390 Processor */ +/* RESERVED 23-35 for future use */ +#define EM_V800 36 /* NEC V800 */ +#define EM_FR20 37 /* Fujitsu FR20 */ +#define EM_RH32 38 /* TRW RH-32 */ +#define EM_RCE 39 /* Motorola RCE */ +#define EM_ARM 40 /* Advanced Risc Machines ARM */ +#define EM_ALPHA 41 /* Digital Alpha */ +#define EM_SH 42 /* Hitachi SH */ +#define EM_SPARCV9 43 /* SPARC Version 9 */ +#define EM_TRICORE 44 /* Siemens TriCore embedded processor */ +#define EM_ARC 45 /* Argonaut RISC Core */ +#define EM_H8_300 46 /* Hitachi H8/300 */ +#define EM_H8_300H 47 /* Hitachi H8/300H */ +#define EM_H8S 48 /* Hitachi H8S */ +#define EM_H8_500 49 /* Hitachi H8/500 */ +#define EM_IA_64 50 /* Intel Merced */ +#define EM_MIPS_X 51 /* Stanford MIPS-X */ +#define EM_COLDFIRE 52 /* Motorola Coldfire */ +#define EM_68HC12 53 /* Motorola M68HC12 */ +#define EM_MMA 54 /* Fujitsu MMA Multimedia Accelerator*/ +#define EM_PCP 55 /* Siemens PCP */ +#define EM_NCPU 56 /* Sony nCPU embeeded RISC */ +#define EM_NDR1 57 /* Denso NDR1 microprocessor */ +#define EM_STARCORE 58 /* Motorola Start*Core processor */ +#define EM_ME16 59 /* Toyota ME16 processor */ +#define EM_ST100 60 /* STMicroelectronic ST100 processor */ +#define EM_TINYJ 61 /* Advanced Logic Corp. Tinyj emb.fam*/ +#define EM_X86_64 62 /* AMD x86-64 */ +#define EM_PDSP 63 /* Sony DSP Processor */ +/* RESERVED 64,65 for future use */ +#define EM_FX66 66 /* Siemens FX66 microcontroller */ +#define EM_ST9PLUS 67 /* STMicroelectronics ST9+ 8/16 mc */ +#define EM_ST7 68 /* STmicroelectronics ST7 8 bit mc */ +#define EM_68HC16 69 /* Motorola MC68HC16 microcontroller */ +#define EM_68HC11 70 /* Motorola MC68HC11 microcontroller */ +#define EM_68HC08 71 /* Motorola MC68HC08 microcontroller */ +#define EM_68HC05 72 /* Motorola MC68HC05 microcontroller */ +#define EM_SVX 73 /* Silicon Graphics SVx */ +#define EM_ST19 74 /* STMicroelectronics ST19 8 bit mc */ +#define EM_VAX 75 /* Digital VAX */ +#define EM_CHRIS 76 /* Axis Communications embedded proc. */ +#define EM_JAVELIN 77 /* Infineon Technologies emb. proc. */ +#define EM_FIREPATH 78 /* Element 14 64-bit DSP Processor */ +#define EM_ZSP 79 /* LSI Logic 16-bit DSP Processor */ +#define EM_MMIX 80 /* Donald Knuth's edu 64-bit proc. */ +#define EM_HUANY 81 /* Harvard University mach-indep objs */ +#define EM_PRISM 82 /* SiTera Prism */ +#define EM_AVR 83 /* Atmel AVR 8-bit microcontroller */ +#define EM_FR30 84 /* Fujitsu FR30 */ +#define EM_D10V 85 /* Mitsubishi DV10V */ +#define EM_D30V 86 /* Mitsubishi DV30V */ +#define EM_V850 87 /* NEC v850 */ +#define EM_M32R 88 /* Mitsubishi M32R */ +#define EM_MN10300 89 /* Matsushita MN10200 */ +#define EM_MN10200 90 /* Matsushita MN10200 */ +#define EM_PJ 91 /* picoJava */ +#define EM_NUM 92 /* number of machine types */ + +/* Version */ +#define EV_NONE 0 /* Invalid */ +#define EV_CURRENT 1 /* Current */ +#define EV_NUM 2 /* number of versions */ + +/* Section Header */ +typedef struct { + Elf32_Word sh_name; /* name - index into section header + string table section */ + Elf32_Word sh_type; /* type */ + Elf32_Word sh_flags; /* flags */ + Elf32_Addr sh_addr; /* address */ + Elf32_Off sh_offset; /* file offset */ + Elf32_Word sh_size; /* section size */ + Elf32_Word sh_link; /* section header table index link */ + Elf32_Word sh_info; /* extra information */ + Elf32_Word sh_addralign; /* address alignment */ + Elf32_Word sh_entsize; /* section entry size */ +} Elf32_Shdr; + +/* Special Section Indexes */ +#define SHN_UNDEF 0 /* undefined */ +#define SHN_LORESERVE 0xff00 /* lower bounds of reserved indexes */ +#define SHN_LOPROC 0xff00 /* reserved range for processor */ +#define SHN_HIPROC 0xff1f /* specific section indexes */ +#define SHN_LOOS 0xff20 /* reserved range for operating */ +#define SHN_HIOS 0xff3f /* specific semantics */ +#define SHN_ABS 0xfff1 /* absolute value */ +#define SHN_COMMON 0xfff2 /* common symbol */ +#define SHN_XINDEX 0xffff /* Index is an extra table */ +#define SHN_HIRESERVE 0xffff /* upper bounds of reserved indexes */ + +/* sh_type */ +#define SHT_NULL 0 /* inactive */ +#define SHT_PROGBITS 1 /* program defined information */ +#define SHT_SYMTAB 2 /* symbol table section */ +#define SHT_STRTAB 3 /* string table section */ +#define SHT_RELA 4 /* relocation section with addends*/ +#define SHT_HASH 5 /* symbol hash table section */ +#define SHT_DYNAMIC 6 /* dynamic section */ +#define SHT_NOTE 7 /* note section */ +#define SHT_NOBITS 8 /* no space section */ +#define SHT_REL 9 /* relation section without addends */ +#define SHT_SHLIB 10 /* reserved - purpose unknown */ +#define SHT_DYNSYM 11 /* dynamic symbol table section */ +#define SHT_INIT_ARRAY 14 /* Array of constructors */ +#define SHT_FINI_ARRAY 15 /* Array of destructors */ +#define SHT_PREINIT_ARRAY 16 /* Array of pre-constructors */ +#define SHT_GROUP 17 /* Section group */ +#define SHT_SYMTAB_SHNDX 18 /* Extended section indeces */ +#define SHT_NUM 19 /* number of section types */ +#define SHT_LOOS 0x60000000 /* Start OS-specific */ +#define SHT_HIOS 0x6fffffff /* End OS-specific */ +#define SHT_LOPROC 0x70000000 /* reserved range for processor */ +#define SHT_HIPROC 0x7fffffff /* specific section header types */ +#define SHT_LOUSER 0x80000000 /* reserved range for application */ +#define SHT_HIUSER 0xffffffff /* specific indexes */ + +/* Section names */ +#define ELF_BSS ".bss" /* uninitialized data */ +#define ELF_COMMENT ".comment" /* version control information */ +#define ELF_DATA ".data" /* initialized data */ +#define ELF_DATA1 ".data1" /* initialized data */ +#define ELF_DEBUG ".debug" /* debug */ +#define ELF_DYNAMIC ".dynamic" /* dynamic linking information */ +#define ELF_DYNSTR ".dynstr" /* dynamic string table */ +#define ELF_DYNSYM ".dynsym" /* dynamic symbol table */ +#define ELF_FINI ".fini" /* termination code */ +#define ELF_FINI_ARRAY ".fini_array" /* Array of destructors */ +#define ELF_GOT ".got" /* global offset table */ +#define ELF_HASH ".hash" /* symbol hash table */ +#define ELF_INIT ".init" /* initialization code */ +#define ELF_INIT_ARRAY ".init_array" /* Array of constuctors */ +#define ELF_INTERP ".interp" /* Pathname of program interpreter */ +#define ELF_LINE ".line" /* Symbolic line numnber information */ +#define ELF_NOTE ".note" /* Contains note section */ +#define ELF_PLT ".plt" /* Procedure linkage table */ +#define ELF_PREINIT_ARRAY ".preinit_array" /* Array of pre-constructors */ +#define ELF_REL_DATA ".rel.data" /* relocation data */ +#define ELF_REL_FINI ".rel.fini" /* relocation termination code */ +#define ELF_REL_INIT ".rel.init" /* relocation initialization code */ +#define ELF_REL_DYN ".rel.dyn" /* relocaltion dynamic link info */ +#define ELF_REL_RODATA ".rel.rodata" /* relocation read-only data */ +#define ELF_REL_TEXT ".rel.text" /* relocation code */ +#define ELF_RODATA ".rodata" /* read-only data */ +#define ELF_RODATA1 ".rodata1" /* read-only data */ +#define ELF_SHSTRTAB ".shstrtab" /* section header string table */ +#define ELF_STRTAB ".strtab" /* string table */ +#define ELF_SYMTAB ".symtab" /* symbol table */ +#define ELF_SYMTAB_SHNDX ".symtab_shndx"/* symbol table section index */ +#define ELF_TBSS ".tbss" /* thread local uninit data */ +#define ELF_TDATA ".tdata" /* thread local init data */ +#define ELF_TDATA1 ".tdata1" /* thread local init data */ +#define ELF_TEXT ".text" /* code */ + +/* Section Attribute Flags - sh_flags */ +#define SHF_WRITE 0x1 /* Writable */ +#define SHF_ALLOC 0x2 /* occupies memory */ +#define SHF_EXECINSTR 0x4 /* executable */ +#define SHF_MERGE 0x10 /* Might be merged */ +#define SHF_STRINGS 0x20 /* Contains NULL terminated strings */ +#define SHF_INFO_LINK 0x40 /* sh_info contains SHT index */ +#define SHF_LINK_ORDER 0x80 /* Preserve order after combining*/ +#define SHF_OS_NONCONFORMING 0x100 /* Non-standard OS specific handling */ +#define SHF_GROUP 0x200 /* Member of section group */ +#define SHF_TLS 0x400 /* Thread local storage */ +#define SHF_MASKOS 0x0ff00000 /* OS specific */ +#define SHF_MASKPROC 0xf0000000 /* reserved bits for processor */ + /* specific section attributes */ + +/* Section Group Flags */ +#define GRP_COMDAT 0x1 /* COMDAT group */ +#define GRP_MASKOS 0x0ff00000 /* Mask OS specific flags */ +#define GRP_MASKPROC 0xf0000000 /* Mask processor specific flags */ + +/* Symbol Table Entry */ +typedef struct elf32_sym { + Elf32_Word st_name; /* name - index into string table */ + Elf32_Addr st_value; /* symbol value */ + Elf32_Word st_size; /* symbol size */ + unsigned char st_info; /* type and binding */ + unsigned char st_other; /* 0 - no defined meaning */ + Elf32_Half st_shndx; /* section header index */ +} Elf32_Sym; + +/* Symbol table index */ +#define STN_UNDEF 0 /* undefined */ + +/* Extract symbol info - st_info */ +#define ELF32_ST_BIND(x) ((x) >> 4) +#define ELF32_ST_TYPE(x) (((unsigned int) x) & 0xf) +#define ELF32_ST_INFO(b,t) (((b) << 4) + ((t) & 0xf)) +#define ELF32_ST_VISIBILITY(x) ((x) & 0x3) + +/* Symbol Binding - ELF32_ST_BIND - st_info */ +#define STB_LOCAL 0 /* Local symbol */ +#define STB_GLOBAL 1 /* Global symbol */ +#define STB_WEAK 2 /* like global - lower precedence */ +#define STB_NUM 3 /* number of symbol bindings */ +#define STB_LOOS 10 /* reserved range for operating */ +#define STB_HIOS 12 /* system specific symbol bindings */ +#define STB_LOPROC 13 /* reserved range for processor */ +#define STB_HIPROC 15 /* specific symbol bindings */ + +/* Symbol type - ELF32_ST_TYPE - st_info */ +#define STT_NOTYPE 0 /* not specified */ +#define STT_OBJECT 1 /* data object */ +#define STT_FUNC 2 /* function */ +#define STT_SECTION 3 /* section */ +#define STT_FILE 4 /* file */ +#define STT_NUM 5 /* number of symbol types */ +#define STT_TLS 6 /* Thread local storage symbol */ +#define STT_LOOS 10 /* reserved range for operating */ +#define STT_HIOS 12 /* system specific symbol types */ +#define STT_LOPROC 13 /* reserved range for processor */ +#define STT_HIPROC 15 /* specific symbol types */ + +/* Symbol visibility - ELF32_ST_VISIBILITY - st_other */ +#define STV_DEFAULT 0 /* Normal visibility rules */ +#define STV_INTERNAL 1 /* Processor specific hidden class */ +#define STV_HIDDEN 2 /* Symbol unavailable in other mods */ +#define STV_PROTECTED 3 /* Not preemptible, not exported */ + + +/* Relocation entry with implicit addend */ +typedef struct +{ + Elf32_Addr r_offset; /* offset of relocation */ + Elf32_Word r_info; /* symbol table index and type */ +} Elf32_Rel; + +/* Relocation entry with explicit addend */ +typedef struct +{ + Elf32_Addr r_offset; /* offset of relocation */ + Elf32_Word r_info; /* symbol table index and type */ + Elf32_Sword r_addend; +} Elf32_Rela; + +/* Extract relocation info - r_info */ +#define ELF32_R_SYM(i) ((i) >> 8) +#define ELF32_R_TYPE(i) ((unsigned char) (i)) +#define ELF32_R_INFO(s,t) (((s) << 8) + (unsigned char)(t)) + +/* Program Header */ +typedef struct { + Elf32_Word p_type; /* segment type */ + Elf32_Off p_offset; /* segment offset */ + Elf32_Addr p_vaddr; /* virtual address of segment */ + Elf32_Addr p_paddr; /* physical address - ignored? */ + Elf32_Word p_filesz; /* number of bytes in file for seg. */ + Elf32_Word p_memsz; /* number of bytes in mem. for seg. */ + Elf32_Word p_flags; /* flags */ + Elf32_Word p_align; /* memory alignment */ +} Elf32_Phdr; + +/* Segment types - p_type */ +#define PT_NULL 0 /* unused */ +#define PT_LOAD 1 /* loadable segment */ +#define PT_DYNAMIC 2 /* dynamic linking section */ +#define PT_INTERP 3 /* the RTLD */ +#define PT_NOTE 4 /* auxiliary information */ +#define PT_SHLIB 5 /* reserved - purpose undefined */ +#define PT_PHDR 6 /* program header */ +#define PT_TLS 7 /* Thread local storage template */ +#define PT_NUM 8 /* Number of segment types */ +#define PT_LOOS 0x60000000 /* reserved range for operating */ +#define PT_HIOS 0x6fffffff /* system specific segment types */ +#define PT_LOPROC 0x70000000 /* reserved range for processor */ +#define PT_HIPROC 0x7fffffff /* specific segment types */ + +/* Segment flags - p_flags */ +#define PF_X 0x1 /* Executable */ +#define PF_W 0x2 /* Writable */ +#define PF_R 0x4 /* Readable */ +#define PF_MASKOS 0x0ff00000 /* OS specific segment flags */ +#define PF_MASKPROC 0xf0000000 /* reserved bits for processor */ + /* specific segment flags */ +/* Dynamic structure */ +typedef struct +{ + Elf32_Sword d_tag; /* controls meaning of d_val */ + union + { + Elf32_Word d_val; /* Multiple meanings - see d_tag */ + Elf32_Addr d_ptr; /* program virtual address */ + } d_un; +} Elf32_Dyn; + +extern Elf32_Dyn _DYNAMIC[]; + +/* Dynamic Array Tags - d_tag */ +#define DT_NULL 0 /* marks end of _DYNAMIC array */ +#define DT_NEEDED 1 /* string table offset of needed lib */ +#define DT_PLTRELSZ 2 /* size of relocation entries in PLT */ +#define DT_PLTGOT 3 /* address PLT/GOT */ +#define DT_HASH 4 /* address of symbol hash table */ +#define DT_STRTAB 5 /* address of string table */ +#define DT_SYMTAB 6 /* address of symbol table */ +#define DT_RELA 7 /* address of relocation table */ +#define DT_RELASZ 8 /* size of relocation table */ +#define DT_RELAENT 9 /* size of relocation entry */ +#define DT_STRSZ 10 /* size of string table */ +#define DT_SYMENT 11 /* size of symbol table entry */ +#define DT_INIT 12 /* address of initialization func. */ +#define DT_FINI 13 /* address of termination function */ +#define DT_SONAME 14 /* string table offset of shared obj */ +#define DT_RPATH 15 /* string table offset of library + search path */ +#define DT_SYMBOLIC 16 /* start sym search in shared obj. */ +#define DT_REL 17 /* address of rel. tbl. w addends */ +#define DT_RELSZ 18 /* size of DT_REL relocation table */ +#define DT_RELENT 19 /* size of DT_REL relocation entry */ +#define DT_PLTREL 20 /* PLT referenced relocation entry */ +#define DT_DEBUG 21 /* bugger */ +#define DT_TEXTREL 22 /* Allow rel. mod. to unwritable seg */ +#define DT_JMPREL 23 /* add. of PLT's relocation entries */ +#define DT_BIND_NOW 24 /* Process relocations of object */ +#define DT_INIT_ARRAY 25 /* Array with addresses of init fct */ +#define DT_FINI_ARRAY 26 /* Array with addresses of fini fct */ +#define DT_INIT_ARRAYSZ 27 /* Size in bytes of DT_INIT_ARRAY */ +#define DT_FINI_ARRAYSZ 28 /* Size in bytes of DT_FINI_ARRAY */ +#define DT_RUNPATH 29 /* Library search path */ +#define DT_FLAGS 30 /* Flags for the object being loaded */ +#define DT_ENCODING 32 /* Start of encoded range */ +#define DT_PREINIT_ARRAY 32 /* Array with addresses of preinit fct*/ +#define DT_PREINIT_ARRAYSZ 33 /* size in bytes of DT_PREINIT_ARRAY */ +#define DT_NUM 34 /* Number used. */ +#define DT_LOOS 0x60000000 /* reserved range for OS */ +#define DT_HIOS 0x6fffffff /* specific dynamic array tags */ +#define DT_LOPROC 0x70000000 /* reserved range for processor */ +#define DT_HIPROC 0x7fffffff /* specific dynamic array tags */ + +/* Dynamic Tag Flags - d_un.d_val */ +#define DF_ORIGIN 0x01 /* Object may use DF_ORIGIN */ +#define DF_SYMBOLIC 0x02 /* Symbol resolutions starts here */ +#define DF_TEXTREL 0x04 /* Object contains text relocations */ +#define DF_BIND_NOW 0x08 /* No lazy binding for this object */ +#define DF_STATIC_TLS 0x10 /* Static thread local storage */ + +/* Standard ELF hashing function */ +unsigned long elf_hash(const unsigned char *name); + +#define ELF_TARG_VER 1 /* The ver for which this code is intended */ + +/* + * XXX - PowerPC defines really don't belong in here, + * but we'll put them in for simplicity. + */ + +/* Values for Elf32/64_Ehdr.e_flags. */ +#define EF_PPC_EMB 0x80000000 /* PowerPC embedded flag */ + +/* Cygnus local bits below */ +#define EF_PPC_RELOCATABLE 0x00010000 /* PowerPC -mrelocatable flag*/ +#define EF_PPC_RELOCATABLE_LIB 0x00008000 /* PowerPC -mrelocatable-lib + flag */ + +/* PowerPC relocations defined by the ABIs */ +#define R_PPC_NONE 0 +#define R_PPC_ADDR32 1 /* 32bit absolute address */ +#define R_PPC_ADDR24 2 /* 26bit address, 2 bits ignored. */ +#define R_PPC_ADDR16 3 /* 16bit absolute address */ +#define R_PPC_ADDR16_LO 4 /* lower 16bit of absolute address */ +#define R_PPC_ADDR16_HI 5 /* high 16bit of absolute address */ +#define R_PPC_ADDR16_HA 6 /* adjusted high 16bit */ +#define R_PPC_ADDR14 7 /* 16bit address, 2 bits ignored */ +#define R_PPC_ADDR14_BRTAKEN 8 +#define R_PPC_ADDR14_BRNTAKEN 9 +#define R_PPC_REL24 10 /* PC relative 26 bit */ +#define R_PPC_REL14 11 /* PC relative 16 bit */ +#define R_PPC_REL14_BRTAKEN 12 +#define R_PPC_REL14_BRNTAKEN 13 +#define R_PPC_GOT16 14 +#define R_PPC_GOT16_LO 15 +#define R_PPC_GOT16_HI 16 +#define R_PPC_GOT16_HA 17 +#define R_PPC_PLTREL24 18 +#define R_PPC_COPY 19 +#define R_PPC_GLOB_DAT 20 +#define R_PPC_JMP_SLOT 21 +#define R_PPC_RELATIVE 22 +#define R_PPC_LOCAL24PC 23 +#define R_PPC_UADDR32 24 +#define R_PPC_UADDR16 25 +#define R_PPC_REL32 26 +#define R_PPC_PLT32 27 +#define R_PPC_PLTREL32 28 +#define R_PPC_PLT16_LO 29 +#define R_PPC_PLT16_HI 30 +#define R_PPC_PLT16_HA 31 +#define R_PPC_SDAREL16 32 +#define R_PPC_SECTOFF 33 +#define R_PPC_SECTOFF_LO 34 +#define R_PPC_SECTOFF_HI 35 +#define R_PPC_SECTOFF_HA 36 +/* Keep this the last entry. */ +#define R_PPC_NUM 37 + +/* The remaining relocs are from the Embedded ELF ABI, and are not + in the SVR4 ELF ABI. */ +#define R_PPC_EMB_NADDR32 101 +#define R_PPC_EMB_NADDR16 102 +#define R_PPC_EMB_NADDR16_LO 103 +#define R_PPC_EMB_NADDR16_HI 104 +#define R_PPC_EMB_NADDR16_HA 105 +#define R_PPC_EMB_SDAI16 106 +#define R_PPC_EMB_SDA2I16 107 +#define R_PPC_EMB_SDA2REL 108 +#define R_PPC_EMB_SDA21 109 /* 16 bit offset in SDA */ +#define R_PPC_EMB_MRKREF 110 +#define R_PPC_EMB_RELSEC16 111 +#define R_PPC_EMB_RELST_LO 112 +#define R_PPC_EMB_RELST_HI 113 +#define R_PPC_EMB_RELST_HA 114 +#define R_PPC_EMB_BIT_FLD 115 +#define R_PPC_EMB_RELSDA 116 /* 16 bit relative offset in SDA */ + +/* Diab tool relocations. */ +#define R_PPC_DIAB_SDA21_LO 180 /* like EMB_SDA21, but lower 16 bit */ +#define R_PPC_DIAB_SDA21_HI 181 /* like EMB_SDA21, but high 16 bit */ +#define R_PPC_DIAB_SDA21_HA 182 /* like EMB_SDA21, adjusted high 16 */ +#define R_PPC_DIAB_RELSDA_LO 183 /* like EMB_RELSDA, but lower 16 bit */ +#define R_PPC_DIAB_RELSDA_HI 184 /* like EMB_RELSDA, but high 16 bit */ +#define R_PPC_DIAB_RELSDA_HA 185 /* like EMB_RELSDA, adjusted high 16 */ + +/* This is a phony reloc to handle any old fashioned TOC16 references + that may still be in object files. */ +#define R_PPC_TOC16 255 + +#endif /* _ELF_H */ + diff --git a/wiipax/client/lzma.c b/wiipax/client/lzma.c new file mode 100644 index 0000000..183b17b --- /dev/null +++ b/wiipax/client/lzma.c @@ -0,0 +1,123 @@ +#include +#include +#include +#include +#include +#include + +#include "common.h" +#include "lzma.h" + +#include "LzmaDec.h" + +static void *lz_malloc(void *p, size_t size) { + (void) p; + return malloc(size); +} + +static void lz_free(void *p, void *address) { + (void) p; + free(address); +} + +static ISzAlloc lz_alloc = { lz_malloc, lz_free }; + +static lzma_t *lzma_alloc(const u32 len) { + lzma_t *lzma; + + lzma = (lzma_t *) calloc(1, sizeof(lzma_t)); + + if (!lzma) + die("Failed to alloc 0x%x bytes", (u32) sizeof(lzma_t)); + + lzma->data = calloc(1, len); + if (!lzma->data) + die("Failed to alloc 0x%x bytes", len); + + return lzma; +} + +void lzma_free(lzma_t *lzma) { + free(lzma->data); + free(lzma); +} + +lzma_t *lzma_compress(const u8 *src, const u32 len) { + lzma_t *lzma; + CLzmaEncProps props; + size_t len_out; + size_t len_props = LZMA_PROPS_SIZE; + SRes res; + + printf("Compressing..."); + fflush(stdout); + + lzma = lzma_alloc(2*len); + + LzmaEncProps_Init(&props); + props.level = 7; + LzmaEncProps_Normalize(&props); + + len_out = 2*len; + res = LzmaEncode(lzma->data, &len_out, src, len, &props, lzma->props, + &len_props, 1, NULL, &lz_alloc, &lz_alloc); + + if (res != SZ_OK) + die(" failed (%d)", res); + + if (len_props != LZMA_PROPS_SIZE) + die(" failed: encoder propsize %u != %u", (u32) len_props, + LZMA_PROPS_SIZE); + + printf(" %u -> %u = %3.2f%%\n", len, (u32) len_out, + 100.0 * (float) len_out / (float) len); + + lzma->len_in = len; + lzma->len_out = len_out; + + return lzma; +} + +void lzma_decode(const lzma_t *lzma, u8 *dst) { + SizeT len_in; + SizeT len_out; + ELzmaStatus status; + SRes res; + + len_in = lzma->len_out; + len_out = lzma->len_in; + + res = LzmaDecode(dst, &len_out, lzma->data, &len_in, lzma->props, + LZMA_PROPS_SIZE, LZMA_FINISH_END, &status, &lz_alloc); + + if (res != SZ_OK) + die("Error decoding %d (%u)\n", res, status); + + *dst = len_out; +} + +void lzma_write(const char *filename, const lzma_t *lzma) { + int fd; + int i; + + fd = open(filename, O_WRONLY | O_CREAT | O_TRUNC, 0755); + if (fd < 0) + perrordie("Could not open file"); + + if (write(fd, lzma->props, LZMA_PROPS_SIZE) != LZMA_PROPS_SIZE) + perrordie("Could not write to file"); + + u64 size = lzma->len_in; + for (i = 0; i < 8; ++i) { + u8 j = size & 0xff; + size >>= 8; + if (write(fd, &j, 1) != 1) + perrordie("Could not write to file"); + } + + if (write(fd, lzma->data, lzma->len_out) != lzma->len_out) + perrordie("Could not write to file"); + + close(fd); +} + diff --git a/wiipax/client/lzma.h b/wiipax/client/lzma.h new file mode 100644 index 0000000..d8badf0 --- /dev/null +++ b/wiipax/client/lzma.h @@ -0,0 +1,20 @@ +#ifndef _LZMA_H_ +#define _LZMA_H_ + +#include "common.h" +#include "LzmaEnc.h" + +typedef struct { + u32 len_in; + u32 len_out; + u8 props[LZMA_PROPS_SIZE]; + u8 *data; +} lzma_t; + +lzma_t *lzma_compress(const u8 *src, const u32 len); +void lzma_decode(const lzma_t *lzma, u8 *dst); +void lzma_write(const char *filename, const lzma_t *lzma); +void lzma_free(lzma_t *lzma); + +#endif /* _LZMA_H_ */ + diff --git a/wiipax/client/main.c b/wiipax/client/main.c new file mode 100644 index 0000000..0cd231d --- /dev/null +++ b/wiipax/client/main.c @@ -0,0 +1,517 @@ +#include +#include +#include +#include +#include +#include +#include +#include + +#include "common.h" +#include "lzma.h" +#include "elf_abi.h" + +#define ALIGNOFFSET 32 + +extern int stub_mini_elf_len; +extern char stub_mini_elf[]; +extern int stub_mini_debug_elf_len; +extern char stub_mini_debug_elf[]; +extern int stub_dkf_elf_len; +extern char stub_dkf_elf[]; +extern int stub_dkf_debug_elf_len; +extern char stub_dkf_debug_elf[]; +extern int stub_dkfc_elf_len; +extern char stub_dkfc_elf[]; +extern int stub_dkfc_debug_elf_len; +extern char stub_dkfc_debug_elf[]; + +typedef struct { + const char *name; + const int *len; + const u8 *data; +} stub_t; + +static const stub_t stubs[] = { + { "mini", &stub_mini_elf_len, (u8 *) stub_mini_elf }, + { "mini_debug", &stub_mini_debug_elf_len, (u8 *) stub_mini_debug_elf }, + { "devkitfail", &stub_dkf_elf_len, (u8 *) stub_dkf_elf }, + { "devkitfail_debug", &stub_dkf_debug_elf_len, (u8 *) stub_dkf_debug_elf }, + { "dkfailchannel", &stub_dkfc_elf_len, (u8 *) stub_dkfc_elf }, + { "dkfailchannel_debug", &stub_dkfc_debug_elf_len, (u8 *) stub_dkfc_debug_elf }, + { NULL, NULL, NULL } +}; + +typedef struct { + u8 *data; + u32 len; + Elf32_Ehdr *ehdr; + Elf32_Phdr *phdrs; + Elf32_Shdr *shdrs; +} elf_t; + +typedef struct { + u32 dataptr; + u32 len_in; + u32 len_out; + u8 props[LZMA_PROPS_SIZE]; +} __attribute__((packed)) payload_t; + +static inline u8 *phdr_data(const elf_t *elf, const u16 ndx, const u32 off) { + return &elf->data[be32(elf->phdrs[ndx].p_offset) + off]; +} + +static elf_t *read_stub(const char *name) { + elf_t *elf = (elf_t *) calloc(1, sizeof(elf_t)); + if (!elf) + die("Error allocating %u bytes", (u32) sizeof(elf_t)); + + int i = 0; + while (stubs[i].name) { + if (!strcmp(name, stubs[i].name)) { + printf("Using stub '%s'\n", name); + elf->len = *(stubs[i].len); + elf->data = (u8 *) stubs[i].data; + + return elf; + } + ++i; + } + + die("Unknown stub '%s'", name); + return NULL; +} + +static elf_t *read_elf(const char *filename) { + int fd; + struct stat st; + elf_t *elf; + + printf("Reading %s\n", filename); + + fd = open(filename, O_RDONLY); + if (fd < 0) + perrordie("Could not open ELF file"); + + if (fstat(fd, &st)) + perrordie("Could not stat ELF file"); + + if ((u32) st.st_size < sizeof(Elf32_Ehdr)) + die("File too short for ELF"); + + elf = (elf_t *) calloc(1, sizeof(elf_t)); + if (!elf) + die("Error allocating %u bytes", (u32) sizeof(elf_t)); + + elf->len = st.st_size; + elf->data = (u8 *) malloc(elf->len); + + if (!elf->data) + die("Error allocating %u bytes", elf->len); + + if (read(fd, elf->data, elf->len) != elf->len) + perrordie("Could not read from file"); + + close(fd); + + return elf; +} + +static void write_elf(const char *filename, const elf_t *elf) { + int fd; + + fd = open(filename, O_WRONLY | O_CREAT | O_TRUNC, 0755); + if (fd < 0) + perrordie("Could not open ELF file"); + + if (write(fd, elf->data, elf->len) != elf->len) + perrordie("Could not write ELF file"); + + close(fd); +} + +static void free_elf(elf_t *elf) { + free(elf->data); + free(elf); +} + +static void check_elf(elf_t *elf) { + if (elf->len < sizeof(Elf32_Phdr)) + die("Too short for an ELF"); + + Elf32_Ehdr *ehdr = (Elf32_Ehdr *) elf->data; + + if (!IS_ELF(*ehdr)) + die("Not an ELF"); + if (ehdr->e_ident[EI_CLASS] != ELFCLASS32) + die("Invalid ELF class"); + if (ehdr->e_ident[EI_DATA] != ELFDATA2MSB) + die("Invalid ELF byte order"); + if (ehdr->e_ident[EI_VERSION] != EV_CURRENT) + die("Invalid ELF ident version"); + if (be16(ehdr->e_type) != ET_EXEC) + die("ELF is not an executable"); + if (be16(ehdr->e_machine) != EM_PPC) + die("Machine is not PowerPC"); + if (be32(ehdr->e_version) != EV_CURRENT) + die("Invalid ELF version"); + if (!be32(ehdr->e_entry)) + die("ELF has no entrypoint"); +} + +static void init_elf(elf_t *elf, int sections) { + elf->ehdr = (Elf32_Ehdr *) elf->data; + + u16 num = be16(elf->ehdr->e_phnum); + u32 off = be32(elf->ehdr->e_phoff); + + if (!num || !off) + die("ELF has no program headers"); + + if (be16(elf->ehdr->e_phentsize) != sizeof(Elf32_Phdr)) + die("Invalid program header entry size"); + + if ((off + num * sizeof(Elf32_Phdr)) > elf->len) + die("Program headers out of bounds"); + + elf->phdrs = (Elf32_Phdr *) &elf->data[off]; + + num = be16(elf->ehdr->e_shnum); + off = be32(elf->ehdr->e_shoff); + + if (!num || !off) { + if (!sections) { + elf->shdrs = NULL; + return; + } + + die("ELF has no section headers"); + } + + if (be16(elf->ehdr->e_shentsize) != sizeof(Elf32_Shdr)) + die("Invalid section header entry size"); + + if ((off + num * sizeof(Elf32_Shdr)) > elf->len) + die("Section headers out of bounds"); + + elf->shdrs = (Elf32_Shdr *) &elf->data[off]; +} + +static u32 find_payload_offset(const elf_t *elf) { + u16 shnum = be16(elf->ehdr->e_shnum); + + u16 shstrndx = be16(elf->ehdr->e_shstrndx); + if (!shstrndx || shstrndx > shnum) + die("Invalid .shstrtab index"); + + u32 off = be32(elf->shdrs[shstrndx].sh_offset); + u32 size = be32(elf->shdrs[shstrndx].sh_size); + + if (off + size > elf->len - 1) + die(".shstrtab section out of bounds"); + + const char *shstr = (const char *) &elf->data[off]; + + u16 i; + for (i = 0; i < shnum; ++i) { + off = be32(elf->shdrs[i].sh_name); + + if (off > size) + die("Section #%u name out of .shstrtab bounds", i); + + if (!strcmp(&shstr[off], ".payload")) { + printf(".payload section found: #%u\n", i); + return be32(elf->shdrs[i].sh_offset); + } + } + + die(".payload section not present"); +} + +static void map_file_offset(const elf_t *elf, const u32 offset, + u16 *phdrndx, u32 *phdroff) { + u16 phnum = be16(elf->ehdr->e_phnum); + + u16 i; + for (i = 0; i < phnum; ++i) { + if (be32(elf->phdrs[i].p_type) != PT_LOAD) + continue; + + if (be32(elf->phdrs[i].p_filesz) < 1) + continue; + + u32 poff = be32(elf->phdrs[i].p_offset); + u32 psize = be32(elf->phdrs[i].p_filesz); + + if (offset >= poff && offset <= poff + psize) { + *phdrndx = i; + *phdroff = offset - poff; + printf("Mapped payload to program header [%u] 0x%06x\n", + *phdrndx, *phdroff); + return; + } + } + + die("File offset 0x%x is not part of any PT_LOAD program header", offset); +} + +static elf_t *strip_elf(const elf_t *elf, u16 *phdrndx) { + elf_t *out; + u32 pos; + u16 count; + u16 phnum = be16(elf->ehdr->e_phnum); + u16 i; + + count = 0; + pos = round_up(sizeof(Elf32_Ehdr), ALIGNOFFSET); + for (i = 0; i < phnum; ++i) { + if (be32(elf->phdrs[i].p_type) != PT_LOAD) + continue; + + if (be32(elf->phdrs[i].p_filesz) < 1) + continue; + + if (be32(elf->phdrs[i].p_memsz) < 1) + continue; + + pos += round_up(be32(elf->phdrs[i].p_filesz), ALIGNOFFSET); + count++; + } + pos += round_up(count * sizeof(Elf32_Phdr), ALIGNOFFSET); + + if (pos > 20 * 1024 * 1024) + die("ELF too big, even after stripping (0x%x)", pos); + + printf("Stripping ELF from 0x%x to 0x%x bytes\n", elf->len, pos); + + out = (elf_t *) calloc(1, sizeof(elf_t)); + if (!out) + die("Error allocating %u bytes", (u32) sizeof(elf_t)); + + out->len = pos; + out->data = calloc(1, pos); + if (!out->data) + die("Error allocating %u bytes", pos); + + out->ehdr = (Elf32_Ehdr *) out->data; + pos = round_up(sizeof(Elf32_Ehdr), ALIGNOFFSET); + out->ehdr->e_ident[EI_MAG0] = ELFMAG0; + out->ehdr->e_ident[EI_MAG1] = ELFMAG1; + out->ehdr->e_ident[EI_MAG2] = ELFMAG2; + out->ehdr->e_ident[EI_MAG3] = ELFMAG3; + out->ehdr->e_ident[EI_CLASS] = ELFCLASS32; + out->ehdr->e_ident[EI_DATA] = ELFDATA2MSB; + out->ehdr->e_ident[EI_VERSION] = EV_CURRENT; + out->ehdr->e_type = be16(ET_EXEC); + out->ehdr->e_machine = be16(EM_PPC); + out->ehdr->e_version = be32(EV_CURRENT); + out->ehdr->e_entry = elf->ehdr->e_entry; + out->ehdr->e_phoff = be32(pos); + out->ehdr->e_phentsize = be16(sizeof(Elf32_Phdr)); + out->ehdr->e_phnum = be16(count); + out->ehdr->e_shentsize = be16(sizeof(Elf32_Shdr)); + + out->phdrs = (Elf32_Phdr *) &out->data[pos]; + pos += round_up(count * sizeof(Elf32_Phdr), ALIGNOFFSET); + + count = 0; + int found = 0; + for (i = 0; i < phnum; ++i) { + if (be32(elf->phdrs[i].p_type) != PT_LOAD) + continue; + + if (be32(elf->phdrs[i].p_filesz) < 1) + continue; + + if (be32(elf->phdrs[i].p_memsz) < 1) + continue; + + if (phdrndx && i == *phdrndx) { + *phdrndx = count; + found = 1; + } + + out->phdrs[count].p_type = elf->phdrs[i].p_type; + out->phdrs[count].p_offset = be32(pos); + out->phdrs[count].p_vaddr = elf->phdrs[i].p_vaddr; + out->phdrs[count].p_paddr = elf->phdrs[i].p_paddr; + out->phdrs[count].p_filesz = elf->phdrs[i].p_filesz; + out->phdrs[count].p_memsz = elf->phdrs[i].p_memsz; + out->phdrs[count].p_flags = elf->phdrs[i].p_flags; + out->phdrs[count].p_align = elf->phdrs[i].p_align; + + u32 p_offset = be32(elf->phdrs[i].p_offset); + u32 p_filesz = be32(elf->phdrs[i].p_filesz); + + printf(" PHDR[%u] 0x%08x 0x%06x -> [%u] 0x%06x\n", + i, p_offset, p_filesz, count, pos); + + memcpy(&out->data[pos], &elf->data[p_offset], p_filesz); + pos += round_up(p_filesz, ALIGNOFFSET); + + count++; + } + + if (phdrndx && !found) + die("PHDR #%u not part of the stripped ELF", *phdrndx); + + return out; +} + +static elf_t *inject_elf(elf_t *dst, const u8 *src, const u32 len, + u32 *dataaddr, u8 **dataptr) { + u16 phdrndx = be16(dst->ehdr->e_phnum) - 1; + Elf32_Phdr *phdr = &dst->phdrs[phdrndx]; + + if (phdr->p_filesz != phdr->p_memsz) + die("File size does not match the memory size for the last PHDR"); + + u32 pos = be32(phdr->p_vaddr) + be32(phdr->p_filesz); + u32 pos_a = round_up(pos, ALIGNOFFSET); + u32 pos_d = pos_a - pos; + u32 size_d = pos_d + round_up(len, ALIGNOFFSET); + + printf("Injecting payload in PHDR %u, size += 0x%x (0x%x/0x%x/0x%x)\n", + phdrndx, size_d, pos_d, len, size_d - pos_d - len); + + elf_t *elf = (elf_t *) calloc(1, sizeof(elf_t)); + if (!elf) + die("Error allocating %u bytes", (u32) sizeof(elf_t)); + + elf->data = calloc(1, dst->len + size_d); + elf->len = dst->len + size_d; + if (!elf->data) + die("Failed to alloc 0x%x bytes", dst->len + size_d); + + memcpy(elf->data, dst->data, dst->len); + init_elf(elf, 0); + + phdr = &elf->phdrs[phdrndx]; + u8 *p = phdr_data(elf, phdrndx, be32(phdr->p_filesz)); + + memcpy(&p[pos_d], src, len); + + *dataaddr = pos_a - be32(phdr->p_vaddr) + be32(phdr->p_paddr); + *dataaddr |= 0x80000000; + + phdr->p_filesz = be32(be32(phdr->p_filesz) + size_d); + phdr->p_memsz = be32(be32(phdr->p_memsz) + size_d); + phdr->p_vaddr = phdr->p_paddr; + + + printf("Payload blob @0x%x at runtime\n", pos_a); + + *dataptr = &p[pos_d]; + return elf; +} + +#define CLOCKS_PER_BYTE 47 +#define BUF_SIZE 256 +#define ROUNDS 256 + + +static void usage(const char *name) { + printf("usage: %s [-s stub] in.elf out.elf\n", name); + + printf("stubs:"); + int i = 0; + while (stubs[i].name) { + printf(" %s", stubs[i].name); + ++i; + } + + printf("\n"); + exit(1); +} + +int main(int argc, char *argv[]) { + elf_t *elf_tmp, *elf_pl, *elf_stub; + u32 offset; + u16 phdrndx; + u32 phdroff; + u32 dataaddr; + u8 *dataptr; + lzma_t *lzma; + + printf("wiipax v0.2 (c) 2009 Team Twiizers\n\n"); + + if (argc < 3) + usage(argv[0]); + + char *stubname = "mini"; + + char **arg = &argv[1]; + argc--; + + while (argc && *arg[0] == '-') { + if (!strcmp(*arg, "-h")) { + usage(argv[0]); + } else if (!strcmp(*arg, "-s")) { + if (argc < 2) + usage(argv[0]); + arg++; + argc--; + stubname = *arg; + } else if (!strcmp(*arg, "--")) { + arg++; + argc--; + break; + } else { + die("Unrecognized option %s\n", *arg); + usage(argv[0]); + } + arg++; + argc--; + } + + if (argc != 2) + usage(argv[0]); + + elf_tmp = read_stub(stubname); + check_elf(elf_tmp); + init_elf(elf_tmp, 1); + + offset = find_payload_offset(elf_tmp); + map_file_offset(elf_tmp, offset, &phdrndx, &phdroff); + elf_stub = strip_elf(elf_tmp, &phdrndx); + free(elf_tmp); + + elf_tmp = read_elf(arg[0]); + check_elf(elf_tmp); + init_elf(elf_tmp, 0); + elf_pl = strip_elf(elf_tmp, NULL); + free_elf(elf_tmp); + + lzma = lzma_compress(elf_pl->data, elf_pl->len); + // test decoding + lzma_decode(lzma, elf_pl->data); + free_elf(elf_pl); + +#if 0 + lzma_write("x.lzma", lzma); +#endif + + u32 aes_len = (lzma->len_out + 15) & (~15); + u8 aiv[16]; + memset(aiv, 0, 16); + + lzma->data = realloc(lzma->data, aes_len); + memset(lzma->data + lzma->len_out, 0, aes_len - lzma->len_out); + + elf_tmp = inject_elf(elf_stub, lzma->data, aes_len, &dataaddr, &dataptr); + free_elf(elf_stub); + + payload_t *pl = (payload_t *) phdr_data(elf_tmp, phdrndx, phdroff); + pl->dataptr = be32(dataaddr); + pl->len_in = be32(lzma->len_out); + pl->len_out = be32(lzma->len_in); + memcpy(pl->props, lzma->props, LZMA_PROPS_SIZE); + lzma_free(lzma); + + write_elf(arg[1], elf_tmp); + free_elf(elf_tmp); + + printf("Done.\n"); + return 0; +} + diff --git a/wiipax/client/stub_dkf.c b/wiipax/client/stub_dkf.c new file mode 120000 index 0000000..846981f --- /dev/null +++ b/wiipax/client/stub_dkf.c @@ -0,0 +1 @@ +../stub/stub_dkf.c \ No newline at end of file diff --git a/wiipax/client/stub_dkf_debug.c b/wiipax/client/stub_dkf_debug.c new file mode 120000 index 0000000..c992edb --- /dev/null +++ b/wiipax/client/stub_dkf_debug.c @@ -0,0 +1 @@ +../stub/stub_dkf_debug.c \ No newline at end of file diff --git a/wiipax/client/stub_dkfc.c b/wiipax/client/stub_dkfc.c new file mode 120000 index 0000000..71be34f --- /dev/null +++ b/wiipax/client/stub_dkfc.c @@ -0,0 +1 @@ +../stub/stub_dkfc.c \ No newline at end of file diff --git a/wiipax/client/stub_dkfc_debug.c b/wiipax/client/stub_dkfc_debug.c new file mode 120000 index 0000000..e6ebaf3 --- /dev/null +++ b/wiipax/client/stub_dkfc_debug.c @@ -0,0 +1 @@ +../stub/stub_dkfc_debug.c \ No newline at end of file diff --git a/wiipax/client/stub_mini.c b/wiipax/client/stub_mini.c new file mode 120000 index 0000000..08525f3 --- /dev/null +++ b/wiipax/client/stub_mini.c @@ -0,0 +1 @@ +../stub/stub_mini.c \ No newline at end of file diff --git a/wiipax/client/stub_mini_debug.c b/wiipax/client/stub_mini_debug.c new file mode 120000 index 0000000..d313e13 --- /dev/null +++ b/wiipax/client/stub_mini_debug.c @@ -0,0 +1 @@ +../stub/stub_mini_debug.c \ No newline at end of file diff --git a/wiipax/stub/.gitignore b/wiipax/stub/.gitignore new file mode 100644 index 0000000..7cb4458 --- /dev/null +++ b/wiipax/stub/.gitignore @@ -0,0 +1,3 @@ +stub_*.elf +stub_*.c + diff --git a/wiipax/stub/LzmaDec.c b/wiipax/stub/LzmaDec.c new file mode 100644 index 0000000..5c19600 --- /dev/null +++ b/wiipax/stub/LzmaDec.c @@ -0,0 +1,1006 @@ +/* LzmaDec.c -- LZMA Decoder +2008-11-06 : Igor Pavlov : Public domain */ + +#include "LzmaDec.h" +#include "string.h" + +#define kNumTopBits 24 +#define kTopValue ((UInt32)1 << kNumTopBits) + +#define kNumBitModelTotalBits 11 +#define kBitModelTotal (1 << kNumBitModelTotalBits) +#define kNumMoveBits 5 + +#define RC_INIT_SIZE 5 + +#define NORMALIZE if (range < kTopValue) { range <<= 8; code = (code << 8) | (*buf++); } + +#define IF_BIT_0(p) ttt = *(p); NORMALIZE; bound = (range >> kNumBitModelTotalBits) * ttt; if (code < bound) +#define UPDATE_0(p) range = bound; *(p) = (CLzmaProb)(ttt + ((kBitModelTotal - ttt) >> kNumMoveBits)); +#define UPDATE_1(p) range -= bound; code -= bound; *(p) = (CLzmaProb)(ttt - (ttt >> kNumMoveBits)); +#define GET_BIT2(p, i, A0, A1) IF_BIT_0(p) \ + { UPDATE_0(p); i = (i + i); A0; } else \ + { UPDATE_1(p); i = (i + i) + 1; A1; } +#define GET_BIT(p, i) GET_BIT2(p, i, ; , ;) + +#define TREE_GET_BIT(probs, i) { GET_BIT((probs + i), i); } +#define TREE_DECODE(probs, limit, i) \ + { i = 1; do { TREE_GET_BIT(probs, i); } while (i < limit); i -= limit; } + +/* #define _LZMA_SIZE_OPT */ + +#ifdef _LZMA_SIZE_OPT +#define TREE_6_DECODE(probs, i) TREE_DECODE(probs, (1 << 6), i) +#else +#define TREE_6_DECODE(probs, i) \ + { i = 1; \ + TREE_GET_BIT(probs, i); \ + TREE_GET_BIT(probs, i); \ + TREE_GET_BIT(probs, i); \ + TREE_GET_BIT(probs, i); \ + TREE_GET_BIT(probs, i); \ + TREE_GET_BIT(probs, i); \ + i -= 0x40; } +#endif + +#define NORMALIZE_CHECK if (range < kTopValue) { if (buf >= bufLimit) return DUMMY_ERROR; range <<= 8; code = (code << 8) | (*buf++); } + +#define IF_BIT_0_CHECK(p) ttt = *(p); NORMALIZE_CHECK; bound = (range >> kNumBitModelTotalBits) * ttt; if (code < bound) +#define UPDATE_0_CHECK range = bound; +#define UPDATE_1_CHECK range -= bound; code -= bound; +#define GET_BIT2_CHECK(p, i, A0, A1) IF_BIT_0_CHECK(p) \ + { UPDATE_0_CHECK; i = (i + i); A0; } else \ + { UPDATE_1_CHECK; i = (i + i) + 1; A1; } +#define GET_BIT_CHECK(p, i) GET_BIT2_CHECK(p, i, ; , ;) +#define TREE_DECODE_CHECK(probs, limit, i) \ + { i = 1; do { GET_BIT_CHECK(probs + i, i) } while (i < limit); i -= limit; } + + +#define kNumPosBitsMax 4 +#define kNumPosStatesMax (1 << kNumPosBitsMax) + +#define kLenNumLowBits 3 +#define kLenNumLowSymbols (1 << kLenNumLowBits) +#define kLenNumMidBits 3 +#define kLenNumMidSymbols (1 << kLenNumMidBits) +#define kLenNumHighBits 8 +#define kLenNumHighSymbols (1 << kLenNumHighBits) + +#define LenChoice 0 +#define LenChoice2 (LenChoice + 1) +#define LenLow (LenChoice2 + 1) +#define LenMid (LenLow + (kNumPosStatesMax << kLenNumLowBits)) +#define LenHigh (LenMid + (kNumPosStatesMax << kLenNumMidBits)) +#define kNumLenProbs (LenHigh + kLenNumHighSymbols) + + +#define kNumStates 12 +#define kNumLitStates 7 + +#define kStartPosModelIndex 4 +#define kEndPosModelIndex 14 +#define kNumFullDistances (1 << (kEndPosModelIndex >> 1)) + +#define kNumPosSlotBits 6 +#define kNumLenToPosStates 4 + +#define kNumAlignBits 4 +#define kAlignTableSize (1 << kNumAlignBits) + +#define kMatchMinLen 2 +#define kMatchSpecLenStart (kMatchMinLen + kLenNumLowSymbols + kLenNumMidSymbols + kLenNumHighSymbols) + +#define IsMatch 0 +#define IsRep (IsMatch + (kNumStates << kNumPosBitsMax)) +#define IsRepG0 (IsRep + kNumStates) +#define IsRepG1 (IsRepG0 + kNumStates) +#define IsRepG2 (IsRepG1 + kNumStates) +#define IsRep0Long (IsRepG2 + kNumStates) +#define PosSlot (IsRep0Long + (kNumStates << kNumPosBitsMax)) +#define SpecPos (PosSlot + (kNumLenToPosStates << kNumPosSlotBits)) +#define Align (SpecPos + kNumFullDistances - kEndPosModelIndex) +#define LenCoder (Align + kAlignTableSize) +#define RepLenCoder (LenCoder + kNumLenProbs) +#define Literal (RepLenCoder + kNumLenProbs) + +#define LZMA_BASE_SIZE 1846 +#define LZMA_LIT_SIZE 768 + +#define LzmaProps_GetNumProbs(p) ((UInt32)LZMA_BASE_SIZE + (LZMA_LIT_SIZE << ((p)->lc + (p)->lp))) + +#if Literal != LZMA_BASE_SIZE +StopCompilingDueBUG +#endif + +static const Byte kLiteralNextStates[kNumStates * 2] = +{ + 0, 0, 0, 0, 1, 2, 3, 4, 5, 6, 4, 5, + 7, 7, 7, 7, 7, 7, 7, 10, 10, 10, 10, 10 +}; + +#define LZMA_DIC_MIN (1 << 12) + +/* First LZMA-symbol is always decoded. +And it decodes new LZMA-symbols while (buf < bufLimit), but "buf" is without last normalization +Out: + Result: + SZ_OK - OK + SZ_ERROR_DATA - Error + p->remainLen: + < kMatchSpecLenStart : normal remain + = kMatchSpecLenStart : finished + = kMatchSpecLenStart + 1 : Flush marker + = kMatchSpecLenStart + 2 : State Init Marker +*/ + +static int MY_FAST_CALL LzmaDec_DecodeReal(CLzmaDec *p, SizeT limit, const Byte *bufLimit) +{ + CLzmaProb *probs = p->probs; + + unsigned state = p->state; + UInt32 rep0 = p->reps[0], rep1 = p->reps[1], rep2 = p->reps[2], rep3 = p->reps[3]; + unsigned pbMask = ((unsigned)1 << (p->prop.pb)) - 1; + unsigned lpMask = ((unsigned)1 << (p->prop.lp)) - 1; + unsigned lc = p->prop.lc; + + Byte *dic = p->dic; + SizeT dicBufSize = p->dicBufSize; + SizeT dicPos = p->dicPos; + + UInt32 processedPos = p->processedPos; + UInt32 checkDicSize = p->checkDicSize; + unsigned len = 0; + + const Byte *buf = p->buf; + UInt32 range = p->range; + UInt32 code = p->code; + + do + { + CLzmaProb *prob; + UInt32 bound; + unsigned ttt; + unsigned posState = processedPos & pbMask; + + prob = probs + IsMatch + (state << kNumPosBitsMax) + posState; + IF_BIT_0(prob) + { + unsigned symbol; + UPDATE_0(prob); + prob = probs + Literal; + if (checkDicSize != 0 || processedPos != 0) + prob += (LZMA_LIT_SIZE * (((processedPos & lpMask) << lc) + + (dic[(dicPos == 0 ? dicBufSize : dicPos) - 1] >> (8 - lc)))); + + if (state < kNumLitStates) + { + symbol = 1; + do { GET_BIT(prob + symbol, symbol) } while (symbol < 0x100); + } + else + { + unsigned matchByte = p->dic[(dicPos - rep0) + ((dicPos < rep0) ? dicBufSize : 0)]; + unsigned offs = 0x100; + symbol = 1; + do + { + unsigned bit; + CLzmaProb *probLit; + matchByte <<= 1; + bit = (matchByte & offs); + probLit = prob + offs + bit + symbol; + GET_BIT2(probLit, symbol, offs &= ~bit, offs &= bit) + } + while (symbol < 0x100); + } + dic[dicPos++] = (Byte)symbol; + processedPos++; + + state = kLiteralNextStates[state]; + /* if (state < 4) state = 0; else if (state < 10) state -= 3; else state -= 6; */ + continue; + } + else + { + UPDATE_1(prob); + prob = probs + IsRep + state; + IF_BIT_0(prob) + { + UPDATE_0(prob); + state += kNumStates; + prob = probs + LenCoder; + } + else + { + UPDATE_1(prob); + if (checkDicSize == 0 && processedPos == 0) + return SZ_ERROR_DATA; + prob = probs + IsRepG0 + state; + IF_BIT_0(prob) + { + UPDATE_0(prob); + prob = probs + IsRep0Long + (state << kNumPosBitsMax) + posState; + IF_BIT_0(prob) + { + UPDATE_0(prob); + dic[dicPos] = dic[(dicPos - rep0) + ((dicPos < rep0) ? dicBufSize : 0)]; + dicPos++; + processedPos++; + state = state < kNumLitStates ? 9 : 11; + continue; + } + UPDATE_1(prob); + } + else + { + UInt32 distance; + UPDATE_1(prob); + prob = probs + IsRepG1 + state; + IF_BIT_0(prob) + { + UPDATE_0(prob); + distance = rep1; + } + else + { + UPDATE_1(prob); + prob = probs + IsRepG2 + state; + IF_BIT_0(prob) + { + UPDATE_0(prob); + distance = rep2; + } + else + { + UPDATE_1(prob); + distance = rep3; + rep3 = rep2; + } + rep2 = rep1; + } + rep1 = rep0; + rep0 = distance; + } + state = state < kNumLitStates ? 8 : 11; + prob = probs + RepLenCoder; + } + { + unsigned limit, offset; + CLzmaProb *probLen = prob + LenChoice; + IF_BIT_0(probLen) + { + UPDATE_0(probLen); + probLen = prob + LenLow + (posState << kLenNumLowBits); + offset = 0; + limit = (1 << kLenNumLowBits); + } + else + { + UPDATE_1(probLen); + probLen = prob + LenChoice2; + IF_BIT_0(probLen) + { + UPDATE_0(probLen); + probLen = prob + LenMid + (posState << kLenNumMidBits); + offset = kLenNumLowSymbols; + limit = (1 << kLenNumMidBits); + } + else + { + UPDATE_1(probLen); + probLen = prob + LenHigh; + offset = kLenNumLowSymbols + kLenNumMidSymbols; + limit = (1 << kLenNumHighBits); + } + } + TREE_DECODE(probLen, limit, len); + len += offset; + } + + if (state >= kNumStates) + { + UInt32 distance; + prob = probs + PosSlot + + ((len < kNumLenToPosStates ? len : kNumLenToPosStates - 1) << kNumPosSlotBits); + TREE_6_DECODE(prob, distance); + if (distance >= kStartPosModelIndex) + { + unsigned posSlot = (unsigned)distance; + int numDirectBits = (int)(((distance >> 1) - 1)); + distance = (2 | (distance & 1)); + if (posSlot < kEndPosModelIndex) + { + distance <<= numDirectBits; + prob = probs + SpecPos + distance - posSlot - 1; + { + UInt32 mask = 1; + unsigned i = 1; + do + { + GET_BIT2(prob + i, i, ; , distance |= mask); + mask <<= 1; + } + while (--numDirectBits != 0); + } + } + else + { + numDirectBits -= kNumAlignBits; + do + { + NORMALIZE + range >>= 1; + + { + UInt32 t; + code -= range; + t = (0 - ((UInt32)code >> 31)); /* (UInt32)((Int32)code >> 31) */ + distance = (distance << 1) + (t + 1); + code += range & t; + } + /* + distance <<= 1; + if (code >= range) + { + code -= range; + distance |= 1; + } + */ + } + while (--numDirectBits != 0); + prob = probs + Align; + distance <<= kNumAlignBits; + { + unsigned i = 1; + GET_BIT2(prob + i, i, ; , distance |= 1); + GET_BIT2(prob + i, i, ; , distance |= 2); + GET_BIT2(prob + i, i, ; , distance |= 4); + GET_BIT2(prob + i, i, ; , distance |= 8); + } + if (distance == (UInt32)0xFFFFFFFF) + { + len += kMatchSpecLenStart; + state -= kNumStates; + break; + } + } + } + rep3 = rep2; + rep2 = rep1; + rep1 = rep0; + rep0 = distance + 1; + if (checkDicSize == 0) + { + if (distance >= processedPos) + return SZ_ERROR_DATA; + } + else if (distance >= checkDicSize) + return SZ_ERROR_DATA; + state = (state < kNumStates + kNumLitStates) ? kNumLitStates : kNumLitStates + 3; + /* state = kLiteralNextStates[state]; */ + } + + len += kMatchMinLen; + + if (limit == dicPos) + return SZ_ERROR_DATA; + { + SizeT rem = limit - dicPos; + unsigned curLen = ((rem < len) ? (unsigned)rem : len); + SizeT pos = (dicPos - rep0) + ((dicPos < rep0) ? dicBufSize : 0); + + processedPos += curLen; + + len -= curLen; + if (pos + curLen <= dicBufSize) + { + Byte *dest = dic + dicPos; + ptrdiff_t src = (ptrdiff_t)pos - (ptrdiff_t)dicPos; + const Byte *lim = dest + curLen; + dicPos += curLen; + do + *(dest) = (Byte)*(dest + src); + while (++dest != lim); + } + else + { + do + { + dic[dicPos++] = dic[pos]; + if (++pos == dicBufSize) + pos = 0; + } + while (--curLen != 0); + } + } + } + } + while (dicPos < limit && buf < bufLimit); + NORMALIZE; + p->buf = buf; + p->range = range; + p->code = code; + p->remainLen = len; + p->dicPos = dicPos; + p->processedPos = processedPos; + p->reps[0] = rep0; + p->reps[1] = rep1; + p->reps[2] = rep2; + p->reps[3] = rep3; + p->state = state; + + return SZ_OK; +} + +static void MY_FAST_CALL LzmaDec_WriteRem(CLzmaDec *p, SizeT limit) +{ + if (p->remainLen != 0 && p->remainLen < kMatchSpecLenStart) + { + Byte *dic = p->dic; + SizeT dicPos = p->dicPos; + SizeT dicBufSize = p->dicBufSize; + unsigned len = p->remainLen; + UInt32 rep0 = p->reps[0]; + if (limit - dicPos < len) + len = (unsigned)(limit - dicPos); + + if (p->checkDicSize == 0 && p->prop.dicSize - p->processedPos <= len) + p->checkDicSize = p->prop.dicSize; + + p->processedPos += len; + p->remainLen -= len; + while (len-- != 0) + { + dic[dicPos] = dic[(dicPos - rep0) + ((dicPos < rep0) ? dicBufSize : 0)]; + dicPos++; + } + p->dicPos = dicPos; + } +} + +static int MY_FAST_CALL LzmaDec_DecodeReal2(CLzmaDec *p, SizeT limit, const Byte *bufLimit) +{ + do + { + SizeT limit2 = limit; + if (p->checkDicSize == 0) + { + UInt32 rem = p->prop.dicSize - p->processedPos; + if (limit - p->dicPos > rem) + limit2 = p->dicPos + rem; + } + RINOK(LzmaDec_DecodeReal(p, limit2, bufLimit)); + if (p->processedPos >= p->prop.dicSize) + p->checkDicSize = p->prop.dicSize; + LzmaDec_WriteRem(p, limit); + } + while (p->dicPos < limit && p->buf < bufLimit && p->remainLen < kMatchSpecLenStart); + + if (p->remainLen > kMatchSpecLenStart) + { + p->remainLen = kMatchSpecLenStart; + } + return 0; +} + +typedef enum +{ + DUMMY_ERROR, /* unexpected end of input stream */ + DUMMY_LIT, + DUMMY_MATCH, + DUMMY_REP +} ELzmaDummy; + +static ELzmaDummy LzmaDec_TryDummy(const CLzmaDec *p, const Byte *buf, SizeT inSize) +{ + UInt32 range = p->range; + UInt32 code = p->code; + const Byte *bufLimit = buf + inSize; + CLzmaProb *probs = p->probs; + unsigned state = p->state; + ELzmaDummy res; + + { + CLzmaProb *prob; + UInt32 bound; + unsigned ttt; + unsigned posState = (p->processedPos) & ((1 << p->prop.pb) - 1); + + prob = probs + IsMatch + (state << kNumPosBitsMax) + posState; + IF_BIT_0_CHECK(prob) + { + UPDATE_0_CHECK + + /* if (bufLimit - buf >= 7) return DUMMY_LIT; */ + + prob = probs + Literal; + if (p->checkDicSize != 0 || p->processedPos != 0) + prob += (LZMA_LIT_SIZE * + ((((p->processedPos) & ((1 << (p->prop.lp)) - 1)) << p->prop.lc) + + (p->dic[(p->dicPos == 0 ? p->dicBufSize : p->dicPos) - 1] >> (8 - p->prop.lc)))); + + if (state < kNumLitStates) + { + unsigned symbol = 1; + do { GET_BIT_CHECK(prob + symbol, symbol) } while (symbol < 0x100); + } + else + { + unsigned matchByte = p->dic[p->dicPos - p->reps[0] + + ((p->dicPos < p->reps[0]) ? p->dicBufSize : 0)]; + unsigned offs = 0x100; + unsigned symbol = 1; + do + { + unsigned bit; + CLzmaProb *probLit; + matchByte <<= 1; + bit = (matchByte & offs); + probLit = prob + offs + bit + symbol; + GET_BIT2_CHECK(probLit, symbol, offs &= ~bit, offs &= bit) + } + while (symbol < 0x100); + } + res = DUMMY_LIT; + } + else + { + unsigned len; + UPDATE_1_CHECK; + + prob = probs + IsRep + state; + IF_BIT_0_CHECK(prob) + { + UPDATE_0_CHECK; + state = 0; + prob = probs + LenCoder; + res = DUMMY_MATCH; + } + else + { + UPDATE_1_CHECK; + res = DUMMY_REP; + prob = probs + IsRepG0 + state; + IF_BIT_0_CHECK(prob) + { + UPDATE_0_CHECK; + prob = probs + IsRep0Long + (state << kNumPosBitsMax) + posState; + IF_BIT_0_CHECK(prob) + { + UPDATE_0_CHECK; + NORMALIZE_CHECK; + return DUMMY_REP; + } + else + { + UPDATE_1_CHECK; + } + } + else + { + UPDATE_1_CHECK; + prob = probs + IsRepG1 + state; + IF_BIT_0_CHECK(prob) + { + UPDATE_0_CHECK; + } + else + { + UPDATE_1_CHECK; + prob = probs + IsRepG2 + state; + IF_BIT_0_CHECK(prob) + { + UPDATE_0_CHECK; + } + else + { + UPDATE_1_CHECK; + } + } + } + state = kNumStates; + prob = probs + RepLenCoder; + } + { + unsigned limit, offset; + CLzmaProb *probLen = prob + LenChoice; + IF_BIT_0_CHECK(probLen) + { + UPDATE_0_CHECK; + probLen = prob + LenLow + (posState << kLenNumLowBits); + offset = 0; + limit = 1 << kLenNumLowBits; + } + else + { + UPDATE_1_CHECK; + probLen = prob + LenChoice2; + IF_BIT_0_CHECK(probLen) + { + UPDATE_0_CHECK; + probLen = prob + LenMid + (posState << kLenNumMidBits); + offset = kLenNumLowSymbols; + limit = 1 << kLenNumMidBits; + } + else + { + UPDATE_1_CHECK; + probLen = prob + LenHigh; + offset = kLenNumLowSymbols + kLenNumMidSymbols; + limit = 1 << kLenNumHighBits; + } + } + TREE_DECODE_CHECK(probLen, limit, len); + len += offset; + } + + if (state < 4) + { + unsigned posSlot; + prob = probs + PosSlot + + ((len < kNumLenToPosStates ? len : kNumLenToPosStates - 1) << + kNumPosSlotBits); + TREE_DECODE_CHECK(prob, 1 << kNumPosSlotBits, posSlot); + if (posSlot >= kStartPosModelIndex) + { + int numDirectBits = ((posSlot >> 1) - 1); + + /* if (bufLimit - buf >= 8) return DUMMY_MATCH; */ + + if (posSlot < kEndPosModelIndex) + { + prob = probs + SpecPos + ((2 | (posSlot & 1)) << numDirectBits) - posSlot - 1; + } + else + { + numDirectBits -= kNumAlignBits; + do + { + NORMALIZE_CHECK + range >>= 1; + code -= range & (((code - range) >> 31) - 1); + /* if (code >= range) code -= range; */ + } + while (--numDirectBits != 0); + prob = probs + Align; + numDirectBits = kNumAlignBits; + } + { + unsigned i = 1; + do + { + GET_BIT_CHECK(prob + i, i); + } + while (--numDirectBits != 0); + } + } + } + } + } + NORMALIZE_CHECK; + return res; +} + + +static void LzmaDec_InitRc(CLzmaDec *p, const Byte *data) +{ + p->code = ((UInt32)data[1] << 24) | ((UInt32)data[2] << 16) | ((UInt32)data[3] << 8) | ((UInt32)data[4]); + p->range = 0xFFFFFFFF; + p->needFlush = 0; +} + +void LzmaDec_InitDicAndState(CLzmaDec *p, Bool initDic, Bool initState) +{ + p->needFlush = 1; + p->remainLen = 0; + p->tempBufSize = 0; + + if (initDic) + { + p->processedPos = 0; + p->checkDicSize = 0; + p->needInitState = 1; + } + if (initState) + p->needInitState = 1; +} + +void LzmaDec_Init(CLzmaDec *p) +{ + p->dicPos = 0; + LzmaDec_InitDicAndState(p, True, True); +} + +static void LzmaDec_InitStateReal(CLzmaDec *p) +{ + UInt32 numProbs = Literal + ((UInt32)LZMA_LIT_SIZE << (p->prop.lc + p->prop.lp)); + UInt32 i; + CLzmaProb *probs = p->probs; + for (i = 0; i < numProbs; i++) + probs[i] = kBitModelTotal >> 1; + p->reps[0] = p->reps[1] = p->reps[2] = p->reps[3] = 1; + p->state = 0; + p->needInitState = 0; +} + +SRes LzmaDec_DecodeToDic(CLzmaDec *p, SizeT dicLimit, const Byte *src, SizeT *srcLen, + ELzmaFinishMode finishMode, ELzmaStatus *status) +{ + SizeT inSize = *srcLen; + (*srcLen) = 0; + LzmaDec_WriteRem(p, dicLimit); + + *status = LZMA_STATUS_NOT_SPECIFIED; + + while (p->remainLen != kMatchSpecLenStart) + { + int checkEndMarkNow; + + if (p->needFlush != 0) + { + for (; inSize > 0 && p->tempBufSize < RC_INIT_SIZE; (*srcLen)++, inSize--) + p->tempBuf[p->tempBufSize++] = *src++; + if (p->tempBufSize < RC_INIT_SIZE) + { + *status = LZMA_STATUS_NEEDS_MORE_INPUT; + return SZ_OK; + } + if (p->tempBuf[0] != 0) + return SZ_ERROR_DATA; + + LzmaDec_InitRc(p, p->tempBuf); + p->tempBufSize = 0; + } + + checkEndMarkNow = 0; + if (p->dicPos >= dicLimit) + { + if (p->remainLen == 0 && p->code == 0) + { + *status = LZMA_STATUS_MAYBE_FINISHED_WITHOUT_MARK; + return SZ_OK; + } + if (finishMode == LZMA_FINISH_ANY) + { + *status = LZMA_STATUS_NOT_FINISHED; + return SZ_OK; + } + if (p->remainLen != 0) + { + *status = LZMA_STATUS_NOT_FINISHED; + return SZ_ERROR_DATA; + } + checkEndMarkNow = 1; + } + + if (p->needInitState) + LzmaDec_InitStateReal(p); + + if (p->tempBufSize == 0) + { + SizeT processed; + const Byte *bufLimit; + if (inSize < LZMA_REQUIRED_INPUT_MAX || checkEndMarkNow) + { + int dummyRes = LzmaDec_TryDummy(p, src, inSize); + if (dummyRes == DUMMY_ERROR) + { + memcpy(p->tempBuf, src, inSize); + p->tempBufSize = (unsigned)inSize; + (*srcLen) += inSize; + *status = LZMA_STATUS_NEEDS_MORE_INPUT; + return SZ_OK; + } + if (checkEndMarkNow && dummyRes != DUMMY_MATCH) + { + *status = LZMA_STATUS_NOT_FINISHED; + return SZ_ERROR_DATA; + } + bufLimit = src; + } + else + bufLimit = src + inSize - LZMA_REQUIRED_INPUT_MAX; + p->buf = src; + if (LzmaDec_DecodeReal2(p, dicLimit, bufLimit) != 0) + return SZ_ERROR_DATA; + processed = (SizeT)(p->buf - src); + (*srcLen) += processed; + src += processed; + inSize -= processed; + } + else + { + unsigned rem = p->tempBufSize, lookAhead = 0; + while (rem < LZMA_REQUIRED_INPUT_MAX && lookAhead < inSize) + p->tempBuf[rem++] = src[lookAhead++]; + p->tempBufSize = rem; + if (rem < LZMA_REQUIRED_INPUT_MAX || checkEndMarkNow) + { + int dummyRes = LzmaDec_TryDummy(p, p->tempBuf, rem); + if (dummyRes == DUMMY_ERROR) + { + (*srcLen) += lookAhead; + *status = LZMA_STATUS_NEEDS_MORE_INPUT; + return SZ_OK; + } + if (checkEndMarkNow && dummyRes != DUMMY_MATCH) + { + *status = LZMA_STATUS_NOT_FINISHED; + return SZ_ERROR_DATA; + } + } + p->buf = p->tempBuf; + if (LzmaDec_DecodeReal2(p, dicLimit, p->buf) != 0) + return SZ_ERROR_DATA; + lookAhead -= (rem - (unsigned)(p->buf - p->tempBuf)); + (*srcLen) += lookAhead; + src += lookAhead; + inSize -= lookAhead; + p->tempBufSize = 0; + } + } + if (p->code == 0) + *status = LZMA_STATUS_FINISHED_WITH_MARK; + return (p->code == 0) ? SZ_OK : SZ_ERROR_DATA; +} + +SRes LzmaDec_DecodeToBuf(CLzmaDec *p, Byte *dest, SizeT *destLen, const Byte *src, SizeT *srcLen, ELzmaFinishMode finishMode, ELzmaStatus *status) +{ + SizeT outSize = *destLen; + SizeT inSize = *srcLen; + *srcLen = *destLen = 0; + for (;;) + { + SizeT inSizeCur = inSize, outSizeCur, dicPos; + ELzmaFinishMode curFinishMode; + SRes res; + if (p->dicPos == p->dicBufSize) + p->dicPos = 0; + dicPos = p->dicPos; + if (outSize > p->dicBufSize - dicPos) + { + outSizeCur = p->dicBufSize; + curFinishMode = LZMA_FINISH_ANY; + } + else + { + outSizeCur = dicPos + outSize; + curFinishMode = finishMode; + } + + res = LzmaDec_DecodeToDic(p, outSizeCur, src, &inSizeCur, curFinishMode, status); + src += inSizeCur; + inSize -= inSizeCur; + *srcLen += inSizeCur; + outSizeCur = p->dicPos - dicPos; + memcpy(dest, p->dic + dicPos, outSizeCur); + dest += outSizeCur; + outSize -= outSizeCur; + *destLen += outSizeCur; + if (res != 0) + return res; + if (outSizeCur == 0 || outSize == 0) + return SZ_OK; + } +} + +void LzmaDec_FreeProbs(CLzmaDec *p, ISzAlloc *alloc) +{ + alloc->Free(alloc, p->probs); + p->probs = 0; +} + +static void LzmaDec_FreeDict(CLzmaDec *p, ISzAlloc *alloc) +{ + alloc->Free(alloc, p->dic); + p->dic = 0; +} + +void LzmaDec_Free(CLzmaDec *p, ISzAlloc *alloc) +{ + LzmaDec_FreeProbs(p, alloc); + LzmaDec_FreeDict(p, alloc); +} + +SRes LzmaProps_Decode(CLzmaProps *p, const Byte *data, unsigned size) +{ + UInt32 dicSize; + Byte d; + + if (size < LZMA_PROPS_SIZE) + return SZ_ERROR_UNSUPPORTED; + else + dicSize = data[1] | ((UInt32)data[2] << 8) | ((UInt32)data[3] << 16) | ((UInt32)data[4] << 24); + + if (dicSize < LZMA_DIC_MIN) + dicSize = LZMA_DIC_MIN; + p->dicSize = dicSize; + + d = data[0]; + if (d >= (9 * 5 * 5)) + return SZ_ERROR_UNSUPPORTED; + + p->lc = d % 9; + d /= 9; + p->pb = d / 5; + p->lp = d % 5; + + return SZ_OK; +} + +static SRes LzmaDec_AllocateProbs2(CLzmaDec *p, const CLzmaProps *propNew, ISzAlloc *alloc) +{ + UInt32 numProbs = LzmaProps_GetNumProbs(propNew); + if (p->probs == 0 || numProbs != p->numProbs) + { + LzmaDec_FreeProbs(p, alloc); + p->probs = (CLzmaProb *)alloc->Alloc(alloc, numProbs * sizeof(CLzmaProb)); + p->numProbs = numProbs; + if (p->probs == 0) + return SZ_ERROR_MEM; + } + return SZ_OK; +} + +SRes LzmaDec_AllocateProbs(CLzmaDec *p, const Byte *props, unsigned propsSize, ISzAlloc *alloc) +{ + CLzmaProps propNew; + RINOK(LzmaProps_Decode(&propNew, props, propsSize)); + RINOK(LzmaDec_AllocateProbs2(p, &propNew, alloc)); + p->prop = propNew; + return SZ_OK; +} + +SRes LzmaDec_Allocate(CLzmaDec *p, const Byte *props, unsigned propsSize, ISzAlloc *alloc) +{ + CLzmaProps propNew; + SizeT dicBufSize; + RINOK(LzmaProps_Decode(&propNew, props, propsSize)); + RINOK(LzmaDec_AllocateProbs2(p, &propNew, alloc)); + dicBufSize = propNew.dicSize; + if (p->dic == 0 || dicBufSize != p->dicBufSize) + { + LzmaDec_FreeDict(p, alloc); + p->dic = (Byte *)alloc->Alloc(alloc, dicBufSize); + if (p->dic == 0) + { + LzmaDec_FreeProbs(p, alloc); + return SZ_ERROR_MEM; + } + } + p->dicBufSize = dicBufSize; + p->prop = propNew; + return SZ_OK; +} + +SRes LzmaDecode(Byte *dest, SizeT *destLen, const Byte *src, SizeT *srcLen, + const Byte *propData, unsigned propSize, ELzmaFinishMode finishMode, + ELzmaStatus *status, ISzAlloc *alloc) +{ + CLzmaDec p; + SRes res; + SizeT inSize = *srcLen; + SizeT outSize = *destLen; + *srcLen = *destLen = 0; + if (inSize < RC_INIT_SIZE) + return SZ_ERROR_INPUT_EOF; + + LzmaDec_Construct(&p); + res = LzmaDec_AllocateProbs(&p, propData, propSize, alloc); + if (res != 0) + return res; + p.dic = dest; + p.dicBufSize = outSize; + + LzmaDec_Init(&p); + + *srcLen = inSize; + res = LzmaDec_DecodeToDic(&p, outSize, src, srcLen, finishMode, status); + + if (res == SZ_OK && *status == LZMA_STATUS_NEEDS_MORE_INPUT) + res = SZ_ERROR_INPUT_EOF; + + (*destLen) = p.dicPos; + LzmaDec_FreeProbs(&p, alloc); + return res; +} diff --git a/wiipax/stub/LzmaDec.h b/wiipax/stub/LzmaDec.h new file mode 100644 index 0000000..ad7d705 --- /dev/null +++ b/wiipax/stub/LzmaDec.h @@ -0,0 +1,223 @@ +/* LzmaDec.h -- LZMA Decoder +2008-10-04 : Igor Pavlov : Public domain */ + +#ifndef __LZMADEC_H +#define __LZMADEC_H + +#include "Types.h" + +/* #define _LZMA_PROB32 */ +/* _LZMA_PROB32 can increase the speed on some CPUs, + but memory usage for CLzmaDec::probs will be doubled in that case */ + +#ifdef _LZMA_PROB32 +#define CLzmaProb UInt32 +#else +#define CLzmaProb UInt16 +#endif + + +/* ---------- LZMA Properties ---------- */ + +#define LZMA_PROPS_SIZE 5 + +typedef struct _CLzmaProps +{ + unsigned lc, lp, pb; + UInt32 dicSize; +} CLzmaProps; + +/* LzmaProps_Decode - decodes properties +Returns: + SZ_OK + SZ_ERROR_UNSUPPORTED - Unsupported properties +*/ + +SRes LzmaProps_Decode(CLzmaProps *p, const Byte *data, unsigned size); + + +/* ---------- LZMA Decoder state ---------- */ + +/* LZMA_REQUIRED_INPUT_MAX = number of required input bytes for worst case. + Num bits = log2((2^11 / 31) ^ 22) + 26 < 134 + 26 = 160; */ + +#define LZMA_REQUIRED_INPUT_MAX 20 + +typedef struct +{ + CLzmaProps prop; + CLzmaProb *probs; + Byte *dic; + const Byte *buf; + UInt32 range, code; + SizeT dicPos; + SizeT dicBufSize; + UInt32 processedPos; + UInt32 checkDicSize; + unsigned state; + UInt32 reps[4]; + unsigned remainLen; + int needFlush; + int needInitState; + UInt32 numProbs; + unsigned tempBufSize; + Byte tempBuf[LZMA_REQUIRED_INPUT_MAX]; +} CLzmaDec; + +#define LzmaDec_Construct(p) { (p)->dic = 0; (p)->probs = 0; } + +void LzmaDec_Init(CLzmaDec *p); + +/* There are two types of LZMA streams: + 0) Stream with end mark. That end mark adds about 6 bytes to compressed size. + 1) Stream without end mark. You must know exact uncompressed size to decompress such stream. */ + +typedef enum +{ + LZMA_FINISH_ANY, /* finish at any point */ + LZMA_FINISH_END /* block must be finished at the end */ +} ELzmaFinishMode; + +/* ELzmaFinishMode has meaning only if the decoding reaches output limit !!! + + You must use LZMA_FINISH_END, when you know that current output buffer + covers last bytes of block. In other cases you must use LZMA_FINISH_ANY. + + If LZMA decoder sees end marker before reaching output limit, it returns SZ_OK, + and output value of destLen will be less than output buffer size limit. + You can check status result also. + + You can use multiple checks to test data integrity after full decompression: + 1) Check Result and "status" variable. + 2) Check that output(destLen) = uncompressedSize, if you know real uncompressedSize. + 3) Check that output(srcLen) = compressedSize, if you know real compressedSize. + You must use correct finish mode in that case. */ + +typedef enum +{ + LZMA_STATUS_NOT_SPECIFIED, /* use main error code instead */ + LZMA_STATUS_FINISHED_WITH_MARK, /* stream was finished with end mark. */ + LZMA_STATUS_NOT_FINISHED, /* stream was not finished */ + LZMA_STATUS_NEEDS_MORE_INPUT, /* you must provide more input bytes */ + LZMA_STATUS_MAYBE_FINISHED_WITHOUT_MARK /* there is probability that stream was finished without end mark */ +} ELzmaStatus; + +/* ELzmaStatus is used only as output value for function call */ + + +/* ---------- Interfaces ---------- */ + +/* There are 3 levels of interfaces: + 1) Dictionary Interface + 2) Buffer Interface + 3) One Call Interface + You can select any of these interfaces, but don't mix functions from different + groups for same object. */ + + +/* There are two variants to allocate state for Dictionary Interface: + 1) LzmaDec_Allocate / LzmaDec_Free + 2) LzmaDec_AllocateProbs / LzmaDec_FreeProbs + You can use variant 2, if you set dictionary buffer manually. + For Buffer Interface you must always use variant 1. + +LzmaDec_Allocate* can return: + SZ_OK + SZ_ERROR_MEM - Memory allocation error + SZ_ERROR_UNSUPPORTED - Unsupported properties +*/ + +SRes LzmaDec_AllocateProbs(CLzmaDec *p, const Byte *props, unsigned propsSize, ISzAlloc *alloc); +void LzmaDec_FreeProbs(CLzmaDec *p, ISzAlloc *alloc); + +SRes LzmaDec_Allocate(CLzmaDec *state, const Byte *prop, unsigned propsSize, ISzAlloc *alloc); +void LzmaDec_Free(CLzmaDec *state, ISzAlloc *alloc); + +/* ---------- Dictionary Interface ---------- */ + +/* You can use it, if you want to eliminate the overhead for data copying from + dictionary to some other external buffer. + You must work with CLzmaDec variables directly in this interface. + + STEPS: + LzmaDec_Constr() + LzmaDec_Allocate() + for (each new stream) + { + LzmaDec_Init() + while (it needs more decompression) + { + LzmaDec_DecodeToDic() + use data from CLzmaDec::dic and update CLzmaDec::dicPos + } + } + LzmaDec_Free() +*/ + +/* LzmaDec_DecodeToDic + + The decoding to internal dictionary buffer (CLzmaDec::dic). + You must manually update CLzmaDec::dicPos, if it reaches CLzmaDec::dicBufSize !!! + +finishMode: + It has meaning only if the decoding reaches output limit (dicLimit). + LZMA_FINISH_ANY - Decode just dicLimit bytes. + LZMA_FINISH_END - Stream must be finished after dicLimit. + +Returns: + SZ_OK + status: + LZMA_STATUS_FINISHED_WITH_MARK + LZMA_STATUS_NOT_FINISHED + LZMA_STATUS_NEEDS_MORE_INPUT + LZMA_STATUS_MAYBE_FINISHED_WITHOUT_MARK + SZ_ERROR_DATA - Data error +*/ + +SRes LzmaDec_DecodeToDic(CLzmaDec *p, SizeT dicLimit, + const Byte *src, SizeT *srcLen, ELzmaFinishMode finishMode, ELzmaStatus *status); + + +/* ---------- Buffer Interface ---------- */ + +/* It's zlib-like interface. + See LzmaDec_DecodeToDic description for information about STEPS and return results, + but you must use LzmaDec_DecodeToBuf instead of LzmaDec_DecodeToDic and you don't need + to work with CLzmaDec variables manually. + +finishMode: + It has meaning only if the decoding reaches output limit (*destLen). + LZMA_FINISH_ANY - Decode just destLen bytes. + LZMA_FINISH_END - Stream must be finished after (*destLen). +*/ + +SRes LzmaDec_DecodeToBuf(CLzmaDec *p, Byte *dest, SizeT *destLen, + const Byte *src, SizeT *srcLen, ELzmaFinishMode finishMode, ELzmaStatus *status); + + +/* ---------- One Call Interface ---------- */ + +/* LzmaDecode + +finishMode: + It has meaning only if the decoding reaches output limit (*destLen). + LZMA_FINISH_ANY - Decode just destLen bytes. + LZMA_FINISH_END - Stream must be finished after (*destLen). + +Returns: + SZ_OK + status: + LZMA_STATUS_FINISHED_WITH_MARK + LZMA_STATUS_NOT_FINISHED + LZMA_STATUS_MAYBE_FINISHED_WITHOUT_MARK + SZ_ERROR_DATA - Data error + SZ_ERROR_MEM - Memory allocation error + SZ_ERROR_UNSUPPORTED - Unsupported properties + SZ_ERROR_INPUT_EOF - It needs more bytes in input buffer (src). +*/ + +SRes LzmaDecode(Byte *dest, SizeT *destLen, const Byte *src, SizeT *srcLen, + const Byte *propData, unsigned propSize, ELzmaFinishMode finishMode, + ELzmaStatus *status, ISzAlloc *alloc); + +#endif diff --git a/wiipax/stub/Makefile b/wiipax/stub/Makefile new file mode 100644 index 0000000..2aa6340 --- /dev/null +++ b/wiipax/stub/Makefile @@ -0,0 +1,66 @@ +include ../../broadway.mk + +CFLAGS += -mno-eabi -mno-sdata -O2 -ffreestanding +CFLAGS += -Wall -Wextra +DEFINES = +LDFLAGS += -nostartfiles -nodefaultlibs + +OBJS_COMMON = crt0.o main.o string.o sync.o elf.o time.o LzmaDec.o +TARGET_ID = + +ifeq ($(DEVKITFAIL),1) +DEFINES += -DDEVKITFAIL +OBJS = $(OBJS_COMMON) +LDSCRIPT = devkitfail.ld +TARGET_ID = _dkf +else +ifeq ($(DKFAILCHANNEL),1) +DEFINES += -DDEVKITFAIL +OBJS = realmode.o $(OBJS_COMMON) +LDSCRIPT = channel.ld +TARGET_ID = _dkfc +else +OBJS = realmode.o plunge.o $(OBJS_COMMON) +LDSCRIPT = realmode.ld +TARGET_ID = _mini +endif +endif + +ifeq ($(NDEBUG),1) +DEFINES += -DNDEBUG +TARGET_DEBUG = +else +OBJS += exception.o exception_asm.o vsprintf.o gecko.o +TARGET_DEBUG = _debug +endif + +TARGET = stub$(TARGET_ID)$(TARGET_DEBUG).elf + +include ../../common.mk + +all: xxd + +xxd: $(TARGET) + @echo " XXD $^" + @xxd -i $(TARGET) > $(subst .elf,.c,$(TARGET)) + +distclean: clean + rm -f stub_*.elf stub_*.c + +release: + $(Q)$(MAKE) clean + $(Q)$(MAKE) + $(Q)$(MAKE) clean + $(Q)$(MAKE) NDEBUG=1 + $(Q)$(MAKE) NDEBUG=1 clean + $(Q)$(MAKE) DEVKITFAIL=1 + $(Q)$(MAKE) DEVKITFAIL=1 clean + $(Q)$(MAKE) DEVKITFAIL=1 NDEBUG=1 + $(Q)$(MAKE) DEVKITFAIL=1 NDEBUG=1 clean + $(Q)$(MAKE) DKFAILCHANNEL=1 + $(Q)$(MAKE) DKFAILCHANNEL=1 clean + $(Q)$(MAKE) DKFAILCHANNEL=1 NDEBUG=1 + $(Q)$(MAKE) DKFAILCHANNEL=1 NDEBUG=1 clean + +.PHONY: release + diff --git a/wiipax/stub/Types.h b/wiipax/stub/Types.h new file mode 100644 index 0000000..2638196 --- /dev/null +++ b/wiipax/stub/Types.h @@ -0,0 +1,208 @@ +/* Types.h -- Basic types +2008-11-23 : Igor Pavlov : Public domain */ + +#ifndef __7Z_TYPES_H +#define __7Z_TYPES_H + +#include + +#ifdef _WIN32 +#include +#endif + +#define SZ_OK 0 + +#define SZ_ERROR_DATA 1 +#define SZ_ERROR_MEM 2 +#define SZ_ERROR_CRC 3 +#define SZ_ERROR_UNSUPPORTED 4 +#define SZ_ERROR_PARAM 5 +#define SZ_ERROR_INPUT_EOF 6 +#define SZ_ERROR_OUTPUT_EOF 7 +#define SZ_ERROR_READ 8 +#define SZ_ERROR_WRITE 9 +#define SZ_ERROR_PROGRESS 10 +#define SZ_ERROR_FAIL 11 +#define SZ_ERROR_THREAD 12 + +#define SZ_ERROR_ARCHIVE 16 +#define SZ_ERROR_NO_ARCHIVE 17 + +typedef int SRes; + +#ifdef _WIN32 +typedef DWORD WRes; +#else +typedef int WRes; +#endif + +#ifndef RINOK +#define RINOK(x) { int __result__ = (x); if (__result__ != 0) return __result__; } +#endif + +typedef unsigned char Byte; +typedef short Int16; +typedef unsigned short UInt16; + +#ifdef _LZMA_UINT32_IS_ULONG +typedef long Int32; +typedef unsigned long UInt32; +#else +typedef int Int32; +typedef unsigned int UInt32; +#endif + +#ifdef _SZ_NO_INT_64 + +/* define _SZ_NO_INT_64, if your compiler doesn't support 64-bit integers. + NOTES: Some code will work incorrectly in that case! */ + +typedef long Int64; +typedef unsigned long UInt64; + +#else + +#if defined(_MSC_VER) || defined(__BORLANDC__) +typedef __int64 Int64; +typedef unsigned __int64 UInt64; +#else +typedef long long int Int64; +typedef unsigned long long int UInt64; +#endif + +#endif + +#ifdef _LZMA_NO_SYSTEM_SIZE_T +typedef UInt32 SizeT; +#else +typedef size_t SizeT; +#endif + +typedef int Bool; +#define True 1 +#define False 0 + + +#ifdef _MSC_VER + +#if _MSC_VER >= 1300 +#define MY_NO_INLINE __declspec(noinline) +#else +#define MY_NO_INLINE +#endif + +#define MY_CDECL __cdecl +#define MY_STD_CALL __stdcall +#define MY_FAST_CALL MY_NO_INLINE __fastcall + +#else + +#define MY_CDECL +#define MY_STD_CALL +#define MY_FAST_CALL + +#endif + + +/* The following interfaces use first parameter as pointer to structure */ + +typedef struct +{ + SRes (*Read)(void *p, void *buf, size_t *size); + /* if (input(*size) != 0 && output(*size) == 0) means end_of_stream. + (output(*size) < input(*size)) is allowed */ +} ISeqInStream; + +/* it can return SZ_ERROR_INPUT_EOF */ +SRes SeqInStream_Read(ISeqInStream *stream, void *buf, size_t size); +SRes SeqInStream_Read2(ISeqInStream *stream, void *buf, size_t size, SRes errorType); +SRes SeqInStream_ReadByte(ISeqInStream *stream, Byte *buf); + +typedef struct +{ + size_t (*Write)(void *p, const void *buf, size_t size); + /* Returns: result - the number of actually written bytes. + (result < size) means error */ +} ISeqOutStream; + +typedef enum +{ + SZ_SEEK_SET = 0, + SZ_SEEK_CUR = 1, + SZ_SEEK_END = 2 +} ESzSeek; + +typedef struct +{ + SRes (*Read)(void *p, void *buf, size_t *size); /* same as ISeqInStream::Read */ + SRes (*Seek)(void *p, Int64 *pos, ESzSeek origin); +} ISeekInStream; + +typedef struct +{ + SRes (*Look)(void *p, void **buf, size_t *size); + /* if (input(*size) != 0 && output(*size) == 0) means end_of_stream. + (output(*size) > input(*size)) is not allowed + (output(*size) < input(*size)) is allowed */ + SRes (*Skip)(void *p, size_t offset); + /* offset must be <= output(*size) of Look */ + + SRes (*Read)(void *p, void *buf, size_t *size); + /* reads directly (without buffer). It's same as ISeqInStream::Read */ + SRes (*Seek)(void *p, Int64 *pos, ESzSeek origin); +} ILookInStream; + +SRes LookInStream_LookRead(ILookInStream *stream, void *buf, size_t *size); +SRes LookInStream_SeekTo(ILookInStream *stream, UInt64 offset); + +/* reads via ILookInStream::Read */ +SRes LookInStream_Read2(ILookInStream *stream, void *buf, size_t size, SRes errorType); +SRes LookInStream_Read(ILookInStream *stream, void *buf, size_t size); + +#define LookToRead_BUF_SIZE (1 << 14) + +typedef struct +{ + ILookInStream s; + ISeekInStream *realStream; + size_t pos; + size_t size; + Byte buf[LookToRead_BUF_SIZE]; +} CLookToRead; + +void LookToRead_CreateVTable(CLookToRead *p, int lookahead); +void LookToRead_Init(CLookToRead *p); + +typedef struct +{ + ISeqInStream s; + ILookInStream *realStream; +} CSecToLook; + +void SecToLook_CreateVTable(CSecToLook *p); + +typedef struct +{ + ISeqInStream s; + ILookInStream *realStream; +} CSecToRead; + +void SecToRead_CreateVTable(CSecToRead *p); + +typedef struct +{ + SRes (*Progress)(void *p, UInt64 inSize, UInt64 outSize); + /* Returns: result. (result != SZ_OK) means break. + Value (UInt64)(Int64)-1 for size means unknown value. */ +} ICompressProgress; + +typedef struct +{ + void *(*Alloc)(void *p, size_t size); + void (*Free)(void *p, void *address); /* address can be 0 */ +} ISzAlloc; + +#define IAlloc_Alloc(p, size) (p)->Alloc((p), size) +#define IAlloc_Free(p, a) (p)->Free((p), a) + +#endif diff --git a/wiipax/stub/channel.ld b/wiipax/stub/channel.ld new file mode 100644 index 0000000..5402765 --- /dev/null +++ b/wiipax/stub/channel.ld @@ -0,0 +1,64 @@ +OUTPUT_FORMAT("elf32-powerpc") +OUTPUT_ARCH(powerpc:common) + +__mem1_start = 0x80004000; +__mem2_start = 0x90010000; + +__mem1_entry = _start - __mem2_start + __mem1_start; +ENTRY(__mem1_entry) + +PHDRS { + realmode PT_LOAD FLAGS(5); + paxx PT_LOAD FLAGS(7); +} + +SECTIONS { + . = 0x80003400; + + .realmode : { KEEP(*(.realmode)) } :realmode = 0 + + . = __mem2_start; + + .start : AT(__mem1_start) { KEEP(*(.start)) } :paxx = 0 + .text : { *(.text) *(.text.*) } + + . = ALIGN(4); + + .payload : { + __payload = .; + LONG(0); + LONG(0); + LONG(0); + LONG(0); + LONG(0); + LONG(0); + } + + .rodata : { *(.rodata) *(.rodata.*) } + + . = (( . +19)&0xFFFFFFF0) - 4; + .padding : { + LONG(0xdeadbeef); + } + + .sdata : { *(.sdata) *(.sdata.*) } + .data : { *(.data) *(.data.*) } + + . = ALIGN(32); + __self_end = .; + + __bss_start = .; + .bss : { *(.bss) } :NONE = 0 + .sbss : { *(.sbss) } + __bss_end = .; + + . = ALIGN(32); + + .stack : { + _stack_top = .; + . += 32768; + _stack_bot = .; + } + +} + diff --git a/wiipax/stub/common.h b/wiipax/stub/common.h new file mode 100644 index 0000000..6cbb2ff --- /dev/null +++ b/wiipax/stub/common.h @@ -0,0 +1,107 @@ +#ifndef _LOADER_H_ +#define _LOADER_H_ + +#include + +// Basic types. + +typedef unsigned char u8; +typedef signed char s8; +typedef unsigned short int u16; +typedef signed short int s16; +typedef unsigned int u32; +typedef signed int s32; + +// Basic I/O. + +static inline u32 read32(u32 addr) { + u32 x; + + asm volatile("lwz %0,0(%1) ; sync" : "=r"(x) : "b"(0xc0000000 | addr)); + + return x; +} + +static inline void write32(u32 addr, u32 x) { + asm("stw %0,0(%1) ; eieio" : : "r"(x), "b"(0xc0000000 | addr)); +} + +static inline u16 read16(u32 addr) { + u16 x; + + asm volatile("lhz %0,0(%1) ; sync" : "=r"(x) : "b"(0xc0000000 | addr)); + + return x; +} + +static inline u8 read8(u32 addr) { + u8 x; + + asm volatile("lbz %0,0(%1) ; sync" : "=r"(x) : "b"(0xc0000000 | addr)); + + return x; +} + +static inline void write16(u32 addr, u16 x) { + asm("sth %0,0(%1) ; eieio" : : "r"(x), "b"(0xc0000000 | addr)); +} + +static inline void write8(u32 addr, u8 x) { + asm("stb %0,0(%1) ; eieio" : : "r"(x), "b"(0xc0000000 | addr)); +} + +static inline void mask32(u32 addr, u32 clear, u32 set) +{ + write32(addr, (read32(addr)&(~clear)) | set); +} + +// Address mapping. + +static inline u32 virt_to_phys(const void *p) { + return (u32)p & 0x7fffffff; +} + +static inline void *phys_to_virt(u32 x) { + return (void *)(x | 0x80000000); +} + + +// Cache synchronisation. + +void sync_before_read(void *p, u32 len); +void sync_after_write(const void *p, u32 len); +void sync_before_exec(const void *p, u32 len); + + +// Time. + +void udelay(u32 us); + + +// Special purpose registers. + +#define mtspr(n, x) do { asm("mtspr %1,%0" : : "r"(x), "i"(n)); } while (0) +#define mfspr(n) ({ \ + u32 x; asm volatile("mfspr %0,%1" : "=r"(x) : "i"(n)); x; \ +}) + + +// Exceptions. + +#ifdef NDEBUG +#define printf(...) { } +#else +void exception_init(void); +void gecko_init(void); +int printf(const char *fmt, ...); +#endif + + +// Debug: blink the tray led. + +static inline void blink(void) { + write32(0x0d8000c0, read32(0x0d8000c0) ^ 0x20); +} + +#endif + diff --git a/wiipax/stub/crt0.s b/wiipax/stub/crt0.s new file mode 100644 index 0000000..f92ca7e --- /dev/null +++ b/wiipax/stub/crt0.s @@ -0,0 +1,330 @@ +# crt0.s file for the GameCube V1.0 by Costis (costis@gbaemu.com)! + + .text + .section .start,"ax",@progbits + .extern _start + .align 2 + .globl _start +_start: + + lis 3,__mem1_start@h + ori 3,3,__mem1_start@l + lis 4,__mem2_start@h + ori 4,4,__mem2_start@l + lis 5,__self_end@h + ori 5,5,__self_end@l + +_reloc_loop: + lwz 2,0(3) + stw 2,0(4) + + addi 3,3,4 + addi 4,4,4 + cmplw 4,5 + blt _reloc_loop + + lis 4,__mem2_start@h + ori 4,4,__mem2_start@l +_flush_loop: + dcbst 0,4 + sync + icbi 0,4 + addi 4,4,32 + cmplw 4,5 + blt _flush_loop + + sync + isync + + lis 3,_mem2_entry@h + ori 3,3,_mem2_entry@l + mtctr 3 + bctr + +_mem2_entry: + # Clear all GPRs except + .irp i, 0,1,2,3,4,5,6,7,8,9,10,11,12,13,14,15,16,17,18,19,20,21,22,23,24,25,26,27,28,29,30,31 + li \i,0 + .endr + + lis 1,_stack_bot@h + ori 1,1,_stack_bot@l + stwu 0,-64(1) + + lis 2,0x8000 + stw 1,0x34(2) # write sp +# lis 13,_SDA_BASE_@h +# ori 13,13,_SDA_BASE_@l # Set the Small Data (Read\Write) base register. + + bl InitHardware # Initialize the GameCube Hardware (Floating Point Registers, Caches, etc.) + bl SystemInit # Initialize more cache aspects, clear a few SPR's, and disable interrupts. + + # clear BSS + lis 3, __bss_start@h + ori 3, 3, __bss_start@l + li 4, 0 + lis 5, __bss_end@h + ori 5, 5, __bss_end@l + sub 5, 5, 3 + + bl memset + + mr 3, 28 + bl stubmain # Branch to the user code! + + b . + +InitHardware: + mflr 31 # Store the link register in r31 + + bl PSInit # Initialize Paired Singles + bl FPRInit # Initialize the FPR's + bl CacheInit # Initialize the system caches + + mtlr 31 # Retreive the link register from r31 + blr + +PSInit: + mfspr 3, 920 # (HID2) + oris 3, 3, 0xA000 + mtspr 920, 3 # (HID2) + + # Set the Instruction Cache invalidation bit in HID0 + mfspr 3,1008 + ori 3,3,0x0800 + mtspr 1008,3 + + sync + + # Clear various Special Purpose Registers + li 3,0 + mtspr 912,3 + mtspr 913,3 + mtspr 914,3 + mtspr 915,3 + mtspr 916,3 + mtspr 917,3 + mtspr 918,3 + mtspr 919,3 + + # Return + blr + +FPRInit: + # Enable the Floating Point Registers + mfmsr 3 + ori 3,3,0x2000 + mtmsr 3 + + # Clear all of the FPR's to 0 + lis 3, zfloat@h + ori 3, 3, zfloat@l + lfd 0, 0(3) + fmr 1,0 + fmr 2,0 + fmr 3,0 + fmr 4,0 + fmr 5,0 + fmr 6,0 + fmr 7,0 + fmr 8,0 + fmr 9,0 + fmr 10,0 + fmr 11,0 + fmr 12,0 + fmr 13,0 + fmr 14,0 + fmr 15,0 + fmr 16,0 + fmr 17,0 + fmr 18,0 + fmr 19,0 + fmr 20,0 + fmr 21,0 + fmr 22,0 + fmr 23,0 + fmr 24,0 + fmr 25,0 + fmr 26,0 + fmr 27,0 + fmr 28,0 + fmr 29,0 + fmr 30,0 + fmr 31,0 + mtfsf 255,0 + + # Return + blr + +CacheInit: + mflr 0 + stw 0, 4(1) + stwu 1, -16(1) + stw 31, 12(1) + stw 30, 8(1) + + mfspr 3,1008 # (HID0) + rlwinm 0, 3, 0, 16, 16 + cmplwi 0, 0x0000 # Check if the Instruction Cache has been enabled or not. + bne ICEnabled + + # If not, then enable it. + isync + mfspr 3, 1008 + ori 3, 3, 0x8000 + mtspr 1008, 3 + +ICEnabled: + mfspr 3, 1008 # bl PPCMfhid0 + rlwinm 0, 3, 0, 17, 17 + cmplwi 0, 0x0000 # Check if the Data Cache has been enabled or not. + bne DCEnabled + + # If not, then enable it. + sync + mfspr 3, 1008 + ori 3, 3, 0x4000 + mtspr 1008, 3 + +DCEnabled: + + mfspr 3, 1017 # (L2CR) + clrrwi 0, 3, 31 # Clear all of the bits except 31 + cmplwi 0, 0x0000 + bne L2GISkip # Skip the L2 Global Cache Invalidation process if it has already been done befor. + + # Store the current state of the MSR in r30 + mfmsr 3 + mr 30,3 + + sync + + # Enable Instruction and Data Address Translation + li 3, 48 + mtmsr 3 + + sync + sync + + # Disable the L2 Global Cache. + mfspr 3, 1017 # (L2CR + clrlwi 3, 3, 1 + mtspr 1017, 3 # (L2CR) + sync + + # Invalidate the L2 Global Cache. + bl L2GlobalInvalidate + + # Restore the previous state of the MSR from r30 + mr 3, 30 + mtmsr 3 + + # Enable the L2 Global Cache and disable the L2 Data Only bit and the L2 Global Invalidate Bit. + mfspr 3, 1017 # (L2CR) + oris 0, 3, 0x8000 + rlwinm 3, 0, 0, 11, 9 + mtspr 1017, 3 # (L2CR) + + +L2GISkip: + # Restore the non-volatile registers to their previous values and return. + lwz 0, 20(1) + lwz 31, 12(1) + lwz 30, 8(1) + addi 1, 1, 16 + mtlr 0 + blr + +L2GlobalInvalidate: + mflr 0 + stw 0, 4(1) + stwu 1, -16(1) + stw 31, 12(1) + sync + + # Disable the L2 Cache. + mfspr 3, 1017 # bl PPCMf1017 + clrlwi 3, 3, 1 + mtspr 1017, 3 # bl PPCMt1017 + + sync + + # Initiate the L2 Cache Global Invalidation process. + mfspr 3, 1017 # (L2CR) + oris 3, 3, 0x0020 + mtspr 1017, 3 # (L2CR) + + # Wait until the L2 Cache Global Invalidation has been completed. +L2GICheckComplete: + mfspr 3, 1017 # (L2CR) + clrlwi 0, 3, 31 + cmplwi 0, 0x0000 + bne L2GICheckComplete + + # Clear the L2 Data Only bit and the L2 Global Invalidate Bit. + mfspr 3, 1017 # (L2CR) + rlwinm 3, 3, 0, 11, 9 + mtspr 1017, 3 # (L2CR) + + # Wait until the L2 Cache Global Invalidation status bit signifies that it is ready. +L2GDICheckComplete: + mfspr 3, 1017 # (L2CR) + clrlwi 0, 3, 31 + cmplwi 0, 0x0000 + bne L2GDICheckComplete + + # Restore the non-volatile registers to their previous values and return. + lwz 0, 20(1) + lwz 31, 12(1) + addi 1, 1, 16 + mtlr 0 + blr + +SystemInit: + mflr 0 + stw 0, 4(1) + stwu 1, -0x18(1) + stw 31, 0x14(1) + stw 30, 0x10(1) + stw 29, 0xC(1) + + # Disable interrupts! + mfmsr 3 + rlwinm 4,3,0,17,15 + rlwinm 4,4,0,26,24 + mtmsr 4 + + # Clear various SPR's + li 3,0 + mtspr 952, 3 + mtspr 956, 3 + mtspr 953, 3 + mtspr 954, 3 + mtspr 957, 3 + mtspr 958, 3 + + # Disable Speculative Bus Accesses to non-guarded space from both caches. + mfspr 3, 1008 # (HID0) + ori 3, 3, 0x0200 + mtspr 1008, 3 + + # Set the Non-IEEE mode in the FPSCR + mtfsb1 29 + + mfspr 3,920 # (HID2) + rlwinm 3, 3, 0, 2, 0 + mtspr 920,3 # (HID2) + + # Restore the non-volatile registers to their previous values and return. + lwz 0, 0x1C(1) + lwz 31, 0x14(1) + lwz 30, 0x10(1) + lwz 29, 0xC(1) + addi 1, 1, 0x18 + mtlr 0 + blr + +zfloat: + .float 0 + .align 4 +_got_start: diff --git a/wiipax/stub/devkitfail.ld b/wiipax/stub/devkitfail.ld new file mode 100644 index 0000000..4579d70 --- /dev/null +++ b/wiipax/stub/devkitfail.ld @@ -0,0 +1,59 @@ +OUTPUT_FORMAT("elf32-powerpc") +OUTPUT_ARCH(powerpc:common) + +__mem1_start = 0x80004000; +__mem2_start = 0x90010000; + +__mem1_entry = _start - __mem2_start + __mem1_start; +ENTRY(__mem1_entry) + +PHDRS { + paxx PT_LOAD FLAGS(7); +} + +SECTIONS { + . = __mem2_start; + + .start : AT(__mem1_start) { KEEP(*(.start)) } :paxx = 0 + .text : { *(.text) *(.text.*) } + + . = ALIGN(4); + + .payload : { + __payload = .; + LONG(0); + LONG(0); + LONG(0); + LONG(0); + LONG(0); + LONG(0); + } + + .rodata : { *(.rodata) *(.rodata.*) } + + . = (( . +19)&0xFFFFFFF0) - 4; + .padding : { + LONG(0xdeadbeef); + } + + .sdata : { *(.sdata) *(.sdata.*) } + .data : { *(.data) *(.data.*) } + + . = ALIGN(32); + __self_end = .; + + __bss_start = .; + .bss : { *(.bss) } :NONE = 0 + .sbss : { *(.sbss) } + __bss_end = .; + + . = ALIGN(32); + + .stack : { + _stack_top = .; + . += 32768; + _stack_bot = .; + } + +} + diff --git a/wiipax/stub/elf.c b/wiipax/stub/elf.c new file mode 100644 index 0000000..099ffa0 --- /dev/null +++ b/wiipax/stub/elf.c @@ -0,0 +1,113 @@ +/* + * Copyright (c) 2001 William L. Pitts + * Modifications (c) 2004 Felix Domke + * All rights reserved. + * + * Redistribution and use in source and binary forms are freely + * permitted provided that the above copyright notice and this + * paragraph and the following disclaimer are duplicated in all + * such forms. + * + * This software is provided "AS IS" and without any express or + * implied warranties, including, without limitation, the implied + * warranties of merchantability and fitness for a particular + * purpose. + */ + +#include "common.h" +#include "elf_abi.h" +#include "string.h" + +int valid_elf_image (void *addr) { + Elf32_Ehdr *ehdr; /* Elf header structure pointer */ + + ehdr = (Elf32_Ehdr *) addr; + + if (!IS_ELF (*ehdr)) + return 0; + + if (ehdr->e_ident[EI_CLASS] != ELFCLASS32) + return -1; + + if (ehdr->e_ident[EI_DATA] != ELFDATA2MSB) + return -1; + + if (ehdr->e_ident[EI_VERSION] != EV_CURRENT) + return -1; + + if (ehdr->e_type != ET_EXEC) + return -1; + + if (ehdr->e_machine != EM_PPC) + return -1; + + return 1; +} + +u32 load_elf_image(void *addr) { + Elf32_Ehdr *ehdr; + Elf32_Phdr *phdrs; + u8 *image; + int i; + + ehdr = (Elf32_Ehdr *)addr; + + if(ehdr->e_phoff == 0 || ehdr->e_phnum == 0) { + printf("ELF has no phdrs\n"); + return 0; + } + + if(ehdr->e_phentsize != sizeof(Elf32_Phdr)) { + printf("Invalid ELF phdr size\n"); + return 0; + } + + phdrs = (Elf32_Phdr*)(addr + ehdr->e_phoff); + + for(i = 0; i < ehdr->e_phnum; i++) { + if(phdrs[i].p_type != PT_LOAD) { + printf("skip PHDR %d of type %d\n", i, phdrs[i].p_type); + continue; + } + + // translate paddr to this BAT setup + phdrs[i].p_paddr &= 0x3FFFFFFF; + phdrs[i].p_paddr |= 0x80000000; + + printf("PHDR %d 0x%08x [0x%x] -> 0x%08x [0x%x] <", i, + phdrs[i].p_offset, phdrs[i].p_filesz, + phdrs[i].p_paddr, phdrs[i].p_memsz); + + if(phdrs[i].p_flags & PF_R) + printf("R"); + if(phdrs[i].p_flags & PF_W) + printf("W"); + if(phdrs[i].p_flags & PF_X) + printf("X"); + printf(">\n"); + + if(phdrs[i].p_filesz > phdrs[i].p_memsz) { + printf("-> file size > mem size\n"); + return 0; + } + + if(phdrs[i].p_filesz) { + printf("-> load 0x%x\n", phdrs[i].p_filesz); + image = (u8 *)(addr + phdrs[i].p_offset); + memcpy((void *)phdrs[i].p_paddr, (const void *)image, + phdrs[i].p_filesz); + memset((void *)image, 0, phdrs[i].p_filesz); + + sync_after_write((void *)phdrs[i].p_paddr, phdrs[i].p_memsz); + + if(phdrs[i].p_flags & PF_X) + sync_before_exec((void *)phdrs[i].p_paddr, phdrs[i].p_memsz); + } else { + printf ("-> skip\n"); + } + } + + // entry point of the ELF _has_ to be correct - no translation done + return ehdr->e_entry; +} + diff --git a/wiipax/stub/elf.h b/wiipax/stub/elf.h new file mode 100644 index 0000000..10edf28 --- /dev/null +++ b/wiipax/stub/elf.h @@ -0,0 +1,7 @@ +#ifndef _ELF_H +#define _ELF_H + +int valid_elf_image (void *addr); +u32 load_elf_image (void *addr); + +#endif diff --git a/wiipax/stub/elf_abi.h b/wiipax/stub/elf_abi.h new file mode 100644 index 0000000..379355c --- /dev/null +++ b/wiipax/stub/elf_abi.h @@ -0,0 +1,594 @@ +/* + * Copyright (c) 1995, 1996, 2001, 2002 + * Erik Theisen. All rights reserved. + * + * Redistribution and use in source and binary forms, with or without + * modification, are permitted provided that the following conditions + * are met: + * 1. Redistributions of source code must retain the above copyright + * notice, this list of conditions and the following disclaimer. + * 2. Redistributions in binary form must reproduce the above copyright + * notice, this list of conditions and the following disclaimer in the + * documentation and/or other materials provided with the distribution. + * 3. The name of the author may not be used to endorse or promote products + * derived from this software without specific prior written permission + * + * THIS SOFTWARE IS PROVIDED BY THE AUTHOR ``AS IS'' AND ANY EXPRESS OR + * IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE IMPLIED WARRANTIES + * OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE ARE DISCLAIMED. + * IN NO EVENT SHALL THE AUTHOR BE LIABLE FOR ANY DIRECT, INDIRECT, + * INCIDENTAL, SPECIAL, EXEMPLARY, OR CONSEQUENTIAL DAMAGES (INCLUDING, BUT + * NOT LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS OR SERVICES; LOSS OF USE, + * DATA, OR PROFITS; OR BUSINESS INTERRUPTION) HOWEVER CAUSED AND ON ANY + * THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT LIABILITY, OR TORT + * (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY OUT OF THE USE OF + * THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF SUCH DAMAGE. + */ + +/* + * This is the ELF ABI header file + * formerly known as "elf_abi.h". + */ + +#ifndef _ELF_ABI_H +#define _ELF_ABI_H + +#include "common.h" + +/* + * This version doesn't work for 64-bit ABIs - Erik. + */ + +/* + * These typedefs need to be handled better. + */ +typedef u32 Elf32_Addr; /* Unsigned program address */ +typedef u32 Elf32_Off; /* Unsigned file offset */ +typedef s32 Elf32_Sword; /* Signed large integer */ +typedef u32 Elf32_Word; /* Unsigned large integer */ +typedef u16 Elf32_Half; /* Unsigned medium integer */ + +/* e_ident[] identification indexes */ +#define EI_MAG0 0 /* file ID */ +#define EI_MAG1 1 /* file ID */ +#define EI_MAG2 2 /* file ID */ +#define EI_MAG3 3 /* file ID */ +#define EI_CLASS 4 /* file class */ +#define EI_DATA 5 /* data encoding */ +#define EI_VERSION 6 /* ELF header version */ +#define EI_OSABI 7 /* OS/ABI specific ELF extensions */ +#define EI_ABIVERSION 8 /* ABI target version */ +#define EI_PAD 9 /* start of pad bytes */ +#define EI_NIDENT 16 /* Size of e_ident[] */ + +/* e_ident[] magic number */ +#define ELFMAG0 0x7f /* e_ident[EI_MAG0] */ +#define ELFMAG1 'E' /* e_ident[EI_MAG1] */ +#define ELFMAG2 'L' /* e_ident[EI_MAG2] */ +#define ELFMAG3 'F' /* e_ident[EI_MAG3] */ +#define ELFMAG "\177ELF" /* magic */ +#define SELFMAG 4 /* size of magic */ + +/* e_ident[] file class */ +#define ELFCLASSNONE 0 /* invalid */ +#define ELFCLASS32 1 /* 32-bit objs */ +#define ELFCLASS64 2 /* 64-bit objs */ +#define ELFCLASSNUM 3 /* number of classes */ + +/* e_ident[] data encoding */ +#define ELFDATANONE 0 /* invalid */ +#define ELFDATA2LSB 1 /* Little-Endian */ +#define ELFDATA2MSB 2 /* Big-Endian */ +#define ELFDATANUM 3 /* number of data encode defines */ + +/* e_ident[] OS/ABI specific ELF extensions */ +#define ELFOSABI_NONE 0 /* No extension specified */ +#define ELFOSABI_HPUX 1 /* Hewlett-Packard HP-UX */ +#define ELFOSABI_NETBSD 2 /* NetBSD */ +#define ELFOSABI_LINUX 3 /* Linux */ +#define ELFOSABI_SOLARIS 6 /* Sun Solaris */ +#define ELFOSABI_AIX 7 /* AIX */ +#define ELFOSABI_IRIX 8 /* IRIX */ +#define ELFOSABI_FREEBSD 9 /* FreeBSD */ +#define ELFOSABI_TRU64 10 /* Compaq TRU64 UNIX */ +#define ELFOSABI_MODESTO 11 /* Novell Modesto */ +#define ELFOSABI_OPENBSD 12 /* OpenBSD */ +/* 64-255 Architecture-specific value range */ + +/* e_ident[] ABI Version */ +#define ELFABIVERSION 0 + +/* e_ident */ +#define IS_ELF(ehdr) ((ehdr).e_ident[EI_MAG0] == ELFMAG0 && \ + (ehdr).e_ident[EI_MAG1] == ELFMAG1 && \ + (ehdr).e_ident[EI_MAG2] == ELFMAG2 && \ + (ehdr).e_ident[EI_MAG3] == ELFMAG3) + +/* ELF Header */ +typedef struct elfhdr{ + unsigned char e_ident[EI_NIDENT]; /* ELF Identification */ + Elf32_Half e_type; /* object file type */ + Elf32_Half e_machine; /* machine */ + Elf32_Word e_version; /* object file version */ + Elf32_Addr e_entry; /* virtual entry point */ + Elf32_Off e_phoff; /* program header table offset */ + Elf32_Off e_shoff; /* section header table offset */ + Elf32_Word e_flags; /* processor-specific flags */ + Elf32_Half e_ehsize; /* ELF header size */ + Elf32_Half e_phentsize; /* program header entry size */ + Elf32_Half e_phnum; /* number of program header entries */ + Elf32_Half e_shentsize; /* section header entry size */ + Elf32_Half e_shnum; /* number of section header entries */ + Elf32_Half e_shstrndx; /* section header table's "section + header string table" entry offset */ +} Elf32_Ehdr; + +/* e_type */ +#define ET_NONE 0 /* No file type */ +#define ET_REL 1 /* relocatable file */ +#define ET_EXEC 2 /* executable file */ +#define ET_DYN 3 /* shared object file */ +#define ET_CORE 4 /* core file */ +#define ET_NUM 5 /* number of types */ +#define ET_LOOS 0xfe00 /* reserved range for operating */ +#define ET_HIOS 0xfeff /* system specific e_type */ +#define ET_LOPROC 0xff00 /* reserved range for processor */ +#define ET_HIPROC 0xffff /* specific e_type */ + +/* e_machine */ +#define EM_NONE 0 /* No Machine */ +#define EM_M32 1 /* AT&T WE 32100 */ +#define EM_SPARC 2 /* SPARC */ +#define EM_386 3 /* Intel 80386 */ +#define EM_68K 4 /* Motorola 68000 */ +#define EM_88K 5 /* Motorola 88000 */ +#if 0 +#define EM_486 6 /* RESERVED - was Intel 80486 */ +#endif +#define EM_860 7 /* Intel 80860 */ +#define EM_MIPS 8 /* MIPS R3000 Big-Endian only */ +#define EM_S370 9 /* IBM System/370 Processor */ +#define EM_MIPS_RS4_BE 10 /* MIPS R4000 Big-Endian */ +#if 0 +#define EM_SPARC64 11 /* RESERVED - was SPARC v9 + 64-bit unoffical */ +#endif +/* RESERVED 11-14 for future use */ +#define EM_PARISC 15 /* HPPA */ +/* RESERVED 16 for future use */ +#define EM_VPP500 17 /* Fujitsu VPP500 */ +#define EM_SPARC32PLUS 18 /* Enhanced instruction set SPARC */ +#define EM_960 19 /* Intel 80960 */ +#define EM_PPC 20 /* PowerPC */ +#define EM_PPC64 21 /* 64-bit PowerPC */ +#define EM_S390 22 /* IBM System/390 Processor */ +/* RESERVED 23-35 for future use */ +#define EM_V800 36 /* NEC V800 */ +#define EM_FR20 37 /* Fujitsu FR20 */ +#define EM_RH32 38 /* TRW RH-32 */ +#define EM_RCE 39 /* Motorola RCE */ +#define EM_ARM 40 /* Advanced Risc Machines ARM */ +#define EM_ALPHA 41 /* Digital Alpha */ +#define EM_SH 42 /* Hitachi SH */ +#define EM_SPARCV9 43 /* SPARC Version 9 */ +#define EM_TRICORE 44 /* Siemens TriCore embedded processor */ +#define EM_ARC 45 /* Argonaut RISC Core */ +#define EM_H8_300 46 /* Hitachi H8/300 */ +#define EM_H8_300H 47 /* Hitachi H8/300H */ +#define EM_H8S 48 /* Hitachi H8S */ +#define EM_H8_500 49 /* Hitachi H8/500 */ +#define EM_IA_64 50 /* Intel Merced */ +#define EM_MIPS_X 51 /* Stanford MIPS-X */ +#define EM_COLDFIRE 52 /* Motorola Coldfire */ +#define EM_68HC12 53 /* Motorola M68HC12 */ +#define EM_MMA 54 /* Fujitsu MMA Multimedia Accelerator*/ +#define EM_PCP 55 /* Siemens PCP */ +#define EM_NCPU 56 /* Sony nCPU embeeded RISC */ +#define EM_NDR1 57 /* Denso NDR1 microprocessor */ +#define EM_STARCORE 58 /* Motorola Start*Core processor */ +#define EM_ME16 59 /* Toyota ME16 processor */ +#define EM_ST100 60 /* STMicroelectronic ST100 processor */ +#define EM_TINYJ 61 /* Advanced Logic Corp. Tinyj emb.fam*/ +#define EM_X86_64 62 /* AMD x86-64 */ +#define EM_PDSP 63 /* Sony DSP Processor */ +/* RESERVED 64,65 for future use */ +#define EM_FX66 66 /* Siemens FX66 microcontroller */ +#define EM_ST9PLUS 67 /* STMicroelectronics ST9+ 8/16 mc */ +#define EM_ST7 68 /* STmicroelectronics ST7 8 bit mc */ +#define EM_68HC16 69 /* Motorola MC68HC16 microcontroller */ +#define EM_68HC11 70 /* Motorola MC68HC11 microcontroller */ +#define EM_68HC08 71 /* Motorola MC68HC08 microcontroller */ +#define EM_68HC05 72 /* Motorola MC68HC05 microcontroller */ +#define EM_SVX 73 /* Silicon Graphics SVx */ +#define EM_ST19 74 /* STMicroelectronics ST19 8 bit mc */ +#define EM_VAX 75 /* Digital VAX */ +#define EM_CHRIS 76 /* Axis Communications embedded proc. */ +#define EM_JAVELIN 77 /* Infineon Technologies emb. proc. */ +#define EM_FIREPATH 78 /* Element 14 64-bit DSP Processor */ +#define EM_ZSP 79 /* LSI Logic 16-bit DSP Processor */ +#define EM_MMIX 80 /* Donald Knuth's edu 64-bit proc. */ +#define EM_HUANY 81 /* Harvard University mach-indep objs */ +#define EM_PRISM 82 /* SiTera Prism */ +#define EM_AVR 83 /* Atmel AVR 8-bit microcontroller */ +#define EM_FR30 84 /* Fujitsu FR30 */ +#define EM_D10V 85 /* Mitsubishi DV10V */ +#define EM_D30V 86 /* Mitsubishi DV30V */ +#define EM_V850 87 /* NEC v850 */ +#define EM_M32R 88 /* Mitsubishi M32R */ +#define EM_MN10300 89 /* Matsushita MN10200 */ +#define EM_MN10200 90 /* Matsushita MN10200 */ +#define EM_PJ 91 /* picoJava */ +#define EM_NUM 92 /* number of machine types */ + +/* Version */ +#define EV_NONE 0 /* Invalid */ +#define EV_CURRENT 1 /* Current */ +#define EV_NUM 2 /* number of versions */ + +/* Section Header */ +typedef struct { + Elf32_Word sh_name; /* name - index into section header + string table section */ + Elf32_Word sh_type; /* type */ + Elf32_Word sh_flags; /* flags */ + Elf32_Addr sh_addr; /* address */ + Elf32_Off sh_offset; /* file offset */ + Elf32_Word sh_size; /* section size */ + Elf32_Word sh_link; /* section header table index link */ + Elf32_Word sh_info; /* extra information */ + Elf32_Word sh_addralign; /* address alignment */ + Elf32_Word sh_entsize; /* section entry size */ +} Elf32_Shdr; + +/* Special Section Indexes */ +#define SHN_UNDEF 0 /* undefined */ +#define SHN_LORESERVE 0xff00 /* lower bounds of reserved indexes */ +#define SHN_LOPROC 0xff00 /* reserved range for processor */ +#define SHN_HIPROC 0xff1f /* specific section indexes */ +#define SHN_LOOS 0xff20 /* reserved range for operating */ +#define SHN_HIOS 0xff3f /* specific semantics */ +#define SHN_ABS 0xfff1 /* absolute value */ +#define SHN_COMMON 0xfff2 /* common symbol */ +#define SHN_XINDEX 0xffff /* Index is an extra table */ +#define SHN_HIRESERVE 0xffff /* upper bounds of reserved indexes */ + +/* sh_type */ +#define SHT_NULL 0 /* inactive */ +#define SHT_PROGBITS 1 /* program defined information */ +#define SHT_SYMTAB 2 /* symbol table section */ +#define SHT_STRTAB 3 /* string table section */ +#define SHT_RELA 4 /* relocation section with addends*/ +#define SHT_HASH 5 /* symbol hash table section */ +#define SHT_DYNAMIC 6 /* dynamic section */ +#define SHT_NOTE 7 /* note section */ +#define SHT_NOBITS 8 /* no space section */ +#define SHT_REL 9 /* relation section without addends */ +#define SHT_SHLIB 10 /* reserved - purpose unknown */ +#define SHT_DYNSYM 11 /* dynamic symbol table section */ +#define SHT_INIT_ARRAY 14 /* Array of constructors */ +#define SHT_FINI_ARRAY 15 /* Array of destructors */ +#define SHT_PREINIT_ARRAY 16 /* Array of pre-constructors */ +#define SHT_GROUP 17 /* Section group */ +#define SHT_SYMTAB_SHNDX 18 /* Extended section indeces */ +#define SHT_NUM 19 /* number of section types */ +#define SHT_LOOS 0x60000000 /* Start OS-specific */ +#define SHT_HIOS 0x6fffffff /* End OS-specific */ +#define SHT_LOPROC 0x70000000 /* reserved range for processor */ +#define SHT_HIPROC 0x7fffffff /* specific section header types */ +#define SHT_LOUSER 0x80000000 /* reserved range for application */ +#define SHT_HIUSER 0xffffffff /* specific indexes */ + +/* Section names */ +#define ELF_BSS ".bss" /* uninitialized data */ +#define ELF_COMMENT ".comment" /* version control information */ +#define ELF_DATA ".data" /* initialized data */ +#define ELF_DATA1 ".data1" /* initialized data */ +#define ELF_DEBUG ".debug" /* debug */ +#define ELF_DYNAMIC ".dynamic" /* dynamic linking information */ +#define ELF_DYNSTR ".dynstr" /* dynamic string table */ +#define ELF_DYNSYM ".dynsym" /* dynamic symbol table */ +#define ELF_FINI ".fini" /* termination code */ +#define ELF_FINI_ARRAY ".fini_array" /* Array of destructors */ +#define ELF_GOT ".got" /* global offset table */ +#define ELF_HASH ".hash" /* symbol hash table */ +#define ELF_INIT ".init" /* initialization code */ +#define ELF_INIT_ARRAY ".init_array" /* Array of constuctors */ +#define ELF_INTERP ".interp" /* Pathname of program interpreter */ +#define ELF_LINE ".line" /* Symbolic line numnber information */ +#define ELF_NOTE ".note" /* Contains note section */ +#define ELF_PLT ".plt" /* Procedure linkage table */ +#define ELF_PREINIT_ARRAY ".preinit_array" /* Array of pre-constructors */ +#define ELF_REL_DATA ".rel.data" /* relocation data */ +#define ELF_REL_FINI ".rel.fini" /* relocation termination code */ +#define ELF_REL_INIT ".rel.init" /* relocation initialization code */ +#define ELF_REL_DYN ".rel.dyn" /* relocaltion dynamic link info */ +#define ELF_REL_RODATA ".rel.rodata" /* relocation read-only data */ +#define ELF_REL_TEXT ".rel.text" /* relocation code */ +#define ELF_RODATA ".rodata" /* read-only data */ +#define ELF_RODATA1 ".rodata1" /* read-only data */ +#define ELF_SHSTRTAB ".shstrtab" /* section header string table */ +#define ELF_STRTAB ".strtab" /* string table */ +#define ELF_SYMTAB ".symtab" /* symbol table */ +#define ELF_SYMTAB_SHNDX ".symtab_shndx"/* symbol table section index */ +#define ELF_TBSS ".tbss" /* thread local uninit data */ +#define ELF_TDATA ".tdata" /* thread local init data */ +#define ELF_TDATA1 ".tdata1" /* thread local init data */ +#define ELF_TEXT ".text" /* code */ + +/* Section Attribute Flags - sh_flags */ +#define SHF_WRITE 0x1 /* Writable */ +#define SHF_ALLOC 0x2 /* occupies memory */ +#define SHF_EXECINSTR 0x4 /* executable */ +#define SHF_MERGE 0x10 /* Might be merged */ +#define SHF_STRINGS 0x20 /* Contains NULL terminated strings */ +#define SHF_INFO_LINK 0x40 /* sh_info contains SHT index */ +#define SHF_LINK_ORDER 0x80 /* Preserve order after combining*/ +#define SHF_OS_NONCONFORMING 0x100 /* Non-standard OS specific handling */ +#define SHF_GROUP 0x200 /* Member of section group */ +#define SHF_TLS 0x400 /* Thread local storage */ +#define SHF_MASKOS 0x0ff00000 /* OS specific */ +#define SHF_MASKPROC 0xf0000000 /* reserved bits for processor */ + /* specific section attributes */ + +/* Section Group Flags */ +#define GRP_COMDAT 0x1 /* COMDAT group */ +#define GRP_MASKOS 0x0ff00000 /* Mask OS specific flags */ +#define GRP_MASKPROC 0xf0000000 /* Mask processor specific flags */ + +/* Symbol Table Entry */ +typedef struct elf32_sym { + Elf32_Word st_name; /* name - index into string table */ + Elf32_Addr st_value; /* symbol value */ + Elf32_Word st_size; /* symbol size */ + unsigned char st_info; /* type and binding */ + unsigned char st_other; /* 0 - no defined meaning */ + Elf32_Half st_shndx; /* section header index */ +} Elf32_Sym; + +/* Symbol table index */ +#define STN_UNDEF 0 /* undefined */ + +/* Extract symbol info - st_info */ +#define ELF32_ST_BIND(x) ((x) >> 4) +#define ELF32_ST_TYPE(x) (((unsigned int) x) & 0xf) +#define ELF32_ST_INFO(b,t) (((b) << 4) + ((t) & 0xf)) +#define ELF32_ST_VISIBILITY(x) ((x) & 0x3) + +/* Symbol Binding - ELF32_ST_BIND - st_info */ +#define STB_LOCAL 0 /* Local symbol */ +#define STB_GLOBAL 1 /* Global symbol */ +#define STB_WEAK 2 /* like global - lower precedence */ +#define STB_NUM 3 /* number of symbol bindings */ +#define STB_LOOS 10 /* reserved range for operating */ +#define STB_HIOS 12 /* system specific symbol bindings */ +#define STB_LOPROC 13 /* reserved range for processor */ +#define STB_HIPROC 15 /* specific symbol bindings */ + +/* Symbol type - ELF32_ST_TYPE - st_info */ +#define STT_NOTYPE 0 /* not specified */ +#define STT_OBJECT 1 /* data object */ +#define STT_FUNC 2 /* function */ +#define STT_SECTION 3 /* section */ +#define STT_FILE 4 /* file */ +#define STT_NUM 5 /* number of symbol types */ +#define STT_TLS 6 /* Thread local storage symbol */ +#define STT_LOOS 10 /* reserved range for operating */ +#define STT_HIOS 12 /* system specific symbol types */ +#define STT_LOPROC 13 /* reserved range for processor */ +#define STT_HIPROC 15 /* specific symbol types */ + +/* Symbol visibility - ELF32_ST_VISIBILITY - st_other */ +#define STV_DEFAULT 0 /* Normal visibility rules */ +#define STV_INTERNAL 1 /* Processor specific hidden class */ +#define STV_HIDDEN 2 /* Symbol unavailable in other mods */ +#define STV_PROTECTED 3 /* Not preemptible, not exported */ + + +/* Relocation entry with implicit addend */ +typedef struct +{ + Elf32_Addr r_offset; /* offset of relocation */ + Elf32_Word r_info; /* symbol table index and type */ +} Elf32_Rel; + +/* Relocation entry with explicit addend */ +typedef struct +{ + Elf32_Addr r_offset; /* offset of relocation */ + Elf32_Word r_info; /* symbol table index and type */ + Elf32_Sword r_addend; +} Elf32_Rela; + +/* Extract relocation info - r_info */ +#define ELF32_R_SYM(i) ((i) >> 8) +#define ELF32_R_TYPE(i) ((unsigned char) (i)) +#define ELF32_R_INFO(s,t) (((s) << 8) + (unsigned char)(t)) + +/* Program Header */ +typedef struct { + Elf32_Word p_type; /* segment type */ + Elf32_Off p_offset; /* segment offset */ + Elf32_Addr p_vaddr; /* virtual address of segment */ + Elf32_Addr p_paddr; /* physical address - ignored? */ + Elf32_Word p_filesz; /* number of bytes in file for seg. */ + Elf32_Word p_memsz; /* number of bytes in mem. for seg. */ + Elf32_Word p_flags; /* flags */ + Elf32_Word p_align; /* memory alignment */ +} Elf32_Phdr; + +/* Segment types - p_type */ +#define PT_NULL 0 /* unused */ +#define PT_LOAD 1 /* loadable segment */ +#define PT_DYNAMIC 2 /* dynamic linking section */ +#define PT_INTERP 3 /* the RTLD */ +#define PT_NOTE 4 /* auxiliary information */ +#define PT_SHLIB 5 /* reserved - purpose undefined */ +#define PT_PHDR 6 /* program header */ +#define PT_TLS 7 /* Thread local storage template */ +#define PT_NUM 8 /* Number of segment types */ +#define PT_LOOS 0x60000000 /* reserved range for operating */ +#define PT_HIOS 0x6fffffff /* system specific segment types */ +#define PT_LOPROC 0x70000000 /* reserved range for processor */ +#define PT_HIPROC 0x7fffffff /* specific segment types */ + +/* Segment flags - p_flags */ +#define PF_X 0x1 /* Executable */ +#define PF_W 0x2 /* Writable */ +#define PF_R 0x4 /* Readable */ +#define PF_MASKOS 0x0ff00000 /* OS specific segment flags */ +#define PF_MASKPROC 0xf0000000 /* reserved bits for processor */ + /* specific segment flags */ +/* Dynamic structure */ +typedef struct +{ + Elf32_Sword d_tag; /* controls meaning of d_val */ + union + { + Elf32_Word d_val; /* Multiple meanings - see d_tag */ + Elf32_Addr d_ptr; /* program virtual address */ + } d_un; +} Elf32_Dyn; + +extern Elf32_Dyn _DYNAMIC[]; + +/* Dynamic Array Tags - d_tag */ +#define DT_NULL 0 /* marks end of _DYNAMIC array */ +#define DT_NEEDED 1 /* string table offset of needed lib */ +#define DT_PLTRELSZ 2 /* size of relocation entries in PLT */ +#define DT_PLTGOT 3 /* address PLT/GOT */ +#define DT_HASH 4 /* address of symbol hash table */ +#define DT_STRTAB 5 /* address of string table */ +#define DT_SYMTAB 6 /* address of symbol table */ +#define DT_RELA 7 /* address of relocation table */ +#define DT_RELASZ 8 /* size of relocation table */ +#define DT_RELAENT 9 /* size of relocation entry */ +#define DT_STRSZ 10 /* size of string table */ +#define DT_SYMENT 11 /* size of symbol table entry */ +#define DT_INIT 12 /* address of initialization func. */ +#define DT_FINI 13 /* address of termination function */ +#define DT_SONAME 14 /* string table offset of shared obj */ +#define DT_RPATH 15 /* string table offset of library + search path */ +#define DT_SYMBOLIC 16 /* start sym search in shared obj. */ +#define DT_REL 17 /* address of rel. tbl. w addends */ +#define DT_RELSZ 18 /* size of DT_REL relocation table */ +#define DT_RELENT 19 /* size of DT_REL relocation entry */ +#define DT_PLTREL 20 /* PLT referenced relocation entry */ +#define DT_DEBUG 21 /* bugger */ +#define DT_TEXTREL 22 /* Allow rel. mod. to unwritable seg */ +#define DT_JMPREL 23 /* add. of PLT's relocation entries */ +#define DT_BIND_NOW 24 /* Process relocations of object */ +#define DT_INIT_ARRAY 25 /* Array with addresses of init fct */ +#define DT_FINI_ARRAY 26 /* Array with addresses of fini fct */ +#define DT_INIT_ARRAYSZ 27 /* Size in bytes of DT_INIT_ARRAY */ +#define DT_FINI_ARRAYSZ 28 /* Size in bytes of DT_FINI_ARRAY */ +#define DT_RUNPATH 29 /* Library search path */ +#define DT_FLAGS 30 /* Flags for the object being loaded */ +#define DT_ENCODING 32 /* Start of encoded range */ +#define DT_PREINIT_ARRAY 32 /* Array with addresses of preinit fct*/ +#define DT_PREINIT_ARRAYSZ 33 /* size in bytes of DT_PREINIT_ARRAY */ +#define DT_NUM 34 /* Number used. */ +#define DT_LOOS 0x60000000 /* reserved range for OS */ +#define DT_HIOS 0x6fffffff /* specific dynamic array tags */ +#define DT_LOPROC 0x70000000 /* reserved range for processor */ +#define DT_HIPROC 0x7fffffff /* specific dynamic array tags */ + +/* Dynamic Tag Flags - d_un.d_val */ +#define DF_ORIGIN 0x01 /* Object may use DF_ORIGIN */ +#define DF_SYMBOLIC 0x02 /* Symbol resolutions starts here */ +#define DF_TEXTREL 0x04 /* Object contains text relocations */ +#define DF_BIND_NOW 0x08 /* No lazy binding for this object */ +#define DF_STATIC_TLS 0x10 /* Static thread local storage */ + +/* Standard ELF hashing function */ +unsigned long elf_hash(const unsigned char *name); + +#define ELF_TARG_VER 1 /* The ver for which this code is intended */ + +/* + * XXX - PowerPC defines really don't belong in here, + * but we'll put them in for simplicity. + */ + +/* Values for Elf32/64_Ehdr.e_flags. */ +#define EF_PPC_EMB 0x80000000 /* PowerPC embedded flag */ + +/* Cygnus local bits below */ +#define EF_PPC_RELOCATABLE 0x00010000 /* PowerPC -mrelocatable flag*/ +#define EF_PPC_RELOCATABLE_LIB 0x00008000 /* PowerPC -mrelocatable-lib + flag */ + +/* PowerPC relocations defined by the ABIs */ +#define R_PPC_NONE 0 +#define R_PPC_ADDR32 1 /* 32bit absolute address */ +#define R_PPC_ADDR24 2 /* 26bit address, 2 bits ignored. */ +#define R_PPC_ADDR16 3 /* 16bit absolute address */ +#define R_PPC_ADDR16_LO 4 /* lower 16bit of absolute address */ +#define R_PPC_ADDR16_HI 5 /* high 16bit of absolute address */ +#define R_PPC_ADDR16_HA 6 /* adjusted high 16bit */ +#define R_PPC_ADDR14 7 /* 16bit address, 2 bits ignored */ +#define R_PPC_ADDR14_BRTAKEN 8 +#define R_PPC_ADDR14_BRNTAKEN 9 +#define R_PPC_REL24 10 /* PC relative 26 bit */ +#define R_PPC_REL14 11 /* PC relative 16 bit */ +#define R_PPC_REL14_BRTAKEN 12 +#define R_PPC_REL14_BRNTAKEN 13 +#define R_PPC_GOT16 14 +#define R_PPC_GOT16_LO 15 +#define R_PPC_GOT16_HI 16 +#define R_PPC_GOT16_HA 17 +#define R_PPC_PLTREL24 18 +#define R_PPC_COPY 19 +#define R_PPC_GLOB_DAT 20 +#define R_PPC_JMP_SLOT 21 +#define R_PPC_RELATIVE 22 +#define R_PPC_LOCAL24PC 23 +#define R_PPC_UADDR32 24 +#define R_PPC_UADDR16 25 +#define R_PPC_REL32 26 +#define R_PPC_PLT32 27 +#define R_PPC_PLTREL32 28 +#define R_PPC_PLT16_LO 29 +#define R_PPC_PLT16_HI 30 +#define R_PPC_PLT16_HA 31 +#define R_PPC_SDAREL16 32 +#define R_PPC_SECTOFF 33 +#define R_PPC_SECTOFF_LO 34 +#define R_PPC_SECTOFF_HI 35 +#define R_PPC_SECTOFF_HA 36 +/* Keep this the last entry. */ +#define R_PPC_NUM 37 + +/* The remaining relocs are from the Embedded ELF ABI, and are not + in the SVR4 ELF ABI. */ +#define R_PPC_EMB_NADDR32 101 +#define R_PPC_EMB_NADDR16 102 +#define R_PPC_EMB_NADDR16_LO 103 +#define R_PPC_EMB_NADDR16_HI 104 +#define R_PPC_EMB_NADDR16_HA 105 +#define R_PPC_EMB_SDAI16 106 +#define R_PPC_EMB_SDA2I16 107 +#define R_PPC_EMB_SDA2REL 108 +#define R_PPC_EMB_SDA21 109 /* 16 bit offset in SDA */ +#define R_PPC_EMB_MRKREF 110 +#define R_PPC_EMB_RELSEC16 111 +#define R_PPC_EMB_RELST_LO 112 +#define R_PPC_EMB_RELST_HI 113 +#define R_PPC_EMB_RELST_HA 114 +#define R_PPC_EMB_BIT_FLD 115 +#define R_PPC_EMB_RELSDA 116 /* 16 bit relative offset in SDA */ + +/* Diab tool relocations. */ +#define R_PPC_DIAB_SDA21_LO 180 /* like EMB_SDA21, but lower 16 bit */ +#define R_PPC_DIAB_SDA21_HI 181 /* like EMB_SDA21, but high 16 bit */ +#define R_PPC_DIAB_SDA21_HA 182 /* like EMB_SDA21, adjusted high 16 */ +#define R_PPC_DIAB_RELSDA_LO 183 /* like EMB_RELSDA, but lower 16 bit */ +#define R_PPC_DIAB_RELSDA_HI 184 /* like EMB_RELSDA, but high 16 bit */ +#define R_PPC_DIAB_RELSDA_HA 185 /* like EMB_RELSDA, adjusted high 16 */ + +/* This is a phony reloc to handle any old fashioned TOC16 references + that may still be in object files. */ +#define R_PPC_TOC16 255 + +#endif /* _ELF_H */ + diff --git a/wiipax/stub/exception.c b/wiipax/stub/exception.c new file mode 100644 index 0000000..4d0ba41 --- /dev/null +++ b/wiipax/stub/exception.c @@ -0,0 +1,52 @@ +#include "common.h" +#include "string.h" + +extern char exception_asm_start, exception_asm_end; + +void exception_handler(int exception) +{ + u32 *x; + u32 i; + + printf("\nException %04x occurred!\n", exception); + + x = (u32 *)0x80003500; + + printf("\n R0..R7 R8..R15 R16..R23 R24..R31\n"); + for (i = 0; i < 8; i++) { + printf("%08x %08x %08x %08x\n", x[0], x[8], x[16], x[24]); + x++; + } + x += 24; + + printf("\n CR/XER LR/CTR SRR0/SRR1 DAR/DSISR\n"); + for (i = 0; i < 2; i++) { + printf("%08x %08x %08x %08x\n", x[0], x[2], x[4], x[6]); + x++; + } + + // Hang. + for (;;) + ; +} + +void exception_init(void) +{ + u32 vector; + u32 len_asm; + + for (vector = 0x100; vector < 0x1800; vector += 0x10) { + u32 *insn = (u32 *)(0x80000000 + vector); + + insn[0] = 0xbc003500; // stmw 0,0x3500(0) + insn[1] = 0x38600000 | vector; // li 3,vector + insn[2] = 0x48003602; // ba 0x3600 + insn[3] = 0; + } + sync_before_exec((void *)0x80000100, 0x1f00); + + len_asm = &exception_asm_end - &exception_asm_start; + memcpy((void *)0x80003600, &exception_asm_start, len_asm); + sync_before_exec((void *)0x80003600, len_asm); +} + diff --git a/wiipax/stub/exception_asm.s b/wiipax/stub/exception_asm.s new file mode 100644 index 0000000..a8729da --- /dev/null +++ b/wiipax/stub/exception_asm.s @@ -0,0 +1,21 @@ + .globl exception_asm_start, exception_asm_end + +exception_asm_start: + # store all interesting regs + mfcr 0 ; stw 0,0x3580(0) + mfxer 0 ; stw 0,0x3584(0) + mflr 0 ; stw 0,0x3588(0) + mfctr 0 ; stw 0,0x358c(0) + mfsrr0 0 ; stw 0,0x3590(0) + mfsrr1 0 ; stw 0,0x3594(0) + mfdar 0 ; stw 0,0x3598(0) + mfdsisr 0 ; stw 0,0x359c(0) + + # switch on FP, DR, IR + mfmsr 0 ; ori 0,0,0x2030 ; mtsrr1 0 + + # go to C handler + lis 0,exception_handler@h ; ori 0,0,exception_handler@l ; mtsrr0 0 + rfi +exception_asm_end: + diff --git a/wiipax/stub/gecko.c b/wiipax/stub/gecko.c new file mode 100644 index 0000000..0ce971c --- /dev/null +++ b/wiipax/stub/gecko.c @@ -0,0 +1,185 @@ +#include "common.h" +#include "vsprintf.h" + +#define EXI_REG_BASE 0xd806800 +#define EXI0_REG_BASE (EXI_REG_BASE+0x000) +#define EXI1_REG_BASE (EXI_REG_BASE+0x014) +#define EXI2_REG_BASE (EXI_REG_BASE+0x028) + +#define EXI0_CSR (EXI0_REG_BASE+0x000) +#define EXI0_MAR (EXI0_REG_BASE+0x004) +#define EXI0_LENGTH (EXI0_REG_BASE+0x008) +#define EXI0_CR (EXI0_REG_BASE+0x00c) +#define EXI0_DATA (EXI0_REG_BASE+0x010) + +#define EXI1_CSR (EXI1_REG_BASE+0x000) +#define EXI1_MAR (EXI1_REG_BASE+0x004) +#define EXI1_LENGTH (EXI1_REG_BASE+0x008) +#define EXI1_CR (EXI1_REG_BASE+0x00c) +#define EXI1_DATA (EXI1_REG_BASE+0x010) + +#define EXI2_CSR (EXI2_REG_BASE+0x000) +#define EXI2_MAR (EXI2_REG_BASE+0x004) +#define EXI2_LENGTH (EXI2_REG_BASE+0x008) +#define EXI2_CR (EXI2_REG_BASE+0x00c) +#define EXI2_DATA (EXI2_REG_BASE+0x010) + +static int gecko_console_enabled = 0; + +static u32 _gecko_command(u32 command) { + u32 i; + // Memory Card Port B (Channel 1, Device 0, Frequency 3 (32Mhz Clock)) + write32(EXI1_CSR, 0xd0); + write32(EXI1_DATA, command); + write32(EXI1_CR, 0x19); + i = 1000; + while ((read32(EXI1_CR) & 1) && (i--)); + i = read32(EXI1_DATA); + write32(EXI1_CSR, 0); + return i; +} + +static u32 _gecko_sendbyte(char sendbyte) { + u32 i = 0; + i = _gecko_command(0xB0000000 | (sendbyte<<20)); + if (i&0x04000000) + return 1; // Return 1 if byte was sent + return 0; +} + +#if 0 +static u32 _gecko_recvbyte(char *recvbyte) { + u32 i = 0; + *recvbyte = 0; + i = _gecko_command(0xA0000000); + if (i&0x08000000) { + // Return 1 if byte was received + *recvbyte = (i>>16)&0xff; + return 1; + } + return 0; +} + +static u32 _gecko_checksend(void) { + u32 i = 0; + i = _gecko_command(0xC0000000); + if (i&0x04000000) + return 1; // Return 1 if safe to send + return 0; +} + +static u32 _gecko_checkrecv(void) { + u32 i = 0; + i = _gecko_command(0xD0000000); + if (i&0x04000000) + return 1; // Return 1 if safe to recv + return 0; +} + +static void gecko_flush(void) { + char tmp; + while(_gecko_recvbyte(&tmp)); +} +#endif + +static int gecko_isalive(void) { + u32 i = 0; + i = _gecko_command(0x90000000); + if (i&0x04700000) + return 1; + return 0; +} + +#if 0 +static int gecko_recvbuffer(void *buffer, u32 size) { + u32 left = size; + char *ptr = (char*)buffer; + + while(left>0) { + if(!_gecko_recvbyte(ptr)) + break; + ptr++; + left--; + } + return (size - left); +} +#endif + +static int gecko_sendbuffer(const void *buffer, u32 size) { + u32 left = size; + char *ptr = (char*)buffer; + + while(left>0) { + if(!_gecko_sendbyte(*ptr)) + break; + ptr++; + left--; + } + return (size - left); +} + +#if 0 +static int gecko_recvbuffer_safe(void *buffer, u32 size) { + u32 left = size; + char *ptr = (char*)buffer; + + while(left>0) { + if(_gecko_checkrecv()) { + if(!_gecko_recvbyte(ptr)) + break; + ptr++; + left--; + } + } + return (size - left); +} + +static int gecko_sendbuffer_safe(const void *buffer, u32 size) { + u32 left = size; + char *ptr = (char*)buffer; + + while(left>0) { + if(_gecko_checksend()) { + if(!_gecko_sendbyte(*ptr)) + break; + ptr++; + left--; + } + } + return (size - left); +} +#endif + +void gecko_init(void) +{ + // unlock EXI + write32(0x0d00643c, 0); + + write32(EXI0_CSR, 0); + write32(EXI1_CSR, 0); + write32(EXI2_CSR, 0); + write32(EXI0_CSR, 0x2000); + write32(EXI0_CSR, 3<<10); + write32(EXI1_CSR, 3<<10); + + if (!gecko_isalive()) + return; + + gecko_console_enabled = 1; +} + +int printf(const char *fmt, ...) { + if (!gecko_console_enabled) + return 0; + + va_list args; + char buffer[1024]; + int i; + + va_start(args, fmt); + i = vsprintf(buffer, fmt, args); + va_end(args); + + return gecko_sendbuffer(buffer, i); +} + diff --git a/wiipax/stub/main.c b/wiipax/stub/main.c new file mode 100644 index 0000000..d7f2cf4 --- /dev/null +++ b/wiipax/stub/main.c @@ -0,0 +1,109 @@ +#include "common.h" + +#include "string.h" +#include "elf.h" + +#include "LzmaDec.h" + +extern const u8 __self_start[], __self_end[]; +extern const u8 __payload[]; + +extern void _plunge(u32 entry); + +typedef struct { + u8 * const data; + const u32 len_in; + const u32 len_out; + const u8 props[LZMA_PROPS_SIZE]; +} __attribute__((packed)) _payload; + +const _payload * const payload = (void *) __payload; + +u8 * const code_buffer = (u8 *) 0x90100000; + +static void *lz_malloc(void *p, size_t size) { + (void) p; + (void) size; + printf("lz_malloc %u\n", size); + return (void *) 0x90081000; +} + +static void lz_free(void *p, void *address) { + (void) p; + (void) address; + printf("lz_free %p\n", address); +} + +static const ISzAlloc lz_alloc = { lz_malloc, lz_free }; + +static inline __attribute__((always_inline)) int decode(void) { + SizeT len_in; + SizeT len_out; + ELzmaStatus status; + SRes res; + + len_in = payload->len_in; + len_out = payload->len_out; + + printf("in: %d out: %d\n", len_in, len_out); + + res = LzmaDecode(code_buffer, &len_out, payload->data, &len_in, + payload->props, LZMA_PROPS_SIZE, LZMA_FINISH_END, + &status, (ISzAlloc*)&lz_alloc); + + if (res != SZ_OK) { + printf("decoding error %d (%u)\n", res, status); + return res; + } + + return 0; +} + +void stubmain(void) { +#ifndef NDEBUG + exception_init(); +#endif + + // clear interrupt mask + write32(0x0c003004, 0); + +#ifndef NDEBUG +#ifndef DEVKITFAIL + udelay(500 * 1000); // wait for mini - avoid EXI battle +#endif + gecko_init(); +#endif + printf("hello world\n"); + printf("payload @%p blob @%p\n", payload, payload->data); + + if (decode() == 0 && valid_elf_image(code_buffer)) { + printf("Valid ELF image detected.\n"); + u32 entry = load_elf_image(code_buffer); + + if (entry) { + // Disable all IRQs; ack all pending IRQs. + write32(0x0c003004, 0); + write32(0x0c003000, 0xffffffff); + + printf("Branching to 0x%08x\n", entry); +#ifndef DEVKITFAIL + // detect failkit apps packed with the mini stub + if (entry & 0xc0000000) { + void (*ep)() = (void (*)()) entry; + ep(); + } else { + _plunge(entry); + } +#else + void (*ep)() = (void (*)()) entry; + ep(); +#endif + } + } + +error: + blink(); + udelay(500000); + goto error; +} + diff --git a/wiipax/stub/plunge.S b/wiipax/stub/plunge.S new file mode 100644 index 0000000..61536c2 --- /dev/null +++ b/wiipax/stub/plunge.S @@ -0,0 +1,13 @@ + .text + .section .text + .globl _plunge + +_plunge: + isync + mtsrr0 3 + + mfmsr 3 + li 4,0x30 + andc 3,3,4 + mtsrr1 3 + rfi diff --git a/wiipax/stub/realmode.S b/wiipax/stub/realmode.S new file mode 100644 index 0000000..b4f179c --- /dev/null +++ b/wiipax/stub/realmode.S @@ -0,0 +1,101 @@ +#define IBAT0U 528 +#define IBAT0L 529 +#define IBAT1U 530 +#define IBAT1L 531 +#define IBAT2U 532 +#define IBAT2L 533 +#define IBAT3U 534 +#define IBAT3L 535 +#define IBAT4U 560 +#define IBAT4L 561 +#define IBAT5U 562 +#define IBAT5L 563 +#define IBAT6U 564 +#define IBAT6L 565 +#define IBAT7U 566 +#define IBAT7L 567 + +#define DBAT0U 536 +#define DBAT0L 537 +#define DBAT1U 538 +#define DBAT1L 539 +#define DBAT2U 540 +#define DBAT2L 541 +#define DBAT3U 542 +#define DBAT3L 543 +#define DBAT4U 568 +#define DBAT4L 569 +#define DBAT5U 570 +#define DBAT5L 571 +#define DBAT6U 572 +#define DBAT6L 573 +#define DBAT7U 574 +#define DBAT7L 575 + + + .text + .section .realmode,"ax",@progbits + .extern _start + .align 2 + .globl _realmode_vector + +_realmode_vector: + // HID0 = 00110c64: + // bus checkstops off, sleep modes off, + // caches off, caches invalidate, + // store gathering off, enable data cache + // flush assist, enable branch target cache, + // enable branch history table + lis 3,0x0011 ; ori 3,3,0x0c64 ; mtspr 1008,3 ; isync + + // MSR = 00002000 (FP on) + li 4,0x2000 ; mtmsr 4 + + // HID0 |= 0000c000 (caches on) + ori 3,3,0xc000 ; mtspr 1008,3 ; isync + + // clear all BATs + li 0,0 + mtspr 528,0 ; mtspr 530,0 ; mtspr 532,0 ; mtspr 534,0 // IBATU 0..3 + mtspr 536,0 ; mtspr 538,0 ; mtspr 540,0 ; mtspr 542,0 // DBATU 0..3 + mtspr 560,0 ; mtspr 562,0 ; mtspr 564,0 ; mtspr 566,0 // IBATU 4..7 + mtspr 568,0 ; mtspr 570,0 ; mtspr 572,0 ; mtspr 574,0 // DBATU 4..7 + isync + + // clear all SRs + lis 0,0x8000 + mtsr 0,0 ; mtsr 1,0 ; mtsr 2,0 ; mtsr 3,0 + mtsr 4,0 ; mtsr 5,0 ; mtsr 6,0 ; mtsr 7,0 + mtsr 8,0 ; mtsr 9,0 ; mtsr 10,0 ; mtsr 11,0 + mtsr 12,0 ; mtsr 13,0 ; mtsr 14,0 ; mtsr 15,0 + isync + + // set [DI]BAT0 for 256MB@80000000, + // real 00000000, WIMG=0000, R/W + li 3,2 ; lis 4,0x8000 ; ori 4,4,0x1fff + mtspr IBAT0L,3 ; mtspr IBAT0U,4 ; mtspr DBAT0L,3 ; mtspr DBAT0U,4 ; isync + + // set [DI]BAT4 for 256MB@90000000, + // real 10000000, WIMG=0000, R/W + addis 3,3,0x1000 ; addis 4,4,0x1000 + mtspr IBAT4L,3 ; mtspr IBAT4U,4 ; mtspr DBAT4L,3 ; mtspr DBAT4U,4 ; isync + + // set DBAT1 for 256MB@c0000000, + // real 00000000, WIMG=0101, R/W + li 3,0x2a ; lis 4,0xc000 ; ori 4,4,0x1fff + mtspr DBAT1L,3 ; mtspr DBAT1U,4 ; isync + + // set DBAT5 for 256MB@d0000000, + // real 10000000, WIMG=0101, R/W + addis 3,3,0x1000 ; addis 4,4,0x1000 + mtspr DBAT5L,3 ; mtspr DBAT5U,4 ; isync + + // enable [DI]BAT4-7 in HID4 + lis 3, 0x8200 + mtspr 1011,3 + + // set MSR[DR:IR] = 11, jump to _start + lis 3,__mem1_entry@h ; ori 3,3,__mem1_entry@l ; mtsrr0 3 + + mfmsr 3 ; ori 3,3,0x30 ; mtsrr1 3 + rfi diff --git a/wiipax/stub/realmode.ld b/wiipax/stub/realmode.ld new file mode 100644 index 0000000..a8fd199 --- /dev/null +++ b/wiipax/stub/realmode.ld @@ -0,0 +1,64 @@ +OUTPUT_FORMAT("elf32-powerpc") +OUTPUT_ARCH(powerpc:common) + +__mem1_start = 0x80004000; +__mem2_start = 0x90010000; + +__mem1_entry = _start - __mem2_start + __mem1_start; +ENTRY(_realmode_vector) + +PHDRS { + realmode PT_LOAD FLAGS(5); + paxx PT_LOAD FLAGS(7); +} + +SECTIONS { + . = 0x00003400; + + .realmode : { *(.realmode) } :realmode = 0 + + . = __mem2_start; + + .start : AT(__mem1_start & 0x3FFFFFFF) { KEEP(*(.start)) } :paxx = 0 + .text : { *(.text) *(.text.*) } + + . = ALIGN(4); + + .payload : { + __payload = .; + LONG(0); + LONG(0); + LONG(0); + LONG(0); + LONG(0); + LONG(0); + } + + .rodata : { *(.rodata) *(.rodata.*) } + + . = (( . +19)&0xFFFFFFF0) - 4; + .padding : { + LONG(0xdeadbeef); + } + + .sdata : { *(.sdata) *(.sdata.*) } + .data : { *(.data) *(.data.*) } + + . = ALIGN(32); + __self_end = .; + + __bss_start = .; + .bss : { *(.bss) } :NONE = 0 + .sbss : { *(.sbss) } + __bss_end = .; + + . = ALIGN(32); + + .stack : { + _stack_top = .; + . += 32768; + _stack_bot = .; + } + +} + diff --git a/wiipax/stub/string.c b/wiipax/stub/string.c new file mode 100644 index 0000000..7d5481c --- /dev/null +++ b/wiipax/stub/string.c @@ -0,0 +1,94 @@ +/* + * linux/lib/string.c + * + * Copyright (C) 1991, 1992 Linus Torvalds + */ + +#include "string.h" + +size_t strnlen(const char *s, size_t count) +{ + const char *sc; + + for (sc = s; count-- && *sc != '\0'; ++sc) + /* nothing */; + return sc - s; +} + +size_t strlen(const char *s) +{ + const char *sc; + + for (sc = s; *sc != '\0'; ++sc) + /* nothing */; + return sc - s; +} + +char *strncpy(char *dst, const char *src, size_t n) +{ + char *ret = dst; + + while (n && (*dst++ = *src++)) + n--; + + while (n--) + *dst++ = 0; + + return ret; +} + +char *strcpy(char *dst, const char *src) +{ + char *ret = dst; + + while ((*dst++ = *src++)) + ; + + return ret; +} + +int strcmp(const char *p, const char *q) +{ + for (;;) { + unsigned char a, b; + a = *p++; + b = *q++; + if (a == 0 || a != b) + return a - b; + } +} + +void *memset(void *dst, int x, size_t n) +{ + unsigned char *p; + + for (p = dst; n; n--) + *p++ = x; + + return dst; +} + +void *memcpy(void *dst, const void *src, size_t n) +{ + unsigned char *p; + const unsigned char *q; + + for (p = dst, q = src; n; n--) + *p++ = *q++; + + return dst; +} + +int memcmp(const void *s1, const void *s2, size_t n) +{ + unsigned char *us1 = (unsigned char *) s1; + unsigned char *us2 = (unsigned char *) s2; + while (n-- != 0) { + if (*us1 != *us2) + return (*us1 < *us2) ? -1 : +1; + us1++; + us2++; + } + return 0; +} + diff --git a/wiipax/stub/string.h b/wiipax/stub/string.h new file mode 100644 index 0000000..6cd8119 --- /dev/null +++ b/wiipax/stub/string.h @@ -0,0 +1,15 @@ +#ifndef _STRING_H_ +#define _STRING_H_ + +#include + +char *strcpy(char *, const char *); +char *strncpy(char *, const char *, size_t); +int strcmp(const char *, const char *); +size_t strlen(const char *); +size_t strnlen(const char *, size_t); +void *memset(void *, int, size_t); +void *memcpy(void *, const void *, size_t); +int memcmp(const void *s1, const void *s2, size_t n); + +#endif diff --git a/wiipax/stub/sync.c b/wiipax/stub/sync.c new file mode 100644 index 0000000..98fec66 --- /dev/null +++ b/wiipax/stub/sync.c @@ -0,0 +1,40 @@ +#include "common.h" + +void sync_before_read(void *p, u32 len) +{ + u32 a, b; + + a = (u32)p & ~0x1f; + b = ((u32)p + len + 0x1f) & ~0x1f; + + for ( ; a < b; a += 32) + asm("dcbi 0,%0" : : "b"(a)); + + asm("sync ; isync"); +} + +void sync_after_write(const void *p, u32 len) +{ + u32 a, b; + + a = (u32)p & ~0x1f; + b = ((u32)p + len + 0x1f) & ~0x1f; + + for ( ; a < b; a += 32) + asm("dcbst 0,%0" : : "b"(a)); + + asm("sync ; isync"); +} + +void sync_before_exec(const void *p, u32 len) +{ + u32 a, b; + + a = (u32)p & ~0x1f; + b = ((u32)p + len + 0x1f) & ~0x1f; + + for ( ; a < b; a += 32) + asm("dcbst 0,%0 ; sync ; icbi 0,%0" : : "b"(a)); + + asm("sync ; isync"); +} diff --git a/wiipax/stub/time.c b/wiipax/stub/time.c new file mode 100644 index 0000000..109016e --- /dev/null +++ b/wiipax/stub/time.c @@ -0,0 +1,27 @@ +#include "common.h" + +// Timebase frequency is bus frequency / 4. Ignore roundoff, this +// doesn't have to be very accurate. +#define TICKS_PER_USEC (243/4) + +static u32 mftb(void) +{ + u32 x; + + asm volatile("mftb %0" : "=r"(x)); + + return x; +} + +static void __delay(u32 ticks) +{ + u32 start = mftb(); + + while (mftb() - start < ticks) + ; +} + +void udelay(u32 us) +{ + __delay(TICKS_PER_USEC * us); +} diff --git a/wiipax/stub/vsprintf.c b/wiipax/stub/vsprintf.c new file mode 100644 index 0000000..12cce95 --- /dev/null +++ b/wiipax/stub/vsprintf.c @@ -0,0 +1,291 @@ +/* + * linux/lib/vsprintf.c + * + * Copyright (C) 1991, 1992 Linus Torvalds + */ + +/* vsprintf.c -- Lars Wirzenius & Linus Torvalds. */ +/* + * Wirzenius wrote this portably, Torvalds fucked it up :-) + */ + +#include +#include "string.h" + +static inline int isdigit(int c) +{ + return c >= '0' && c <= '9'; +} + +static inline int isxdigit(int c) +{ + return (c >= '0' && c <= '9') + || (c >= 'a' && c <= 'f') + || (c >= 'A' && c <= 'F'); +} + +static inline int islower(int c) +{ + return c >= 'a' && c <= 'z'; +} + +static inline int toupper(int c) +{ + if (islower(c)) + c -= 'a'-'A'; + return c; +} + +static int skip_atoi(const char **s) +{ + int i=0; + + while (isdigit(**s)) + i = i*10 + *((*s)++) - '0'; + return i; +} + +#define ZEROPAD 1 /* pad with zero */ +#define SIGN 2 /* unsigned/signed long */ +#define PLUS 4 /* show plus */ +#define SPACE 8 /* space if plus */ +#define LEFT 16 /* left justified */ +#define SPECIAL 32 /* 0x */ +#define LARGE 64 /* use 'ABCDEF' instead of 'abcdef' */ + +#define do_div(n,base) ({ \ +int __res; \ +__res = ((unsigned long) n) % (unsigned) base; \ +n = ((unsigned long) n) / (unsigned) base; \ +__res; }) + +static char * number(char * str, long num, int base, int size, int precision + ,int type) +{ + char c,sign,tmp[66]; + const char *digits="0123456789abcdefghijklmnopqrstuvwxyz"; + int i; + + if (type & LARGE) + digits = "0123456789ABCDEFGHIJKLMNOPQRSTUVWXYZ"; + if (type & LEFT) + type &= ~ZEROPAD; + if (base < 2 || base > 36) + return 0; + c = (type & ZEROPAD) ? '0' : ' '; + sign = 0; + if (type & SIGN) { + if (num < 0) { + sign = '-'; + num = -num; + size--; + } else if (type & PLUS) { + sign = '+'; + size--; + } else if (type & SPACE) { + sign = ' '; + size--; + } + } + if (type & SPECIAL) { + if (base == 16) + size -= 2; + else if (base == 8) + size--; + } + i = 0; + if (num == 0) + tmp[i++]='0'; + else while (num != 0) + tmp[i++] = digits[do_div(num,base)]; + if (i > precision) + precision = i; + size -= precision; + if (!(type&(ZEROPAD+LEFT))) + while(size-->0) + *str++ = ' '; + if (sign) + *str++ = sign; + if (type & SPECIAL) { + if (base==8) + *str++ = '0'; + else if (base==16) { + *str++ = '0'; + *str++ = digits[33]; + } + } + if (!(type & LEFT)) + while (size-- > 0) + *str++ = c; + while (i < precision--) + *str++ = '0'; + while (i-- > 0) + *str++ = tmp[i]; + while (size-- > 0) + *str++ = ' '; + return str; +} + +int vsprintf(char *buf, const char *fmt, va_list args) +{ + int len; + unsigned long num; + int i, base; + char * str; + const char *s; + + int flags; /* flags to number() */ + + int field_width; /* width of output field */ + int precision; /* min. # of digits for integers; max + number of chars for from string */ + int qualifier; /* 'h', 'l', or 'L' for integer fields */ + + for (str=buf ; *fmt ; ++fmt) { + if (*fmt != '%') { + *str++ = *fmt; + continue; + } + + /* process flags */ + flags = 0; + repeat: + ++fmt; /* this also skips first '%' */ + switch (*fmt) { + case '-': flags |= LEFT; goto repeat; + case '+': flags |= PLUS; goto repeat; + case ' ': flags |= SPACE; goto repeat; + case '#': flags |= SPECIAL; goto repeat; + case '0': flags |= ZEROPAD; goto repeat; + } + + /* get field width */ + field_width = -1; + if (isdigit(*fmt)) + field_width = skip_atoi(&fmt); + else if (*fmt == '*') { + ++fmt; + /* it's the next argument */ + field_width = va_arg(args, int); + if (field_width < 0) { + field_width = -field_width; + flags |= LEFT; + } + } + + /* get the precision */ + precision = -1; + if (*fmt == '.') { + ++fmt; + if (isdigit(*fmt)) + precision = skip_atoi(&fmt); + else if (*fmt == '*') { + ++fmt; + /* it's the next argument */ + precision = va_arg(args, int); + } + if (precision < 0) + precision = 0; + } + + /* get the conversion qualifier */ + qualifier = -1; + if (*fmt == 'h' || *fmt == 'l' || *fmt == 'L') { + qualifier = *fmt; + ++fmt; + } + + /* default base */ + base = 10; + + switch (*fmt) { + case 'c': + if (!(flags & LEFT)) + while (--field_width > 0) + *str++ = ' '; + *str++ = (unsigned char) va_arg(args, int); + while (--field_width > 0) + *str++ = ' '; + continue; + + case 's': + s = va_arg(args, char *); + if (!s) + s = ""; + + len = strnlen(s, precision); + + if (!(flags & LEFT)) + while (len < field_width--) + *str++ = ' '; + for (i = 0; i < len; ++i) + *str++ = *s++; + while (len < field_width--) + *str++ = ' '; + continue; + + case 'p': + if (field_width == -1) { + field_width = 2*sizeof(void *); + flags |= ZEROPAD; + } + str = number(str, + (unsigned long) va_arg(args, void *), 16, + field_width, precision, flags); + continue; + + + case 'n': + if (qualifier == 'l') { + long * ip = va_arg(args, long *); + *ip = (str - buf); + } else { + int * ip = va_arg(args, int *); + *ip = (str - buf); + } + continue; + + case '%': + *str++ = '%'; + continue; + + /* integer number formats - set up the flags and "break" */ + case 'o': + base = 8; + break; + + case 'X': + flags |= LARGE; + case 'x': + base = 16; + break; + + case 'd': + case 'i': + flags |= SIGN; + case 'u': + break; + + default: + *str++ = '%'; + if (*fmt) + *str++ = *fmt; + else + --fmt; + continue; + } + if (qualifier == 'l') + num = va_arg(args, unsigned long); + else if (qualifier == 'h') { + num = (unsigned short) va_arg(args, int); + if (flags & SIGN) + num = (short) num; + } else if (flags & SIGN) + num = va_arg(args, int); + else + num = va_arg(args, unsigned int); + str = number(str, num, base, field_width, precision, flags); + } + *str = '\0'; + return str-buf; +} diff --git a/wiipax/stub/vsprintf.h b/wiipax/stub/vsprintf.h new file mode 100644 index 0000000..aca3816 --- /dev/null +++ b/wiipax/stub/vsprintf.h @@ -0,0 +1,9 @@ +#ifndef _VSPRINTF_H_ +#define _VSPRINTF_H_ + +#include + +int vsprintf(char *buf, const char *fmt, va_list args); + +#endif +