diff --git a/Makefile b/Makefile new file mode 100644 index 0000000..abf1940 --- /dev/null +++ b/Makefile @@ -0,0 +1,147 @@ +#--------------------------------------------------------------------------------- +# Clear the implicit built in rules +#--------------------------------------------------------------------------------- +.SUFFIXES: +#--------------------------------------------------------------------------------- +#DEVKITPRO = /opt/devkitPPC +#DEVKITPPC = /opt/devkitPPC + +ifeq ($(strip $(DEVKITPPC)),) +$(error "Please set DEVKITPPC in your environment. export DEVKITPPC=devkitPPC") +endif + +include $(DEVKITPPC)/wii_rules + +#--------------------------------------------------------------------------------- +# TARGET is the name of the output +# BUILD is the directory where object files & intermediate files will be placed +# SOURCES is a list of directories containing source code +# INCLUDES is a list of directories containing extra header files +#--------------------------------------------------------------------------------- +TARGET := $(notdir $(CURDIR)) +BUILD := build +SOURCES := source include source/libtinysmb source/libpng source/libpng/pngu +DATA := data +INCLUDES := + +#--------------------------------------------------------------------------------- +# options for code generation +#--------------------------------------------------------------------------------- + +CFLAGS = -Os -Wall $(MACHDEP) $(INCLUDE) +CXXFLAGS = $(CFLAGS) + +LDFLAGS = $(MACHDEP) -Wl,-Map,$(notdir $@).map + +#--------------------------------------------------------------------------------- +# any extra libraries we wish to link with the project +#--------------------------------------------------------------------------------- +LIBS := -ltinysmb -lpng -lfat -lwiidrc -lwiiuse -lbte -logc -lm -lz -lwiilight + +#--------------------------------------------------------------------------------- +# list of directories containing libraries, this must be the top level containing +# include and lib +#--------------------------------------------------------------------------------- +LIBDIRS := $(CURDIR) $(PORTLIBS) + +#--------------------------------------------------------------------------------- +# no real need to edit anything past this point unless you need to add additional +# rules for different file extensions +#--------------------------------------------------------------------------------- +ifneq ($(BUILD),$(notdir $(CURDIR))) +#--------------------------------------------------------------------------------- + +export OUTPUT := $(CURDIR)/$(TARGET) + +export VPATH := $(foreach dir,$(SOURCES),$(CURDIR)/$(dir)) \ + $(foreach dir,$(DATA),$(CURDIR)/$(dir)) + +export DEPSDIR := $(CURDIR)/$(BUILD) + +#--------------------------------------------------------------------------------- +# automatically build a list of object files for our project +#--------------------------------------------------------------------------------- +CFILES := $(foreach dir,$(SOURCES),$(notdir $(wildcard $(dir)/*.c))) +CPPFILES := $(foreach dir,$(SOURCES),$(notdir $(wildcard $(dir)/*.cpp))) +sFILES := $(foreach dir,$(SOURCES),$(notdir $(wildcard $(dir)/*.s))) +SFILES := $(foreach dir,$(SOURCES),$(notdir $(wildcard $(dir)/*.S))) +BINFILES := $(foreach dir,$(DATA),$(notdir $(wildcard $(dir)/*.*))) + +#--------------------------------------------------------------------------------- +# use CXX for linking C++ projects, CC for standard C +#--------------------------------------------------------------------------------- +ifeq ($(strip $(CPPFILES)),) + export LD := $(CC) +else + export LD := $(CXX) +endif + +export OFILES := $(addsuffix .o,$(BINFILES)) \ + $(CPPFILES:.cpp=.o) $(CFILES:.c=.o) \ + $(sFILES:.s=.o) $(SFILES:.S=.o) + +#--------------------------------------------------------------------------------- +# build a list of include paths +#--------------------------------------------------------------------------------- +export INCLUDE := $(foreach dir,$(INCLUDES), -iquote $(CURDIR)/$(dir)) \ + $(foreach dir,$(LIBDIRS),-I$(dir)/include) \ + -I$(CURDIR)/$(BUILD) \ + -I$(LIBOGC_INC) + +#--------------------------------------------------------------------------------- +# build a list of library paths +#--------------------------------------------------------------------------------- +export LIBPATHS := $(foreach dir,$(LIBDIRS),-L$(dir)/lib) \ + -L$(LIBOGC_LIB) + +export OUTPUT := $(CURDIR)/$(TARGET) +.PHONY: $(BUILD) clean + +#--------------------------------------------------------------------------------- +$(BUILD): + @[ -d $@ ] || mkdir -p $@ + @make --no-print-directory -C $(BUILD) -f $(CURDIR)/Makefile + +#--------------------------------------------------------------------------------- +clean: + @echo clean ... + @rm -fr $(BUILD) $(OUTPUT).elf $(OUTPUT).dol + +#--------------------------------------------------------------------------------- +run: + wiiload $(TARGET).dol + + +#--------------------------------------------------------------------------------- +else + +DEPENDS := $(OFILES:.o=.d) + +#--------------------------------------------------------------------------------- +# main targets +#--------------------------------------------------------------------------------- +$(OUTPUT).dol: $(OUTPUT).elf +$(OUTPUT).elf: $(OFILES) + +#--------------------------------------------------------------------------------- +# This rule links in binary data with the .jpg extension +#--------------------------------------------------------------------------------- +%.jpg.o : %.jpg +#--------------------------------------------------------------------------------- + @echo $(notdir $<) + $(bin2o) + +%.elf.o : %.elf + @echo $(notdir $<) + $(bin2o) + +%.dat.o : %.dat + @echo $(notdir $<) + $(bin2o) + +-include $(DEPENDS) + +#--------------------------------------------------------------------------------- +endif +#--------------------------------------------------------------------------------- + diff --git a/README.md b/README.md index 1d2e4cc..3677af9 100644 --- a/README.md +++ b/README.md @@ -1,2 +1,9 @@ # Some-YAWMM-Mod -yet another mod of yet another mod manager mod +Based on YAWMM which itself is based on WAD Manager, modded up by various people. +Changes from the last YAWMM googlecode version: +-updated to be compiled in the latest devkitppc, libogc versions +-added on-the-fly IOS patches when AHBPROT is disabled, so no cIOS is required in those cases +-support for classic controller, wiiu pro controller (on both wii and vwii) and wiiu gamepad (in wii vc mode) +-small corrections in how the root path is selected (having no "wad" folder now correctly displays on device root) + +For more info on YAWMM itself, check its [original readme](README_YAWMM.txt). \ No newline at end of file diff --git a/README_YAWMM.txt b/README_YAWMM.txt new file mode 100644 index 0000000..dfcbc9c --- /dev/null +++ b/README_YAWMM.txt @@ -0,0 +1,172 @@ + + =YET ANOTHER WAD MANAGER MOD (YAWMM)= + +==[ DISCLAIMER ]:== + + THIS APPLICATION COMES WITH NO WARRANTY AT ALL, NEITHER EXPRESSED NOR IMPLIED. + NO ONE BUT YOURSELF IS RESPONSIBLE FOR ANY DAMAGE TO YOUR WII CONSOLE + BECAUSE OF A IMPROPER USAGE OF THIS SOFTWARE. + + +==[ DESCRIPTION ]:== + + This is an application that allows you to (un)install WADs. + + It lists all the available WADs in a storage device + so you can select which one to (un)install. + + +==[ SUPPORTED DEVICES ]:== + + * SDGecko. + * Internal SD slot (with SDHC support). + * USB device (1.1 and 2.0). + + +==[ USAGE ]:== + + * Copy the WAD(s) you wish to insall to your storage device. + * Run the application with any method to load homebrew. + + * The following 3 options can be skipped/pre-set, see the [ CONFIG FILE USAGE ] Section for details + # Select an IOS to use (must have proper patches), 2 commonly used IOSs are 249 and 36. + # Select the device where you have saved the WADs. + # Choose Nand Emulation Device (leave "disabled" to install to WADs to the Wii's real Nand) + + * Browse device to locate WADs ("A" to open folders, and "B" to go back) + + * Press the "A" Button on an individual WAD to (un)install. + * If no file is marked, the normal single file (un)install menu will appear. + * If at least one file is marked, the batch (un)install menu will appear. + + * Press the "+" Button to (un)mark the selected WAD for batch installation + * Press the "-" Button to (un)mark the selected WAD for batch uninstallation + * Hold +/- for 2 seconds to (un)mark all items in a directory. + * A "+" will appear in front of the name of marked WADs for installation + * A "-" will appear in front of the name of marked WADs for uninstallation + + * Press the "1" Button to go to the operations menu (currently can only delete single WADs) + + +==[ NOTES ]:== + + To use the NAND emulation is necessary to have a COMPLETE copy + of the NAND filesystem in the root of the FAT device. + + +==;[ CONFIG FILE USAGE ]:== + +; wm_config.txt resides in sd:/wad, and it is optional. You will get all the prompts if you don't have this file. If you are missing this file, copy and paste the entire [ CONFIG FILE USAGE ] Section to a new text file, rename it wm_config.txt, and Save it to sd:/wad. + +*; To bypass any of the params, just comment out the line using a ";" at the beginning of the line* + +; If you don't have any of the other parameters, it will prompt you for it + +; The param keywords are case-sensitive at this point. + +; No spaces precede the keyword on a line + +; If you don't have a password in the config file, the default is no password + +; If you don't have a startupPath, the default is /wad + +; Blank lines are ignored. + +; Password=your_password ("LRUD" only, where L=left, R=right, U=up, D=down on the WiiMote or GC Controller, max 10 characters) + +; StartupPath=startupPath (starting at the root dir "/"). Be sure that the path exists, else you will get an error. + + + +*;Password=UDLR* + +*;StartupPath=/myWAD* + +; Example of StartupPath at the root of the SD card + +;StartupPath=/ + +; cIOS: 249, 222, whatever + +*;cIOSVersion=249* + +; FatDevice: sd usb usb2 gcsda gcsdb + +*;FatDevice=sd* + +; NANDDevice: Disable SD USB + +; Note that WM will prompt for NAND device only if you selected cIOS=249 + +*;NANDDevice=Disable* + + + + +==[ CHANGELOG ]:== + + * YAWMM (cwstjdenobs) + * Hold +/- for 2 seconds to select all items in a directory. + * Supports Hermes v4/v5 cIOS. Mainly useful if 202 works best for your HDD/SDHC card. + * More detailed failed report after batch un/installs. + * Will not uninstall The System Menu, the SM's region EULA or rgsel, or their IOSs. + * Will not install the wrong regions SM. + * Will not load stub IOS. + * Will not install titles if they rely on a stub IOS. + * Will not install stub SM, EULA and rgsel IOS. + * Will not install a SM lower than 4.0 on a boot2v4 Wii. + * Gives a warning when uninstalling the HBCs IOS. + * Read config file from usb. + * Can load an alternative background from /wad/background.png. + * Won't load incompatible cIOS in SNEEK. + + * Wad Manager Multi-Mod (Leathl) v3: + * Reassigned the buttons again (Read Usage section) + * Shortened on-screen instructions down to 2 lines + * Fixed bug when changing directory with marked WADs + + * Wad Manager Multi-Mod (Leathl) v2: + * Reassigned the buttons (Read the Usage section) + * Batch un- and installation is now possible in one turn + * Added confirmation screen before (un-)installation + * Added file operations (can yet only delete single files) + + * Wad Manager Multi-Mod (Leathl) v1: + * Added batch (un)installation + + * Wad Manager Folder Mod (WiiNinja) v3: + * Config file contains additional params to automate the selection of IOS, Storage Device, and NAND Emulation + * WiiLight mod by mariomaniac33 + + * Wad Manager Folder Mod (WiiNinja) v2: + * Config file in sd:/wad/wm_config.txt + * Optional startup password. Very simple password using the D-Pads + * Optional startup path + + * Wad Manager Folder Mod (WiiNinja) v1: + * Folder support (10 levels deep) + * GC Controller support + * Removed disclaimer prompt + * Sorg's enhancements are included + + * Wad Manager (Waninkoko) v1.5: + * Allows NAND Emulation. + + * Wad Manager (Waninkoko) v1.4: + * Allows user to choose which IOS to install WAD Manager. + + +==[ SOURCE ]:== + * http://code.google.com/p/yawmm/source/checkout + + +==[ THANKS ]:== + * X-Flak + * Pepxl + +==[ KUDOS ]:== + * Leathl + * WiiNinja + * Sorg + * Waninkoko + * Team Twiizers/devkitPRO \ No newline at end of file diff --git a/apps/some-yawmm-mod/icon.png b/apps/some-yawmm-mod/icon.png new file mode 100644 index 0000000..6f05bfb Binary files /dev/null and b/apps/some-yawmm-mod/icon.png differ diff --git a/apps/some-yawmm-mod/meta.xml b/apps/some-yawmm-mod/meta.xml new file mode 100644 index 0000000..c4c8da3 --- /dev/null +++ b/apps/some-yawmm-mod/meta.xml @@ -0,0 +1,19 @@ + + + Some YAWMM Mod + 1.0 + various + Install\Uninstall WADs + Press the "A" key to (un)install WADs. +If no files are selected for batch (un)installation, the normal individual (un)installation menu appears. +If at least one file is selected, the batch (un)installation menu appears. + +Press the "+" button to add\remove the selected WAD to the batch installer list +Press the "-" button to add\remove the selected WAD to batch uninstaller list +Press the "1" key to enter the extension menu + +A "+" before the name means the WAD will be installed +A "-" before the name means the WAD will be uninstalled + + + diff --git a/data/background b/data/background new file mode 100644 index 0000000..7e60c1d Binary files /dev/null and b/data/background differ diff --git a/data/ehcmodule.elf b/data/ehcmodule.elf new file mode 100644 index 0000000..ea10c88 Binary files /dev/null and b/data/ehcmodule.elf differ diff --git a/include/wiilight.h b/include/wiilight.h new file mode 100644 index 0000000..aaaec6a --- /dev/null +++ b/include/wiilight.h @@ -0,0 +1,7 @@ +void WIILIGHT_Init(); +void WIILIGHT_TurnOn(); +int WIILIGHT_GetLevel(); +int WIILIGHT_SetLevel(int level); + +void WIILIGHT_Toggle(); +void WIILIGHT_TurnOff(); diff --git a/lib/libwiilight.a b/lib/libwiilight.a new file mode 100644 index 0000000..358db7e Binary files /dev/null and b/lib/libwiilight.a differ diff --git a/source/fat.c b/source/fat.c new file mode 100644 index 0000000..5c8081b --- /dev/null +++ b/source/fat.c @@ -0,0 +1,58 @@ +#include +#include +#include +//#include + +#include "fat.h" + + +s32 Fat_Mount(fatDevice *dev) +{ + s32 ret; + + /* Initialize interface */ + ret = dev->interface->startup(); + if (!ret) + return -1; + + /* Mount device */ + ret = fatMountSimple(dev->mount, dev->interface); + if (!ret) + return -1; + + return 0; +} + +void Fat_Unmount(fatDevice *dev) +{ + /* Unmount device */ + fatUnmount(dev->mount); + + /* Shutdown interface */ + dev->interface->shutdown(); +} + +char *Fat_ToFilename(const char *filename) +{ + static char buffer[128]; + + u32 cnt, idx, len; + + /* Clear buffer */ + memset(buffer, 0, sizeof(buffer)); + + /* Get filename length */ + len = strlen(filename); + + for (cnt = idx = 0; idx < len; idx++) { + char c = filename[idx]; + + /* Valid characters */ + if ( (c >= '#' && c <= ')') || (c >= '-' && c <= '.') || + (c >= '0' && c <= '9') || (c >= 'A' && c <= 'z') || + (c >= 'a' && c <= 'z') || (c == '!') ) + buffer[cnt++] = c; + } + + return buffer; +} diff --git a/source/fat.h b/source/fat.h new file mode 100644 index 0000000..34e2ead --- /dev/null +++ b/source/fat.h @@ -0,0 +1,46 @@ +#ifndef _FAT_H_ +#define _FAT_H_ + +/* libfat header */ +#include +#include + +/* SD headers */ +#include +#include + + +/* 'FAT Device' structure */ +typedef struct { + /* Device mount point */ + char *mount; + + /* Device name */ + char *name; + + /* Device interface */ + const DISC_INTERFACE *interface; +} fatDevice; + +/* 'FAT File' structure */ +typedef struct { + /* Filename */ + char filename[128]; + /* 1 = Batch Install, 2 = Batch Uninstall - Leathl */ + int install; + + int installstate; + + /* Filestat */ + bool isdir; + size_t fsize; +} fatFile; + + +/* Prototypes */ +s32 Fat_Mount(fatDevice *); +void Fat_Unmount(fatDevice *); +char *Fat_ToFilename(const char *); + +#endif + diff --git a/source/globals.h b/source/globals.h new file mode 100644 index 0000000..1db1364 --- /dev/null +++ b/source/globals.h @@ -0,0 +1,58 @@ +#ifndef _GLOBALS_H_ +#define _GLOBALS_H_ + +// Constants +#define CIOS_VERSION 249 +#define ENTRIES_PER_PAGE 14 +#define MAX_FILE_PATH_LEN 1024 +#define MAX_DIR_LEVELS 10 +#define WAD_DIRECTORY "/" +#define WAD_ROOT_DIRECTORY "/wad" + +#define MAX_PASSWORD_LENGTH 10 +#define MAX_FAT_DEVICE_LENGTH 10 +#define MAX_NAND_DEVICE_LENGTH 10 + +#define WM_CONFIG_FILE_PATH ":/wad/wm_config.txt" +#define WM_BACKGROUND_PATH ":/wad/background.png" + +// These are indices into the fatDevice fdevList +#define FAT_DEVICE_INDEX_WII_SD 0 +#define FAT_DEVICE_INDXE_USB 1 +#define FAT_DEVICE_INDEX_USB2 2 +#define FAT_DEVICE_INDEX_GC_SDA 3 +#define FAT_DEVICE_INDEX_GC_SDB 4 +#define FAT_DEVICE_INDEX_INVALID -1 + +// These are the indices into the nandDevice ndevList +#define NAND_DEVICE_INDEX_DISABLE 0 +#define NAND_DEVICE_INDEX_SD 1 +#define NAND_DEVICE_INDEX_USB 2 +#define NAND_DEVICE_INDEX_INVALID -1 + +#define CIOS_VERSION_INVALID -1 + +// For the WiiLight +#define WII_LIGHT_OFF 0 +#define WII_LIGHT_ON 1 + +typedef struct +{ + char password[MAX_PASSWORD_LENGTH]; + char startupPath [256]; + int cIOSVersion; + int fatDeviceIndex; + int nandDeviceIndex; + const char *smbuser; + const char *smbpassword; + const char *share; + const char *ip; +} CONFIG; + + +extern CONFIG gConfig; +extern nandDevice ndevList[]; +extern fatDevice fdevList[]; + + +#endif diff --git a/source/gui.c b/source/gui.c new file mode 100644 index 0000000..c24b51e --- /dev/null +++ b/source/gui.c @@ -0,0 +1,90 @@ +#include +#include +#include + +#include "video.h" +#include "fat.h" +#include "menu.h" +#include "nand.h" +#include "globals.h" + +/* Constants */ +#define CONSOLE_XCOORD 70 +#define CONSOLE_YCOORD 118 +#define CONSOLE_WIDTH 502 +#define CONSOLE_HEIGHT 320 + +bool file_exists(const char * filename) +{ + FILE * file; + if ((file = fopen(filename, "r"))) + { + fclose(file); + return true; + } + return false; +} + + +s32 __Gui_DrawPng(void *img, u32 x, u32 y) +{ + IMGCTX ctx = NULL; + PNGUPROP imgProp; + + s32 ret; + + fatDevice *fdev = &fdevList[0]; + ret = Fat_Mount(fdev); + if (file_exists("sd:/wad/background.png")) ctx = PNGU_SelectImageFromDevice ("sd:/wad/background.png"); + + if (ret < 0) + { + fdev = &fdevList[2]; + Fat_Mount(fdev); + if (file_exists("usb2:/wad/background.png")) ctx = PNGU_SelectImageFromDevice ("usb2:/wad/background.png"); + } + + if(!ctx) + { + /* Select PNG data */ + ctx = PNGU_SelectImageFromBuffer(img); + if (!ctx) { + ret = -1; + goto out; + } + } + /* Get image properties */ + ret = PNGU_GetImageProperties(ctx, &imgProp); + if (ret != PNGU_OK) { + ret = -1; + goto out; + } + + /* Draw image */ + Video_DrawPng(ctx, imgProp, x, y); + + /* Success */ + ret = 0; + +out: + /* Free memory */ + if (ctx) + PNGU_ReleaseImageContext(ctx); + + return ret; +} + + +void Gui_InitConsole(void) +{ + /* Initialize console */ + Con_Init(CONSOLE_XCOORD, CONSOLE_YCOORD, CONSOLE_WIDTH, CONSOLE_HEIGHT); +} + +void Gui_DrawBackground(void) +{ + extern char bgData[]; + + /* Draw background */ + __Gui_DrawPng(bgData, 0, 0); +} diff --git a/source/gui.h b/source/gui.h new file mode 100644 index 0000000..80f174c --- /dev/null +++ b/source/gui.h @@ -0,0 +1,8 @@ +#ifndef _GUI_H_ +#define _GUI_H_ + +/* Prototypes */ +void Gui_InitConsole(void); +void Gui_DrawBackground(void); + +#endif diff --git a/source/iospatch.c b/source/iospatch.c new file mode 100644 index 0000000..abb1f3a --- /dev/null +++ b/source/iospatch.c @@ -0,0 +1,116 @@ +// 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, version 2.0. + +// 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 2.0 for more details. + +// Copyright 2010 Joseph Jordan +// Wii U vWii patches Copyright 2012/2013 damysteryman + + +#include +#include +#include +#include + +#include "iospatch.h" + +#define MEM_REG_BASE 0xd8b4000 +#define MEM_PROT (MEM_REG_BASE + 0x20a) + +static void disable_memory_protection() { + write32(MEM_PROT, read32(MEM_PROT) & 0x0000FFFF); +} + +static u32 apply_patch(char *name, const u8 *old, u32 old_size, const u8 *patch, u32 patch_size, u32 patch_offset) { + u8 *ptr_start = (u8*)*((u32*)0x80003134), *ptr_end = (u8*)0x94000000; + u32 found = 0; + printf(" Patching %-30s", name); + u8 *location = NULL; + while (ptr_start < (ptr_end - patch_size)) { + if (!memcmp(ptr_start, old, old_size)) { + found++; + location = ptr_start + patch_offset; + u8 *start = location; + u32 i; + for (i = 0; i < patch_size; i++) { + *location++ = patch[i]; + } + DCFlushRange((u8 *)(((u32)start) >> 5 << 5), (patch_size >> 5 << 5) + 64); + ICInvalidateRange((u8 *)(((u32)start) >> 5 << 5), (patch_size >> 5 << 5) + 64); + } + ptr_start++; + } + if (found) + printf(" patched\n"); + else + printf(" not patched\n"); + return found; +} +/* +static const u8 di_readlimit_old[] = { + 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, + 0x00, 0x01, 0x40, 0x00, 0x00, 0x00, 0x00, 0x00, 0x46, 0x0A, 0x00, 0x00, + 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x08, 0x00, 0x00, 0x00, 0x00, + 0x7E, 0xD4, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x08 +}; +static const u8 di_readlimit_patch[] = { 0x7e, 0xd4 }; + +const u8 isfs_permissions_old[] = { 0x42, 0x8B, 0xD0, 0x01, 0x25, 0x66 }; +const u8 isfs_permissions_patch[] = { 0x42, 0x8B, 0xE0, 0x01, 0x25, 0x66 }; +static const u8 setuid_old[] = { 0xD1, 0x2A, 0x1C, 0x39 }; +static const u8 setuid_patch[] = { 0x46, 0xC0 }; +const u8 es_identify_old[] = { 0x28, 0x03, 0xD1, 0x23 }; +const u8 es_identify_patch[] = { 0x00, 0x00 };*/ +const u8 hash_old[] = { 0x20, 0x07, 0x23, 0xA2 }; +const u8 hash_patch[] = { 0x00 }; +const u8 new_hash_old[] = { 0x20, 0x07, 0x4B, 0x0B }; +const u8 es_set_ahbprot_old[] = { 0x68, 0x5B, 0x22, 0xEC, 0x00, 0x52, 0x18, 0x9B, 0x68, 0x1B, 0x46, 0x98, 0x07, 0xDB }; +const u8 es_set_ahbprot_patch[] = { 0x01 }; +const u8 ES_TitleVersionCheck_old[] = { 0xD2, 0x01, 0x4E, 0x56 }; +const u8 ES_TitleVersionCheck_patch[] = { 0xE0, 0x01, 0x4E, 0x56 }; +const u8 ES_TitleDeleteCheck_old[] = { 0xD8, 0x00, 0x4A, 0x04 }; +const u8 ES_TitleDeleteCheck_patch[] = { 0xE0, 0x00, 0x4A, 0x04 }; + +//Following patches made my damysteryman for use with Wii U's vWii +const u8 Kill_AntiSysTitleInstallv3_pt1_old[] = { 0x68, 0x1A, 0x2A, 0x01, 0xD0, 0x05 }; // Make sure that the pt1 +const u8 Kill_AntiSysTitleInstallv3_pt1_patch[] = { 0x68, 0x1A, 0x2A, 0x01, 0x46, 0xC0 }; // patch is applied twice. -dmm +const u8 Kill_AntiSysTitleInstallv3_pt2_old[] = { 0xD0, 0x02, 0x33, 0x06, 0x42, 0x9A, 0xD1, 0x01 }; // Make sure that the pt2 patch +const u8 Kill_AntiSysTitleInstallv3_pt2_patch[] = { 0x46, 0xC0, 0x33, 0x06, 0x42, 0x9A, 0xE0, 0x01 }; // is also applied twice. -dmm +const u8 Kill_AntiSysTitleInstallv3_pt3_old[] = { 0x68, 0xFB, 0x2B, 0x00, 0xDB, 0x01 }; +const u8 Kill_AntiSysTitleInstallv3_pt3_patch[] = { 0x68, 0xFB, 0x2B, 0x00, 0xDB, 0x10 }; + +u32 IOSPATCH_AHBPROT() { + if (AHBPROT_DISABLED) { + write32(MEM_PROT, read32(MEM_PROT) & 0x0000FFFF); + //return apply_patch("set_ahbprot", check_tmd_old, sizeof(check_tmd_old), check_tmd_patch, sizeof(check_tmd_patch), 6); + return apply_patch("es_set_ahbprot", es_set_ahbprot_old, sizeof(es_set_ahbprot_old), es_set_ahbprot_patch, sizeof(es_set_ahbprot_patch), 25); + } + return 0; +} + +u32 IOSPATCH_Apply() { + u32 count = 0; + if (AHBPROT_DISABLED) { + disable_memory_protection(); + //count += apply_patch("di_readlimit", di_readlimit_old, sizeof(di_readlimit_old), di_readlimit_patch, sizeof(di_readlimit_patch), 12); + //count += apply_patch("isfs_permissions", isfs_permissions_old, sizeof(isfs_permissions_old), isfs_permissions_patch, sizeof(isfs_permissions_patch), 0); + //count += apply_patch("es_setuid", setuid_old, sizeof(setuid_old), setuid_patch, sizeof(setuid_patch), 0); + //count += apply_patch("es_identify", es_identify_old, sizeof(es_identify_old), es_identify_patch, sizeof(es_identify_patch), 2); + count += apply_patch("hash_check", hash_old, sizeof(hash_old), hash_patch, sizeof(hash_patch), 1); + count += apply_patch("new_hash_check", new_hash_old, sizeof(new_hash_old), hash_patch, sizeof(hash_patch), 1); + count += apply_patch("ES_TitleVersionCheck", ES_TitleVersionCheck_old, sizeof(ES_TitleVersionCheck_old), ES_TitleVersionCheck_patch, sizeof(ES_TitleVersionCheck_patch), 0); + count += apply_patch("ES_TitleDeleteCheck", ES_TitleDeleteCheck_old, sizeof(ES_TitleDeleteCheck_old), ES_TitleDeleteCheck_patch, sizeof(ES_TitleDeleteCheck_patch), 0); + + if((*(vu16*)0xCD8005A0 == 0xCAFE)) + { + count += apply_patch("Kill_AntiSysTitleInstallv3_pt1", Kill_AntiSysTitleInstallv3_pt1_old, sizeof(Kill_AntiSysTitleInstallv3_pt1_old), Kill_AntiSysTitleInstallv3_pt1_patch, sizeof(Kill_AntiSysTitleInstallv3_pt1_patch), 0); + count += apply_patch("Kill_AntiSysTitleInstallv3_pt2", Kill_AntiSysTitleInstallv3_pt2_old, sizeof(Kill_AntiSysTitleInstallv3_pt2_old), Kill_AntiSysTitleInstallv3_pt2_patch, sizeof(Kill_AntiSysTitleInstallv3_pt2_patch), 0); + count += apply_patch("Kill_AntiSysTitleInstallv3_pt3", Kill_AntiSysTitleInstallv3_pt3_old, sizeof(Kill_AntiSysTitleInstallv3_pt3_old), Kill_AntiSysTitleInstallv3_pt3_patch, sizeof(Kill_AntiSysTitleInstallv3_pt3_patch), 0); + } + } + return count; +} diff --git a/source/iospatch.h b/source/iospatch.h new file mode 100644 index 0000000..9adfdbc --- /dev/null +++ b/source/iospatch.h @@ -0,0 +1,32 @@ +// 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, version 2.0. + +// 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 2.0 for more details. + +// Copyright (C) 2012 damysteryman + + +#ifndef _IOSPATCH_H +#define _IOSPATCH_H + +#ifdef __cplusplus +extern "C" { +#endif +/* __cplusplus */ + +#include + +#define AHBPROT_DISABLED ((*(vu32*)0xcd800064 == 0xFFFFFFFF) ? 1 : 0) + +u32 IOSPATCH_AHBPROT(); +u32 IOSPATCH_Apply(); + +#ifdef __cplusplus +} +#endif /* __cplusplus */ + +#endif /* _IOSPATCH_H */ diff --git a/source/libpng/pngu/pngu.c b/source/libpng/pngu/pngu.c new file mode 100644 index 0000000..2f6a3b2 --- /dev/null +++ b/source/libpng/pngu/pngu.c @@ -0,0 +1,1132 @@ +/******************************************************************************************** + +PNGU Version : 0.2a + +Coder : frontier + +More info : http://frontier-dev.net + +********************************************************************************************/ +#include +#include +#include "pngu.h" +#include + + +// Constants +#define PNGU_SOURCE_BUFFER 1 +#define PNGU_SOURCE_DEVICE 2 + + +// Prototypes of helper functions +int pngu_info (IMGCTX ctx); +int pngu_decode (IMGCTX ctx, PNGU_u32 width, PNGU_u32 height, PNGU_u32 stripAlpha); +void pngu_free_info (IMGCTX ctx); +void pngu_read_data_from_buffer (png_structp png_ptr, png_bytep data, png_size_t length); +void pngu_write_data_to_buffer (png_structp png_ptr, png_bytep data, png_size_t length); +void pngu_flush_data_to_buffer (png_structp png_ptr); +int pngu_clamp (int value, int min, int max); + + +// PNGU Image context struct +struct _IMGCTX +{ + int source; + void *buffer; + char *filename; + PNGU_u32 cursor; + + PNGU_u32 propRead; + PNGUPROP prop; + + PNGU_u32 infoRead; + png_structp png_ptr; + png_infop info_ptr; + FILE *fd; + + png_bytep *row_pointers; + png_bytep img_data; +}; + + +// PNGU Implementation // + +IMGCTX PNGU_SelectImageFromBuffer (const void *buffer) +{ + IMGCTX ctx = NULL; + + if (!buffer) + return NULL; + + ctx = malloc (sizeof (struct _IMGCTX)); + if (!ctx) + return NULL; + + ctx->buffer = (void *) buffer; + ctx->source = PNGU_SOURCE_BUFFER; + ctx->cursor = 0; + ctx->filename = NULL; + ctx->propRead = 0; + ctx->infoRead = 0; + + return ctx; +} + + +IMGCTX PNGU_SelectImageFromDevice (const char *filename) +{ + IMGCTX ctx = NULL; + + if (!filename) + return NULL; + + ctx = malloc (sizeof (struct _IMGCTX)); + if (!ctx) + return NULL; + + ctx->buffer = NULL; + ctx->source = PNGU_SOURCE_DEVICE; + ctx->cursor = 0; + + ctx->filename = malloc (strlen (filename) + 1); + if (!ctx->filename) + { + free (ctx); + return NULL; + } + strcpy(ctx->filename, filename); + + ctx->propRead = 0; + ctx->infoRead = 0; + + return ctx; +} + + +void PNGU_ReleaseImageContext (IMGCTX ctx) +{ + if (!ctx) + return; + + if (ctx->filename) + free (ctx->filename); + + if ((ctx->propRead) && (ctx->prop.trans)) + free (ctx->prop.trans); + + pngu_free_info (ctx); + + free (ctx); +} + + +int PNGU_GetImageProperties (IMGCTX ctx, PNGUPROP *imgprop) +{ + int res; + + if (!ctx->propRead) + { + res = pngu_info (ctx); + if (res != PNGU_OK) + return res; + } + + *imgprop = ctx->prop; + + return PNGU_OK; +} + + +int PNGU_DecodeToYCbYCr (IMGCTX ctx, PNGU_u32 width, PNGU_u32 height, void *buffer, PNGU_u32 stride) +{ + int result; + PNGU_u32 x, y, buffWidth; + + // width needs to be divisible by two + if (width % 2) + return PNGU_ODD_WIDTH; + + // stride needs to be divisible by two + if (stride % 2) + return PNGU_ODD_STRIDE; + + result = pngu_decode (ctx, width, height, 1); + if (result != PNGU_OK) + return result; + + // Copy image to the output buffer + buffWidth = (width + stride) / 2; + for (y = 0; y < height; y++) + for (x = 0; x < (width / 2); x++) + ((PNGU_u32 *)buffer)[y*buffWidth+x] = PNGU_RGB8_TO_YCbYCr (*(ctx->row_pointers[y]+x*6), *(ctx->row_pointers[y]+x*6+1), *(ctx->row_pointers[y]+x*6+2), + *(ctx->row_pointers[y]+x*6+3), *(ctx->row_pointers[y]+x*6+4), *(ctx->row_pointers[y]+x*6+5)); + + // Free resources + free (ctx->img_data); + free (ctx->row_pointers); + + // Success + return PNGU_OK; +} + + +int PNGU_DecodeToRGB565 (IMGCTX ctx, PNGU_u32 width, PNGU_u32 height, void *buffer, PNGU_u32 stride) +{ + int result; + PNGU_u32 x, y, buffWidth; + + result = pngu_decode (ctx, width, height, 1); + if (result != PNGU_OK) + return result; + + buffWidth = width + stride; + + // Copy image to the output buffer + for (y = 0; y < height; y++) + for (x = 0; x < width; x++) + ((PNGU_u16 *)buffer)[y*buffWidth+x] = + (((PNGU_u16) (ctx->row_pointers[y][x*3] & 0xF8)) << 8) | + (((PNGU_u16) (ctx->row_pointers[y][x*3+1] & 0xFC)) << 3) | + (((PNGU_u16) (ctx->row_pointers[y][x*3+2] & 0xF8)) >> 3); + + // Free resources + free (ctx->img_data); + free (ctx->row_pointers); + + // Success + return PNGU_OK; +} + + +int PNGU_DecodeToRGBA8 (IMGCTX ctx, PNGU_u32 width, PNGU_u32 height, void *buffer, PNGU_u32 stride, PNGU_u8 default_alpha) +{ + int result; + PNGU_u32 x, y, buffWidth; + + result = pngu_decode (ctx, width, height, 0); + if (result != PNGU_OK) + return result; + + buffWidth = width + stride; + + // Check is source image has an alpha channel + if ( (ctx->prop.imgColorType == PNGU_COLOR_TYPE_GRAY_ALPHA) || (ctx->prop.imgColorType == PNGU_COLOR_TYPE_RGB_ALPHA) ) + { + // Alpha channel present, copy image to the output buffer + for (y = 0; y < height; y++) + memcpy (buffer + (y * buffWidth * 4), ctx->row_pointers[y], width * 4); + } + else + { + // No alpha channel present, copy image to the output buffer + for (y = 0; y < height; y++) + for (x = 0; x < width; x++) + ((PNGU_u32 *)buffer)[y*buffWidth+x] = + (((PNGU_u32) ctx->row_pointers[y][x*3]) << 24) | + (((PNGU_u32) ctx->row_pointers[y][x*3+1]) << 16) | + (((PNGU_u32) ctx->row_pointers[y][x*3+2]) << 8) | + ((PNGU_u32) default_alpha); + } + + // Free resources + free (ctx->img_data); + free (ctx->row_pointers); + + // Success + return PNGU_OK; +} + + +int PNGU_DecodeTo4x4RGB565 (IMGCTX ctx, PNGU_u32 width, PNGU_u32 height, void *buffer) +{ + int result; + PNGU_u32 x, y, qwidth, qheight; + + // width and height need to be divisible by four + if ((width % 4) || (height % 4)) + return PNGU_INVALID_WIDTH_OR_HEIGHT; + + result = pngu_decode (ctx, width, height, 1); + if (result != PNGU_OK) + return result; + + // Copy image to the output buffer + qwidth = width / 4; + qheight = height / 4; + + for (y = 0; y < qheight; y++) + for (x = 0; x < qwidth; x++) + { + int blockbase = (y * qwidth + x) * 4; + + PNGU_u64 field64 = *((PNGU_u64 *)(ctx->row_pointers[y*4]+x*12)); + PNGU_u64 field32 = (PNGU_u64) *((PNGU_u32 *)(ctx->row_pointers[y*4]+x*12+8)); + ((PNGU_u64 *) buffer)[blockbase] = + (((field64 & 0xF800000000000000ULL) | ((field64 & 0xFC000000000000ULL) << 3) | ((field64 & 0xF80000000000ULL) << 5)) | + (((field64 & 0xF800000000ULL) << 8) | ((field64 & 0xFC000000ULL) << 11) | ((field64 & 0xF80000ULL) << 13)) | + (((field64 & 0xF800ULL) << 16) | ((field64 & 0xFCULL) << 19) | ((field32 & 0xF8000000ULL) >> 11)) | + (((field32 & 0xF80000ULL) >> 8) | ((field32 & 0xFC00ULL) >> 5) | ((field32 & 0xF8ULL) >> 3))); + + field64 = *((PNGU_u64 *)(ctx->row_pointers[y*4+1]+x*12)); + field32 = (PNGU_u64) *((PNGU_u32 *)(ctx->row_pointers[y*4+1]+x*12+8)); + ((PNGU_u64 *) buffer)[blockbase+1] = + (((field64 & 0xF800000000000000ULL) | ((field64 & 0xFC000000000000ULL) << 3) | ((field64 & 0xF80000000000ULL) << 5)) | + (((field64 & 0xF800000000ULL) << 8) | ((field64 & 0xFC000000ULL) << 11) | ((field64 & 0xF80000ULL) << 13)) | + (((field64 & 0xF800ULL) << 16) | ((field64 & 0xFCULL) << 19) | ((field32 & 0xF8000000ULL) >> 11)) | + (((field32 & 0xF80000ULL) >> 8) | ((field32 & 0xFC00ULL) >> 5) | ((field32 & 0xF8ULL) >> 3))); + + field64 = *((PNGU_u64 *)(ctx->row_pointers[y*4+2]+x*12)); + field32 = (PNGU_u64) *((PNGU_u32 *)(ctx->row_pointers[y*4+2]+x*12+8)); + ((PNGU_u64 *) buffer)[blockbase+2] = + (((field64 & 0xF800000000000000ULL) | ((field64 & 0xFC000000000000ULL) << 3) | ((field64 & 0xF80000000000ULL) << 5)) | + (((field64 & 0xF800000000ULL) << 8) | ((field64 & 0xFC000000ULL) << 11) | ((field64 & 0xF80000ULL) << 13)) | + (((field64 & 0xF800ULL) << 16) | ((field64 & 0xFCULL) << 19) | ((field32 & 0xF8000000ULL) >> 11)) | + (((field32 & 0xF80000ULL) >> 8) | ((field32 & 0xFC00ULL) >> 5) | ((field32 & 0xF8ULL) >> 3))); + + field64 = *((PNGU_u64 *)(ctx->row_pointers[y*4+3]+x*12)); + field32 = (PNGU_u64) *((PNGU_u32 *)(ctx->row_pointers[y*4+3]+x*12+8)); + ((PNGU_u64 *) buffer)[blockbase+3] = + (((field64 & 0xF800000000000000ULL) | ((field64 & 0xFC000000000000ULL) << 3) | ((field64 & 0xF80000000000ULL) << 5)) | + (((field64 & 0xF800000000ULL) << 8) | ((field64 & 0xFC000000ULL) << 11) | ((field64 & 0xF80000ULL) << 13)) | + (((field64 & 0xF800ULL) << 16) | ((field64 & 0xFCULL) << 19) | ((field32 & 0xF8000000ULL) >> 11)) | + (((field32 & 0xF80000ULL) >> 8) | ((field32 & 0xFC00ULL) >> 5) | ((field32 & 0xF8ULL) >> 3))); + } + + // Free resources + free (ctx->img_data); + free (ctx->row_pointers); + + // Success + return PNGU_OK; +} + + +int PNGU_DecodeTo4x4RGB5A3 (IMGCTX ctx, PNGU_u32 width, PNGU_u32 height, void *buffer, PNGU_u8 default_alpha) +{ + int result; + PNGU_u32 x, y, qwidth, qheight; + PNGU_u64 alphaMask; + + // width and height need to be divisible by four + if ((width % 4) || (height % 4)) + return PNGU_INVALID_WIDTH_OR_HEIGHT; + + result = pngu_decode (ctx, width, height, 0); + if (result != PNGU_OK) + return result; + + // Init some vars + qwidth = width / 4; + qheight = height / 4; + + // Check is source image has an alpha channel + if ( (ctx->prop.imgColorType == PNGU_COLOR_TYPE_GRAY_ALPHA) || (ctx->prop.imgColorType == PNGU_COLOR_TYPE_RGB_ALPHA) ) + { + // Alpha channel present, copy image to the output buffer + for (y = 0; y < qheight; y++) + for (x = 0; x < qwidth; x++) + { + int blockbase = (y * qwidth + x) * 4; + PNGU_u64 tmp; + + PNGU_u64 fieldA = *((PNGU_u64 *)(ctx->row_pointers[y*4]+x*16)); + PNGU_u64 fieldB = *((PNGU_u64 *)(ctx->row_pointers[y*4]+x*16+8)); + // If first pixel is opaque set MSB to 1 and encode colors in RGB555, else set MSB to 0 and encode colors in ARGB3444 + if ((fieldA & 0xE000000000ULL) == 0xE000000000ULL) + tmp = 0x8000000000000000ULL | ((fieldA & 0xF800000000000000ULL) >> 1) | ((fieldA & 0xF8000000000000ULL) << 2) | ((fieldA & 0xF80000000000ULL) << 5); + else + tmp = ((fieldA & 0xE000000000ULL) << 23) | ((fieldA & 0xF000000000000000ULL) >> 4) | (fieldA & 0xF0000000000000ULL) | ((fieldA & 0xF00000000000ULL) << 4); + + // If second pixel is opaque set MSB to 1 and encode colors in RGB555, else set MSB to 0 and encode colors in ARGB3444 + if ((fieldA & 0xE0ULL) == 0xE0ULL) + tmp = tmp | 0x800000000000ULL | ((fieldA & 0xF8000000ULL) << 15) | ((fieldA & 0xF80000ULL) << 18) | ((fieldA & 0xF800ULL) << 21); + else + tmp = tmp | ((fieldA & 0xE0ULL) << 39) | ((fieldA & 0xF0000000ULL) << 12) | ((fieldA & 0xF00000ULL) << 16) | ((fieldA & 0xF000ULL) << 20); + + // If third pixel is opaque set MSB to 1 and encode colors in RGB555, else set MSB to 0 and encode colors in ARGB3444 + if ((fieldB & 0xE000000000ULL) == 0xE000000000ULL) + tmp = tmp | 0x80000000ULL | ((fieldB & 0xF800000000000000ULL) >> 33) | ((fieldB & 0xF8000000000000ULL) >> 30) | ((fieldB & 0xF80000000000ULL) >> 27); + else + tmp = tmp | ((fieldB & 0xE000000000ULL) >> 9) | ((fieldB & 0xF000000000000000ULL) >> 36) | ((fieldB & 0xF0000000000000ULL) >> 32) | ((fieldB & 0xF00000000000ULL) >> 28); + + // If fourth pixel is opaque set MSB to 1 and encode colors in RGB555, else set MSB to 0 and encode colors in ARGB3444 + if ((fieldB & 0xE0ULL) == 0xE0ULL) + tmp = tmp | 0x8000ULL | ((fieldB & 0xF8000000ULL) >> 17) | ((fieldB & 0xF80000ULL) >> 14) | ((fieldB & 0xF800ULL) >> 11); + else + tmp = tmp | ((fieldB & 0xE0ULL) << 7) | ((fieldB & 0xF0000000ULL) >> 20) | ((fieldB & 0xF00000ULL) >> 16) | ((fieldB & 0xF000ULL) >> 12); + ((PNGU_u64 *) buffer)[blockbase] = tmp; + + fieldA = *((PNGU_u64 *)(ctx->row_pointers[y*4+1]+x*16)); + fieldB = *((PNGU_u64 *)(ctx->row_pointers[y*4+1]+x*16+8)); + if ((fieldA & 0xE000000000ULL) == 0xE000000000ULL) + // Opaque pixel, so set MSB to 1 and encode colors in RGB555 + tmp = 0x8000000000000000ULL | ((fieldA & 0xF800000000000000ULL) >> 1) | ((fieldA & 0xF8000000000000ULL) << 2) | ((fieldA & 0xF80000000000ULL) << 5); + else + // Tranlucid pixel, so set MSB to 0 and encode colors in ARGB3444 + tmp = ((fieldA & 0xE000000000ULL) << 23) | ((fieldA & 0xF000000000000000ULL) >> 4) | (fieldA & 0xF0000000000000ULL) | ((fieldA & 0xF00000000000ULL) << 4); + + if ((fieldA & 0xE0ULL) == 0xE0ULL) + // Opaque pixel, so set MSB to 1 and encode colors in RGB555 + tmp = tmp | 0x800000000000ULL | ((fieldA & 0xF8000000ULL) << 15) | ((fieldA & 0xF80000ULL) << 18) | ((fieldA & 0xF800ULL) << 21); + else + // Tranlucid pixel, so set MSB to 0 and encode colors in ARGB3444 + tmp = tmp | ((fieldA & 0xE0ULL) << 39) | ((fieldA & 0xF0000000ULL) << 12) | ((fieldA & 0xF00000ULL) << 16) | ((fieldA & 0xF000ULL) << 20); + + if ((fieldB & 0xE000000000ULL) == 0xE000000000ULL) + // Opaque pixel, so set MSB to 1 and encode colors in RGB555 + tmp = tmp | 0x80000000ULL | ((fieldB & 0xF800000000000000ULL) >> 33) | ((fieldB & 0xF8000000000000ULL) >> 30) | ((fieldB & 0xF80000000000ULL) >> 27); + else + // Tranlucid pixel, so set MSB to 0 and encode colors in ARGB3444 + tmp = tmp | ((fieldB & 0xE000000000ULL) >> 9) | ((fieldB & 0xF000000000000000ULL) >> 36) | ((fieldB & 0xF0000000000000ULL) >> 32) | ((fieldB & 0xF00000000000ULL) >> 28); + + if ((fieldB & 0xE0ULL) == 0xE0ULL) + // Opaque pixel, so set MSB to 1 and encode colors in RGB555 + tmp = tmp | 0x8000ULL | ((fieldB & 0xF8000000ULL) >> 17) | ((fieldB & 0xF80000ULL) >> 14) | ((fieldB & 0xF800ULL) >> 11); + else + // Tranlucid pixel, so set MSB to 0 and encode colors in ARGB3444 + tmp = tmp | ((fieldB & 0xE0ULL) << 7) | ((fieldB & 0xF0000000ULL) >> 20) | ((fieldB & 0xF00000ULL) >> 16) | ((fieldB & 0xF000ULL) >> 12); + ((PNGU_u64 *) buffer)[blockbase+1] = tmp; + + fieldA = *((PNGU_u64 *)(ctx->row_pointers[y*4+2]+x*16)); + fieldB = *((PNGU_u64 *)(ctx->row_pointers[y*4+2]+x*16+8)); + if ((fieldA & 0xE000000000ULL) == 0xE000000000ULL) + // Opaque pixel, so set MSB to 1 and encode colors in RGB555 + tmp = 0x8000000000000000ULL | ((fieldA & 0xF800000000000000ULL) >> 1) | ((fieldA & 0xF8000000000000ULL) << 2) | ((fieldA & 0xF80000000000ULL) << 5); + else + // Tranlucid pixel, so set MSB to 0 and encode colors in ARGB3444 + tmp = ((fieldA & 0xE000000000ULL) << 23) | ((fieldA & 0xF000000000000000ULL) >> 4) | (fieldA & 0xF0000000000000ULL) | ((fieldA & 0xF00000000000ULL) << 4); + + if ((fieldA & 0xE0ULL) == 0xE0ULL) + // Opaque pixel, so set MSB to 1 and encode colors in RGB555 + tmp = tmp | 0x800000000000ULL | ((fieldA & 0xF8000000ULL) << 15) | ((fieldA & 0xF80000ULL) << 18) | ((fieldA & 0xF800ULL) << 21); + else + // Tranlucid pixel, so set MSB to 0 and encode colors in ARGB3444 + tmp = tmp | ((fieldA & 0xE0ULL) << 39) | ((fieldA & 0xF0000000ULL) << 12) | ((fieldA & 0xF00000ULL) << 16) | ((fieldA & 0xF000ULL) << 20); + + if ((fieldB & 0xE000000000ULL) == 0xE000000000ULL) + // Opaque pixel, so set MSB to 1 and encode colors in RGB555 + tmp = tmp | 0x80000000ULL | ((fieldB & 0xF800000000000000ULL) >> 33) | ((fieldB & 0xF8000000000000ULL) >> 30) | ((fieldB & 0xF80000000000ULL) >> 27); + else + // Tranlucid pixel, so set MSB to 0 and encode colors in ARGB3444 + tmp = tmp | ((fieldB & 0xE000000000ULL) >> 9) | ((fieldB & 0xF000000000000000ULL) >> 36) | ((fieldB & 0xF0000000000000ULL) >> 32) | ((fieldB & 0xF00000000000ULL) >> 28); + + if ((fieldB & 0xE0ULL) == 0xE0ULL) + // Opaque pixel, so set MSB to 1 and encode colors in RGB555 + tmp = tmp | 0x8000ULL | ((fieldB & 0xF8000000ULL) >> 17) | ((fieldB & 0xF80000ULL) >> 14) | ((fieldB & 0xF800ULL) >> 11); + else + // Tranlucid pixel, so set MSB to 0 and encode colors in ARGB3444 + tmp = tmp | ((fieldB & 0xE0ULL) << 7) | ((fieldB & 0xF0000000ULL) >> 20) | ((fieldB & 0xF00000ULL) >> 16) | ((fieldB & 0xF000ULL) >> 12); + ((PNGU_u64 *) buffer)[blockbase+2] = tmp; + + fieldA = *((PNGU_u64 *)(ctx->row_pointers[y*4+3]+x*16)); + fieldB = *((PNGU_u64 *)(ctx->row_pointers[y*4+3]+x*16+8)); + if ((fieldA & 0xE000000000ULL) == 0xE000000000ULL) + // Opaque pixel, so set MSB to 1 and encode colors in RGB555 + tmp = 0x8000000000000000ULL | ((fieldA & 0xF800000000000000ULL) >> 1) | ((fieldA & 0xF8000000000000ULL) << 2) | ((fieldA & 0xF80000000000ULL) << 5); + else + // Tranlucid pixel, so set MSB to 0 and encode colors in ARGB3444 + tmp = ((fieldA & 0xE000000000ULL) << 23) | ((fieldA & 0xF000000000000000ULL) >> 4) | (fieldA & 0xF0000000000000ULL) | ((fieldA & 0xF00000000000ULL) << 4); + + if ((fieldA & 0xE0ULL) == 0xE0ULL) + // Opaque pixel, so set MSB to 1 and encode colors in RGB555 + tmp = tmp | 0x800000000000ULL | ((fieldA & 0xF8000000ULL) << 15) | ((fieldA & 0xF80000ULL) << 18) | ((fieldA & 0xF800ULL) << 21); + else + // Tranlucid pixel, so set MSB to 0 and encode colors in ARGB3444 + tmp = tmp | ((fieldA & 0xE0ULL) << 39) | ((fieldA & 0xF0000000ULL) << 12) | ((fieldA & 0xF00000ULL) << 16) | ((fieldA & 0xF000ULL) << 20); + + if ((fieldB & 0xE000000000ULL) == 0xE000000000ULL) + // Opaque pixel, so set MSB to 1 and encode colors in RGB555 + tmp = tmp | 0x80000000ULL | ((fieldB & 0xF800000000000000ULL) >> 33) | ((fieldB & 0xF8000000000000ULL) >> 30) | ((fieldB & 0xF80000000000ULL) >> 27); + else + // Tranlucid pixel, so set MSB to 0 and encode colors in ARGB3444 + tmp = tmp | ((fieldB & 0xE000000000ULL) >> 9) | ((fieldB & 0xF000000000000000ULL) >> 36) | ((fieldB & 0xF0000000000000ULL) >> 32) | ((fieldB & 0xF00000000000ULL) >> 28); + + if ((fieldB & 0xE0ULL) == 0xE0ULL) + // Opaque pixel, so set MSB to 1 and encode colors in RGB555 + tmp = tmp | 0x8000ULL | ((fieldB & 0xF8000000ULL) >> 17) | ((fieldB & 0xF80000ULL) >> 14) | ((fieldB & 0xF800ULL) >> 11); + else + // Tranlucid pixel, so set MSB to 0 and encode colors in ARGB3444 + tmp = tmp | ((fieldB & 0xE0ULL) << 7) | ((fieldB & 0xF0000000ULL) >> 20) | ((fieldB & 0xF00000ULL) >> 16) | ((fieldB & 0xF000ULL) >> 12); + ((PNGU_u64 *) buffer)[blockbase+3] = tmp; + } + } + else + { + // No alpha channel present, copy image to the output buffer + default_alpha = (default_alpha >> 5); + if (default_alpha == 7) + { + // The user wants an opaque texture, so set MSB to 1 and encode colors in RGB555 + alphaMask = 0x8000800080008000ULL; + + for (y = 0; y < qheight; y++) + for (x = 0; x < qwidth; x++) + { + int blockbase = (y * qwidth + x) * 4; + + PNGU_u64 field64 = *((PNGU_u64 *)(ctx->row_pointers[y*4]+x*12)); + PNGU_u64 field32 = (PNGU_u64) *((PNGU_u32 *)(ctx->row_pointers[y*4]+x*12+8)); + ((PNGU_u64 *) buffer)[blockbase] = + alphaMask | ((field64 & 0xF800000000000000ULL) >> 1) | ((field64 & 0xF8000000000000ULL) << 2) | + ((field64 & 0xF80000000000ULL) << 5) | ((field64 & 0xF800000000ULL) << 7) | ((field64 & 0xF8000000ULL) << 10) | + ((field64 & 0xF80000ULL) << 13) | ((field64 & 0xF800ULL) << 15) | ((field64 & 0xF8ULL) << 18) | + ((field32 & 0xF8000000ULL) >> 11) | ((field32 & 0xF80000ULL) >> 9) | ((field32 & 0xF800ULL) >> 6) | ((field32 & 0xF8ULL) >> 3); + + field64 = *((PNGU_u64 *)(ctx->row_pointers[y*4+1]+x*12)); + field32 = (PNGU_u64) *((PNGU_u32 *)(ctx->row_pointers[y*4+1]+x*12+8)); + ((PNGU_u64 *) buffer)[blockbase+1] = + alphaMask | ((field64 & 0xF800000000000000ULL) >> 1) | ((field64 & 0xF8000000000000ULL) << 2) | + ((field64 & 0xF80000000000ULL) << 5) | ((field64 & 0xF800000000ULL) << 7) | ((field64 & 0xF8000000ULL) << 10) | + ((field64 & 0xF80000ULL) << 13) | ((field64 & 0xF800ULL) << 15) | ((field64 & 0xF8ULL) << 18) | + ((field32 & 0xF8000000ULL) >> 11) | ((field32 & 0xF80000ULL) >> 9) | ((field32 & 0xF800ULL) >> 6) | ((field32 & 0xF8ULL) >> 3); + + field64 = *((PNGU_u64 *)(ctx->row_pointers[y*4+2]+x*12)); + field32 = (PNGU_u64) *((PNGU_u32 *)(ctx->row_pointers[y*4+2]+x*12+8)); + ((PNGU_u64 *) buffer)[blockbase+2] = + alphaMask | ((field64 & 0xF800000000000000ULL) >> 1) | ((field64 & 0xF8000000000000ULL) << 2) | + ((field64 & 0xF80000000000ULL) << 5) | ((field64 & 0xF800000000ULL) << 7) | ((field64 & 0xF8000000ULL) << 10) | + ((field64 & 0xF80000ULL) << 13) | ((field64 & 0xF800ULL) << 15) | ((field64 & 0xF8ULL) << 18) | + ((field32 & 0xF8000000ULL) >> 11) | ((field32 & 0xF80000ULL) >> 9) | ((field32 & 0xF800ULL) >> 6) | ((field32 & 0xF8ULL) >> 3); + + field64 = *((PNGU_u64 *)(ctx->row_pointers[y*4+3]+x*12)); + field32 = (PNGU_u64) *((PNGU_u32 *)(ctx->row_pointers[y*4+3]+x*12+8)); + ((PNGU_u64 *) buffer)[blockbase+3] = + alphaMask | ((field64 & 0xF800000000000000ULL) >> 1) | ((field64 & 0xF8000000000000ULL) << 2) | + ((field64 & 0xF80000000000ULL) << 5) | ((field64 & 0xF800000000ULL) << 7) | ((field64 & 0xF8000000ULL) << 10) | + ((field64 & 0xF80000ULL) << 13) | ((field64 & 0xF800ULL) << 15) | ((field64 & 0xF8ULL) << 18) | + ((field32 & 0xF8000000ULL) >> 11) | ((field32 & 0xF80000ULL) >> 9) | ((field32 & 0xF800ULL) >> 6) | ((field32 & 0xF8ULL) >> 3); + } + } + else + { + // The user wants a translucid texture, so set MSB to 0 and encode colors in ARGB3444 + default_alpha = (default_alpha << 4); + alphaMask = (((PNGU_u64) default_alpha) << 56) | (((PNGU_u64) default_alpha) << 40) | + (((PNGU_u64) default_alpha) << 24) | (((PNGU_u64) default_alpha) << 8); + + for (y = 0; y < qheight; y++) + for (x = 0; x < qwidth; x++) + { + int blockbase = (y * qwidth + x) * 4; + + PNGU_u64 field64 = *((PNGU_u64 *)(ctx->row_pointers[y*4]+x*12)); + PNGU_u64 field32 = (PNGU_u64) *((PNGU_u32 *)(ctx->row_pointers[y*4]+x*12+8)); + ((PNGU_u64 *) buffer)[blockbase] = + alphaMask | ((field64 & 0xF000000000000000ULL) >> 4) | (field64 & 0xF0000000000000ULL) | ((field64 & 0xF00000000000ULL) << 4) | + ((field64 & 0xF000000000ULL) << 4) | ((field64 & 0xF0000000ULL) << 8) | ((field64 & 0xF00000ULL) << 12) | + ((field64 & 0xF000ULL) << 12) | ((field64 & 0xF0ULL) << 16) | ((field32 & 0xF0000000ULL) >> 12) | + ((field32 & 0xF00000ULL) >> 12) | ((field32 & 0xF000ULL) >> 8) | ((field32 & 0xF0ULL) >> 4); + + field64 = *((PNGU_u64 *)(ctx->row_pointers[y*4+1]+x*12)); + field32 = (PNGU_u64) *((PNGU_u32 *)(ctx->row_pointers[y*4+1]+x*12+8)); + ((PNGU_u64 *) buffer)[blockbase+1] = + alphaMask | ((field64 & 0xF000000000000000ULL) >> 4) | (field64 & 0xF0000000000000ULL) | ((field64 & 0xF00000000000ULL) << 4) | + ((field64 & 0xF000000000ULL) << 4) | ((field64 & 0xF0000000ULL) << 8) | ((field64 & 0xF00000ULL) << 12) | + ((field64 & 0xF000ULL) << 12) | ((field64 & 0xF0ULL) << 16) | ((field32 & 0xF0000000ULL) >> 12) | + ((field32 & 0xF00000ULL) >> 12) | ((field32 & 0xF000ULL) >> 8) | ((field32 & 0xF0ULL) >> 4); + + field64 = *((PNGU_u64 *)(ctx->row_pointers[y*4+2]+x*12)); + field32 = (PNGU_u64) *((PNGU_u32 *)(ctx->row_pointers[y*4+2]+x*12+8)); + ((PNGU_u64 *) buffer)[blockbase+2] = + alphaMask | ((field64 & 0xF000000000000000ULL) >> 4) | (field64 & 0xF0000000000000ULL) | ((field64 & 0xF00000000000ULL) << 4) | + ((field64 & 0xF000000000ULL) << 4) | ((field64 & 0xF0000000ULL) << 8) | ((field64 & 0xF00000ULL) << 12) | + ((field64 & 0xF000ULL) << 12) | ((field64 & 0xF0ULL) << 16) | ((field32 & 0xF0000000ULL) >> 12) | + ((field32 & 0xF00000ULL) >> 12) | ((field32 & 0xF000ULL) >> 8) | ((field32 & 0xF0ULL) >> 4); + + field64 = *((PNGU_u64 *)(ctx->row_pointers[y*4+3]+x*12)); + field32 = (PNGU_u64) *((PNGU_u32 *)(ctx->row_pointers[y*4+3]+x*12+8)); + ((PNGU_u64 *) buffer)[blockbase+3] = + alphaMask | ((field64 & 0xF000000000000000ULL) >> 4) | (field64 & 0xF0000000000000ULL) | ((field64 & 0xF00000000000ULL) << 4) | + ((field64 & 0xF000000000ULL) << 4) | ((field64 & 0xF0000000ULL) << 8) | ((field64 & 0xF00000ULL) << 12) | + ((field64 & 0xF000ULL) << 12) | ((field64 & 0xF0ULL) << 16) | ((field32 & 0xF0000000ULL) >> 12) | + ((field32 & 0xF00000ULL) >> 12) | ((field32 & 0xF000ULL) >> 8) | ((field32 & 0xF0ULL) >> 4); + } + } + } + + // Free resources + free (ctx->img_data); + free (ctx->row_pointers); + + // Success + return PNGU_OK; +} + + +int PNGU_DecodeTo4x4RGBA8 (IMGCTX ctx, PNGU_u32 width, PNGU_u32 height, void *buffer, PNGU_u8 default_alpha) +{ + int result; + PNGU_u32 x, y, qwidth, qheight; + PNGU_u64 alphaMask; + + // width and height need to be divisible by four + if ((width % 4) || (height % 4)) + return PNGU_INVALID_WIDTH_OR_HEIGHT; + + result = pngu_decode (ctx, width, height, 0); + if (result != PNGU_OK) + return result; + + // Init some variables + qwidth = width / 4; + qheight = height / 4; + + // Check is source image has an alpha channel + if ( (ctx->prop.imgColorType == PNGU_COLOR_TYPE_GRAY_ALPHA) || (ctx->prop.imgColorType == PNGU_COLOR_TYPE_RGB_ALPHA) ) + { + // Alpha channel present, copy image to the output buffer + for (y = 0; y < qheight; y++) + for (x = 0; x < qwidth; x++) + { + int blockbase = (y * qwidth + x) * 8; + + PNGU_u64 fieldA = *((PNGU_u64 *)(ctx->row_pointers[y*4]+x*16)); + PNGU_u64 fieldB = *((PNGU_u64 *)(ctx->row_pointers[y*4]+x*16+8)); + ((PNGU_u64 *) buffer)[blockbase] = + ((fieldA & 0xFF00000000ULL) << 24) | ((fieldA & 0xFF00000000000000ULL) >> 8) | + ((fieldA & 0xFFULL) << 40) | ((fieldA & 0xFF000000ULL) << 8) | + ((fieldB & 0xFF00000000ULL) >> 8) | ((fieldB & 0xFF00000000000000ULL) >> 40) | + ((fieldB & 0xFFULL) << 8) | ((fieldB & 0xFF000000ULL) >> 24); + ((PNGU_u64 *) buffer)[blockbase+4] = + ((fieldA & 0xFFFF0000000000ULL) << 8) | ((fieldA & 0xFFFF00ULL) << 24) | + ((fieldB & 0xFFFF0000000000ULL) >> 24) | ((fieldB & 0xFFFF00ULL) >> 8); + + fieldA = *((PNGU_u64 *)(ctx->row_pointers[y*4+1]+x*16)); + fieldB = *((PNGU_u64 *)(ctx->row_pointers[y*4+1]+x*16+8)); + ((PNGU_u64 *) buffer)[blockbase+1] = + ((fieldA & 0xFF00000000ULL) << 24) | ((fieldA & 0xFF00000000000000ULL) >> 8) | + ((fieldA & 0xFFULL) << 40) | ((fieldA & 0xFF000000ULL) << 8) | + ((fieldB & 0xFF00000000ULL) >> 8) | ((fieldB & 0xFF00000000000000ULL) >> 40) | + ((fieldB & 0xFFULL) << 8) | ((fieldB & 0xFF000000ULL) >> 24); + ((PNGU_u64 *) buffer)[blockbase+5] = + ((fieldA & 0xFFFF0000000000ULL) << 8) | ((fieldA & 0xFFFF00ULL) << 24) | + ((fieldB & 0xFFFF0000000000ULL) >> 24) | ((fieldB & 0xFFFF00ULL) >> 8); + + fieldA = *((PNGU_u64 *)(ctx->row_pointers[y*4+2]+x*16)); + fieldB = *((PNGU_u64 *)(ctx->row_pointers[y*4+2]+x*16+8)); + ((PNGU_u64 *) buffer)[blockbase+2] = + ((fieldA & 0xFF00000000ULL) << 24) | ((fieldA & 0xFF00000000000000ULL) >> 8) | + ((fieldA & 0xFFULL) << 40) | ((fieldA & 0xFF000000ULL) << 8) | + ((fieldB & 0xFF00000000ULL) >> 8) | ((fieldB & 0xFF00000000000000ULL) >> 40) | + ((fieldB & 0xFFULL) << 8) | ((fieldB & 0xFF000000ULL) >> 24); + ((PNGU_u64 *) buffer)[blockbase+6] = + ((fieldA & 0xFFFF0000000000ULL) << 8) | ((fieldA & 0xFFFF00ULL) << 24) | + ((fieldB & 0xFFFF0000000000ULL) >> 24) | ((fieldB & 0xFFFF00ULL) >> 8); + + fieldA = *((PNGU_u64 *)(ctx->row_pointers[y*4+3]+x*16)); + fieldB = *((PNGU_u64 *)(ctx->row_pointers[y*4+3]+x*16+8)); + ((PNGU_u64 *) buffer)[blockbase+3] = + ((fieldA & 0xFF00000000ULL) << 24) | ((fieldA & 0xFF00000000000000ULL) >> 8) | + ((fieldA & 0xFFULL) << 40) | ((fieldA & 0xFF000000ULL) << 8) | + ((fieldB & 0xFF00000000ULL) >> 8) | ((fieldB & 0xFF00000000000000ULL) >> 40) | + ((fieldB & 0xFFULL) << 8) | ((fieldB & 0xFF000000ULL) >> 24); + ((PNGU_u64 *) buffer)[blockbase+7] = + ((fieldA & 0xFFFF0000000000ULL) << 8) | ((fieldA & 0xFFFF00ULL) << 24) | + ((fieldB & 0xFFFF0000000000ULL) >> 24) | ((fieldB & 0xFFFF00ULL) >> 8); + } + } + else + { + // No alpha channel present, copy image to the output buffer + alphaMask = (((PNGU_u64)default_alpha) << 56) | (((PNGU_u64)default_alpha) << 40) | + (((PNGU_u64)default_alpha) << 24) | (((PNGU_u64)default_alpha) << 8); + + for (y = 0; y < qheight; y++) + for (x = 0; x < qwidth; x++) + { + int blockbase = (y * qwidth + x) * 8; + + PNGU_u64 field64 = *((PNGU_u64 *)(ctx->row_pointers[y*4]+x*12)); + PNGU_u64 field32 = (PNGU_u64) *((PNGU_u32 *)(ctx->row_pointers[y*4]+x*12+8)); + ((PNGU_u64 *) buffer)[blockbase] = + (((field64 & 0xFF00000000000000ULL) >> 8) | (field64 & 0xFF00000000ULL) | + ((field64 & 0xFF00ULL) << 8) | ((field32 & 0xFF0000ULL) >> 16) | alphaMask); + ((PNGU_u64 *) buffer)[blockbase+4] = + (((field64 & 0xFFFF0000000000ULL) << 8) | ((field64 & 0xFFFF0000ULL) << 16) | + ((field64 & 0xFFULL) << 24) | ((field32 & 0xFF000000ULL) >> 8) | (field32 & 0xFFFFULL)); + + field64 = *((PNGU_u64 *)(ctx->row_pointers[y*4+1]+x*12)); + field32 = (PNGU_u64) *((PNGU_u32 *)(ctx->row_pointers[y*4+1]+x*12+8)); + ((PNGU_u64 *) buffer)[blockbase+1] = + (((field64 & 0xFF00000000000000ULL) >> 8) | (field64 & 0xFF00000000ULL) | + ((field64 & 0xFF00ULL) << 8) | ((field32 & 0xFF0000ULL) >> 16) | alphaMask); + ((PNGU_u64 *) buffer)[blockbase+5] = + (((field64 & 0xFFFF0000000000ULL) << 8) | ((field64 & 0xFFFF0000ULL) << 16) | + ((field64 & 0xFFULL) << 24) | ((field32 & 0xFF000000ULL) >> 8) | (field32 & 0xFFFFULL)); + + field64 = *((PNGU_u64 *)(ctx->row_pointers[y*4+2]+x*12)); + field32 = (PNGU_u64) *((PNGU_u32 *)(ctx->row_pointers[y*4+2]+x*12+8)); + ((PNGU_u64 *) buffer)[blockbase+2] = + (((field64 & 0xFF00000000000000ULL) >> 8) | (field64 & 0xFF00000000ULL) | + ((field64 & 0xFF00ULL) << 8) | ((field32 & 0xFF0000ULL) >> 16) | alphaMask); + ((PNGU_u64 *) buffer)[blockbase+6] = + (((field64 & 0xFFFF0000000000ULL) << 8) | ((field64 & 0xFFFF0000ULL) << 16) | + ((field64 & 0xFFULL) << 24) | ((field32 & 0xFF000000ULL) >> 8) | (field32 & 0xFFFFULL)); + + field64 = *((PNGU_u64 *)(ctx->row_pointers[y*4+3]+x*12)); + field32 = (PNGU_u64) *((PNGU_u32 *)(ctx->row_pointers[y*4+3]+x*12+8)); + ((PNGU_u64 *) buffer)[blockbase+3] = + (((field64 & 0xFF00000000000000ULL) >> 8) | (field64 & 0xFF00000000ULL) | + ((field64 & 0xFF00ULL) << 8) | ((field32 & 0xFF0000ULL) >> 16) | alphaMask); + ((PNGU_u64 *) buffer)[blockbase+7] = + (((field64 & 0xFFFF0000000000ULL) << 8) | ((field64 & 0xFFFF0000ULL) << 16) | + ((field64 & 0xFFULL) << 24) | ((field32 & 0xFF000000ULL) >> 8) | (field32 & 0xFFFFULL)); + } + } + + // Free resources + free (ctx->img_data); + free (ctx->row_pointers); + + // Success + return PNGU_OK; +} + + +int PNGU_EncodeFromYCbYCr (IMGCTX ctx, PNGU_u32 width, PNGU_u32 height, void *buffer, PNGU_u32 stride) +{ + png_uint_32 rowbytes; + PNGU_u32 x, y, buffWidth; + + // Erase from the context any readed info + pngu_free_info (ctx); + ctx->propRead = 0; + + // Check if the user has selected a file to write the image + if (ctx->source == PNGU_SOURCE_BUFFER); + + else if (ctx->source == PNGU_SOURCE_DEVICE) + { + // Open file + if (!(ctx->fd = fopen (ctx->filename, "wb"))) + return PNGU_CANT_OPEN_FILE; + } + + else + return PNGU_NO_FILE_SELECTED; + + // Allocation of libpng structs + ctx->png_ptr = png_create_write_struct (PNG_LIBPNG_VER_STRING, NULL, NULL, NULL); + if (!(ctx->png_ptr)) + { + if (ctx->source == PNGU_SOURCE_DEVICE) + fclose (ctx->fd); + return PNGU_LIB_ERROR; + } + + ctx->info_ptr = png_create_info_struct (ctx->png_ptr); + if (!(ctx->info_ptr)) + { + png_destroy_write_struct (&(ctx->png_ptr), (png_infopp)NULL); + if (ctx->source == PNGU_SOURCE_DEVICE) + fclose (ctx->fd); + return PNGU_LIB_ERROR; + } + + if (ctx->source == PNGU_SOURCE_BUFFER) + { + // Installation of our custom data writer function + ctx->cursor = 0; + png_set_write_fn (ctx->png_ptr, ctx, pngu_write_data_to_buffer, pngu_flush_data_to_buffer); + } + else if (ctx->source == PNGU_SOURCE_DEVICE) + { + // Default data writer uses function fwrite, so it needs to use our FILE* + png_init_io (ctx->png_ptr, ctx->fd); + } + + // Setup output file properties + png_set_IHDR (ctx->png_ptr, ctx->info_ptr, width, height, 8, PNG_COLOR_TYPE_RGB, + PNG_INTERLACE_NONE, PNG_COMPRESSION_TYPE_DEFAULT, PNG_FILTER_TYPE_DEFAULT); + + // Allocate memory to store the image in RGB format + rowbytes = width * 3; + if (rowbytes % 4) + rowbytes = ((rowbytes / 4) + 1) * 4; // Add extra padding so each row starts in a 4 byte boundary + + ctx->img_data = malloc (rowbytes * height); + if (!ctx->img_data) + { + png_destroy_write_struct (&(ctx->png_ptr), (png_infopp)NULL); + if (ctx->source == PNGU_SOURCE_DEVICE) + fclose (ctx->fd); + return PNGU_LIB_ERROR; + } + + ctx->row_pointers = malloc (sizeof (png_bytep) * height); + if (!ctx->row_pointers) + { + png_destroy_write_struct (&(ctx->png_ptr), (png_infopp)NULL); + if (ctx->source == PNGU_SOURCE_DEVICE) + fclose (ctx->fd); + return PNGU_LIB_ERROR; + } + + // Encode YCbYCr image into RGB8 format + buffWidth = (width + stride) / 2; + for (y = 0; y < height; y++) + { + ctx->row_pointers[y] = ctx->img_data + (y * rowbytes); + + for (x = 0; x < (width / 2); x++) + PNGU_YCbYCr_TO_RGB8 ( ((PNGU_u32 *)buffer)[y*buffWidth+x], + ((PNGU_u8 *) ctx->row_pointers[y]+x*6), ((PNGU_u8 *) ctx->row_pointers[y]+x*6+1), + ((PNGU_u8 *) ctx->row_pointers[y]+x*6+2), ((PNGU_u8 *) ctx->row_pointers[y]+x*6+3), + ((PNGU_u8 *) ctx->row_pointers[y]+x*6+4), ((PNGU_u8 *) ctx->row_pointers[y]+x*6+5) ); + } + + // Tell libpng where is our image data + png_set_rows (ctx->png_ptr, ctx->info_ptr, ctx->row_pointers); + + // Write file header and image data + png_write_png (ctx->png_ptr, ctx->info_ptr, PNG_TRANSFORM_IDENTITY, NULL); + + // Tell libpng we have no more data to write + png_write_end (ctx->png_ptr, (png_infop) NULL); + + // Free resources + free (ctx->img_data); + free (ctx->row_pointers); + png_destroy_write_struct (&(ctx->png_ptr), &(ctx->info_ptr)); + if (ctx->source == PNGU_SOURCE_DEVICE) + fclose (ctx->fd); + + // Success + return PNGU_OK; +} + + +// This function is taken from a libogc example +PNGU_u32 PNGU_RGB8_TO_YCbYCr (PNGU_u8 r1, PNGU_u8 g1, PNGU_u8 b1, PNGU_u8 r2, PNGU_u8 g2, PNGU_u8 b2) +{ + int y1, cb1, cr1, y2, cb2, cr2, cb, cr; + + y1 = (299 * r1 + 587 * g1 + 114 * b1) / 1000; + cb1 = (-16874 * r1 - 33126 * g1 + 50000 * b1 + 12800000) / 100000; + cr1 = (50000 * r1 - 41869 * g1 - 8131 * b1 + 12800000) / 100000; + + y2 = (299 * r2 + 587 * g2 + 114 * b2) / 1000; + cb2 = (-16874 * r2 - 33126 * g2 + 50000 * b2 + 12800000) / 100000; + cr2 = (50000 * r2 - 41869 * g2 - 8131 * b2 + 12800000) / 100000; + + cb = (cb1 + cb2) >> 1; + cr = (cr1 + cr2) >> 1; + + return (PNGU_u32) ((y1 << 24) | (cb << 16) | (y2 << 8) | cr); +} + + +void PNGU_YCbYCr_TO_RGB8 (PNGU_u32 ycbycr, PNGU_u8 *r1, PNGU_u8 *g1, PNGU_u8 *b1, PNGU_u8 *r2, PNGU_u8 *g2, PNGU_u8 *b2) +{ + PNGU_u8 *val = (PNGU_u8 *) &ycbycr; + int r, g, b; + + r = 1.371f * (val[3] - 128); + g = - 0.698f * (val[3] - 128) - 0.336f * (val[1] - 128); + b = 1.732f * (val[1] - 128); + + *r1 = pngu_clamp (val[0] + r, 0, 255); + *g1 = pngu_clamp (val[0] + g, 0, 255); + *b1 = pngu_clamp (val[0] + b, 0, 255); + + *r2 = pngu_clamp (val[2] + r, 0, 255); + *g2 = pngu_clamp (val[2] + g, 0, 255); + *b2 = pngu_clamp (val[2] + b, 0, 255); +} + + +int pngu_info (IMGCTX ctx) +{ + png_byte magic[8]; + png_uint_32 width; + png_uint_32 height; + png_color_16p background; + png_bytep trans; + png_color_16p trans_values; + int scale, i; + + // Check if there is a file selected and if it is a valid .png + if (ctx->source == PNGU_SOURCE_BUFFER) + memcpy (magic, ctx->buffer, 8); + + else if (ctx->source == PNGU_SOURCE_DEVICE) + { + // Open file + if (!(ctx->fd = fopen (ctx->filename, "rb"))) + return PNGU_CANT_OPEN_FILE; + + // Load first 8 bytes into magic buffer + if (fread (magic, 1, 8, ctx->fd) != 8) + { + fclose (ctx->fd); + return PNGU_CANT_READ_FILE; + } + } + + else + return PNGU_NO_FILE_SELECTED;; + + if (png_sig_cmp(magic, 0, 8) != 0) + { + if (ctx->source == PNGU_SOURCE_DEVICE) + fclose (ctx->fd); + return PNGU_FILE_IS_NOT_PNG; + } + + // Allocation of libpng structs + ctx->png_ptr = png_create_read_struct (PNG_LIBPNG_VER_STRING, NULL, NULL, NULL); + if (!(ctx->png_ptr)) + { + if (ctx->source == PNGU_SOURCE_DEVICE) + fclose (ctx->fd); + return PNGU_LIB_ERROR; + } + + ctx->info_ptr = png_create_info_struct (ctx->png_ptr); + if (!(ctx->info_ptr)) + { + if (ctx->source == PNGU_SOURCE_DEVICE) + fclose (ctx->fd); + png_destroy_read_struct (&(ctx->png_ptr), (png_infopp)NULL, (png_infopp)NULL); + return PNGU_LIB_ERROR; + } + + if (ctx->source == PNGU_SOURCE_BUFFER) + { + // Installation of our custom data provider function + ctx->cursor = 0; + png_set_read_fn (ctx->png_ptr, ctx, pngu_read_data_from_buffer); + } + else if (ctx->source == PNGU_SOURCE_DEVICE) + { + // Default data provider uses function fread, so it needs to use our FILE* + png_init_io (ctx->png_ptr, ctx->fd); + png_set_sig_bytes (ctx->png_ptr, 8); // We have read 8 bytes already to check PNG authenticity + } + + // Read png header + png_read_info (ctx->png_ptr, ctx->info_ptr); + + // Query image properties if they have not been queried before + if (!ctx->propRead) + { + png_get_IHDR(ctx->png_ptr, ctx->info_ptr, &width, &height, + (int *) &(ctx->prop.imgBitDepth), + (int *) &(ctx->prop.imgColorType), + NULL, NULL, NULL); + + ctx->prop.imgWidth = width; + ctx->prop.imgHeight = height; + switch (ctx->prop.imgColorType) + { + case PNG_COLOR_TYPE_GRAY: + ctx->prop.imgColorType = PNGU_COLOR_TYPE_GRAY; + break; + case PNG_COLOR_TYPE_GRAY_ALPHA: + ctx->prop.imgColorType = PNGU_COLOR_TYPE_GRAY_ALPHA; + break; + case PNG_COLOR_TYPE_PALETTE: + ctx->prop.imgColorType = PNGU_COLOR_TYPE_PALETTE; + break; + case PNG_COLOR_TYPE_RGB: + ctx->prop.imgColorType = PNGU_COLOR_TYPE_RGB; + break; + case PNG_COLOR_TYPE_RGB_ALPHA: + ctx->prop.imgColorType = PNGU_COLOR_TYPE_RGB_ALPHA; + break; + default: + ctx->prop.imgColorType = PNGU_COLOR_TYPE_UNKNOWN; + break; + } + + // Constant used to scale 16 bit values to 8 bit values + scale = 1; + if (ctx->prop.imgBitDepth == 16) + scale = 256; + + // Query background color, if any. + ctx->prop.validBckgrnd = 0; + if (((ctx->prop.imgColorType == PNGU_COLOR_TYPE_RGB) || (ctx->prop.imgColorType == PNGU_COLOR_TYPE_RGB_ALPHA)) && + (png_get_bKGD (ctx->png_ptr, ctx->info_ptr, &background))) + { + ctx->prop.validBckgrnd = 1; + ctx->prop.bckgrnd.r = background->red / scale; + ctx->prop.bckgrnd.g = background->green / scale; + ctx->prop.bckgrnd.b = background->blue / scale; + } + else if (((ctx->prop.imgColorType == PNGU_COLOR_TYPE_GRAY) || (ctx->prop.imgColorType == PNGU_COLOR_TYPE_GRAY_ALPHA)) && + (png_get_bKGD (ctx->png_ptr, ctx->info_ptr, &background))) + { + ctx->prop.validBckgrnd = 1; + ctx->prop.bckgrnd.r = ctx->prop.bckgrnd.g = ctx->prop.bckgrnd.b = background->gray / scale; + } + + // Query list of transparent colors, if any. + ctx->prop.numTrans = 0; + ctx->prop.trans = NULL; + if (((ctx->prop.imgColorType == PNGU_COLOR_TYPE_RGB) || (ctx->prop.imgColorType == PNGU_COLOR_TYPE_RGB_ALPHA)) && + (png_get_tRNS (ctx->png_ptr, ctx->info_ptr, &trans, (int *) &(ctx->prop.numTrans), &trans_values))) + { + if (ctx->prop.numTrans) + { + ctx->prop.trans = malloc (sizeof (PNGUCOLOR) * ctx->prop.numTrans); + if (ctx->prop.trans) + for (i = 0; i < ctx->prop.numTrans; i++) + { + ctx->prop.trans[i].r = trans_values[i].red / scale; + ctx->prop.trans[i].g = trans_values[i].green / scale; + ctx->prop.trans[i].b = trans_values[i].blue / scale; + } + else + ctx->prop.numTrans = 0; + } + } + else if (((ctx->prop.imgColorType == PNGU_COLOR_TYPE_GRAY) || (ctx->prop.imgColorType == PNGU_COLOR_TYPE_GRAY_ALPHA)) && + (png_get_tRNS (ctx->png_ptr, ctx->info_ptr, &trans, (int *) &(ctx->prop.numTrans), &trans_values))) + { + if (ctx->prop.numTrans) + { + ctx->prop.trans = malloc (sizeof (PNGUCOLOR) * ctx->prop.numTrans); + if (ctx->prop.trans) + for (i = 0; i < ctx->prop.numTrans; i++) + ctx->prop.trans[i].r = ctx->prop.trans[i].g = ctx->prop.trans[i].b = + trans_values[i].gray / scale; + else + ctx->prop.numTrans = 0; + } + } + + ctx->propRead = 1; + } + + // Success + ctx->infoRead = 1; + + return PNGU_OK; +} + + +int pngu_decode (IMGCTX ctx, PNGU_u32 width, PNGU_u32 height, PNGU_u32 stripAlpha) +{ + png_uint_32 rowbytes; + int i; + + // Read info if it hasn't been read before + if (!ctx->infoRead) + { + i = pngu_info (ctx); + if (i != PNGU_OK) + return i; + } + + // Check if the user has specified the real width and height of the image + if ( (ctx->prop.imgWidth != width) || (ctx->prop.imgHeight != height) ) + return PNGU_INVALID_WIDTH_OR_HEIGHT; + + // Check if color type is supported by PNGU + if ( (ctx->prop.imgColorType == PNGU_COLOR_TYPE_PALETTE) || (ctx->prop.imgColorType == PNGU_COLOR_TYPE_UNKNOWN) ) + return PNGU_UNSUPPORTED_COLOR_TYPE; + + // Scale 16 bit samples to 8 bit + if (ctx->prop.imgBitDepth == 16) + png_set_strip_16 (ctx->png_ptr); + + // Remove alpha channel if we don't need it + if (stripAlpha && ((ctx->prop.imgColorType == PNGU_COLOR_TYPE_RGB_ALPHA) || (ctx->prop.imgColorType == PNGU_COLOR_TYPE_GRAY_ALPHA))) + png_set_strip_alpha (ctx->png_ptr); + + // Expand 1, 2 and 4 bit samples to 8 bit + if (ctx->prop.imgBitDepth < 8) + png_set_packing (ctx->png_ptr); + + // Transform grayscale images to RGB + if ( (ctx->prop.imgColorType == PNGU_COLOR_TYPE_GRAY) || (ctx->prop.imgColorType == PNGU_COLOR_TYPE_GRAY_ALPHA) ) + png_set_gray_to_rgb (ctx->png_ptr); + + // Flush transformations + png_read_update_info (ctx->png_ptr, ctx->info_ptr); + + // Allocate memory to store the image + rowbytes = png_get_rowbytes (ctx->png_ptr, ctx->info_ptr); + if (rowbytes % 4) + rowbytes = ((rowbytes / 4) + 1) * 4; // Add extra padding so each row starts in a 4 byte boundary + + ctx->img_data = malloc (rowbytes * ctx->prop.imgHeight); + if (!ctx->img_data) + { + pngu_free_info (ctx); + return PNGU_LIB_ERROR; + } + + ctx->row_pointers = malloc (sizeof (png_bytep) * ctx->prop.imgHeight); + if (!ctx->row_pointers) + { + free (ctx->img_data); + pngu_free_info (ctx); + return PNGU_LIB_ERROR; + } + + for (i = 0; i < ctx->prop.imgHeight; i++) + ctx->row_pointers[i] = ctx->img_data + (i * rowbytes); + + // Transform the image and copy it to our allocated memory + png_read_image (ctx->png_ptr, ctx->row_pointers); + + // Free resources + pngu_free_info (ctx); + + // Success + return PNGU_OK; +} + + +void pngu_free_info (IMGCTX ctx) +{ + if (ctx->infoRead) + { + if (ctx->source == PNGU_SOURCE_DEVICE) + fclose (ctx->fd); + + png_destroy_read_struct (&(ctx->png_ptr), &(ctx->info_ptr), (png_infopp)NULL); + + ctx->infoRead = 0; + } +} + + +// Custom data provider function used for reading from memory buffers. +void pngu_read_data_from_buffer (png_structp png_ptr, png_bytep data, png_size_t length) +{ + IMGCTX ctx = (IMGCTX) png_get_io_ptr (png_ptr); + memcpy (data, ctx->buffer + ctx->cursor, length); + ctx->cursor += length; +} + + +// Custom data writer function used for writing to memory buffers. +void pngu_write_data_to_buffer (png_structp png_ptr, png_bytep data, png_size_t length) +{ + IMGCTX ctx = (IMGCTX) png_get_io_ptr (png_ptr); + memcpy (ctx->buffer + ctx->cursor, data, length); + ctx->cursor += length; +} + + +// Custom data flusher function used for writing to memory buffers. +void pngu_flush_data_to_buffer (png_structp png_ptr) +{ + // Nothing to do here +} + + +// Function used in YCbYCr to RGB decoding +int pngu_clamp (int value, int min, int max) +{ + if (value < min) + value = min; + else if (value > max) + value = max; + + return value; +} + diff --git a/source/libpng/pngu/pngu.h b/source/libpng/pngu/pngu.h new file mode 100644 index 0000000..12c3ad4 --- /dev/null +++ b/source/libpng/pngu/pngu.h @@ -0,0 +1,171 @@ +/******************************************************************************************** + +PNGU Version : 0.2a + +Coder : frontier + +More info : http://frontier-dev.net + +********************************************************************************************/ +#ifndef __PNGU__ +#define __PNGU__ + +// Return codes +#define PNGU_OK 0 +#define PNGU_ODD_WIDTH 1 +#define PNGU_ODD_STRIDE 2 +#define PNGU_INVALID_WIDTH_OR_HEIGHT 3 +#define PNGU_FILE_IS_NOT_PNG 4 +#define PNGU_UNSUPPORTED_COLOR_TYPE 5 +#define PNGU_NO_FILE_SELECTED 6 +#define PNGU_CANT_OPEN_FILE 7 +#define PNGU_CANT_READ_FILE 8 +#define PNGU_LIB_ERROR 9 + +// Color types +#define PNGU_COLOR_TYPE_GRAY 1 +#define PNGU_COLOR_TYPE_GRAY_ALPHA 2 +#define PNGU_COLOR_TYPE_PALETTE 3 +#define PNGU_COLOR_TYPE_RGB 4 +#define PNGU_COLOR_TYPE_RGB_ALPHA 5 +#define PNGU_COLOR_TYPE_UNKNOWN 6 + + +#ifdef __cplusplus + extern "C" { +#endif + +// Types +typedef unsigned char PNGU_u8; +typedef unsigned short PNGU_u16; +typedef unsigned int PNGU_u32; +typedef unsigned long long PNGU_u64; + +typedef struct +{ + PNGU_u8 r; + PNGU_u8 g; + PNGU_u8 b; +} PNGUCOLOR; + +typedef struct +{ + PNGU_u32 imgWidth; // In pixels + PNGU_u32 imgHeight; // In pixels + PNGU_u32 imgBitDepth; // In bitx + PNGU_u32 imgColorType; // PNGU_COLOR_TYPE_* + PNGU_u32 validBckgrnd; // Non zero if there is a background color + PNGUCOLOR bckgrnd; // Backgroun color + PNGU_u32 numTrans; // Number of transparent colors + PNGUCOLOR *trans; // Transparent colors +} PNGUPROP; + +// Image context, always initialize with SelectImageFrom* and free with ReleaseImageContext +struct _IMGCTX; +typedef struct _IMGCTX *IMGCTX; + + +/**************************************************************************** +* Pixel conversion * +****************************************************************************/ + +// Macro to convert RGB8 values to RGB565 +#define PNGU_RGB8_TO_RGB565(r,g,b) ( ((((PNGU_u16) r) & 0xF8U) << 8) | ((((PNGU_u16) g) & 0xFCU) << 3) | (((PNGU_u16) b) >> 3) ) + +// Macro to convert RGBA8 values to RGB5A3 +#define PNGU_RGB8_TO_RGB5A3(r,g,b,a) (PNGU_u16) (((a & 0xE0U) == 0xE0U) ? \ + (0x8000U | ((((PNGU_u16) r) & 0xF8U) << 7) | ((((PNGU_u16) g) & 0xF8U) << 2) | (((PNGU_u16) b) >> 3)) : \ + (((((PNGU_u16) a) & 0xE0U) << 7) | ((((PNGU_u16) r) & 0xF0U) << 4) | (((PNGU_u16) g) & 0xF0U) | ((((PNGU_u16) b) & 0xF0U) >> 4))) + +// Function to convert two RGB8 values to YCbYCr +PNGU_u32 PNGU_RGB8_TO_YCbYCr (PNGU_u8 r1, PNGU_u8 g1, PNGU_u8 b1, PNGU_u8 r2, PNGU_u8 g2, PNGU_u8 b2); + +// Function to convert an YCbYCr to two RGB8 values. +void PNGU_YCbYCr_TO_RGB8 (PNGU_u32 ycbycr, PNGU_u8 *r1, PNGU_u8 *g1, PNGU_u8 *b1, PNGU_u8 *r2, PNGU_u8 *g2, PNGU_u8 *b2); + + +/**************************************************************************** +* Image context handling * +****************************************************************************/ + +// Selects a PNG file, previosly loaded into a buffer, and creates an image context for subsequent procesing. +IMGCTX PNGU_SelectImageFromBuffer (const void *buffer); + +// Selects a PNG file, from any devoptab device, and creates an image context for subsequent procesing. +IMGCTX PNGU_SelectImageFromDevice (const char *filename); + +// Frees resources associated with an image context. Always call this function when you no longer need the IMGCTX. +void PNGU_ReleaseImageContext (IMGCTX ctx); + + +/**************************************************************************** +* Miscelaneous * +****************************************************************************/ + +// Retrieves info from selected PNG file, including image dimensions, color format, background and transparency colors. +int PNGU_GetImageProperties (IMGCTX ctx, PNGUPROP *fileproperties); + + +/**************************************************************************** +* Image conversion * +****************************************************************************/ + +// Expands selected image into an YCbYCr buffer. You need to specify context, image dimensions, +// destination address and stride in pixels (stride = buffer width - image width). +int PNGU_DecodeToYCbYCr (IMGCTX ctx, PNGU_u32 width, PNGU_u32 height, void *buffer, PNGU_u32 stride); + +// Macro for decoding an image inside a buffer at given coordinates. +#define PNGU_DECODE_TO_COORDS_YCbYCr(ctx,coordX,coordY,imgWidth,imgHeight,bufferWidth,bufferHeight,buffer) \ + \ + PNGU_DecodeToYCbYCr (ctx, imgWidth, imgHeight, ((void *) buffer) + (coordY) * (bufferWidth) * 2 + \ + (coordX) * 2, (bufferWidth) - (imgWidth)) + +// Expands selected image into a linear RGB565 buffer. You need to specify context, image dimensions, +// destination address and stride in pixels (stride = buffer width - image width). +int PNGU_DecodeToRGB565 (IMGCTX ctx, PNGU_u32 width, PNGU_u32 height, void *buffer, PNGU_u32 stride); + +// Macro for decoding an image inside a buffer at given coordinates. +#define PNGU_DECODE_TO_COORDS_RGB565(ctx,coordX,coordY,imgWidth,imgHeight,bufferWidth,bufferHeight,buffer) \ + \ + PNGU_DecodeToRGB565 (ctx, imgWidth, imgHeight, ((void *) buffer) + (coordY) * (bufferWidth) * 2 + \ + (coordX) * 2, (bufferWidth) - (imgWidth)) + +// Expands selected image into a linear RGBA8 buffer. You need to specify context, image dimensions, +// destination address, stride in pixels and default alpha value, which is used if the source image +// doesn't have an alpha channel. +int PNGU_DecodeToRGBA8 (IMGCTX ctx, PNGU_u32 width, PNGU_u32 height, void *buffer, PNGU_u32 stride, PNGU_u8 default_alpha); + +// Macro for decoding an image inside a buffer at given coordinates. +#define PNGU_DECODE_TO_COORDS_RGBA8(ctx,coordX,coordY,imgWidth,imgHeight,default_alpha,bufferWidth,bufferHeight,buffer) \ + \ + PNGU_DecodeToRGBA8 (ctx, imgWidth, imgHeight, ((void *) buffer) + (coordY) * (bufferWidth) * 2 + \ + (coordX) * 2, (bufferWidth) - (imgWidth), default_alpha) + +// Expands selected image into a 4x4 tiled RGB565 buffer. You need to specify context, image dimensions +// and destination address. +int PNGU_DecodeTo4x4RGB565 (IMGCTX ctx, PNGU_u32 width, PNGU_u32 height, void *buffer); + +// Expands selected image into a 4x4 tiled RGB5A3 buffer. You need to specify context, image dimensions, +// destination address and default alpha value, which is used if the source image doesn't have an alpha channel. +int PNGU_DecodeTo4x4RGB5A3 (IMGCTX ctx, PNGU_u32 width, PNGU_u32 height, void *buffer, PNGU_u8 default_alpha); + +// Expands selected image into a 4x4 tiled RGBA8 buffer. You need to specify context, image dimensions, +// destination address and default alpha value, which is used if the source image doesn't have an alpha channel. +int PNGU_DecodeTo4x4RGBA8 (IMGCTX ctx, PNGU_u32 width, PNGU_u32 height, void *buffer, PNGU_u8 default_alpha); + +// Encodes an YCbYCr image in PNG format and stores it in the selected device or memory buffer. You need to +// specify context, image dimensions, destination address and stride in pixels (stride = buffer width - image width). +int PNGU_EncodeFromYCbYCr (IMGCTX ctx, PNGU_u32 width, PNGU_u32 height, void *buffer, PNGU_u32 stride); + +// Macro for encoding an image stored into an YCbYCr buffer at given coordinates. +#define PNGU_ENCODE_TO_COORDS_YCbYCr(ctx,coordX,coordY,imgWidth,imgHeight,bufferWidth,bufferHeight,buffer) \ + \ + PNGU_EncodeFromYCbYCr (ctx, imgWidth, imgHeight, ((void *) buffer) + (coordY) * (bufferWidth) * 2 + \ + (coordX) * 2, (bufferWidth) - (imgWidth)) + +#ifdef __cplusplus + } +#endif + +#endif + diff --git a/source/menu.c b/source/menu.c new file mode 100644 index 0000000..8eee9dc --- /dev/null +++ b/source/menu.c @@ -0,0 +1,1382 @@ +#include +#include +#include +#include +#include +#include +#include +#include + +#include "sys.h" +#include "fat.h" +#include "nand.h" +#include "restart.h" +#include "title.h" +#include "usbstorage.h" +#include "utils.h" +#include "video.h" +#include "wad.h" +#include "wpad.h" +#include +#include "globals.h" +#include "iospatch.h" + +/* FAT device list */ +//static fatDevice fdevList[] = { +fatDevice fdevList[] = { + { "sd", "Wii SD Slot", &__io_wiisd }, + { "usb", "USB Mass Storage Device", &__io_usbstorage }, + { "usb2", "USB 2.0 Mass Storage Device", &__io_wiiums }, + { "gcsda", "SD Gecko (Slot A)", &__io_gcsda }, + { "gcsdb", "SD Gecko (Slot B)", &__io_gcsdb }, + //{ "smb", "SMB share", NULL }, +}; + +/* NAND device list */ +//static nandDevice ndevList[] = { +nandDevice ndevList[] = { + { "Disable", 0, 0x00, 0x00 }, + { "SD/SDHC Card", 1, 0xF0, 0xF1 }, + { "USB 2.0 Mass Storage Device", 2, 0xF2, 0xF3 }, +}; + +/* FAT device */ +static fatDevice *fdev = NULL; +static nandDevice *ndev = NULL; + +// wiiNinja: Define a buffer holding the previous path names as user +// traverses the directory tree. Max of 10 levels is define at this point +static u8 gDirLevel = 0; +static char gDirList [MAX_DIR_LEVELS][MAX_FILE_PATH_LEN]; +static s32 gSeleted[MAX_DIR_LEVELS]; +static s32 gStart[MAX_DIR_LEVELS]; + +/* Macros */ +#define NB_FAT_DEVICES (sizeof(fdevList) / sizeof(fatDevice)) +#define NB_NAND_DEVICES (sizeof(ndevList) / sizeof(nandDevice)) + +// Local prototypes: wiiNinja +void WaitPrompt (char *prompt); +int PushCurrentDir(char *dirStr, int Selected, int Start); +char *PopCurrentDir(int *Selected, int *Start); +bool IsListFull (void); +char *PeekCurrentDir (void); +u32 WaitButtons(void); +u32 Pad_GetButtons(void); +void WiiLightControl (int state); + +int __Menu_IsGreater(const void *p1, const void *p2) +{ + u32 n1 = *(u32 *)p1; + u32 n2 = *(u32 *)p2; + + /* Equal */ + if (n1 == n2) + return 0; + + return (n1 > n2) ? 1 : -1; +} + + +int __Menu_EntryCmp(const void *p1, const void *p2) +{ + fatFile *f1 = (fatFile *)p1; + fatFile *f2 = (fatFile *)p2; + + /* Compare entries */ // wiiNinja: Include directory + if ((f1->isdir) && !(f2->isdir)) + return (-1); + else if (!(f1->isdir) && (f2->isdir)) + return (1); + else + return strcasecmp(f1->filename, f2->filename); +} + +static bool __FolderExists(const char *path) +{ + DIR *dir; + dir = opendir(path); + if(dir) + { + closedir(dir); + return true; + } + return false; +} + +static size_t __GetFileSizeBytes(const char *path) +{ + FILE *f; + size_t size = 0; + + f = fopen(path, "rb"); + if(!f) return 0; + + //Get file size + fseek(f, 0, SEEK_END); + size = ftell(f); + fclose(f); + + return size; +} + +char gFileName[MAX_FILE_PATH_LEN]; +s32 __Menu_RetrieveList(char *inPath, fatFile **outbuf, u32 *outlen) +{ + fatFile *buffer = NULL; + DIR *dir = NULL; + struct dirent *ent = NULL; + + //char dirpath[256], filename[768]; + u32 cnt; + + /* Generate dirpath */ + //sprintf(dirpath, "%s:" WAD_DIRECTORY, fdev->mount); + + /* Open directory */ + dir = opendir(inPath); + if (!dir) + return -1; + + /* Count entries */ + for (cnt = 0; ((ent = readdir(dir)) != NULL);) { + cnt++; + } + + if (cnt > 0) { + /* Allocate memory */ + buffer = malloc(sizeof(fatFile) * cnt); + if (!buffer) { + closedir(dir); + return -2; + } + + /* Reset directory */ + rewinddir(dir); + + /* Get entries */ + for (cnt = 0; ((ent = readdir(dir)) != NULL);) + { + bool addFlag = false; + bool isdir = false; + size_t fsize = 0; + + snprintf(gFileName, MAX_FILE_PATH_LEN, "%s/%s", inPath, ent->d_name); + if (__FolderExists(gFileName)) // wiiNinja + { + isdir = true; + // Add only the item ".." which is the previous directory + // AND if we're not at the root directory + if ((strcmp (ent->d_name, "..") == 0) && (gDirLevel > 1)) + addFlag = true; + else if (strcmp (ent->d_name, ".") != 0) + addFlag = true; + } + else + { + if(strlen(ent->d_name)>4) + { + if(!strcasecmp(ent->d_name+strlen(ent->d_name)-4, ".wad")) + { + fsize = __GetFileSizeBytes(gFileName); + addFlag = true; + } + } + } + + if (addFlag == true) + { + fatFile *file = &buffer[cnt++]; + + /* File name */ + strcpy(file->filename, ent->d_name); + + /* File stats */ + file->isdir = isdir; + file->fsize = fsize; + + } + } + + /* Sort list */ + qsort(buffer, cnt, sizeof(fatFile), __Menu_EntryCmp); + } + + /* Close directory */ + closedir(dir); + + /* Set values */ + *outbuf = buffer; + *outlen = cnt; + + return 0; +} + + +void Menu_SelectIOS(void) +{ + u8 *iosVersion = NULL; + u32 iosCnt; + u8 tmpVersion; + + u32 cnt; + s32 ret, selected = 0; + bool found = false; + + /* Get IOS versions */ + ret = Title_GetIOSVersions(&iosVersion, &iosCnt); + if (ret < 0) + return; + + /* Sort list */ + qsort(iosVersion, iosCnt, sizeof(u8), __Menu_IsGreater); + + if (gConfig.cIOSVersion < 0) + tmpVersion = CIOS_VERSION; + else + { + tmpVersion = (u8)gConfig.cIOSVersion; + // For debugging only + //printf ("User pre-selected cIOS: %i\n", tmpVersion); + //WaitButtons(); + } + + /* Set default version */ + for (cnt = 0; cnt < iosCnt; cnt++) { + u8 version = iosVersion[cnt]; + + /* Custom IOS available */ + //if (version == CIOS_VERSION) + if (version == tmpVersion) + { + selected = cnt; + found = true; + break; + } + + /* Current IOS */ + if (version == IOS_GetVersion()) + selected = cnt; + } + + /* Ask user for IOS version */ + if ((gConfig.cIOSVersion < 0) || (found == false)) + { + for (;;) + { + /* Clear console */ + Con_Clear(); + + printf("\t>> Select IOS version to use: < IOS%d >\n\n", iosVersion[selected]); + + printf("\t Press LEFT/RIGHT to change IOS version.\n\n"); + + printf("\t Press A button to continue.\n"); + printf("\t Press HOME button to restart.\n\n"); + + u32 buttons = WaitButtons(); + + /* LEFT/RIGHT buttons */ + if (buttons & WPAD_BUTTON_LEFT) { + if ((--selected) <= -1) + selected = (iosCnt - 1); + } + if (buttons & WPAD_BUTTON_RIGHT) { + if ((++selected) >= iosCnt) + selected = 0; + } + + /* HOME button */ + if (buttons & WPAD_BUTTON_HOME) + Restart(); + + /* A button */ + if (buttons & WPAD_BUTTON_A) + break; + } + } + + + u8 version = iosVersion[selected]; + + if (IOS_GetVersion() != version) { + /* Shutdown subsystems */ + Wpad_Disconnect(); + //mload_close(); + + /* Load IOS */ + + if(!loadIOS(version)) Wpad_Init(), Menu_SelectIOS(); + + /* Initialize subsystems */ + Wpad_Init(); + } +} + +void Menu_FatDevice(void) +{ + int ret, selected = 0; + + /* Unmount FAT device */ + //if (fdev) + //Fat_Unmount(fdev); + //if (((fdevList[selected].mount[0] == 's') && (ndev->name[0] == 'S'))) + //selected++; + + /* Select source device */ + if (gConfig.fatDeviceIndex < 0) + { + for (;;) { + /* Clear console */ + Con_Clear(); + + /* Selected device */ + fdev = &fdevList[selected]; + + printf("\t>> Select source device: < %s >\n\n", fdev->name); + + printf("\t Press LEFT/RIGHT to change the selected device.\n\n"); + + printf("\t Press A button to continue.\n"); + printf("\t Press HOME button to restart.\n\n"); + + u32 buttons = WaitButtons(); + + /* LEFT/RIGHT buttons */ + if (buttons & WPAD_BUTTON_LEFT) { + if ((--selected) <= -1) + selected = (NB_FAT_DEVICES - 1); + if ((fdevList[selected].mount[0] == 's') && (ndev->name[0] == 'S')) + selected--; + if ((fdevList[selected].mount[0] == 'u' && fdevList[selected].mount[3] == '2') && (ndev->name[0] == 'U')) + selected--; + if ((selected) <= -1) + selected = (NB_FAT_DEVICES - 1); + } + if (buttons & WPAD_BUTTON_RIGHT) { + if ((++selected) >= NB_FAT_DEVICES) + selected = 0; + if ((fdevList[selected].mount[0] == 's') && (ndev->name[0] == 'S')) + selected++; + if ((fdevList[selected].mount[0] == 'u' && fdevList[selected].mount[3] == '2') && (ndev->name[0] == 'U')) + selected++; + } + + /* HOME button */ + if (buttons & WPAD_BUTTON_HOME) + Restart(); + + /* A button */ + if (buttons & WPAD_BUTTON_A) + break; + } + } + else + { + sleep(5); + fdev = &fdevList[gConfig.fatDeviceIndex]; + } + + printf("[+] Mounting %s, please wait...", fdev->name ); + fflush(stdout); + + /* Mount FAT device */ + + ret = Fat_Mount(fdev); + if (ret < 0) { + printf(" ERROR! (ret = %d)\n", ret); + goto err; + } else + printf(" OK!\n"); + + return; + +err: + + if(gConfig.fatDeviceIndex >= 0) gConfig.fatDeviceIndex = -1; + WiiLightControl (WII_LIGHT_OFF); + printf("\n"); + printf(" Press any button to continue...\n"); + + WaitButtons(); + + /* Prompt menu again */ + Menu_FatDevice(); +} + +void Menu_NandDevice(void) +{ + int ret, selected = 0; + + /* Disable NAND emulator */ + if (ndev) { + Nand_Unmount(ndev); + Nand_Disable(); + } + + /* Select source device */ + if (gConfig.nandDeviceIndex < 0) + { + for (;;) { + /* Clear console */ + Con_Clear(); + + /* Selected device */ + ndev = &ndevList[selected]; + + printf("\t>> Select NAND emulator device: < %s >\n\n", ndev->name); + + printf("\t Press LEFT/RIGHT to change the selected device.\n\n"); + + printf("\t Press A button to continue.\n"); + printf("\t Press HOME button to restart.\n\n"); + + u32 buttons = WaitButtons(); + + /* LEFT/RIGHT buttons */ + if (buttons & WPAD_BUTTON_LEFT) { + if ((--selected) <= -1) + selected = (NB_NAND_DEVICES - 1); + } + if (buttons & WPAD_BUTTON_RIGHT) { + if ((++selected) >= NB_NAND_DEVICES) + selected = 0; + } + + /* HOME button */ + if (buttons & WPAD_BUTTON_HOME) + Restart(); + + /* A button */ + if (buttons & WPAD_BUTTON_A) + break; + } + } + else + { + ndev = &ndevList[gConfig.nandDeviceIndex]; + } + + /* No NAND device */ + if (!ndev->mode) + return; + + printf("[+] Enabling NAND emulator..."); + fflush(stdout); + + /* Mount NAND device */ + ret = Nand_Mount(ndev); + if (ret < 0) { + printf(" ERROR! (ret = %d)\n", ret); + goto err; + } + + /* Enable NAND emulator */ + ret = Nand_Enable(ndev); + if (ret < 0) { + printf(" ERROR! (ret = %d)\n", ret); + goto err; + } else + printf(" OK!\n"); + + return; + +err: + printf("\n"); + printf(" Press any button to continue...\n"); + + WaitButtons(); + + /* Prompt menu again */ + Menu_NandDevice(); +} + +char gTmpFilePath[MAX_FILE_PATH_LEN]; +/* Install and/or Uninstall multiple WADs - Leathl */ +int Menu_BatchProcessWads(fatFile *files, int fileCount, char *inFilePath, int installCnt, int uninstallCnt) +{ + int count; + + for (;;) + { + Con_Clear(); + + if ((installCnt > 0) & (uninstallCnt == 0)) { + printf("[+] %d file%s marked for installation.\n", installCnt, (installCnt == 1) ? "" : "s"); + printf(" Do you want to proceed?\n"); + } + else if ((installCnt == 0) & (uninstallCnt > 0)) { + printf("[+] %d file%s marked for uninstallation.\n", uninstallCnt, (uninstallCnt == 1) ? "" : "s"); + printf(" Do you want to proceed?\n"); + } + else { + printf("[+] %d file%s marked for installation and %d file%s for uninstallation.\n", installCnt, (installCnt == 1) ? "" : "s", uninstallCnt, (uninstallCnt == 1) ? "" : "s"); + printf(" Do you want to proceed?\n"); + } + + printf("\n\n Press A to continue.\n"); + printf(" Press B to go back to the menu.\n\n"); + + u32 buttons = WaitButtons(); + + if (buttons & WPAD_BUTTON_A) + break; + + if (buttons & WPAD_BUTTON_B) + return 0; + } + + WiiLightControl (WII_LIGHT_ON); + int errors = 0; + int success = 0; + s32 ret; + + for (count = 0; count < fileCount; count++) + { + fatFile *thisFile = &files[count]; + + if ((thisFile->install == 1) | (thisFile->install == 2)) { + int mode = thisFile->install; + Con_Clear(); + printf("[+] Opening \"%s\", please wait...\n\n", thisFile->filename); + + sprintf(gTmpFilePath, "%s/%s", inFilePath, thisFile->filename); + + FILE *fp = fopen(gTmpFilePath, "rb"); + if (!fp) { + printf(" ERROR!\n"); + errors += 1; + continue; + } + + printf("[+] %s WAD, please wait...\n", (mode == 2) ? "Uninstalling" : "Installing"); + if (mode == 2) { + ret = Wad_Uninstall(fp); + } + else { + ret = Wad_Install(fp); + } + + if (ret < 0) errors += 1; + else success += 1; + + thisFile->installstate = ret; + + if (fp) + fclose(fp); + } + } + + WiiLightControl (WII_LIGHT_OFF); + + printf("\n"); + printf(" %d titles succeeded and %d failed...\n", success, errors); + + if (errors > 0) + { + printf("\n Some operations failed"); + printf("\n Press A to list.\n"); + printf(" Press B skip.\n"); + + u32 buttons = WaitButtons(); + + if ((buttons & WPAD_BUTTON_A)) + { + Con_Clear(); + + int i=0; + for (count = 0; count < fileCount; count++) + { + fatFile *thisFile = &files[count]; + + if (thisFile->installstate <0) + { + char str[41]; + strncpy(str, thisFile->filename, 40); //Only 40 chars to fit the screen + str[40]=0; + i++; + if(thisFile->installstate == -999) printf(" %s BRICK BLOCKED\n", str); + else if(thisFile->installstate == -998) printf(" %s Skipped\n", str); + else if(thisFile->installstate == -106) printf(" %s Not installed?\n", str); + else if(thisFile->installstate == -1036 ) printf(" %s Needed IOS missing\n", str); + else if(thisFile->installstate == -4100 ) printf(" %s No trucha bug?\n", str); + else printf(" %s error %d\n", str, thisFile->installstate); + if( i == 17 ) + { + printf("\n Press any button to continue\n"); + WaitButtons(); + i = 0; + } + } + } + } + } + printf("\n Press any button to continue...\n"); + WaitButtons(); + + return 1; +} + +/* File Operations - Leathl */ +int Menu_FileOperations(fatFile *file, char *inFilePath) +{ + f32 filesize = (file->fsize / MB_SIZE); + + for (;;) + { + Con_Clear(); + + printf("[+] WAD Filename : %s\n", file->filename); + printf(" WAD Filesize : %.2f MB\n\n\n", filesize); + + + printf("[+] Select action: < %s WAD >\n\n", "Delete"); //There's yet nothing else than delete + + printf(" Press LEFT/RIGHT to change selected action.\n\n"); + + printf(" Press A to continue.\n"); + printf(" Press B to go back to the menu.\n\n"); + + u32 buttons = WaitButtons(); + + /* A button */ + if (buttons & WPAD_BUTTON_A) + break; + + /* B button */ + if (buttons & WPAD_BUTTON_B) + return 0; + } + + Con_Clear(); + + printf("[+] Deleting \"%s\", please wait...\n", file->filename); + + sprintf(gTmpFilePath, "%s/%s", inFilePath, file->filename); + int error = remove(gTmpFilePath); + if (error != 0) + printf(" ERROR!"); + else + printf(" Successfully deleted!"); + + printf("\n"); + printf(" Press any button to continue...\n"); + + WaitButtons(); + + return !error; +} + +void Menu_WadManage(fatFile *file, char *inFilePath) +{ + FILE *fp = NULL; + + //char filepath[128]; + f32 filesize; + + u32 mode = 0; + + /* File size in megabytes */ + filesize = (file->fsize / MB_SIZE); + + for (;;) { + /* Clear console */ + Con_Clear(); + + printf("[+] WAD Filename : %s\n", file->filename); + printf(" WAD Filesize : %.2f MB\n\n\n", filesize); + + + printf("[+] Select action: < %s WAD >\n\n", (!mode) ? "Install" : "Uninstall"); + + printf(" Press LEFT/RIGHT to change selected action.\n\n"); + + printf(" Press A to continue.\n"); + printf(" Press B to go back to the menu.\n\n"); + + u32 buttons = WaitButtons(); + + /* LEFT/RIGHT buttons */ + if (buttons & (WPAD_BUTTON_LEFT | WPAD_BUTTON_RIGHT)) + mode ^= 1; + + /* A button */ + if (buttons & WPAD_BUTTON_A) + break; + + /* B button */ + if (buttons & WPAD_BUTTON_B) + return; + } + + /* Clear console */ + Con_Clear(); + + printf("[+] Opening \"%s\", please wait...", file->filename); + fflush(stdout); + + /* Generate filepath */ + // sprintf(filepath, "%s:" WAD_DIRECTORY "/%s", fdev->mount, file->filename); + sprintf(gTmpFilePath, "%s/%s", inFilePath, file->filename); // wiiNinja + + /* Open WAD */ + fp = fopen(gTmpFilePath, "rb"); + if (!fp) { + printf(" ERROR!\n"); + goto out; + } else + printf(" OK!\n\n"); + + printf("[+] %s WAD, please wait...\n", (!mode) ? "Installing" : "Uninstalling"); + + /* Do install/uninstall */ + WiiLightControl (WII_LIGHT_ON); + if (!mode) + Wad_Install(fp); + else + Wad_Uninstall(fp); + WiiLightControl (WII_LIGHT_OFF); + +out: + /* Close file */ + if (fp) + fclose(fp); + + printf("\n"); + printf(" Press any button to continue...\n"); + + /* Wait for button */ + WaitButtons(); +} + +void Menu_WadList(void) +{ + char str [100]; + fatFile *fileList = NULL; + u32 fileCnt; + int ret, selected = 0, start = 0; + char *tmpPath = malloc (MAX_FILE_PATH_LEN); + int installCnt = 0; + int uninstallCnt = 0; + + //fatFile *installFiles = malloc(sizeof(fatFile) * 50); + //int installCount = 0; + + // wiiNinja: check for malloc error + if (tmpPath == NULL) + { + ret = -997; // What am I gonna use here? + printf(" ERROR! Out of memory (ret = %d)\n", ret); + return; + } + + printf("[+] Retrieving file list..."); + fflush(stdout); + + gDirLevel = 0; + + // push root dir as base folder + sprintf(tmpPath, "%s:%s", fdev->mount, WAD_DIRECTORY); + PushCurrentDir(tmpPath,0,0); + // if user provides startup directory, try it out first + if (strcmp (WAD_DIRECTORY, gConfig.startupPath) != 0) + { + // replace root dir with provided startup directory + sprintf(tmpPath, "%s:%s", fdev->mount, gConfig.startupPath); + // If the directory can be successfully opened, it must exists + DIR *tmpDirPtr = opendir(tmpPath); + if (tmpDirPtr) + { + closedir (tmpDirPtr); + PushCurrentDir(tmpPath,0,0); + } + else // unable to open provided dir, stick with root dir + sprintf(tmpPath, "%s:%s", fdev->mount, WAD_DIRECTORY); + } + + /* Retrieve filelist */ +getList: + if (fileList) + { + free (fileList); + fileList = NULL; + } + + ret = __Menu_RetrieveList(tmpPath, &fileList, &fileCnt); + if (ret < 0) { + printf(" ERROR! (ret = %d)\n", ret); + goto err; + } + + /* No files */ + if (!fileCnt) { + printf(" No files found!\n"); + goto err; + } + + /* Set install-values to 0 - Leathl */ + int counter; + for (counter = 0; counter < fileCnt; counter++) { + fatFile *file = &fileList[counter]; + file->install = 0; + } + + for (;;) + { + u32 cnt; + s32 index; + + /* Clear console */ + Con_Clear(); + + /** Print entries **/ + cnt = strlen(tmpPath); + if(cnt>30) + index = cnt-30; + else + index = 0; + + printf("[+] WAD files on [%s]:\n\n", tmpPath+index); + + /* Print entries */ + for (cnt = start; cnt < fileCnt; cnt++) + { + fatFile *file = &fileList[cnt]; + f32 filesize = file->fsize / MB_SIZE; + + /* Entries per page limit */ + if ((cnt - start) >= ENTRIES_PER_PAGE) + break; + + strncpy(str, file->filename, 40); //Only 40 chars to fit the screen + str[40]=0; + + /* Print filename */ + //printf("\t%2s %s (%.2f MB)\n", (cnt == selected) ? ">>" : " ", file->filename, filesize); + if (file->isdir) // wiiNinja + printf("\t%2s [%s]\n", (cnt == selected) ? ">>" : " ", str); + else + printf("\t%2s%s%s (%.2f MB)\n", (cnt == selected) ? ">>" : " ", (file->install == 1) ? "+" : ((file->install == 2) ? "-" : " "), str, filesize); + + } + + printf("\n"); + + printf("[+] Press A to (un)install."); + if(gDirLevel>1) + printf(" Press B to go up-level DIR.\n"); + else + printf(" Press B to select a device.\n"); + printf(" Use +/X and -/Y to (un)mark. Press 1/Z/ZR for delete menu."); + + /** Controls **/ + u32 buttons = WaitButtons(); + + /* DPAD buttons */ + if (buttons & WPAD_BUTTON_UP) { + selected--; + + if (selected <= -1) + selected = (fileCnt - 1); + } + if (buttons & WPAD_BUTTON_LEFT) { + selected = selected + ENTRIES_PER_PAGE; + + if (selected >= fileCnt) + selected = 0; + } + if (buttons & WPAD_BUTTON_DOWN) { + selected ++; + + if (selected >= fileCnt) + selected = 0; + } + if (buttons & WPAD_BUTTON_RIGHT) { + selected = selected - ENTRIES_PER_PAGE; + + if (selected <= -1) + selected = (fileCnt - 1); + } + + /* HOME button */ + if (buttons & WPAD_BUTTON_HOME) + Restart(); + + /* Plus Button - Leathl */ + if (buttons & WPAD_BUTTON_PLUS) + { + if(Wpad_TimeButton()) + { + installCnt = 0; + int i = 0; + while( i < fileCnt) + { + fatFile *file = &fileList[i]; + if (((file->isdir) == false) & (file->install == 0)) { + file->install = 1; + + installCnt += 1; + } + else if (((file->isdir) == false) & (file->install == 1)) { + file->install = 0; + + installCnt -= 1; + } + else if (((file->isdir) == false) & (file->install == 2)) { + file->install = 1; + + installCnt += 1; + uninstallCnt -= 1; + } + i++; + } + + } + else + { + fatFile *file = &fileList[selected]; + if (((file->isdir) == false) & (file->install == 0)) { + file->install = 1; + + installCnt += 1; + } + else if (((file->isdir) == false) & (file->install == 1)) { + file->install = 0; + + installCnt -= 1; + } + else if (((file->isdir) == false) & (file->install == 2)) { + file->install = 1; + + installCnt += 1; + uninstallCnt -= 1; + } + selected++; + + if (selected >= fileCnt) + selected = 0; + } + } + + /* Minus Button - Leathl */ + if (buttons & WPAD_BUTTON_MINUS) + { + if(Wpad_TimeButton()) + { + installCnt = 0; + int i = 0; + while( i < fileCnt) + { + fatFile *file = &fileList[i]; + if (((file->isdir) == false) & (file->install == 0)) { + file->install = 2; + + uninstallCnt += 1; + } + else if (((file->isdir) == false) & (file->install == 1)) { + file->install = 2; + + uninstallCnt += 1; + installCnt -= 1; + } + else if (((file->isdir) == false) & (file->install == 2)) { + file->install = 0; + + uninstallCnt -= 1; + } + i++; + } + + } + else + { + fatFile *file = &fileList[selected]; + if (((file->isdir) == false) & (file->install == 0)) { + file->install = 2; + + uninstallCnt += 1; + } + else if (((file->isdir) == false) & (file->install == 1)) { + file->install = 2; + + uninstallCnt += 1; + installCnt -= 1; + } + else if (((file->isdir) == false) & (file->install == 2)) { + file->install = 0; + + uninstallCnt -= 1; + } + selected++; + + if (selected >= fileCnt) + selected = 0; + } + } + + /* 1 Button - Leathl */ + if (buttons & WPAD_BUTTON_1) + { + fatFile *tmpFile = &fileList[selected]; + char *tmpCurPath = PeekCurrentDir (); + if (tmpCurPath != NULL) { + int res = Menu_FileOperations(tmpFile, tmpCurPath); + if (res != 0) + goto getList; + } + } + + + /* A button */ + if (buttons & WPAD_BUTTON_A) + { + fatFile *tmpFile = &fileList[selected]; + char *tmpCurPath; + if (tmpFile->isdir) // wiiNinja + { + if (strcmp (tmpFile->filename, "..") == 0) + { + selected = 0; + start = 0; + + // Previous dir + tmpCurPath = PopCurrentDir(&selected, &start); + if (tmpCurPath != NULL) + sprintf(tmpPath, "%s", tmpCurPath); + + installCnt = 0; + uninstallCnt = 0; + + goto getList; + } + else if (IsListFull () == true) + { + WaitPrompt ("Maximum number of directory levels is reached.\n"); + } + else + { + tmpCurPath = PeekCurrentDir (); + if (tmpCurPath != NULL) + { + if(gDirLevel>1) + sprintf(tmpPath, "%s/%s", tmpCurPath, tmpFile->filename); + else + sprintf(tmpPath, "%s%s", tmpCurPath, tmpFile->filename); + } + // wiiNinja: Need to PopCurrentDir + PushCurrentDir (tmpPath, selected, start); + selected = 0; + start = 0; + + installCnt = 0; + uninstallCnt = 0; + + goto getList; + } + } + else + { + //If at least one WAD is marked, goto batch screen - Leathl + if ((installCnt > 0) | (uninstallCnt > 0)) { + char *thisCurPath = PeekCurrentDir (); + if (thisCurPath != NULL) { + int res = Menu_BatchProcessWads(fileList, fileCnt, thisCurPath, installCnt, uninstallCnt); + + if (res == 1) { + int counter; + for (counter = 0; counter < fileCnt; counter++) { + fatFile *temp = &fileList[counter]; + temp->install = 0; + } + + installCnt = 0; + uninstallCnt = 0; + } + } + } + //else use standard wadmanage menu - Leathl + else { + tmpCurPath = PeekCurrentDir (); + if (tmpCurPath != NULL) + Menu_WadManage(tmpFile, tmpCurPath); + } + } + } + + /* B button */ + if (buttons & WPAD_BUTTON_B) + { + if(gDirLevel<=1) + { + return; + } + + char *tmpCurPath; + selected = 0; + start = 0; + // Previous dir + tmpCurPath = PopCurrentDir(&selected, &start); + if (tmpCurPath != NULL) + sprintf(tmpPath, "%s", tmpCurPath); + goto getList; + //return; + } + + /** Scrolling **/ + /* List scrolling */ + index = (selected - start); + + if (index >= ENTRIES_PER_PAGE) + start += index - (ENTRIES_PER_PAGE - 1); + if (index <= -1) + start += index; + } + +err: + printf("\n"); + printf(" Press any button to continue...\n"); + + free (tmpPath); + + /* Wait for button */ + WaitButtons(); +} + + +void Menu_Loop(void) +{ + u8 iosVersion; + if(AHBPROT_DISABLED) + IOSPATCH_Apply(); + else + { + /* Select IOS menu */ + Menu_SelectIOS(); + } + + /* Retrieve IOS version */ + iosVersion = IOS_GetVersion(); + + ndev = &ndevList[0]; + + /* NAND device menu */ + if ((iosVersion == CIOS_VERSION || iosVersion == 250) && IOS_GetRevision() >13) + { + Menu_NandDevice(); + } + for (;;) { + /* FAT device menu */ + Menu_FatDevice(); + + /* WAD list menu */ + Menu_WadList(); + } +} + +// Start of wiiNinja's added routines + +int PushCurrentDir (char *dirStr, int Selected, int Start) +{ + int retval = 0; + + // Store dirStr into the list and increment the gDirLevel + // WARNING: Make sure dirStr is no larger than MAX_FILE_PATH_LEN + if (gDirLevel < MAX_DIR_LEVELS) + { + strcpy (gDirList [gDirLevel], dirStr); + gSeleted[gDirLevel]=Selected; + gStart[gDirLevel]=Start; + gDirLevel++; + //if (gDirLevel >= MAX_DIR_LEVELS) + // gDirLevel = 0; + } + else + retval = -1; + + return (retval); +} + +char *PopCurrentDir(int *Selected, int *Start) +{ + if (gDirLevel > 1) + gDirLevel--; + else { + gDirLevel = 0; + } + *Selected = gSeleted[gDirLevel]; + *Start = gStart[gDirLevel]; + return PeekCurrentDir(); +} + +bool IsListFull (void) +{ + if (gDirLevel < MAX_DIR_LEVELS) + return (false); + else + return (true); +} + +char *PeekCurrentDir (void) +{ + // Return the current path + if (gDirLevel > 0) + return (gDirList [gDirLevel-1]); + else + return (NULL); +} + +void WaitPrompt (char *prompt) +{ + printf("\n%s", prompt); + printf(" Press any button to continue...\n"); + + /* Wait for button */ + WaitButtons(); +} + +u32 Pad_GetButtons(void) +{ + u32 buttons = 0, cnt; + + /* Scan pads */ + PAD_ScanPads(); + + /* Get pressed buttons */ + //for (cnt = 0; cnt < MAX_WIIMOTES; cnt++) + for (cnt = 0; cnt < 4; cnt++) + buttons |= PAD_ButtonsDown(cnt); + + return buttons; +} + +u32 WiiDRC_GetButtons(void) +{ + if(!WiiDRC_Inited() || !WiiDRC_Connected()) + return 0; + + /* Scan pads */ + WiiDRC_ScanPads(); + + /* Get pressed buttons */ + return WiiDRC_ButtonsDown(); +} + +// Routine to wait for a button from either the Wiimote or a gamecube +// controller. The return value will mimic the WPAD buttons to minimize +// the amount of changes to the original code, that is expecting only +// Wiimote button presses. Note that the "HOME" button on the Wiimote +// is mapped to the "SELECT" button on the Gamecube Ctrl. (wiiNinja 5/15/2009) +u32 WaitButtons(void) +{ + u32 buttons = 0; + u32 buttonsGC = 0; + u32 buttonsDRC = 0; + + /* Wait for button pressing */ + while (!buttons && !buttonsGC && !buttonsDRC) + { + // Wii buttons + buttons = Wpad_GetButtons(); + + // GC buttons + buttonsGC = Pad_GetButtons(); + + // DRC buttons + buttonsDRC = WiiDRC_GetButtons(); + + VIDEO_WaitVSync(); + } + + if(buttons & WPAD_CLASSIC_BUTTON_A) + buttons |= WPAD_BUTTON_A; + else if(buttons & WPAD_CLASSIC_BUTTON_B) + buttons |= WPAD_BUTTON_B; + else if(buttons & WPAD_CLASSIC_BUTTON_LEFT) + buttons |= WPAD_BUTTON_LEFT; + else if(buttons & WPAD_CLASSIC_BUTTON_RIGHT) + buttons |= WPAD_BUTTON_RIGHT; + else if(buttons & WPAD_CLASSIC_BUTTON_DOWN) + buttons |= WPAD_BUTTON_DOWN; + else if(buttons & WPAD_CLASSIC_BUTTON_UP) + buttons |= WPAD_BUTTON_UP; + else if(buttons & WPAD_CLASSIC_BUTTON_HOME) + buttons |= WPAD_BUTTON_HOME; + else if(buttons & (WPAD_CLASSIC_BUTTON_X | WPAD_CLASSIC_BUTTON_PLUS)) + buttons |= WPAD_BUTTON_PLUS; + else if(buttons & (WPAD_CLASSIC_BUTTON_Y | WPAD_CLASSIC_BUTTON_MINUS)) + buttons |= WPAD_BUTTON_MINUS; + else if(buttons & WPAD_CLASSIC_BUTTON_ZR) + buttons |= WPAD_BUTTON_1; + + if (buttonsGC) + { + if(buttonsGC & PAD_BUTTON_A) + buttons |= WPAD_BUTTON_A; + else if(buttonsGC & PAD_BUTTON_B) + buttons |= WPAD_BUTTON_B; + else if(buttonsGC & PAD_BUTTON_LEFT) + buttons |= WPAD_BUTTON_LEFT; + else if(buttonsGC & PAD_BUTTON_RIGHT) + buttons |= WPAD_BUTTON_RIGHT; + else if(buttonsGC & PAD_BUTTON_DOWN) + buttons |= WPAD_BUTTON_DOWN; + else if(buttonsGC & PAD_BUTTON_UP) + buttons |= WPAD_BUTTON_UP; + else if(buttonsGC & PAD_BUTTON_START) + buttons |= WPAD_BUTTON_HOME; + else if(buttonsGC & PAD_BUTTON_X) + buttons |= WPAD_BUTTON_PLUS; + else if(buttonsGC & PAD_BUTTON_Y) + buttons |= WPAD_BUTTON_MINUS; + else if(buttonsGC & PAD_TRIGGER_Z) + buttons |= WPAD_BUTTON_1; + } + + if (buttonsDRC) + { + if(buttonsDRC & WIIDRC_BUTTON_A) + buttons |= WPAD_BUTTON_A; + else if(buttonsDRC & WIIDRC_BUTTON_B) + buttons |= WPAD_BUTTON_B; + else if(buttonsDRC & WIIDRC_BUTTON_LEFT) + buttons |= WPAD_BUTTON_LEFT; + else if(buttonsDRC & WIIDRC_BUTTON_RIGHT) + buttons |= WPAD_BUTTON_RIGHT; + else if(buttonsDRC & WIIDRC_BUTTON_DOWN) + buttons |= WPAD_BUTTON_DOWN; + else if(buttonsDRC & WIIDRC_BUTTON_UP) + buttons |= WPAD_BUTTON_UP; + else if(buttonsDRC & WIIDRC_BUTTON_HOME) + buttons |= WPAD_BUTTON_HOME; + else if(buttonsDRC & (WIIDRC_BUTTON_X | WIIDRC_BUTTON_PLUS)) + buttons |= WPAD_BUTTON_PLUS; + else if(buttonsDRC & (WIIDRC_BUTTON_Y | WIIDRC_BUTTON_MINUS)) + buttons |= WPAD_BUTTON_MINUS; + else if(buttonsDRC & WIIDRC_BUTTON_ZR) + buttons |= WPAD_BUTTON_1; + } + + return buttons; +} // WaitButtons + + +void WiiLightControl (int state) +{ + switch (state) + { + case WII_LIGHT_ON: + /* Turn on Wii Light */ + WIILIGHT_SetLevel(255); + WIILIGHT_TurnOn(); + break; + + case WII_LIGHT_OFF: + default: + /* Turn off Wii Light */ + WIILIGHT_SetLevel(0); + WIILIGHT_TurnOn(); + WIILIGHT_Toggle(); + break; + } +} // WiiLightControl + diff --git a/source/menu.h b/source/menu.h new file mode 100644 index 0000000..92cd259 --- /dev/null +++ b/source/menu.h @@ -0,0 +1,8 @@ +#ifndef _MENU_H_ +#define _MENU_H_ + +/* Prototypes */ +void Menu_Loop(void); + +#endif + diff --git a/source/mload.c b/source/mload.c new file mode 100644 index 0000000..2f774d0 --- /dev/null +++ b/source/mload.c @@ -0,0 +1,381 @@ +/* mload.c (for PPC) (c) 2009, Hermes + + 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 +*/ + +#include "mload.h" + +static const char mload_fs[] ATTRIBUTE_ALIGN(32) = "/dev/mload"; + +static s32 mload_fd = -1; + +/*--------------------------------------------------------------------------------------------------------------*/ + +// to init/test if the device is running + +int mload_init() +{ +int n; + + if(mload_fd>=0) return 0; + + for(n=0;n<10;n++) // try 2.5 seconds + { + mload_fd=IOS_Open(mload_fs, 0); + + if(mload_fd>=0) break; + + usleep(250*1000); + } + +return mload_fd; +} + +/*--------------------------------------------------------------------------------------------------------------*/ + +// to close the device (remember call it when rebooting the IOS!) + +int mload_close() +{ +int ret; + + if(mload_fd<0) return -1; + + ret=IOS_Close(mload_fd); + + mload_fd=-1; + +return ret; +} + +/*--------------------------------------------------------------------------------------------------------------*/ + +// to get the thread id of mload + +int mload_get_thread_id() +{ +int ret; +s32 hid = -1; + + + if(mload_init()<0) return -1; + + hid = iosCreateHeap(0x800); + + if(hid<0) return hid; + + ret= IOS_IoctlvFormat(hid, mload_fd, MLOAD_MLOAD_THREAD_ID, ":"); + + +return ret; + +} + +/*--------------------------------------------------------------------------------------------------------------*/ + +// get the base and the size of the memory readable/writable to load modules + +int mload_get_load_base(u32 *starlet_base, int *size) +{ +int ret; +s32 hid = -1; + + + if(mload_init()<0) return -1; + + hid = iosCreateHeap(0x800); + + if(hid<0) return hid; + + ret= IOS_IoctlvFormat(hid, mload_fd, MLOAD_GET_LOAD_BASE, ":ii",starlet_base, size); + + +return ret; + +} + +/*--------------------------------------------------------------------------------------------------------------*/ + +// load and run a module from starlet (it need to allocate MEM2 to send the elf file) +// the module must be a elf made with stripios + +int mload_module(void *addr, int len) +{ +int ret; +void *buf=NULL; +s32 hid = -1; + + if(mload_init()<0) return -1; + + hid = iosCreateHeap(len+0x800); + + if(hid<0) return hid; + + buf= iosAlloc(hid, len); + + if(!buf) {ret= -1;goto out;} + + + memcpy(buf, addr,len); + + ret = IOS_IoctlvFormat(hid, mload_fd, MLOAD_LOAD_MODULE, ":d", buf, len); + + if(ret<0) goto out; + + ret=IOS_IoctlvFormat(hid, mload_fd, MLOAD_RUN_MODULE, ":"); + + if(ret<0) {ret= -666;goto out;} + +out: + +return ret; + +} + +/*--------------------------------------------------------------------------------------------------------------*/ + +// load a module from the PPC +// the module must be a elf made with stripios + +int mload_elf(void *my_elf, data_elf *data_elf) +{ +int n,m; +int p; +u8 *adr; +u32 elf=(u32) my_elf; + +if(elf & 3) return -1; // aligned to 4 please! + +elfheader *head=(void *) elf; +elfphentry *entries; + +if(head->ident0!=0x7F454C46) return -1; +if(head->ident1!=0x01020161) return -1; +if(head->ident2!=0x01000000) return -1; + +p=head->phoff; + +data_elf->start=(void *) head->entry; + +for(n=0; nphnum; n++) + { + entries=(void *) (elf+p); + p+=sizeof(elfphentry); + + if(entries->type == 4) + { + adr=(void *) (elf + entries->offset); + + if(getbe32(0)!=0) return -2; // bad info (sure) + + for(m=4; m < entries->memsz; m+=8) + { + switch(getbe32(m)) + { + case 0x9: + data_elf->start= (void *) getbe32(m+4); + break; + case 0x7D: + data_elf->prio= getbe32(m+4); + break; + case 0x7E: + data_elf->size_stack= getbe32(m+4); + break; + case 0x7F: + data_elf->stack= (void *) (getbe32(m+4)); + break; + + } + + } + + } + else + if(entries->type == 1 && entries->memsz != 0 && entries->vaddr!=0) + { + + if(mload_memset((void *) entries->vaddr, 0, entries->memsz)<0) return -1; + if(mload_seek(entries->vaddr, SEEK_SET)<0) return -1; + if(mload_write((void *) (elf + entries->offset), entries->filesz)<0) return -1; + + } + } + +return 0; +} + +/*--------------------------------------------------------------------------------------------------------------*/ + +// run one thread (you can use to load modules or binary files) + +int mload_run_thread(void *starlet_addr, void *starlet_top_stack, int stack_size, int priority) +{ +int ret; +s32 hid = -1; + + + if(mload_init()<0) return -1; + + hid = iosCreateHeap(0x800); + + if(hid<0) return hid; + + ret= IOS_IoctlvFormat(hid, mload_fd, MLOAD_RUN_THREAD, "iiii:", starlet_addr,starlet_top_stack, stack_size, priority); + + +return ret; +} + +/*--------------------------------------------------------------------------------------------------------------*/ + +// stops one starlet thread + +int mload_stop_thread(int id) +{ +int ret; +s32 hid = -1; + + + if(mload_init()<0) return -1; + + hid = iosCreateHeap(0x800); + + if(hid<0) return hid; + + ret= IOS_IoctlvFormat(hid, mload_fd, MLOAD_STOP_THREAD, "i:", id); + + +return ret; + +} + +/*--------------------------------------------------------------------------------------------------------------*/ + +// continue one stopped starlet thread + +int mload_continue_thread(int id) +{ +int ret; +s32 hid = -1; + + + if(mload_init()<0) return -1; + + hid = iosCreateHeap(0x800); + + if(hid<0) return hid; + + ret= IOS_IoctlvFormat(hid, mload_fd, MLOAD_CONTINUE_THREAD, "i:", id); + + +return ret; + +} +/*--------------------------------------------------------------------------------------------------------------*/ + +// fix starlet address to read/write (uses SEEK_SET, etc as mode) + +int mload_seek(int offset, int mode) +{ + if(mload_init()<0) return -1; + return IOS_Seek(mload_fd, offset, mode); +} + +/*--------------------------------------------------------------------------------------------------------------*/ + +// read bytes from starlet (it update the offset) + +int mload_read(void* buf, u32 size) +{ + if(mload_init()<0) return -1; + return IOS_Read(mload_fd, buf, size); +} + +/*--------------------------------------------------------------------------------------------------------------*/ + +// write bytes from starlet (it update the offset) + +int mload_write(const void * buf, u32 size) +{ + if(mload_init()<0) return -1; + return IOS_Write(mload_fd, buf, size); +} + +/*--------------------------------------------------------------------------------------------------------------*/ + +// fill a block (similar to memset) + +int mload_memset(void *starlet_addr, int set, int len) +{ +int ret; +s32 hid = -1; + + + if(mload_init()<0) return -1; + + hid = iosCreateHeap(0x800); + + if(hid<0) return hid; + + ret= IOS_IoctlvFormat(hid, mload_fd, MLOAD_MEMSET, "iii:", starlet_addr, set, len); + + +return ret; +} + +/*--------------------------------------------------------------------------------------------------------------*/ + +// get the ehci datas ( ehcmodule.elf uses this address) + +void * mload_get_ehci_data() +{ +int ret; +s32 hid = -1; + + + if(mload_init()<0) return NULL; + + hid = iosCreateHeap(0x800); + + if(hid<0) return NULL; + + ret= IOS_IoctlvFormat(hid, mload_fd, MLOAD_GET_EHCI_DATA, ":"); + if(ret<0) return NULL; + +return (void *) ret; +} + +/*--------------------------------------------------------------------------------------------------------------*/ + +// set the dev/es ioctlv in routine + +int mload_set_ES_ioctlv_vector(void *starlet_addr) +{ +int ret; +s32 hid = -1; + + + if(mload_init()<0) return -1; + + hid = iosCreateHeap(0x800); + + if(hid<0) return hid; + + ret= IOS_IoctlvFormat(hid, mload_fd, MLOAD_SET_ES_IOCTLV, "i:", starlet_addr); + + +return ret; +} + diff --git a/source/mload.h b/source/mload.h new file mode 100644 index 0000000..b50eeb6 --- /dev/null +++ b/source/mload.h @@ -0,0 +1,194 @@ +/* mload.c (for PPC) (c) 2009, Hermes + + 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 +*/ + +#ifndef __MLOAD_H__ +#define __MLOAD_H__ + + +#include +#include +#include +#include +#include +#include "unistd.h" + +#define MLOAD_MLOAD_THREAD_ID 0x4D4C4400 +#define MLOAD_LOAD_MODULE 0x4D4C4480 +#define MLOAD_RUN_MODULE 0x4D4C4481 +#define MLOAD_RUN_THREAD 0x4D4C4482 + +#define MLOAD_STOP_THREAD 0x4D4C4484 +#define MLOAD_CONTINUE_THREAD 0x4D4C4485 + +#define MLOAD_GET_LOAD_BASE 0x4D4C4490 +#define MLOAD_MEMSET 0x4D4C4491 + +#define MLOAD_GET_EHCI_DATA 0x4D4C44A0 + +#define MLOAD_SET_ES_IOCTLV 0x4D4C44B0 + +#ifdef __cplusplus +extern "C" { +#endif + + +// from IOS ELF stripper of neimod + +#define getbe32(x) ((adr[x]<<24) | (adr[x+1]<<16) | (adr[x+2]<<8) | (adr[x+3])) + +typedef struct +{ + u32 ident0; + u32 ident1; + u32 ident2; + u32 ident3; + u32 machinetype; + u32 version; + u32 entry; + u32 phoff; + u32 shoff; + u32 flags; + u16 ehsize; + u16 phentsize; + u16 phnum; + u16 shentsize; + u16 shnum; + u16 shtrndx; +} elfheader; + +typedef struct +{ + u32 type; + u32 offset; + u32 vaddr; + u32 paddr; + u32 filesz; + u32 memsz; + u32 flags; + u32 align; +} elfphentry; + +typedef struct +{ + void *start; + int prio; + void *stack; + int size_stack; +} data_elf; + +/*--------------------------------------------------------------------------------------------------------------*/ + +// to init/test if the device is running + +int mload_init(); + +/*--------------------------------------------------------------------------------------------------------------*/ + +// to close the device (remember call it when rebooting the IOS!) + +int mload_close(); + +/*--------------------------------------------------------------------------------------------------------------*/ + +// to get the thread id of mload + +int mload_get_thread_id(); + +/*--------------------------------------------------------------------------------------------------------------*/ + +// get the base and the size of the memory readable/writable to load modules + +int mload_get_load_base(u32 *starlet_base, int *size); + +/*--------------------------------------------------------------------------------------------------------------*/ + +// load and run a module from starlet (it need to allocate MEM2 to send the elf file) +// the module must be a elf made with stripios + +int mload_module(void *addr, int len); + +/*--------------------------------------------------------------------------------------------------------------*/ + +// load a module from the PPC +// the module must be a elf made with stripios + +int mload_elf(void *my_elf, data_elf *data_elf); + +/*--------------------------------------------------------------------------------------------------------------*/ + +// run one thread (you can use to load modules or binary files) + +int mload_run_thread(void *starlet_addr, void *starlet_top_stack, int stack_size, int priority); + +/*--------------------------------------------------------------------------------------------------------------*/ + +// stops one starlet thread + +int mload_stop_thread(int id); + +/*--------------------------------------------------------------------------------------------------------------*/ + +// continue one stopped starlet thread + +int mload_continue_thread(int id); + +/*--------------------------------------------------------------------------------------------------------------*/ + +// fix starlet address to read/write (uses SEEK_SET, etc as mode) + +int mload_seek(int offset, int mode); + +/*--------------------------------------------------------------------------------------------------------------*/ + +// read bytes from starlet (it update the offset) + +int mload_read(void* buf, u32 size); + +/*--------------------------------------------------------------------------------------------------------------*/ + +// write bytes from starlet (it update the offset) + +int mload_write(const void * buf, u32 size); + +/*--------------------------------------------------------------------------------------------------------------*/ + +// fill a block (similar to memset) + +int mload_memset(void *starlet_addr, int set, int len); + +/*--------------------------------------------------------------------------------------------------------------*/ + +// get the ehci datas ( ehcmodule.elf uses this address) + +void * mload_get_ehci_data(); + +/*--------------------------------------------------------------------------------------------------------------*/ + +// set the dev/es ioctlv in routine + +int mload_set_ES_ioctlv_vector(void *starlet_addr); + +/*--------------------------------------------------------------------------------------------------------------*/ + + + +#ifdef __cplusplus + } +#endif + + +#endif diff --git a/source/nand.c b/source/nand.c new file mode 100644 index 0000000..a5c99be --- /dev/null +++ b/source/nand.c @@ -0,0 +1,86 @@ +#include +#include + +#include "nand.h" + +/* Buffer */ +static u32 inbuf[8] ATTRIBUTE_ALIGN(32); + + +s32 Nand_Mount(nandDevice *dev) +{ + s32 fd, ret; + + /* Open FAT module */ + fd = IOS_Open("fat", 0); + if (fd < 0) + return fd; + + /* Mount device */ + ret = IOS_Ioctlv(fd, dev->mountCmd, 0, 0, NULL); + + /* Close FAT module */ + IOS_Close(fd); + + return ret; +} + +s32 Nand_Unmount(nandDevice *dev) +{ + s32 fd, ret; + + /* Open FAT module */ + fd = IOS_Open("fat", 0); + if (fd < 0) + return fd; + + /* Unmount device */ + ret = IOS_Ioctlv(fd, dev->umountCmd, 0, 0, NULL); + + /* Close FAT module */ + IOS_Close(fd); + + return ret; +} + +s32 Nand_Enable(nandDevice *dev) +{ + s32 fd, ret; + + /* Open /dev/fs */ + fd = IOS_Open("/dev/fs", 0); + if (fd < 0) + return fd; + + /* Set input buffer */ + inbuf[0] = dev->mode; + + /* Enable NAND emulator */ + ret = IOS_Ioctl(fd, 100, inbuf, sizeof(inbuf), NULL, 0); + + /* Close /dev/fs */ + IOS_Close(fd); + + return ret; +} + +s32 Nand_Disable(void) +{ + s32 fd, ret; + + /* Open /dev/fs */ + fd = IOS_Open("/dev/fs", 0); + if (fd < 0) + return fd; + + /* Set input buffer */ + inbuf[0] = 0; + + /* Disable NAND emulator */ + ret = IOS_Ioctl(fd, 100, inbuf, sizeof(inbuf), NULL, 0); + + /* Close /dev/fs */ + IOS_Close(fd); + + return ret; +} diff --git a/source/nand.h b/source/nand.h new file mode 100644 index 0000000..0b769d8 --- /dev/null +++ b/source/nand.h @@ -0,0 +1,24 @@ +#ifndef _NAND_H_ +#define _NAND_H_ + +/* 'NAND Device' structure */ +typedef struct { + /* Device name */ + char *name; + + /* Mode value */ + u32 mode; + + /* Un/mount command */ + u32 mountCmd; + u32 umountCmd; +} nandDevice; + + +/* Prototypes */ +s32 Nand_Mount(nandDevice *); +s32 Nand_Unmount(nandDevice *); +s32 Nand_Enable(nandDevice *); +s32 Nand_Disable(void); + +#endif diff --git a/source/restart.c b/source/restart.c new file mode 100644 index 0000000..d979817 --- /dev/null +++ b/source/restart.c @@ -0,0 +1,36 @@ +#include +#include + +#include "nand.h" +#include "sys.h" +#include "wpad.h" +#include "video.h" + + +void Restart(void) +{ + Con_Clear (); + printf("\n Restarting Wii..."); + fflush(stdout); + + /* Disable NAND emulator */ + Nand_Disable(); + + /* Load system menu */ + Sys_LoadMenu(); +} + +void Restart_Wait(void) +{ + printf("\n"); + + printf(" Press any button to restart..."); + fflush(stdout); + + /* Wait for button */ + Wpad_WaitButtons(); + + /* Restart */ + Restart(); +} + diff --git a/source/restart.h b/source/restart.h new file mode 100644 index 0000000..3df94d7 --- /dev/null +++ b/source/restart.h @@ -0,0 +1,8 @@ +#ifndef _RESTART_H_ +#define _RESTART_H_ + +/* Prototypes */ +void Restart(void); +void Restart_Wait(void); + +#endif diff --git a/source/sha1.c b/source/sha1.c new file mode 100644 index 0000000..7ce9e6d --- /dev/null +++ b/source/sha1.c @@ -0,0 +1,177 @@ +/* +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); + +typedef struct { + unsigned long state[5]; + unsigned long count[2]; + unsigned char buffer[64]; +} SHA1_CTX; + + +/* Hash a single 512-bit block. This is the core of the algorithm. */ + +void SHA1Transform(unsigned long state[5], 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, 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/source/sha1.h b/source/sha1.h new file mode 100644 index 0000000..35b3388 --- /dev/null +++ b/source/sha1.h @@ -0,0 +1,6 @@ +#ifndef _SHA1_H_ +#define _SHA1_H_ + +void SHA1(unsigned char *, unsigned int, unsigned char *); + +#endif diff --git a/source/stub.S b/source/stub.S new file mode 100644 index 0000000..418c838 --- /dev/null +++ b/source/stub.S @@ -0,0 +1,6 @@ + .rodata + + .globl bgData + .balign 32 +bgData: + .incbin "../data/background" diff --git a/source/sys.c b/source/sys.c new file mode 100644 index 0000000..ebb8fb4 --- /dev/null +++ b/source/sys.c @@ -0,0 +1,188 @@ +#include +#include +#include +#include + +#include "sys.h" +#include "mload.h" +#include "ehcmodule_elf.h" + +/* Constants */ +#define CERTS_LEN 0x280 + +/* Variables */ +static const char certs_fs[] ATTRIBUTE_ALIGN(32) = "/sys/cert.sys"; + +void __Sys_ResetCallback(void) +{ + /* Reboot console */ + Sys_Reboot(); +} + +void __Sys_PowerCallback(void) +{ + /* Poweroff console */ + Sys_Shutdown(); +} + +bool isIOSstub(u8 ios_number) +{ + u32 tmd_size; + tmd_view *ios_tmd; + + + if((boot2version >= 5) && ( ios_number == 202 || ios_number == 222 || ios_number == 223 || ios_number == 224)) return true; + + ES_GetTMDViewSize(0x0000000100000000ULL | ios_number, &tmd_size); + if (!tmd_size) + { + //getting size failed. invalid or fake tmd for sure! + //gprintf("failed to get tmd for ios %d\n",ios_number); + return true; + } + ios_tmd = (tmd_view *)memalign( 32, (tmd_size+31)&(~31) ); + if(!ios_tmd) + { + //gprintf("failed to mem align the TMD struct!\n"); + return true; + } + memset(ios_tmd , 0, tmd_size); + ES_GetTMDView(0x0000000100000000ULL | ios_number, (u8*)ios_tmd , tmd_size); + //gprintf("IOS %d is rev %d(0x%x) with tmd size of %u and %u contents\n",ios_number,ios_tmd->title_version,ios_tmd->title_version,tmd_size,ios_tmd->num_contents); + /*Stubs have a few things in common: + - title version : it is mostly 65280 , or even better : in hex the last 2 digits are 0. + example : IOS 60 rev 6400 = 0x1900 = 00 = stub + - exception for IOS21 which is active, the tmd size is 592 bytes (or 140 with the views) + - the stub ios' have 1 app of their own (type 0x1) and 2 shared apps (type 0x8001). + eventho the 00 check seems to work fine , we'll only use other knowledge as well cause some + people/applications install an ios with a stub rev >_> ...*/ + u8 Version = ios_tmd->title_version; + + if((boot2version >= 5) && (ios_number == 249 || ios_number == 250) && (Version < 18)) return true; + if(( ios_number == 202 || ios_number == 222 || ios_number == 223 || ios_number == 224) && (Version < 4)) return true; + //version now contains the last 2 bytes. as said above, if this is 00, its a stub + if ( Version == 0 ) + { + if ( ( ios_tmd->num_contents == 3) && (ios_tmd->contents[0].type == 1 && ios_tmd->contents[1].type == 0x8001 && ios_tmd->contents[2].type == 0x8001) ) + { + //gprintf("IOS %d is a stub\n",ios_number); + free(ios_tmd); + return true; + } + else + { + //gprintf("IOS %d is active\n",ios_number); + free(ios_tmd); + return false; + } + } + //gprintf("IOS %d is active\n",ios_number); + free(ios_tmd); + return false; +} + + +bool loadIOS(int ios) +{ + if(isIOSstub(ios)) return false; + mload_close(); + if(IOS_ReloadIOS(ios)>=0) + { + if (IOS_GetVersion() != 249 && IOS_GetVersion() != 250) + { + if (mload_init() >= 0) + { + data_elf my_data_elf; + mload_elf((void *) ehcmodule_elf, &my_data_elf); + mload_run_thread(my_data_elf.start, my_data_elf.stack, my_data_elf.size_stack, 0x47); + } + } + return true; + } + return false; +} + +void Sys_Init(void) +{ + /* Initialize video subsytem */ + VIDEO_Init(); + + /* Set RESET/POWER button callback */ + SYS_SetResetCallback(__Sys_ResetCallback); + SYS_SetPowerCallback(__Sys_PowerCallback); +} + +void Sys_Reboot(void) +{ + /* Restart console */ + STM_RebootSystem(); +} + +void Sys_Shutdown(void) +{ + /* Poweroff console */ + if(CONF_GetShutdownMode() == CONF_SHUTDOWN_IDLE) { + s32 ret; + + /* Set LED mode */ + ret = CONF_GetIdleLedMode(); + if(ret >= 0 && ret <= 2) + STM_SetLedMode(ret); + + /* Shutdown to idle */ + STM_ShutdownToIdle(); + } else { + /* Shutdown to standby */ + STM_ShutdownToStandby(); + } +} + +void Sys_LoadMenu(void) +{ + int HBC = 0; + char * sig = (char *)0x80001804; + if( sig[0] == 'S' && + sig[1] == 'T' && + sig[2] == 'U' && + sig[3] == 'B' && + sig[4] == 'H' && + sig[5] == 'A' && + sig[6] == 'X' && + sig[7] == 'X') + { + HBC=1; // Exit to HBC + } + + /* Homebrew Channel stub */ + if (HBC == 1) { + exit(0); + } + /* Return to the Wii system menu */ + SYS_ResetSystem(SYS_RETURNTOMENU, 0, 0); +} + +s32 Sys_GetCerts(signed_blob **certs, u32 *len) +{ + static signed_blob certificates[CERTS_LEN] ATTRIBUTE_ALIGN(32); + + s32 fd, ret; + + /* Open certificates file */ + fd = IOS_Open(certs_fs, 1); + if (fd < 0) + return fd; + + /* Read certificates */ + ret = IOS_Read(fd, certificates, sizeof(certificates)); + + /* Close file */ + IOS_Close(fd); + + /* Set values */ + if (ret > 0) { + *certs = certificates; + *len = sizeof(certificates); + } + + return ret; +} diff --git a/source/sys.h b/source/sys.h new file mode 100644 index 0000000..419ed68 --- /dev/null +++ b/source/sys.h @@ -0,0 +1,14 @@ +#ifndef _SYS_H_ +#define _SYS_H_ + +u32 boot2version; +/* Prototypes */ +bool isIOSstub(u8 ios_number); +bool loadIOS(int ios); +void Sys_Init(void); +void Sys_Reboot(void); +void Sys_Shutdown(void); +void Sys_LoadMenu(void); +s32 Sys_GetCerts(signed_blob **, u32 *); + +#endif diff --git a/source/title.c b/source/title.c new file mode 100644 index 0000000..76fe3a6 --- /dev/null +++ b/source/title.c @@ -0,0 +1,324 @@ +#include +#include +#include +#include +#include +#include + +#include "sha1.h" +#include "utils.h" + + +s32 Title_ZeroSignature(signed_blob *p_sig) +{ + u8 *ptr = (u8 *)p_sig; + + /* Fill signature with zeroes */ + memset(ptr + 4, 0, SIGNATURE_SIZE(p_sig) - 4); + + return 0; +} + +s32 Title_FakesignTik(signed_blob *p_tik) +{ + tik *tik_data = NULL; + u16 fill; + + /* Zero signature */ + Title_ZeroSignature(p_tik); + + /* Ticket data */ + tik_data = (tik *)SIGNATURE_PAYLOAD(p_tik); + + for (fill = 0; fill < USHRT_MAX; fill++) { + sha1 hash; + + /* Modify ticket padding field */ + tik_data->padding = fill; + + /* Calculate hash */ + SHA1((u8 *)tik_data, sizeof(tik), hash); + + /* Found valid hash */ + if (!hash[0]) + return 0; + } + + return -1; +} + +s32 Title_FakesignTMD(signed_blob *p_tmd) +{ + tmd *tmd_data = NULL; + u16 fill; + + /* Zero signature */ + Title_ZeroSignature(p_tmd); + + /* TMD data */ + tmd_data = (tmd *)SIGNATURE_PAYLOAD(p_tmd); + + for (fill = 0; fill < USHRT_MAX; fill++) { + sha1 hash; + + /* Modify TMD fill field */ + tmd_data->fill3 = fill; + + /* Calculate hash */ + SHA1((u8 *)tmd_data, TMD_SIZE(tmd_data), hash); + + /* Found valid hash */ + if (!hash[0]) + return 0; + } + + return -1; +} + +s32 Title_GetList(u64 **outbuf, u32 *outlen) +{ + u64 *titles = NULL; + + u32 len, nb_titles; + s32 ret; + + /* Get number of titles */ + ret = ES_GetNumTitles(&nb_titles); + if (ret < 0) + return ret; + + /* Calculate buffer lenght */ + len = round_up(sizeof(u64) * nb_titles, 32); + + /* Allocate memory */ + titles = memalign(32, len); + if (!titles) + return -1; + + /* Get titles */ + ret = ES_GetTitles(titles, nb_titles); + if (ret < 0) + goto err; + + /* Set values */ + *outbuf = titles; + *outlen = nb_titles; + + return 0; + +err: + /* Free memory */ + if (titles) + free(titles); + + return ret; +} + +s32 Title_GetTicketViews(u64 tid, tikview **outbuf, u32 *outlen) +{ + tikview *views = NULL; + + u32 nb_views; + s32 ret; + + /* Get number of ticket views */ + ret = ES_GetNumTicketViews(tid, &nb_views); + if (ret < 0) + return ret; + + /* Allocate memory */ + views = (tikview *)memalign(32, sizeof(tikview) * nb_views); + if (!views) + return -1; + + /* Get ticket views */ + ret = ES_GetTicketViews(tid, views, nb_views); + if (ret < 0) + goto err; + + /* Set values */ + *outbuf = views; + *outlen = nb_views; + + return 0; + +err: + /* Free memory */ + if (views) + free(views); + + return ret; +} + +s32 Title_GetTMD(u64 tid, signed_blob **outbuf, u32 *outlen) +{ + void *p_tmd = NULL; + + u32 len; + s32 ret; + + /* Get TMD size */ + ret = ES_GetStoredTMDSize(tid, &len); + if (ret < 0) + return ret; + + /* Allocate memory */ + p_tmd = memalign(32, round_up(len, 32)); + if (!p_tmd) + return -1; + + /* Read TMD */ + ret = ES_GetStoredTMD(tid, p_tmd, len); + if (ret < 0) + goto err; + + /* Set values */ + *outbuf = p_tmd; + *outlen = len; + + return 0; + +err: + /* Free memory */ + if (p_tmd) + free(p_tmd); + + return ret; +} + +s32 Title_GetVersion(u64 tid, u16 *outbuf) +{ + signed_blob *p_tmd = NULL; + tmd *tmd_data = NULL; + + u32 len; + s32 ret; + + /* Get title TMD */ + ret = Title_GetTMD(tid, &p_tmd, &len); + if (ret < 0) + return ret; + + /* Retrieve TMD info */ + tmd_data = (tmd *)SIGNATURE_PAYLOAD(p_tmd); + + /* Set values */ + *outbuf = tmd_data->title_version; + + /* Free memory */ + free(p_tmd); + + return 0; +} + +s32 Title_GetSysVersion(u64 tid, u64 *outbuf) +{ + signed_blob *p_tmd = NULL; + tmd *tmd_data = NULL; + + u32 len; + s32 ret; + + /* Get title TMD */ + ret = Title_GetTMD(tid, &p_tmd, &len); + if (ret < 0) + return ret; + + /* Retrieve TMD info */ + tmd_data = (tmd *)SIGNATURE_PAYLOAD(p_tmd); + + /* Set values */ + *outbuf = tmd_data->sys_version; + + /* Free memory */ + free(p_tmd); + + return 0; +} + +s32 Title_GetSize(u64 tid, u32 *outbuf) +{ + signed_blob *p_tmd = NULL; + tmd *tmd_data = NULL; + + u32 cnt, len, size = 0; + s32 ret; + + /* Get title TMD */ + ret = Title_GetTMD(tid, &p_tmd, &len); + if (ret < 0) + return ret; + + /* Retrieve TMD info */ + tmd_data = (tmd *)SIGNATURE_PAYLOAD(p_tmd); + + /* Calculate title size */ + for (cnt = 0; cnt < tmd_data->num_contents; cnt++) { + tmd_content *content = &tmd_data->contents[cnt]; + + /* Add content size */ + size += content->size; + } + + /* Set values */ + *outbuf = size; + + /* Free memory */ + free(p_tmd); + + return 0; +} + +s32 Title_GetIOSVersions(u8 **outbuf, u32 *outlen) +{ + u8 *buffer = NULL; + u64 *list = NULL; + + u32 count, cnt, idx; + s32 ret; + + /* Get title list */ + ret = Title_GetList(&list, &count); + if (ret < 0) + return ret; + + /* Count IOS */ + for (cnt = idx = 0; idx < count; idx++) { + u32 tidh = (list[idx] >> 32); + u32 tidl = (list[idx] & 0xFFFFFFFF); + + /* Title is IOS */ + if ((tidh == 0x1) && (tidl >= 3) && (tidl <= 255)) + cnt++; + } + + /* Allocate memory */ + buffer = (u8 *)memalign(32, cnt); + if (!buffer) { + ret = -1; + goto out; + } + + /* Copy IOS */ + for (cnt = idx = 0; idx < count; idx++) { + u32 tidh = (list[idx] >> 32); + u32 tidl = (list[idx] & 0xFFFFFFFF); + + /* Title is IOS */ + if ((tidh == 0x1) && (tidl >= 3) && (tidl <= 255)) + buffer[cnt++] = (u8)(tidl & 0xFF); + } + + /* Set values */ + *outbuf = buffer; + *outlen = cnt; + + goto out; + +out: + /* Free memory */ + if (list) + free(list); + + return ret; +} diff --git a/source/title.h b/source/title.h new file mode 100644 index 0000000..4faba41 --- /dev/null +++ b/source/title.h @@ -0,0 +1,19 @@ +#ifndef _TITLE_H_ +#define _TITLE_H_ + +/* Constants */ +#define BLOCK_SIZE 1024 + +/* Prototypes */ +s32 Title_ZeroSignature(signed_blob *); +s32 Title_FakesignTik(signed_blob *); +s32 Title_FakesignTMD(signed_blob *); +s32 Title_GetList(u64 **, u32 *); +s32 Title_GetTicketViews(u64, tikview **, u32 *); +s32 Title_GetTMD(u64, signed_blob **, u32 *); +s32 Title_GetVersion(u64, u16 *); +s32 Title_GetSysVersion(u64, u64 *); +s32 Title_GetSize(u64, u32 *); +s32 Title_GetIOSVersions(u8 **, u32 *); + +#endif diff --git a/source/usbstorage.c b/source/usbstorage.c new file mode 100644 index 0000000..d0d3432 --- /dev/null +++ b/source/usbstorage.c @@ -0,0 +1,400 @@ +/*------------------------------------------------------------- + +usbstorage_starlet.c -- USB mass storage support, inside starlet +Copyright (C) 2009 Kwiirk + +If this driver is linked before libogc, this will replace the original +usbstorage driver by svpe from libogc +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 +#include +#include +#include + +/* IOCTL commands */ +#define UMS_BASE (('U'<<24)|('M'<<16)|('S'<<8)) +#define USB_IOCTL_UMS_INIT (UMS_BASE+0x1) +#define USB_IOCTL_UMS_GET_CAPACITY (UMS_BASE+0x2) +#define USB_IOCTL_UMS_READ_SECTORS (UMS_BASE+0x3) +#define USB_IOCTL_UMS_WRITE_SECTORS (UMS_BASE+0x4) +#define USB_IOCTL_UMS_READ_STRESS (UMS_BASE+0x5) +#define USB_IOCTL_UMS_SET_VERBOSE (UMS_BASE+0x6) +#define USB_IOCTL_UMS_UNMOUNT (UMS_BASE+0x10) +#define USB_IOCTL_UMS_WATCHDOG (UMS_BASE+0x80) + +#define WBFS_BASE (('W'<<24)|('F'<<16)|('S'<<8)) +#define USB_IOCTL_WBFS_OPEN_DISC (WBFS_BASE+0x1) +#define USB_IOCTL_WBFS_READ_DISC (WBFS_BASE+0x2) +#define USB_IOCTL_WBFS_READ_DEBUG (WBFS_BASE+0x3) +#define USB_IOCTL_WBFS_SET_DEVICE (WBFS_BASE+0x4) +#define USB_IOCTL_WBFS_SET_FRAGLIST (WBFS_BASE+0x5) + +#define UMS_HEAPSIZE 0x1000 + +/* Variables */ +static char fs[] ATTRIBUTE_ALIGN(32) = "/dev/usb2"; +static char fs2[] ATTRIBUTE_ALIGN(32) = "/dev/usb/ehc"; +static char fs3[] ATTRIBUTE_ALIGN(32) = "/dev/usb/usb123"; + +static s32 hid = -1, fd = -1; +static u32 sector_size; + +s32 USBStorage_GetCapacity(u32 *_sector_size) { + if (fd > 0) { + s32 ret; + + ret = IOS_IoctlvFormat(hid, fd, USB_IOCTL_UMS_GET_CAPACITY, ":i", §or_size); + + if (ret && _sector_size) + *_sector_size = sector_size; + + return ret; + } + + return IPC_ENOENT; +} + +s32 USBStorage_Init(void) { + s32 ret; + + /* Already open */ + if (fd > 0) + return 0; + + /* Create heap */ + if (hid < 0) { + hid = iosCreateHeap(UMS_HEAPSIZE); + if (hid < 0) + return IPC_ENOMEM; + } + + /* Open USB device */ + fd = IOS_Open(fs, 0); + if (fd < 0) + fd = IOS_Open(fs2, 0); + if (fd < 0) + fd = IOS_Open(fs3, 0); + if (fd < 0) + return fd; + + /* Initialize USB storage */ + ret = IOS_IoctlvFormat(hid, fd, USB_IOCTL_UMS_INIT, ":"); + if (ret<0) goto err; + + /* Get device capacity */ + ret = USBStorage_GetCapacity(NULL); + if (!ret) + goto err; + + return 0; + +err: + /* Close USB device */ + if (fd > 0) { + IOS_Close(fd); + fd = -1; + } + + return -1; +} + +/** Hermes **/ +s32 USBStorage_Watchdog(u32 on_off) { + if (fd >= 0) { + s32 ret; + + ret = IOS_IoctlvFormat(hid, fd, USB_IOCTL_UMS_WATCHDOG, "i:", on_off); + + return ret; + } + + return IPC_ENOENT; +} + +s32 USBStorage_Umount(void) { + if (fd >= 0) { + s32 ret; + ret = IOS_IoctlvFormat(hid, fd, USB_IOCTL_UMS_UNMOUNT, ":"); + return ret; + } + + return IPC_ENOENT; +} + +void USBStorage_Deinit(void) { + /* Close USB device */ + if (fd > 0) { + IOS_Close(fd); + fd = -1; + } +} + +s32 USBStorage_ReadSectors(u32 sector, u32 numSectors, void *buffer) { + +// void *buf = (void *)buffer; + u32 len = (sector_size * numSectors); + + s32 ret; + + /* Device not opened */ + if (fd < 0) + return fd; + + + ret = IOS_IoctlvFormat(hid, fd, USB_IOCTL_UMS_READ_SECTORS, "ii:d", sector, numSectors, buffer, len); + return ret; +} + +s32 USBStorage_WriteSectors(u32 sector, u32 numSectors, const void *buffer) { + u32 len = (sector_size * numSectors); + + s32 ret; + + /* Device not opened */ + if (fd < 0) + return fd; + + /* Write data */ + ret = IOS_IoctlvFormat(hid, fd, USB_IOCTL_UMS_WRITE_SECTORS, "ii:d", sector, numSectors, buffer, len); + + return ret; +} + +static bool __io_usb_Startup(void) +{ + return USBStorage_Init() >= 0; +} + +static bool __io_usb_IsInserted(void) +{ + s32 ret; + if (fd < 0) return false; + ret = USBStorage_GetCapacity(NULL); + if (ret == 0) return false; + return true; +} + +bool __io_usb_ReadSectors(u32 sector, u32 count, void *buffer) +{ + s32 ret = USBStorage_ReadSectors(sector, count, buffer); + return ret > 0; +} + +bool __io_usb_WriteSectors(u32 sector, u32 count, void *buffer) +{ + s32 ret = USBStorage_WriteSectors(sector, count, buffer); + return ret > 0; +} + +static bool __io_usb_ClearStatus(void) +{ + return true; +} + +static bool __io_usb_Shutdown(void) +{ + // do nothing + return true; +} + +static bool __io_usb_NOP(void) +{ + // do nothing + return true; +} + +const DISC_INTERFACE __io_usbstorage_ro = { + DEVICE_TYPE_WII_USB, + FEATURE_MEDIUM_CANREAD | FEATURE_WII_USB, + (FN_MEDIUM_STARTUP) &__io_usb_Startup, + (FN_MEDIUM_ISINSERTED) &__io_usb_IsInserted, + (FN_MEDIUM_READSECTORS) &__io_usb_ReadSectors, + (FN_MEDIUM_WRITESECTORS) &__io_usb_NOP, //&__io_usb_WriteSectors, + (FN_MEDIUM_CLEARSTATUS) &__io_usb_ClearStatus, + (FN_MEDIUM_SHUTDOWN) &__io_usb_Shutdown +}; + +s32 USBStorage_WBFS_Open(char *buffer) +{ + u32 len = 8; + + s32 ret; + + /* Device not opened */ + if (fd < 0) + return fd; + + extern u32 wbfs_part_lba; + u32 part = wbfs_part_lba; + + /* Read data */ + ret = IOS_IoctlvFormat(hid, fd, USB_IOCTL_WBFS_OPEN_DISC, "dd:", buffer, len, &part, 4); + + return ret; +} + +// woffset is in 32bit words, len is in bytes +s32 USBStorage_WBFS_Read(u32 woffset, u32 len, void *buffer) +{ + s32 ret; + + USBStorage_Init(); + /* Device not opened */ + if (fd < 0) + return fd; + + /* Read data */ + ret = IOS_IoctlvFormat(hid, fd, USB_IOCTL_WBFS_READ_DISC, "ii:d", woffset, len, buffer, len); + + return ret; +} + + +s32 USBStorage_WBFS_ReadDebug(u32 off, u32 size, void *buffer) +{ + s32 ret; + + USBStorage_Init(); + /* Device not opened */ + if (fd < 0) + return fd; + + /* Read data */ + ret = IOS_IoctlvFormat(hid, fd, USB_IOCTL_WBFS_READ_DEBUG, "ii:d", off, size, buffer, size); + + return ret; +} + + +s32 USBStorage_WBFS_SetDevice(int dev) +{ + s32 ret; + static s32 retval = 0; + retval = 0; + USBStorage_Init(); + // Device not opened + if (fd < 0) return fd; + // ioctl + ret = IOS_IoctlvFormat(hid, fd, USB_IOCTL_WBFS_SET_DEVICE, "i:i", dev, &retval); + if (retval) return retval; + return ret; +} + +s32 USBStorage_WBFS_SetFragList(void *p, int size) +{ + s32 ret; + USBStorage_Init(); + // Device not opened + if (fd < 0) return fd; + // ioctl + ret = IOS_IoctlvFormat(hid, fd, USB_IOCTL_WBFS_SET_FRAGLIST, "d:", p, size); + return ret; +} + +#define DEVICE_TYPE_WII_UMS (('W'<<24)|('U'<<16)|('M'<<8)|'S') + +bool umsio_Startup() { + return USBStorage_Init() == 0; +} + +bool umsio_IsInserted() { + return true; // allways true +} + +bool umsio_ReadSectors(sec_t sector, sec_t numSectors, u8 *buffer) { + u32 cnt = 0; + s32 ret; + /* Do reads */ + while (cnt < numSectors) { + u32 sectors = (numSectors - cnt); + + /* Read sectors is too big */ + if (sectors > 32) + sectors = 32; + + /* USB read */ + ret = USBStorage_ReadSectors(sector + cnt, sectors, &buffer[cnt*512]); + if (ret < 0) + return false; + + /* Increment counter */ + cnt += sectors; + } + + return true; +} + +bool umsio_WriteSectors(sec_t sector, sec_t numSectors, const u8* buffer) { + u32 cnt = 0; + s32 ret; + + /* Do writes */ + while (cnt < numSectors) { + u32 sectors = (numSectors - cnt); + + /* Write sectors is too big */ + if (sectors > 32) + sectors = 32; + + /* USB write */ + ret = USBStorage_WriteSectors(sector + cnt, sectors, &buffer[cnt * 512]); + if (ret < 0) + return false; + + /* Increment counter */ + cnt += sectors; + } + + return true; +} + +bool umsio_ClearStatus(void) { + return true; +} + +bool umsio_Shutdown() { + USBStorage_Deinit(); + return true; +} + +const DISC_INTERFACE __io_wiiums = { + DEVICE_TYPE_WII_UMS, + FEATURE_MEDIUM_CANREAD | FEATURE_MEDIUM_CANWRITE | FEATURE_WII_USB, + (FN_MEDIUM_STARTUP) &umsio_Startup, + (FN_MEDIUM_ISINSERTED) &umsio_IsInserted, + (FN_MEDIUM_READSECTORS) &umsio_ReadSectors, + (FN_MEDIUM_WRITESECTORS) &umsio_WriteSectors, + (FN_MEDIUM_CLEARSTATUS) &umsio_ClearStatus, + (FN_MEDIUM_SHUTDOWN) &umsio_Shutdown +}; + +const DISC_INTERFACE __io_wiiums_ro = { + DEVICE_TYPE_WII_UMS, + FEATURE_MEDIUM_CANREAD | FEATURE_MEDIUM_CANWRITE | FEATURE_WII_USB, + (FN_MEDIUM_STARTUP) &umsio_Startup, + (FN_MEDIUM_ISINSERTED) &umsio_IsInserted, + (FN_MEDIUM_READSECTORS) &umsio_ReadSectors, + (FN_MEDIUM_WRITESECTORS) &__io_usb_NOP, + (FN_MEDIUM_CLEARSTATUS) &umsio_ClearStatus, + (FN_MEDIUM_SHUTDOWN) &umsio_Shutdown +}; diff --git a/source/usbstorage.h b/source/usbstorage.h new file mode 100644 index 0000000..6c6cec5 --- /dev/null +++ b/source/usbstorage.h @@ -0,0 +1,27 @@ +#ifndef _USBSTORAGE_H_ +#define _USBSTORAGE_H_ + +#ifdef __cplusplus +extern "C" { +#endif + /* Prototypes */ + s32 USBStorage_GetCapacity(u32 *); + s32 USBStorage_Init(void); + void USBStorage_Deinit(void); + s32 USBStorage_Watchdog(u32 on_off); + s32 USBStorage_ReadSectors(u32, u32, void *); + s32 USBStorage_WriteSectors(u32, u32, const void *); + + s32 USBStorage_WBFS_Open(char *buf_id); + s32 USBStorage_WBFS_Read(u32 woffset, u32 len, void *buffer); + s32 USBStorage_WBFS_ReadDebug(u32 off, u32 size, void *buffer); + s32 USBStorage_WBFS_SetDevice(int dev); + s32 USBStorage_WBFS_SetFragList(void *p, int size); + + extern const DISC_INTERFACE __io_wiiums; + extern const DISC_INTERFACE __io_wiiums_ro; +#ifdef __cplusplus +} +#endif + +#endif diff --git a/source/utils.h b/source/utils.h new file mode 100644 index 0000000..3a4862b --- /dev/null +++ b/source/utils.h @@ -0,0 +1,15 @@ +#ifndef _UTILS_H_ +#define _UTILS_H_ + +/* Constants */ +#define KB_SIZE 1024.0 +#define MB_SIZE 1048576.0 +#define GB_SIZE 1073741824.0 + +/* Macros */ +#define round_up(x,n) (-(-(x) & -(n))) + +/* Prototypes */ +u32 swap32(u32); + +#endif diff --git a/source/video.c b/source/video.c new file mode 100644 index 0000000..5fd492a --- /dev/null +++ b/source/video.c @@ -0,0 +1,141 @@ +#include +#include + +#include "sys.h" +#include "video.h" + +/* Video variables */ +static void *framebuffer = NULL; +static GXRModeObj *vmode = NULL; + + +void Con_Init(u32 x, u32 y, u32 w, u32 h) +{ + /* Create console in the framebuffer */ + CON_InitEx(vmode, x, y, w, h); +} + +void Con_Clear(void) +{ + /* Clear console */ + printf("\x1b[2J"); + fflush(stdout); +} + +void Con_ClearLine(void) +{ + int cols, rows; + u32 cnt; + + printf("\r"); + fflush(stdout); + + /* Get console metrics */ + CON_GetMetrics(&cols, &rows); + + /* Erase line */ + for (cnt = 1; cnt < cols; cnt++) { + printf(" "); + fflush(stdout); + } + + printf("\r"); + fflush(stdout); +} + +void Con_FgColor(u32 color, u8 bold) +{ + /* Set foreground color */ + printf("\x1b[%lu;%um", color + 30, bold); + fflush(stdout); +} + +void Con_BgColor(u32 color, u8 bold) +{ + /* Set background color */ + printf("\x1b[%lu;%um", color + 40, bold); + fflush(stdout); +} + +void Con_FillRow(u32 row, u32 color, u8 bold) +{ + int cols, rows; + u32 cnt; + + /* Set color */ + printf("\x1b[%lu;%um", color + 40, bold); + fflush(stdout); + + /* Get console metrics */ + CON_GetMetrics(&cols, &rows); + + /* Save current row and col */ + printf("\x1b[s"); + fflush(stdout); + + /* Move to specified row */ + printf("\x1b[%lu;0H", row); + fflush(stdout); + + /* Fill row */ + for (cnt = 0; cnt < cols; cnt++) { + printf(" "); + fflush(stdout); + } + + /* Load saved row and col */ + printf("\x1b[u"); + fflush(stdout); + + /* Set default color */ + Con_BgColor(0, 0); + Con_FgColor(7, 1); +} + +void Video_Configure(GXRModeObj *rmode) +{ + /* Configure the video subsystem */ + VIDEO_Configure(rmode); + + /* Setup video */ + VIDEO_SetBlack(FALSE); + VIDEO_Flush(); + VIDEO_WaitVSync(); + + if (rmode->viTVMode & VI_NON_INTERLACE) + VIDEO_WaitVSync(); +} + +void Video_SetMode(void) +{ + /* Select preferred video mode */ + vmode = VIDEO_GetPreferredMode(NULL); + + /* Allocate memory for the framebuffer */ + framebuffer = MEM_K0_TO_K1(SYS_AllocateFramebuffer(vmode)); + + /* Configure the video subsystem */ + VIDEO_Configure(vmode); + + /* Setup video */ + VIDEO_SetNextFramebuffer(framebuffer); + VIDEO_SetBlack(FALSE); + VIDEO_Flush(); + VIDEO_WaitVSync(); + + if (vmode->viTVMode & VI_NON_INTERLACE) + VIDEO_WaitVSync(); + + /* Clear the screen */ + Video_Clear(COLOR_BLACK); +} + +void Video_Clear(s32 color) +{ + VIDEO_ClearFrameBuffer(vmode, framebuffer, color); +} + +void Video_DrawPng(IMGCTX ctx, PNGUPROP imgProp, u16 x, u16 y) +{ + PNGU_DECODE_TO_COORDS_YCbYCr(ctx, x, y, imgProp.imgWidth, imgProp.imgHeight, vmode->fbWidth, vmode->xfbHeight, framebuffer); +} diff --git a/source/video.h b/source/video.h new file mode 100644 index 0000000..e0fb7fd --- /dev/null +++ b/source/video.h @@ -0,0 +1,19 @@ +#ifndef _VIDEO_H_ +#define _VIDEO_H_ + +#include "libpng/pngu/pngu.h" + +/* Prototypes */ +void Con_Init(u32, u32, u32, u32); +void Con_Clear(void); +void Con_ClearLine(void); +void Con_FgColor(u32, u8); +void Con_BgColor(u32, u8); +void Con_FillRow(u32, u32, u8); + +void Video_Configure(GXRModeObj *); +void Video_SetMode(void); +void Video_Clear(s32); +void Video_DrawPng(IMGCTX, PNGUPROP, u16, u16); + +#endif diff --git a/source/wad-manager.c b/source/wad-manager.c new file mode 100644 index 0000000..f3f768a --- /dev/null +++ b/source/wad-manager.c @@ -0,0 +1,426 @@ +#include +#include +#include +#include +#include +#include +#include +#include + +#include "sys.h" +#include "gui.h" +#include "menu.h" +#include "restart.h" +#include "sys.h" +#include "video.h" +#include "wpad.h" +#include "fat.h" +#include "nand.h" +#include "globals.h" +#include "iospatch.h" + +// Globals +CONFIG gConfig; + +// Prototypes +extern u32 WaitButtons (void); +void CheckPassword (void); +void SetDefaultConfig (void); +int ReadConfigFile (char *configFilePath); +int GetIntParam (char *inputStr); +int GetStartupPath (char *startupPath, char *inputStr); +int GetStringParam (char *outParam, char *inputStr, int maxChars); + +// Default password Up-Down-Left-Right-Up-Down +//#define PASSWORD "UDLRUD" +void CheckPassword (void) +{ + char curPassword [11]; // Max 10 characters password, NULL terminated + int count = 0; + + if (strlen (gConfig.password) == 0) + return; + + // Ask user for a password. Press "B" to restart Wii + printf("[+] [Enter Password to Continue]:\n\n"); + + printf(">> Press A to continue.\n"); + printf(">> Press B button to restart your Wii.\n"); + + /* Wait for user answer */ + for (;;) + { + u32 buttons = WaitButtons(); + + if (buttons & WPAD_BUTTON_A) + { + // A button, validate the pw + curPassword [count] = 0; + //if (strcmp (curPassword, PASSWORD) == 0) + if (strcmp (curPassword, gConfig.password) == 0) + { + printf(">> Password Accepted...\n"); + break; + } + else + { + printf ("\n"); + printf(">> Incorrect Password. Try again...\n"); + printf("[+] [Enter Password to Continue]:\n\n"); + printf(">> Press A to continue.\n"); + printf(">> Press B button to restart your Wii.\n"); + count = 0; + } + } + else if (buttons & WPAD_BUTTON_B) + // B button, restart + Restart(); + else + { + if (count < 10) + { + // Other buttons, build the password + if (buttons & WPAD_BUTTON_LEFT) + { + curPassword [count++] = 'L'; + printf ("*"); + } + else if (buttons & WPAD_BUTTON_RIGHT) + { + curPassword [count++] = 'R'; + printf ("*"); + } + else if (buttons & WPAD_BUTTON_UP) + { + curPassword [count++] = 'U'; + printf ("*"); + } + else if (buttons & WPAD_BUTTON_DOWN) + { + curPassword [count++] = 'D'; + printf ("*"); + } + else if (buttons & WPAD_BUTTON_1) + { + curPassword [count++] = '1'; + printf ("*"); + } + else if (buttons & WPAD_BUTTON_2) + { + curPassword [count++] = '2'; + printf ("*"); + } + } + } + } +} + +void Disclaimer(void) +{ + /* Print disclaimer */ + printf("[+] [DISCLAIMER]:\n\n"); + + printf(" THIS APPLICATION COMES WITH NO WARRANTY AT ALL,\n"); + printf(" NEITHER EXPRESS NOR IMPLIED.\n"); + printf(" I DO NOT TAKE ANY RESPONSIBILITY FOR ANY DAMAGE IN YOUR\n"); + printf(" WII CONSOLE BECAUSE OF A IMPROPER USAGE OF THIS SOFTWARE.\n\n"); + + printf(">> If you agree, press A button to continue.\n"); + printf(">> Otherwise, press B button to restart your Wii.\n"); + + /* Wait for user answer */ + for (;;) { + //u32 buttons = Wpad_WaitButtons(); + u32 buttons = WaitButtons(); + + /* A button */ + if (buttons & WPAD_BUTTON_A) + break; + + /* B button */ + if (buttons & WPAD_BUTTON_B) + Restart(); + } +} + +int main(int argc, char **argv) +{ + ES_GetBoot2Version(&boot2version); + if(!AHBPROT_DISABLED) + { + if(boot2version < 5) + { + if(!loadIOS(202)) if(!loadIOS(222)) if(!loadIOS(223)) if(!loadIOS(224)) if(!loadIOS(249)) loadIOS(36); + }else{ + if(!loadIOS(249)) loadIOS(36); + } + } + /* Initialize subsystems */ + Sys_Init(); + + /* Set video mode */ + Video_SetMode(); + + /* Initialize console */ + Gui_InitConsole(); + + /* Draw background */ + Gui_DrawBackground(); + + /* Initialize Wiimote and GC Controller */ + Wpad_Init(); + PAD_Init (); + WiiDRC_Init(); + WIILIGHT_Init(); + + /* Print disclaimer */ + //Disclaimer(); + + // Set the defaults + SetDefaultConfig (); + + // Read the config file + ReadConfigFile (WM_CONFIG_FILE_PATH); + + // Check password + CheckPassword (); + + /* Menu loop */ + Menu_Loop(); + + /* Restart Wii */ + Restart_Wait(); + + return 0; +} + + +int ReadConfigFile (char *configFilePath) +{ + int retval = 0; + FILE *fptr; + char *tmpStr = malloc (MAX_FILE_PATH_LEN); + char tmpOutStr [40], path[128]; + int i; + + if (tmpStr == NULL) + return (-1); + + fatDevice *fdev = &fdevList[0]; + int ret = Fat_Mount(fdev); + snprintf(path, sizeof(path), "%s%s", fdev->mount, configFilePath); + + if (ret < 0) + { + fdev = &fdevList[2]; + ret = Fat_Mount(fdev); + snprintf(path, sizeof(path), "%s%s", fdev->mount, configFilePath); + snprintf(path, sizeof(path), "%s%s", fdev->mount, configFilePath); + } + + if (ret < 0) + { + printf(" ERROR! (ret = %d)\n", ret); + // goto err; + retval = -1; + } + else + { + // Read the file + fptr = fopen (path, "rb"); + if (fptr != NULL) + { + // Read the options + char done = 0; + + while (!done) + { + if (fgets (tmpStr, MAX_FILE_PATH_LEN, fptr) == NULL) + done = 1; + else if (isalpha(tmpStr[0])) + { + // Get the password + if (strncmp (tmpStr, "Password", 8) == 0) + { + // Get password + // GetPassword (gConfig.password, tmpStr); + GetStringParam (gConfig.password, tmpStr, MAX_PASSWORD_LENGTH); + + // If password is too long, ignore it + if (strlen (gConfig.password) > 10) + { + gConfig.password [0] = 0; + printf ("Password longer than 10 characters; will be ignored. Press a button...\n"); + WaitButtons (); + } + } + + // Get startup path + else if (strncmp (tmpStr, "StartupPath", 11) == 0) + { + // Get startup Path + GetStartupPath (gConfig.startupPath, tmpStr); + } + + // cIOS + else if (strncmp (tmpStr, "cIOSVersion", 11) == 0) + { + // Get cIOSVersion + gConfig.cIOSVersion = (u8)GetIntParam (tmpStr); + } + + // FatDevice + else if (strncmp (tmpStr, "FatDevice", 9) == 0) + { + // Get fatDevice + GetStringParam (tmpOutStr, tmpStr, MAX_FAT_DEVICE_LENGTH); + for (i = 0; i < 5; i++) + { + if (strncmp (fdevList[i].mount, tmpOutStr, 4) == 0) + { + gConfig.fatDeviceIndex = i; + } + } + } + + // NandDevice + else if (strncmp (tmpStr, "NANDDevice", 10) == 0) + { + // Get fatDevice + GetStringParam (tmpOutStr, tmpStr, MAX_NAND_DEVICE_LENGTH); + for (i = 0; i < 3; i++) + { + if (strncmp (ndevList[i].name, tmpOutStr, 2) == 0) + { + gConfig.nandDeviceIndex = i; + } + } + } + } + } // EndWhile + + // Close the config file + fclose (fptr); + } + else + { + // If the wm_config.txt file is not found, just take the default config params + //printf ("Config file is not found\n"); // This is for testing only + //WaitButtons(); + } + Fat_Unmount(fdev); + } + + // Free memory + free (tmpStr); + + return (retval); +} // ReadConfig + + +void SetDefaultConfig (void) +{ + // Default password is NULL or no password + gConfig.password [0] = 0; + + // Default startup folder + strcpy (gConfig.startupPath, WAD_ROOT_DIRECTORY); + + gConfig.cIOSVersion = CIOS_VERSION_INVALID; // Means that user has to select later + gConfig.fatDeviceIndex = FAT_DEVICE_INDEX_INVALID; // Means that user has to select + gConfig.nandDeviceIndex = NAND_DEVICE_INDEX_INVALID; // Means that user has to select + +} // SetDefaultConfig + + +int GetStartupPath (char *startupPath, char *inputStr) +{ + int i = 0; + int len = strlen (inputStr); + + // Find the "=" + while ((inputStr [i] != '=') && (i < len)) + { + i++; + } + i++; + + // Get to the "/" + while ((inputStr [i] != '/') && (i < len)) + { + i++; + } + + // Get the startup Path + int count = 0; + while (isascii(inputStr [i]) && (i < len) && (inputStr [i] != '\n') && + (inputStr [i] != '\r') && (inputStr [i] != ' ')) + { + startupPath [count++] = inputStr [i++]; + } + startupPath [count] = 0; // NULL terminate + + return (0); +} // GetStartupPath + +int GetIntParam (char *inputStr) +{ + int retval = 0; + int i = 0; + int len = strlen (inputStr); + char outParam [40]; + + // Find the "=" + while ((inputStr [i] != '=') && (i < len)) + { + i++; + } + i++; + + // Get to the first alpha numeric character + while ((isdigit(inputStr [i]) == 0) && (i < len)) + { + i++; + } + + // Get the string param + int outCount = 0; + while ((isdigit(inputStr [i])) && (i < len) && (outCount < 40)) + { + outParam [outCount++] = inputStr [i++]; + } + outParam [outCount] = 0; // NULL terminate + retval = atoi (outParam); + + return (retval); +} // GetIntParam + + +int GetStringParam (char *outParam, char *inputStr, int maxChars) +{ + int i = 0; + int len = strlen (inputStr); + + // Find the "=" + while ((inputStr [i] != '=') && (i < len)) + { + i++; + } + i++; + + // Get to the first alpha character + while ((isalpha(inputStr [i]) == 0) && (i < len)) + { + i++; + } + + // Get the string param + int outCount = 0; + while ((isalnum(inputStr [i])) && (i < len) && (outCount < maxChars)) + { + outParam [outCount++] = inputStr [i++]; + } + outParam [outCount] = 0; // NULL terminate + + return (0); +} // GetStringParam diff --git a/source/wad.c b/source/wad.c new file mode 100644 index 0000000..36aee55 --- /dev/null +++ b/source/wad.c @@ -0,0 +1,677 @@ +#include +#include +#include +#include +#include + +#include "sys.h" +#include "title.h" +#include "utils.h" +#include "video.h" +#include "wad.h" +#include "wpad.h" + +// Turn upper and lower into a full title ID +#define TITLE_ID(x,y) (((u64)(x) << 32) | (y)) +// Get upper or lower half of a title ID +#define TITLE_UPPER(x) ((u32)((x) >> 32)) +// Turn upper and lower into a full title ID +#define TITLE_LOWER(x) ((u32)(x)) + +typedef struct { + int version; + int region; + +} SMRegion; + +SMRegion regionlist[] = { + {33, 'X'}, + {128, 'J'}, {97, 'E'}, {130, 'P'}, + {162, 'P'}, + {192, 'J'}, {193, 'E'}, {194, 'P'}, + {224, 'J'}, {225, 'E'}, {226, 'P'}, + {256, 'J'}, {257, 'E'}, {258, 'P'}, + {288, 'J'}, {289, 'E'}, {290, 'P'}, + {352, 'J'}, {353, 'E'}, {354, 'P'}, {326, 'K'}, + {384, 'J'}, {385, 'E'}, {386, 'P'}, + {390, 'K'}, + {416, 'J'}, {417, 'E'}, {418, 'P'}, + {448, 'J'}, {449, 'E'}, {450, 'P'}, {454, 'K'}, + {480, 'J'}, {481, 'E'}, {482, 'P'}, {486, 'K'}, + {512, 'E'}, {513, 'E'}, {514, 'P'}, {518, 'K'}, +}; + +#define NB_SM (sizeof(regionlist) / sizeof(SMRegion)) + +u32 WaitButtons(void); + +u32 be32(const u8 *p) +{ + return (p[0] << 24) | (p[1] << 16) | (p[2] << 8) | p[3]; +} + +u64 be64(const u8 *p) +{ + return ((u64)be32(p) << 32) | be32(p + 4); +} + +u64 get_title_ios(u64 title) { + s32 ret, fd; + static char filepath[256] ATTRIBUTE_ALIGN(32); + + // Check to see if title exists + if (ES_GetDataDir(title, filepath) >= 0 ) { + u32 tmd_size; + static u8 tmd_buf[MAX_SIGNED_TMD_SIZE] ATTRIBUTE_ALIGN(32); + + ret = ES_GetStoredTMDSize(title, &tmd_size); + if (ret < 0){ + // If we fail to use the ES function, try reading manually + // This is a workaround added since some IOS (like 21) don't like our + // call to ES_GetStoredTMDSize + + //printf("Error! ES_GetStoredTMDSize: %d\n", ret); + + sprintf(filepath, "/title/%08lx/%08lx/content/title.tmd", TITLE_UPPER(title), TITLE_LOWER(title)); + + ret = ISFS_Open(filepath, ISFS_OPEN_READ); + if (ret <= 0) + { + //printf("Error! ISFS_Open (ret = %d)\n", ret); + return 0; + } + + fd = ret; + + ret = ISFS_Seek(fd, 0x184, 0); + if (ret < 0) + { + //printf("Error! ISFS_Seek (ret = %d)\n", ret); + return 0; + } + + ret = ISFS_Read(fd,tmd_buf,8); + if (ret < 0) + { + //printf("Error! ISFS_Read (ret = %d)\n", ret); + return 0; + } + + ret = ISFS_Close(fd); + if (ret < 0) + { + //printf("Error! ISFS_Close (ret = %d)\n", ret); + return 0; + } + + return be64(tmd_buf); + + } else { + // Normal versions of IOS won't have a problem, so we do things the "right" way. + + // Some of this code adapted from bushing's title_lister.c + signed_blob *s_tmd = (signed_blob *)tmd_buf; + ret = ES_GetStoredTMD(title, s_tmd, tmd_size); + if (ret < 0){ + //printf("Error! ES_GetStoredTMD: %d\n", ret); + return -1; + } + tmd *t = SIGNATURE_PAYLOAD(s_tmd); + return t->sys_version; + } + + + } + return 0; +} + +int get_sm_region_basic() +{ + u32 tmd_size; + + u64 title = TITLE_ID(1, 2); + static u8 tmd_buf[MAX_SIGNED_TMD_SIZE] ATTRIBUTE_ALIGN(32); + + int ret = ES_GetStoredTMDSize(title, &tmd_size); + + // Some of this code adapted from bushing's title_lister.c + signed_blob *s_tmd = (signed_blob *)tmd_buf; + ret = ES_GetStoredTMD(title, s_tmd, tmd_size); + if (ret < 0){ + //printf("Error! ES_GetStoredTMD: %d\n", ret); + return -1; + } + tmd *t = SIGNATURE_PAYLOAD(s_tmd); + ret = t->title_version; + int i = 0; + while( i <= NB_SM) + { + if( regionlist[i].version == ret) return regionlist[i].region; + i++; + } + return 0; +} + +/* 'WAD Header' structure */ +typedef struct { + /* Header length */ + u32 header_len; + + /* WAD type */ + u16 type; + + u16 padding; + + /* Data length */ + u32 certs_len; + u32 crl_len; + u32 tik_len; + u32 tmd_len; + u32 data_len; + u32 footer_len; +} ATTRIBUTE_PACKED wadHeader; + +/* Variables */ +static u8 wadBuffer[BLOCK_SIZE] ATTRIBUTE_ALIGN(32); + + +s32 __Wad_ReadFile(FILE *fp, void *outbuf, u32 offset, u32 len) +{ + s32 ret; + + /* Seek to offset */ + fseek(fp, offset, SEEK_SET); + + /* Read data */ + ret = fread(outbuf, len, 1, fp); + if (ret < 0) + return ret; + + return 0; +} + +s32 __Wad_ReadAlloc(FILE *fp, void **outbuf, u32 offset, u32 len) +{ + void *buffer = NULL; + s32 ret; + + /* Allocate memory */ + buffer = memalign(32, len); + if (!buffer) + return -1; + + /* Read file */ + ret = __Wad_ReadFile(fp, buffer, offset, len); + if (ret < 0) { + free(buffer); + return ret; + } + + /* Set pointer */ + *outbuf = buffer; + + return 0; +} + +s32 __Wad_GetTitleID(FILE *fp, wadHeader *header, u64 *tid) +{ + signed_blob *p_tik = NULL; + tik *tik_data = NULL; + + u32 offset = 0; + s32 ret; + + /* Ticket offset */ + offset += round_up(header->header_len, 64); + offset += round_up(header->certs_len, 64); + offset += round_up(header->crl_len, 64); + + /* Read ticket */ + ret = __Wad_ReadAlloc(fp, (void *)&p_tik, offset, header->tik_len); + if (ret < 0) + goto out; + + /* Ticket data */ + tik_data = (tik *)SIGNATURE_PAYLOAD(p_tik); + + /* Copy title ID */ + *tid = tik_data->titleid; + +out: + /* Free memory */ + if (p_tik) + free(p_tik); + + return ret; +} + +void __Wad_FixTicket(signed_blob *p_tik) +{ + u8 *data = (u8 *)p_tik; + u8 *ckey = data + 0x1F1; + + /* Check common key */ + if (*ckey > 1) + *ckey = 0; + + /* Fakesign ticket */ + Title_FakesignTik(p_tik); +} + +s32 Wad_Install(FILE *fp) +{ + wadHeader *header = NULL; + signed_blob *p_certs = NULL, *p_crl = NULL, *p_tik = NULL, *p_tmd = NULL; + + tmd *tmd_data = NULL; + + u32 cnt, offset = 0; + int ret; + u64 tid; + + printf("\t\t>> Reading WAD data..."); + fflush(stdout); + + ret = __Wad_ReadAlloc(fp, (void *)&header, offset, sizeof(wadHeader)); + if (ret >= 0) + offset += round_up(header->header_len, 64); + else + goto err; + + //Don't try to install boot2 + __Wad_GetTitleID(fp, header, &tid); + + if (tid == TITLE_ID(1, 1)) + { + printf("\n I can't let you do that Dave\n"); + ret = -999; + goto out; + } + + /* WAD certificates */ + ret = __Wad_ReadAlloc(fp, (void *)&p_certs, offset, header->certs_len); + if (ret >= 0) + offset += round_up(header->certs_len, 64); + else + goto err; + + /* WAD crl */ + if (header->crl_len) { + ret = __Wad_ReadAlloc(fp, (void *)&p_crl, offset, header->crl_len); + if (ret < 0) + goto err; + else + offset += round_up(header->crl_len, 64); + } + + /* WAD ticket */ + ret = __Wad_ReadAlloc(fp, (void *)&p_tik, offset, header->tik_len); + if (ret < 0) + goto err; + else + offset += round_up(header->tik_len, 64); + + /* WAD TMD */ + ret = __Wad_ReadAlloc(fp, (void *)&p_tmd, offset, header->tmd_len); + if (ret < 0) + goto err; + else + offset += round_up(header->tmd_len, 64); + + Con_ClearLine(); + + /* Get TMD info */ + + tmd_data = (tmd *)SIGNATURE_PAYLOAD(p_tmd); + + if(TITLE_LOWER(tmd_data->sys_version) != 0 && isIOSstub(TITLE_LOWER(tmd_data->sys_version))) + { + printf("\n This Title wants IOS%li but the installed version\n is a stub.\n", TITLE_LOWER(tmd_data->sys_version)); + ret = -999; + goto err; + } + + if(get_title_ios(TITLE_ID(1, 2)) == tid) + { + if ( ( tmd_data->num_contents == 3) && (tmd_data->contents[0].type == 1 && tmd_data->contents[1].type == 0x8001 && tmd_data->contents[2].type == 0x8001) ) + { + printf("\n I won't install a stub System Menu IOS\n"); + ret = -999; + goto err; + } + } + + if(tid == get_title_ios(TITLE_ID(0x10008, 0x48414B00 | 'E')) || tid == get_title_ios(TITLE_ID(0x10008, 0x48414B00 | 'P')) || tid == get_title_ios(TITLE_ID(0x10008, 0x48414B00 | 'J')) || tid == get_title_ios(TITLE_ID(0x10008, 0x48414B00 | 'K'))) + { + if ( ( tmd_data->num_contents == 3) && (tmd_data->contents[0].type == 1 && tmd_data->contents[1].type == 0x8001 && tmd_data->contents[2].type == 0x8001) ) + { + printf("\n I won't install a stub EULA IOS\n"); + ret = -999; + goto err; + } + } + + if(tid == get_title_ios(TITLE_ID(0x10008, 0x48414C00 | 'E')) || tid == get_title_ios(TITLE_ID(0x10008, 0x48414C00 | 'P')) || tid == get_title_ios(TITLE_ID(0x10008, 0x48414C00 | 'J')) || tid == get_title_ios(TITLE_ID(0x10008, 0x48414C00 | 'K'))) + { + if ( ( tmd_data->num_contents == 3) && (tmd_data->contents[0].type == 1 && tmd_data->contents[1].type == 0x8001 && tmd_data->contents[2].type == 0x8001) ) + { + printf("\n I won't install a stub rgsel IOS\n"); + ret = -999; + goto err; + } + } + if (tid == get_title_ios(TITLE_ID(0x10001, 0x48415858)) || tid == get_title_ios(TITLE_ID(0x10001, 0x4A4F4449))) + { + if ( ( tmd_data->num_contents == 3) && (tmd_data->contents[0].type == 1 && tmd_data->contents[1].type == 0x8001 && tmd_data->contents[2].type == 0x8001) ) + { + printf("\n Are you sure you wan't to install a stub HBC IOS?\n"); + printf("\n Press A to continue.\n"); + printf(" Press B skip."); + + u32 buttons = WaitButtons(); + + if (!(buttons & WPAD_BUTTON_A)) + { + ret = -998; + goto err; + } + } + } + + if (tid == TITLE_ID(1, 2)) + { + if(get_sm_region_basic() == 0) + { + printf("\n Can't get the SM region\n Please check the site for updates\n"); + ret = -999; + goto err; + } + int i, ret = -1; + for(i = 0; i <= NB_SM; i++) + { + if( regionlist[i].version == tmd_data->title_version) + { + ret = 1; + break; + } + } + if(ret -1) + { + printf("\n Can't get the SM region\n Please check the site for updates\n"); + ret = -999; + goto err; + } + if( get_sm_region_basic() != regionlist[i].region) + { + printf("\n I won't install the wrong regions SM\n"); + ret = -999; + goto err; + } + if(tmd_data->title_version < 416) + { + if(boot2version == 4) + { + printf("\n This version of the System Menu\n is not compatible with your Wii\n"); + ret = -999; + goto err; + } + } + } + + /* Fix ticket */ + __Wad_FixTicket(p_tik); + + printf("\t\t>> Installing ticket..."); + fflush(stdout); + + /* Install ticket */ + ret = ES_AddTicket(p_tik, header->tik_len, p_certs, header->certs_len, p_crl, header->crl_len); + if (ret < 0) + goto err; + + Con_ClearLine(); + + printf("\r\t\t>> Installing title..."); + fflush(stdout); + + /* Install title */ + ret = ES_AddTitleStart(p_tmd, header->tmd_len, p_certs, header->certs_len, p_crl, header->crl_len); + if (ret < 0) + goto err; + + /* Install contents */ + for (cnt = 0; cnt < tmd_data->num_contents; cnt++) { + tmd_content *content = &tmd_data->contents[cnt]; + + u32 idx = 0, len; + s32 cfd; + + Con_ClearLine(); + + printf("\r\t\t>> Installing content #%02ld...", content->cid); + fflush(stdout); + + /* Encrypted content size */ + len = round_up(content->size, 64); + + /* Install content */ + cfd = ES_AddContentStart(tmd_data->title_id, content->cid); + if (cfd < 0) { + ret = cfd; + goto err; + } + + /* Install content data */ + while (idx < len) { + u32 size; + + /* Data length */ + size = (len - idx); + if (size > BLOCK_SIZE) + size = BLOCK_SIZE; + + /* Read data */ + ret = __Wad_ReadFile(fp, &wadBuffer, offset, size); + if (ret < 0) + goto err; + + /* Install data */ + ret = ES_AddContentData(cfd, wadBuffer, size); + if (ret < 0) + goto err; + + /* Increase variables */ + idx += size; + offset += size; + } + + /* Finish content installation */ + ret = ES_AddContentFinish(cfd); + if (ret < 0) + goto err; + } + + Con_ClearLine(); + + printf("\r\t\t>> Finishing installation..."); + fflush(stdout); + + /* Finish title install */ + ret = ES_AddTitleFinish(); + if (ret >= 0) { + printf(" OK!\n"); + goto out; + } + +err: + printf(" ERROR! (ret = %d)\n", ret); + + /* Cancel install */ + ES_AddTitleCancel(); + +out: + /* Free memory */ + if (header) + free(header); + if (p_certs) + free(p_certs); + if (p_crl) + free(p_crl); + if (p_tik) + free(p_tik); + if (p_tmd) + free(p_tmd); + + return ret; +} + +s32 Wad_Uninstall(FILE *fp) +{ + wadHeader *header = NULL; + tikview *viewData = NULL; + + u64 tid; + u32 viewCnt; + int ret; + + printf("\t\t>> Reading WAD data..."); + fflush(stdout); + + /* WAD header */ + ret = __Wad_ReadAlloc(fp, (void *)&header, 0, sizeof(wadHeader)); + if (ret < 0) { + printf(" ERROR! (ret = %d)\n", ret); + goto out; + } + + /* Get title ID */ + ret = __Wad_GetTitleID(fp, header, &tid); + if (ret < 0) { + printf(" ERROR! (ret = %d)\n", ret); + goto out; + } + //Assorted Checks + if (TITLE_UPPER(tid) == 1 && get_title_ios(TITLE_ID(1, 2)) == 0) + { + printf("\n I can't determine the System Menus IOS\nDeleting system titles is disabled\n"); + ret = -999; + goto out; + } + if (tid == TITLE_ID(1, 1)) + { + printf("\n I won't try to uninstall boot2\n"); + ret = -999; + goto out; + } + if (tid == TITLE_ID(1, 2)) + { + printf("\n I won't uninstall the System Menu\n"); + ret = -999; + goto out; + } + if(get_title_ios(TITLE_ID(1, 2)) == tid) + { + printf("\n I won't uninstall the System Menus IOS\n"); + ret = -999; + goto out; + } + if (tid == get_title_ios(TITLE_ID(0x10001, 0x48415858)) || tid == get_title_ios(TITLE_ID(0x10001, 0x4A4F4449))) + { + printf("\n This is the HBCs IOS, uninstalling will break the HBC!\n"); + printf("\n Press A to continue.\n"); + printf(" Press B skip."); + + u32 buttons = WaitButtons(); + + if (!(buttons & WPAD_BUTTON_A)) + { + ret = -998; + goto out; + } + } + if((tid == TITLE_ID(0x10008, 0x48414B00 | 'E') || tid == TITLE_ID(0x10008, 0x48414B00 | 'P') || tid == TITLE_ID(0x10008, 0x48414B00 | 'J') || tid == TITLE_ID(0x10008, 0x48414B00 | 'K') + || (tid == TITLE_ID(0x10008, 0x48414C00 | 'E') || tid == TITLE_ID(0x10008, 0x48414C00 | 'P') || tid == TITLE_ID(0x10008, 0x48414C00 | 'J') || tid == TITLE_ID(0x10008, 0x48414C00 | 'K'))) && get_sm_region_basic() == 0) + { + printf("\n Can't get the SM region\n Please check the site for updates\n"); + ret = -999; + goto out; + } + if(tid == TITLE_ID(0x10008, 0x48414B00 | get_sm_region_basic())) + { + printf("\n I won't uninstall the EULA\n"); + ret = -999; + goto out; + } + if(tid == TITLE_ID(0x10008, 0x48414C00 | get_sm_region_basic())) + { + printf("\n I won't uninstall rgsel\n"); + ret = -999; + goto out; + } + if(tid == get_title_ios(TITLE_ID(0x10008, 0x48414B00 | get_sm_region_basic()))) + { + printf("\n I won't uninstall the EULAs IOS\n"); + ret = -999; + goto out; + } + if(tid == get_title_ios(TITLE_ID(0x10008, 0x48414C00 | get_sm_region_basic()))) + { + printf("\n I won't uninstall the rgsel IOS\n"); + ret = -999; + goto out; + } + + Con_ClearLine(); + + printf("\t\t>> Deleting tickets..."); + fflush(stdout); + + /* Get ticket views */ + ret = Title_GetTicketViews(tid, &viewData, &viewCnt); + if (ret < 0) + printf(" ERROR! (ret = %d)\n", ret); + + /* Delete tickets */ + if (ret >= 0) { + u32 cnt; + + /* Delete all tickets */ + for (cnt = 0; cnt < viewCnt; cnt++) { + ret = ES_DeleteTicket(&viewData[cnt]); + if (ret < 0) + break; + } + + if (ret < 0) + printf(" ERROR! (ret = %d\n", ret); + else + printf(" OK!\n"); + } + + printf("\t\t>> Deleting title contents..."); + fflush(stdout); + + /* Delete title contents */ + ret = ES_DeleteTitleContent(tid); + if (ret < 0) + printf(" ERROR! (ret = %d)\n", ret); + else + printf(" OK!\n"); + + + printf("\t\t>> Deleting title..."); + fflush(stdout); + + /* Delete title */ + ret = ES_DeleteTitle(tid); + if (ret < 0) + printf(" ERROR! (ret = %d)\n", ret); + else + printf(" OK!\n"); + +out: + /* Free memory */ + if (header) + free(header); + return ret; +} diff --git a/source/wad.h b/source/wad.h new file mode 100644 index 0000000..f5b2586 --- /dev/null +++ b/source/wad.h @@ -0,0 +1,8 @@ +#ifndef _WAD_H_ +#define _WAD_H_ + +/* Prototypes */ +s32 Wad_Install(FILE *); +s32 Wad_Uninstall(FILE *); + +#endif diff --git a/source/wkb.c b/source/wkb.c new file mode 100644 index 0000000..f04ed68 --- /dev/null +++ b/source/wkb.c @@ -0,0 +1,47 @@ +#include "wkb.h" + +/* + +s32 USBKeyboard_Open(const eventcallback cb); +void USBKeyboard_Close(void); + +bool USBKeyboard_IsConnected(void); +s32 USBKeyboard_Scan(void); + +s32 USBKeyboard_SetLed(const USBKeyboard_led led, bool on); +s32 USBKeyboard_ToggleLed(const USBKeyboard_led led); +*/ + +s32 WkbInit(void) +{ + s32 retval = 0; + + retval = USBKeyboard_Initialize(); + + return (retval); + +} // WkbInit + +s32 WkbDeInit(void) +{ + s32 retval = 0; + + retval = USBKeyboard_Deinitialize(); + + return (retval); + +} // WkbDeInit + +u32 WkbWaitKey (void) +{ + u32 retval = 0; + + // Stub + return (retval); + +} // WkbWaitKey + +//void Wpad_Disconnect(void); +//u32 Wpad_GetButtons(void); + + diff --git a/source/wkb.h b/source/wkb.h new file mode 100644 index 0000000..568572f --- /dev/null +++ b/source/wkb.h @@ -0,0 +1,19 @@ +#ifndef _WKB_H_ +#define _WKB_H_ + +//#include +//#include +//#include +//#include +#include // u8, u16, etc... + +#include +#include + +/* Prototypes */ +s32 WkbInit(void); +u32 WkbWaitKey (void); +//void Wpad_Disconnect(void); +//u32 Wpad_GetButtons(void); + +#endif diff --git a/source/wpad.c b/source/wpad.c new file mode 100644 index 0000000..2d60dfe --- /dev/null +++ b/source/wpad.c @@ -0,0 +1,105 @@ +#include +#include +#include +#include + +#include "sys.h" +#include "wpad.h" +//#include "wkb.h" + +/* Constants */ +#define MAX_WIIMOTES 4 + +int start; + +void __Wpad_PowerCallback(s32 chan) +{ + /* Poweroff console */ + Sys_Shutdown(); +} + + +s32 Wpad_Init(void) +{ + s32 ret; + + /* Initialize Wiimote subsystem */ + ret = WPAD_Init(); + if (ret < 0) + return ret; + + /* Set POWER button callback */ + WPAD_SetPowerButtonCallback(__Wpad_PowerCallback); + + return ret; +} + +void Wpad_Disconnect(void) +{ + u32 cnt; + + /* Disconnect Wiimotes */ + for (cnt = 0; cnt < MAX_WIIMOTES; cnt++) + WPAD_Disconnect(cnt); + + /* Shutdown Wiimote subsystem */ + WPAD_Shutdown(); +} + +u32 Wpad_GetButtons(void) +{ + u32 buttons = 0, cnt; + + /* Scan pads */ + WPAD_ScanPads(); + + /* Get pressed buttons */ + for (cnt = 0; cnt < MAX_WIIMOTES; cnt++) + buttons |= WPAD_ButtonsDown(cnt); + + return buttons; +} + +u32 Wpad_WaitButtons(void) +{ + u32 buttons = 0; + /* Wait for button pressing */ + while (!buttons) { + buttons = Wpad_GetButtons(); + VIDEO_WaitVSync(); + } + + return buttons; +} + +u32 Wpad_HeldButtons(void) +{ + u32 buttons = 0, cnt; + + /* Scan pads */ + WPAD_ScanPads(); + + /* Get pressed buttons */ + for (cnt = 0; cnt < MAX_WIIMOTES; cnt++) + buttons |= WPAD_ButtonsHeld(cnt); + + return buttons; +} + +bool Wpad_TimeButton(void) +{ + u32 buttons = 1; + + time_t start,end; + time (&start); + int dif; + /* Wait for button pressing */ + while (buttons) { + buttons = Wpad_HeldButtons(); + VIDEO_WaitVSync(); + time (&end); + dif = difftime (end,start); + if(dif>=2) return true; + } + return false; +} \ No newline at end of file diff --git a/source/wpad.h b/source/wpad.h new file mode 100644 index 0000000..913973d --- /dev/null +++ b/source/wpad.h @@ -0,0 +1,13 @@ +#ifndef _WPAD_H_ +#define _WPAD_H_ + +#include + +/* Prototypes */ +s32 Wpad_Init(void); +void Wpad_Disconnect(void); +u32 Wpad_GetButtons(void); +u32 Wpad_WaitButtons(void); +bool Wpad_TimeButton(void); + +#endif diff --git a/wm_config.txt b/wm_config.txt new file mode 100644 index 0000000..f169aa0 --- /dev/null +++ b/wm_config.txt @@ -0,0 +1,25 @@ +;Config file format +; +;No spaces precedes the keyword on a line +; +Password= + +; StartupPath: +StartupPath=/wad + +; cIOS: 249, 222, whatever +:cIOSVersion=249 + +; FatDevice: sd usb usb2 gcsda gcsdb +:FatDevice=sd + +; NANDDevice: Disable SD USB +; Note that WM will prompt for NAND device only if you selected cIOS=249 +:NANDDevice=Disable + +: Settings for SMB shares + +:SMBUser= +:SMBPassword= +:SMBShare= +:SMBhostIP= \ No newline at end of file