From 7e0e4cab5c38e9ccb24413d2615293fd023375e6 Mon Sep 17 00:00:00 2001 From: dimok321 <15055714+dimok789@users.noreply.github.com> Date: Wed, 29 Dec 2010 15:42:26 +0000 Subject: [PATCH] *Added back the trNOOP (which excluded a lot of settings languages) which i removed by mistake when adding themes *Added option for file splitting on install. 2GB, 4GB and on ntfs/ext2/3/4 partitions no splitting at all option. *Fixed the install dir naming patterns option for fat32/ntfs/ext2/3/4 game installs *Changed/Fixed Home Menu exit to loader / shutdown *Added reload of game list on SD button click *Fixed language file loading on startup *Changed browse for theme path to file browser instead of OnScreenKeyboard Several settings were changed in this revision. It is recommended to reset the configs before using this rev. Forwarder Channel Update: *Use of IOS58 and AHBPROT *Complete change of forwarder. Using same as on WiiXplorer now. *Looking for dol in every primary fat/ntfs partition in the known paths (/apps/usbloader_gx/ and /apps/usbloadergx/). (Booting loader from ntfs will be usefull once images and settings can also be stored on ntfs.) Here is a download link for the channel: http://www.mediafire.com/?r11bwt1occlanmk The channel will be uploaded to the download section later when it is thoroughly tested. --- Makefile | 23 +- data/app_booter.dol | Bin 0 -> 106048 bytes forwarder.pnproj | 1 + forwarder.pnps | 1 + source/background_image.c | 74 ++ source/background_image.h | 9 + source/cfg.c | 154 +-- source/devicemounter.c | 120 ++ source/{fatmounter.h => devicemounter.h} | 56 +- source/dolloader.c | 116 +- source/dolloader.h | 43 +- source/elf_abi.h | 594 ---------- source/elfloader.c | 103 -- source/elfloader.h | 18 - source/fatmounter.c | 184 --- source/filelist.h | 40 +- source/main.c | 205 ++++ source/main.cpp | 233 ---- source/pngu.c | 1306 ++++++++++++++++++++++ source/pngu.h | 175 +++ source/{video.cpp => video.c} | 51 +- 21 files changed, 2170 insertions(+), 1336 deletions(-) create mode 100644 data/app_booter.dol create mode 100644 forwarder.pnproj create mode 100644 forwarder.pnps create mode 100644 source/background_image.c create mode 100644 source/background_image.h create mode 100644 source/devicemounter.c rename source/{fatmounter.h => devicemounter.h} (53%) delete mode 100644 source/elf_abi.h delete mode 100644 source/elfloader.c delete mode 100644 source/elfloader.h delete mode 100644 source/fatmounter.c create mode 100644 source/main.c delete mode 100644 source/main.cpp create mode 100644 source/pngu.c create mode 100644 source/pngu.h rename source/{video.cpp => video.c} (87%) diff --git a/Makefile b/Makefile index 795229b9..374e9e4e 100644 --- a/Makefile +++ b/Makefile @@ -25,20 +25,20 @@ INCLUDES := # options for code generation #--------------------------------------------------------------------------------- -CFLAGS = -g -O2 -Wall $(MACHDEP) $(INCLUDE) +CFLAGS = -g -O3 -Wall $(MACHDEP) $(INCLUDE) CXXFLAGS = $(CFLAGS) #--------------------------------------------------------------------------------- # move loader to another location - THANKS CREDIAR - 0x81330000 for HBC #--------------------------------------------------------------------------------- -LDFLAGS = -g $(MACHDEP) -Wl,-Map,$(notdir $@).map -Wl,--section-start,.init=0x81230000 -#LDFLAGS = -g $(MACHDEP) -Wl,-Map,$(notdir $@).map +#LDFLAGS = -g $(MACHDEP) -Wl,-Map,$(notdir $@).map -Wl,--section-start,.init=0x81000000 +LDFLAGS = -g $(MACHDEP) -Wl,-Map,$(notdir $@).map #LDFLAGS = -g $(MACHDEP) -Wl,-Map,$(notdir $@).map -Wl,--section-start,.init=0x80003f00 #--------------------------------------------------------------------------------- # any extra libraries we wish to link with the project #--------------------------------------------------------------------------------- -LIBS := -lfat -lpngu -lpng -lz -lbte -logc -lm +LIBS := -lfat -lntfs -lpng -lz -lbte -logc -lm #--------------------------------------------------------------------------------- # list of directories containing libraries, this must be the top level containing @@ -69,6 +69,8 @@ sFILES := $(foreach dir,$(SOURCES),$(notdir $(wildcard $(dir)/*.s))) SFILES := $(foreach dir,$(SOURCES),$(notdir $(wildcard $(dir)/*.S))) BINFILES := $(foreach dir,$(DATA),$(notdir $(wildcard $(dir)/*.*))) PNGFILES := $(foreach dir,$(SOURCES),$(notdir $(wildcard $(dir)/*.png))) +DOLFILES := $(foreach dir,$(SOURCES),$(notdir $(wildcard $(dir)/*.dol))) +ELFFILES := $(foreach dir,$(SOURCES),$(notdir $(wildcard $(dir)/*.elf))) #--------------------------------------------------------------------------------- # use CXX for linking C++ projects, CC for standard C @@ -82,7 +84,8 @@ endif export OFILES := $(addsuffix .o,$(BINFILES)) \ $(CPPFILES:.cpp=.o) $(CFILES:.c=.o) \ $(sFILES:.s=.o) $(SFILES:.S=.o) \ - $(PNGFILES:.png=.png.o) + $(PNGFILES:.png=.png.o) $(addsuffix .o,$(DOLFILES)) \ + $(addsuffix .o,$(ELFFILES)) #--------------------------------------------------------------------------------- # build a list of include paths @@ -137,7 +140,15 @@ $(OUTPUT).elf: $(OFILES) #--------------------------------------------------------------------------------- %.png.o : %.png @echo $(notdir $<) - $(bin2o) + @bin2s -a 32 $< | $(AS) -o $(@) + +%.dol.o : %.dol + @echo $(notdir $<) + @bin2s -a 32 $< | $(AS) -o $(@) + +%.elf.o : %.elf + @echo $(notdir $<) + @bin2s -a 32 $< | $(AS) -o $(@) -include $(DEPENDS) diff --git a/data/app_booter.dol b/data/app_booter.dol new file mode 100644 index 0000000000000000000000000000000000000000..62c156fe91698e4e5ec274cceade27c4206e24e6 GIT binary patch literal 106048 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ΠpMlei!)Rl9Sv;L~ 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 literal 0 HcmV?d00001 diff --git a/forwarder.pnproj b/forwarder.pnproj new file mode 100644 index 00000000..37bf9717 --- /dev/null +++ b/forwarder.pnproj @@ -0,0 +1 @@ + \ No newline at end of file diff --git a/forwarder.pnps b/forwarder.pnps new file mode 100644 index 00000000..1697fa21 --- /dev/null +++ b/forwarder.pnps @@ -0,0 +1 @@ + \ No newline at end of file diff --git a/source/background_image.c b/source/background_image.c new file mode 100644 index 00000000..efe6ce78 --- /dev/null +++ b/source/background_image.c @@ -0,0 +1,74 @@ +#include +#include "pngu.h" +#include "video.h" +#include "filelist.h" + +static int imagewidth = 0; +static int imageheight = 0; + +u8 * GetImageData() +{ + PNGUPROP imgProp; + IMGCTX ctx; + u8 * data = NULL; + int ret; + + if(CONF_GetAspectRatio() == CONF_ASPECT_16_9) + ctx = PNGU_SelectImageFromBuffer(background169_png); + else + ctx = PNGU_SelectImageFromBuffer(background_png); + + + if (!ctx) + return NULL; + + ret = PNGU_GetImageProperties(ctx, &imgProp); + if (ret != PNGU_OK) + return NULL; + + imagewidth = imgProp.imgWidth; + imageheight = imgProp.imgHeight; + + int len = ((((imgProp.imgWidth+3)>>2)*((imgProp.imgHeight+3)>>2)*32*2) + 31) & ~31; + + data = (u8 *)memalign (32, len); + + ret = PNGU_DecodeTo4x4RGBA8 (ctx, imgProp.imgWidth, imgProp.imgHeight, data, 255); + + DCFlushRange(data, len); + + PNGU_ReleaseImageContext(ctx); + + return data; +} + +void Background_Show(float x, float y, float z, u8 * data, float angle, float scaleX, float scaleY, u8 alpha) +{ + /* Draw image */ + Menu_DrawImg(x, y, z, imagewidth, imageheight, data, angle, scaleX, scaleY, alpha); + Menu_Render(); +} + +void fadein(u8 * imgdata) +{ + int i; + + /* fadein of image */ + for(i = 0; i < 255; i = i+10) + { + if(i>255) i = 255; + Background_Show(0, 0, 0, imgdata, 0, 1, 1, i); + } +} + +void fadeout(u8 * imgdata) +{ + int i; + + /* fadeoout of image */ + for(i = 255; i > 1; i = i-7) + { + if(i < 0) i = 0; + Background_Show(0, 0, 0, imgdata, 0, 1, 1, i); + } +} diff --git a/source/background_image.h b/source/background_image.h new file mode 100644 index 00000000..d90830ff --- /dev/null +++ b/source/background_image.h @@ -0,0 +1,9 @@ +#ifndef BACKGROUND_IMAGE_H_ +#define BACKGROUND_IMAGE_H_ + +u8 * GetImageData(); +void Background_Show(float x, float y, float z, u8 * data, float angle, float scaleX, float scaleY, u8 alpha); +void fadein(u8 * imgdata); +void fadeout(u8 * imgdata); + +#endif diff --git a/source/cfg.c b/source/cfg.c index 85bc8a88..68fa241e 100644 --- a/source/cfg.c +++ b/source/cfg.c @@ -1,77 +1,77 @@ -#include -#include -#include -#include -#include -#include -#include - -#include "cfg.h" - -char update_path[150]; - -static char *cfg_name, *cfg_val; - -char* strcopy(char *dest, char *src, int size) -{ - strncpy(dest,src,size); - dest[size-1] = 0; - return dest; -} - -char* trimcopy(char *dest, char *src, int size) -{ - int len; - while (*src == ' ') src++; - len = strlen(src); - // trim trailing " \r\n" - while (len > 0 && strchr(" \r\n", src[len-1])) len--; - if (len >= size) len = size-1; - strncpy(dest, src, len); - dest[len] = 0; - return dest; -} - -void cfg_parseline(char *line, void (*set_func)(char*, char*)) -{ - // split name = value - char tmp[200], name[100], val[100]; - strcopy(tmp, line, sizeof(tmp)); - char *eq = strchr(tmp, '='); - if (!eq) return; - *eq = 0; - trimcopy(name, tmp, sizeof(name)); - trimcopy(val, eq+1, sizeof(val)); - //printf("CFG: %s = %s\n", name, val); - set_func(name, val); -} - - -bool cfg_parsefile(char *fname, void (*set_func)(char*, char*)) -{ - FILE *f; - char line[200]; - - //printf("opening(%s)\n", fname); - f = fopen(fname, "r"); - if (!f) { - //printf("error opening(%s)\n", fname); - return false; - } - while (fgets(line, sizeof(line), f)) { - // lines starting with # are comments - if (line[0] == '#') continue; - cfg_parseline(line, set_func); - } - fclose(f); - return true; -} - -void cfg_set(char *name, char *val) -{ - cfg_name = name; - cfg_val = val; - if (strcmp(name, "update_path") == 0) { - strcopy(update_path, val, sizeof(update_path)); - } -} +#include +#include +#include +#include +#include +#include +#include + +#include "cfg.h" + +char update_path[150]; + +static char *cfg_name, *cfg_val; + +char* strcopy(char *dest, char *src, int size) +{ + strncpy(dest,src,size); + dest[size-1] = 0; + return dest; +} + +char* trimcopy(char *dest, char *src, int size) +{ + int len; + while (*src == ' ') src++; + len = strlen(src); + // trim trailing " \r\n" + while (len > 0 && strchr(" \r\n", src[len-1])) len--; + if (len >= size) len = size-1; + strncpy(dest, src, len); + dest[len] = 0; + return dest; +} + +void cfg_parseline(char *line, void (*set_func)(char*, char*)) +{ + // split name = value + char tmp[200], name[100], val[100]; + strcopy(tmp, line, sizeof(tmp)); + char *eq = strchr(tmp, '='); + if (!eq) return; + *eq = 0; + trimcopy(name, tmp, sizeof(name)); + trimcopy(val, eq+1, sizeof(val)); + //printf("CFG: %s = %s\n", name, val); + set_func(name, val); +} + + +bool cfg_parsefile(char *fname, void (*set_func)(char*, char*)) +{ + FILE *f; + char line[200]; + + //printf("opening(%s)\n", fname); + f = fopen(fname, "r"); + if (!f) { + //printf("error opening(%s)\n", fname); + return false; + } + while (fgets(line, sizeof(line), f)) { + // lines starting with # are comments + if (line[0] == '#') continue; + cfg_parseline(line, set_func); + } + fclose(f); + return true; +} + +void cfg_set(char *name, char *val) +{ + cfg_name = name; + cfg_val = val; + if (strcmp(name, "update_path") == 0) { + strcopy(update_path, val, sizeof(update_path)); + } +} diff --git a/source/devicemounter.c b/source/devicemounter.c new file mode 100644 index 00000000..4d535598 --- /dev/null +++ b/source/devicemounter.c @@ -0,0 +1,120 @@ +#include +#include +#include +#include +#include +#include +#include +#include +#include +#include +#include +#include +#include "devicemounter.h" + +//these are the only stable and speed is good +#define CACHE 8 +#define SECTORS 64 + +typedef struct _PARTITION_RECORD { + u8 status; /* Partition status; see above */ + u8 chs_start[3]; /* Cylinder-head-sector address to first block of partition */ + u8 type; /* Partition type; see above */ + u8 chs_end[3]; /* Cylinder-head-sector address to last block of partition */ + u32 lba_start; /* Local block address to first sector of partition */ + u32 block_count; /* Number of blocks in partition */ +} __attribute__((__packed__)) PARTITION_RECORD; + + +typedef struct _MASTER_BOOT_RECORD { + u8 code_area[446]; /* Code area; normally empty */ + PARTITION_RECORD partitions[4]; /* 4 primary partitions */ + u16 signature; /* MBR signature; 0xAA55 */ +} __attribute__((__packed__)) MASTER_BOOT_RECORD; + + +#define le32(i) (((((u32) i) & 0xFF) << 24) | ((((u32) i) & 0xFF00) << 8) | \ + ((((u32) i) & 0xFF0000) >> 8) | ((((u32) i) & 0xFF000000) >> 24)) + +int USBDevice_Init() +{ + time_t start = time(0); + + while(start-time(0) < 10) // 10 sec + { + if(__io_usbstorage.startup() && __io_usbstorage.isInserted()) + break; + + usleep(200000); // 1/5 sec + } + + if(!__io_usbstorage.startup() || !__io_usbstorage.isInserted()) + return -1; + + int i; + MASTER_BOOT_RECORD mbr; + char ntfsBootSector[512]; + + __io_usbstorage.readSectors(0, 1, &mbr); + + for(i = 0; i < 4; ++i) + { + switch(mbr.partitions[i].type) + { + case 0x01: + case 0x04: + case 0x06: + case 0x0b: + case 0x0c: + case 0x0e: + fatMount(DeviceName[USB1+i], &__io_usbstorage, le32(mbr.partitions[i].lba_start), CACHE, SECTORS); + break; + case 0x07: + __io_usbstorage.readSectors(le32(mbr.partitions[i].lba_start), 1, ntfsBootSector); + if(strncmp(&ntfsBootSector[0x03], "NTFS", 4) == 0) + ntfsMount(DeviceName[USB1+i], &__io_usbstorage, le32(mbr.partitions[i].lba_start), CACHE, SECTORS, NTFS_SHOW_HIDDEN_FILES | NTFS_RECOVER | NTFS_IGNORE_CASE); + break; + default: + break; + } + } + + return -1; +} + +void USBDevice_deInit() +{ + int dev; + char Name[20]; + + for(dev = USB1; dev <= USB4; ++dev) + { + sprintf(Name, "%s:/", DeviceName[dev]); + fatUnmount(Name); + ntfsUnmount(Name, true); + } + //Let's not shutdown so it stays awake for the application + __io_usbstorage.shutdown(); + USB_Deinitialize(); +} + +int SDCard_Init() +{ + if(!__io_wiisd.startup() || !__io_wiisd.isInserted()) + return -1; + + if (fatMount(DeviceName[SD], &__io_wiisd, 0, CACHE, SECTORS)) + return 1; + + return -1; +} + +void SDCard_deInit() +{ + char Name[20]; + sprintf(Name, "%s:/", DeviceName[SD]); + //closing all open Files write back the cache and then shutdown em! + fatUnmount(Name); + //Let's not shutdown so it stays awake for the application + __io_wiisd.shutdown(); +} diff --git a/source/fatmounter.h b/source/devicemounter.h similarity index 53% rename from source/fatmounter.h rename to source/devicemounter.h index e2357020..c747b94a 100644 --- a/source/fatmounter.h +++ b/source/devicemounter.h @@ -1,19 +1,37 @@ -#ifndef _FATMOUNTER_H_ -#define _FATMOUNTER_H_ - -#ifdef __cplusplus -extern "C" -{ -#endif - -int USBDevice_Init(); -void USBDevice_deInit(); -int isSdInserted(); -int SDCard_Init(); -void SDCard_deInit(); - -#ifdef __cplusplus -} -#endif - -#endif +#ifndef _FATMOUNTER_H_ +#define _FATMOUNTER_H_ + +#ifdef __cplusplus +extern "C" +{ +#endif + +enum +{ + SD = 0, + USB1, + USB2, + USB3, + USB4, + MAXDEVICES +}; + +static const char DeviceName[MAXDEVICES][6] = +{ + "sd", + "usb1", + "usb2", + "usb3", + "usb4" +}; + +int USBDevice_Init(); +void USBDevice_deInit(); +int SDCard_Init(); +void SDCard_deInit(); + +#ifdef __cplusplus +} +#endif + +#endif diff --git a/source/dolloader.c b/source/dolloader.c index 7a6629d9..923e2cc1 100644 --- a/source/dolloader.c +++ b/source/dolloader.c @@ -1,54 +1,62 @@ -#include -#include -#include -#include -#include -#include -#include - -#include "dolloader.h" - -typedef struct _dolheader { - u32 text_pos[7]; - u32 data_pos[11]; - u32 text_start[7]; - u32 data_start[11]; - u32 text_size[7]; - u32 data_size[11]; - u32 bss_start; - u32 bss_size; - u32 entry_point; -} dolheader; - -u32 load_dol_image (void *dolstart, struct __argv *argv) { - u32 i; - dolheader *dolfile; - - if (dolstart) { - dolfile = (dolheader *) dolstart; - for (i = 0; i < 7; i++) { - if ((!dolfile->text_size[i]) || (dolfile->text_start[i] < 0x100)) continue; - VIDEO_WaitVSync(); - ICInvalidateRange ((void *) dolfile->text_start[i],dolfile->text_size[i]); - memmove ((void *) dolfile->text_start[i],dolstart+dolfile->text_pos[i],dolfile->text_size[i]); - } - - for(i = 0; i < 11; i++) { - if ((!dolfile->data_size[i]) || (dolfile->data_start[i] < 0x100)) continue; - VIDEO_WaitVSync(); - memmove ((void *) dolfile->data_start[i],dolstart+dolfile->data_pos[i],dolfile->data_size[i]); - DCFlushRangeNoSync ((void *) dolfile->data_start[i],dolfile->data_size[i]); - } - - memset ((void *) dolfile->bss_start, 0, dolfile->bss_size); - DCFlushRange((void *) dolfile->bss_start, dolfile->bss_size); - - if (argv && argv->argvMagic == ARGV_MAGIC) { - void *new_argv = (void *)(dolfile->entry_point + 8); - memmove(new_argv, argv, sizeof(*argv)); - DCFlushRange(new_argv, sizeof(*argv)); - } - return dolfile->entry_point; - } - return 0; -} +#include +#include +#include +#include +#include +#include +#include + +#include "dolloader.h" + +typedef struct _dolheader { + u32 text_pos[7]; + u32 data_pos[11]; + u32 text_start[7]; + u32 data_start[11]; + u32 text_size[7]; + u32 data_size[11]; + u32 bss_start; + u32 bss_size; + u32 entry_point; +} dolheader; + +u32 load_dol_image(const void *dolstart, struct __argv *argv) +{ + u32 i; + dolheader *dolfile; + + if (dolstart) + { + dolfile = (dolheader *) dolstart; + for (i = 0; i < 7; i++) + { + if ((!dolfile->text_size[i]) || (dolfile->text_start[i] < 0x100)) + continue; + + VIDEO_WaitVSync(); + memmove((void *) dolfile->text_start[i], dolstart + dolfile->text_pos[i], dolfile->text_size[i]); + DCFlushRange ((void *) dolfile->text_start[i], dolfile->text_size[i]); + ICInvalidateRange((void *) dolfile->text_start[i], dolfile->text_size[i]); + } + + for (i = 0; i < 11; i++) + { + if ((!dolfile->data_size[i]) || (dolfile->data_start[i] < 0x100)) + continue; + + VIDEO_WaitVSync(); + memmove((void *) dolfile->data_start[i], dolstart + dolfile->data_pos[i], dolfile->data_size[i]); + DCFlushRange((void *) dolfile->data_start[i], + dolfile->data_size[i]); + } + + if (argv && argv->argvMagic == ARGV_MAGIC) + { + void *new_argv = (void *) (dolfile->entry_point + 8); + memmove(new_argv, argv, sizeof(*argv)); + DCFlushRange(new_argv, sizeof(*argv)); + } + return dolfile->entry_point; + } + return 0; +} diff --git a/source/dolloader.h b/source/dolloader.h index 7dcc2921..e4c50820 100644 --- a/source/dolloader.h +++ b/source/dolloader.h @@ -1,23 +1,20 @@ -#ifndef _DOLLOADER_H_ -#define _DOLLOADER_H_ - -#ifdef __cplusplus -extern "C" -{ -#endif - -// Apps goes here -#define EXECUTABLE_MEM_ADDR 0x92000000 -/* dol header */ - -extern void __exception_closeall(); -typedef void (*entrypoint) (void); - -u32 load_dol_image (void *dolstart, struct __argv *argv); - - -#ifdef __cplusplus -} -#endif - -#endif +#ifndef _DOLLOADER_H_ +#define _DOLLOADER_H_ + +#ifdef __cplusplus +extern "C" { +#endif + +#define EXECUTABLE_MEM_ADDR 0x92000000 + +extern void __exception_closeall(); +typedef void (*entrypoint) (void); + +u32 load_dol_image(const void *dolstart, struct __argv *argv); + + +#ifdef __cplusplus +} +#endif + +#endif diff --git a/source/elf_abi.h b/source/elf_abi.h deleted file mode 100644 index c9e705ef..00000000 --- a/source/elf_abi.h +++ /dev/null @@ -1,594 +0,0 @@ -/* - * Copyright (c) 1995, 1996, 2001, 2002 - * Erik Theisen. All rights reserved. - * - * Redistribution and use in source and binary forms, with or without - * modification, are permitted provided that the following conditions - * are met: - * 1. Redistributions of source code must retain the above copyright - * notice, this list of conditions and the following disclaimer. - * 2. Redistributions in binary form must reproduce the above copyright - * notice, this list of conditions and the following disclaimer in the - * documentation and/or other materials provided with the distribution. - * 3. The name of the author may not be used to endorse or promote products - * derived from this software without specific prior written permission - * - * THIS SOFTWARE IS PROVIDED BY THE AUTHOR ``AS IS'' AND ANY EXPRESS OR - * IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE IMPLIED WARRANTIES - * OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE ARE DISCLAIMED. - * IN NO EVENT SHALL THE AUTHOR BE LIABLE FOR ANY DIRECT, INDIRECT, - * INCIDENTAL, SPECIAL, EXEMPLARY, OR CONSEQUENTIAL DAMAGES (INCLUDING, BUT - * NOT LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS OR SERVICES; LOSS OF USE, - * DATA, OR PROFITS; OR BUSINESS INTERRUPTION) HOWEVER CAUSED AND ON ANY - * THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT LIABILITY, OR TORT - * (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY OUT OF THE USE OF - * THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF SUCH DAMAGE. - */ - -/* - * This is the ELF ABI header file - * formerly known as "elf_abi.h". - */ - -#ifndef _ELF_ABI_H -#define _ELF_ABI_H - -#include - -/* - * This version doesn't work for 64-bit ABIs - Erik. - */ - -/* - * These typedefs need to be handled better. - */ -typedef u32 Elf32_Addr; /* Unsigned program address */ -typedef u32 Elf32_Off; /* Unsigned file offset */ -typedef s32 Elf32_Sword; /* Signed large integer */ -typedef u32 Elf32_Word; /* Unsigned large integer */ -typedef u16 Elf32_Half; /* Unsigned medium integer */ - -/* e_ident[] identification indexes */ -#define EI_MAG0 0 /* file ID */ -#define EI_MAG1 1 /* file ID */ -#define EI_MAG2 2 /* file ID */ -#define EI_MAG3 3 /* file ID */ -#define EI_CLASS 4 /* file class */ -#define EI_DATA 5 /* data encoding */ -#define EI_VERSION 6 /* ELF header version */ -#define EI_OSABI 7 /* OS/ABI specific ELF extensions */ -#define EI_ABIVERSION 8 /* ABI target version */ -#define EI_PAD 9 /* start of pad bytes */ -#define EI_NIDENT 16 /* Size of e_ident[] */ - -/* e_ident[] magic number */ -#define ELFMAG0 0x7f /* e_ident[EI_MAG0] */ -#define ELFMAG1 'E' /* e_ident[EI_MAG1] */ -#define ELFMAG2 'L' /* e_ident[EI_MAG2] */ -#define ELFMAG3 'F' /* e_ident[EI_MAG3] */ -#define ELFMAG "\177ELF" /* magic */ -#define SELFMAG 4 /* size of magic */ - -/* e_ident[] file class */ -#define ELFCLASSNONE 0 /* invalid */ -#define ELFCLASS32 1 /* 32-bit objs */ -#define ELFCLASS64 2 /* 64-bit objs */ -#define ELFCLASSNUM 3 /* number of classes */ - -/* e_ident[] data encoding */ -#define ELFDATANONE 0 /* invalid */ -#define ELFDATA2LSB 1 /* Little-Endian */ -#define ELFDATA2MSB 2 /* Big-Endian */ -#define ELFDATANUM 3 /* number of data encode defines */ - -/* e_ident[] OS/ABI specific ELF extensions */ -#define ELFOSABI_NONE 0 /* No extension specified */ -#define ELFOSABI_HPUX 1 /* Hewlett-Packard HP-UX */ -#define ELFOSABI_NETBSD 2 /* NetBSD */ -#define ELFOSABI_LINUX 3 /* Linux */ -#define ELFOSABI_SOLARIS 6 /* Sun Solaris */ -#define ELFOSABI_AIX 7 /* AIX */ -#define ELFOSABI_IRIX 8 /* IRIX */ -#define ELFOSABI_FREEBSD 9 /* FreeBSD */ -#define ELFOSABI_TRU64 10 /* Compaq TRU64 UNIX */ -#define ELFOSABI_MODESTO 11 /* Novell Modesto */ -#define ELFOSABI_OPENBSD 12 /* OpenBSD */ -/* 64-255 Architecture-specific value range */ - -/* e_ident[] ABI Version */ -#define ELFABIVERSION 0 - -/* e_ident */ -#define IS_ELF(ehdr) ((ehdr).e_ident[EI_MAG0] == ELFMAG0 && \ - (ehdr).e_ident[EI_MAG1] == ELFMAG1 && \ - (ehdr).e_ident[EI_MAG2] == ELFMAG2 && \ - (ehdr).e_ident[EI_MAG3] == ELFMAG3) - -/* ELF Header */ -typedef struct elfhdr{ - unsigned char e_ident[EI_NIDENT]; /* ELF Identification */ - Elf32_Half e_type; /* object file type */ - Elf32_Half e_machine; /* machine */ - Elf32_Word e_version; /* object file version */ - Elf32_Addr e_entry; /* virtual entry point */ - Elf32_Off e_phoff; /* program header table offset */ - Elf32_Off e_shoff; /* section header table offset */ - Elf32_Word e_flags; /* processor-specific flags */ - Elf32_Half e_ehsize; /* ELF header size */ - Elf32_Half e_phentsize; /* program header entry size */ - Elf32_Half e_phnum; /* number of program header entries */ - Elf32_Half e_shentsize; /* section header entry size */ - Elf32_Half e_shnum; /* number of section header entries */ - Elf32_Half e_shstrndx; /* section header table's "section - header string table" entry offset */ -} Elf32_Ehdr; - -/* e_type */ -#define ET_NONE 0 /* No file type */ -#define ET_REL 1 /* relocatable file */ -#define ET_EXEC 2 /* executable file */ -#define ET_DYN 3 /* shared object file */ -#define ET_CORE 4 /* core file */ -#define ET_NUM 5 /* number of types */ -#define ET_LOOS 0xfe00 /* reserved range for operating */ -#define ET_HIOS 0xfeff /* system specific e_type */ -#define ET_LOPROC 0xff00 /* reserved range for processor */ -#define ET_HIPROC 0xffff /* specific e_type */ - -/* e_machine */ -#define EM_NONE 0 /* No Machine */ -#define EM_M32 1 /* AT&T WE 32100 */ -#define EM_SPARC 2 /* SPARC */ -#define EM_386 3 /* Intel 80386 */ -#define EM_68K 4 /* Motorola 68000 */ -#define EM_88K 5 /* Motorola 88000 */ -#if 0 -#define EM_486 6 /* RESERVED - was Intel 80486 */ -#endif -#define EM_860 7 /* Intel 80860 */ -#define EM_MIPS 8 /* MIPS R3000 Big-Endian only */ -#define EM_S370 9 /* IBM System/370 Processor */ -#define EM_MIPS_RS4_BE 10 /* MIPS R4000 Big-Endian */ -#if 0 -#define EM_SPARC64 11 /* RESERVED - was SPARC v9 - 64-bit unoffical */ -#endif -/* RESERVED 11-14 for future use */ -#define EM_PARISC 15 /* HPPA */ -/* RESERVED 16 for future use */ -#define EM_VPP500 17 /* Fujitsu VPP500 */ -#define EM_SPARC32PLUS 18 /* Enhanced instruction set SPARC */ -#define EM_960 19 /* Intel 80960 */ -#define EM_PPC 20 /* PowerPC */ -#define EM_PPC64 21 /* 64-bit PowerPC */ -#define EM_S390 22 /* IBM System/390 Processor */ -/* RESERVED 23-35 for future use */ -#define EM_V800 36 /* NEC V800 */ -#define EM_FR20 37 /* Fujitsu FR20 */ -#define EM_RH32 38 /* TRW RH-32 */ -#define EM_RCE 39 /* Motorola RCE */ -#define EM_ARM 40 /* Advanced Risc Machines ARM */ -#define EM_ALPHA 41 /* Digital Alpha */ -#define EM_SH 42 /* Hitachi SH */ -#define EM_SPARCV9 43 /* SPARC Version 9 */ -#define EM_TRICORE 44 /* Siemens TriCore embedded processor */ -#define EM_ARC 45 /* Argonaut RISC Core */ -#define EM_H8_300 46 /* Hitachi H8/300 */ -#define EM_H8_300H 47 /* Hitachi H8/300H */ -#define EM_H8S 48 /* Hitachi H8S */ -#define EM_H8_500 49 /* Hitachi H8/500 */ -#define EM_IA_64 50 /* Intel Merced */ -#define EM_MIPS_X 51 /* Stanford MIPS-X */ -#define EM_COLDFIRE 52 /* Motorola Coldfire */ -#define EM_68HC12 53 /* Motorola M68HC12 */ -#define EM_MMA 54 /* Fujitsu MMA Multimedia Accelerator*/ -#define EM_PCP 55 /* Siemens PCP */ -#define EM_NCPU 56 /* Sony nCPU embeeded RISC */ -#define EM_NDR1 57 /* Denso NDR1 microprocessor */ -#define EM_STARCORE 58 /* Motorola Start*Core processor */ -#define EM_ME16 59 /* Toyota ME16 processor */ -#define EM_ST100 60 /* STMicroelectronic ST100 processor */ -#define EM_TINYJ 61 /* Advanced Logic Corp. Tinyj emb.fam*/ -#define EM_X86_64 62 /* AMD x86-64 */ -#define EM_PDSP 63 /* Sony DSP Processor */ -/* RESERVED 64,65 for future use */ -#define EM_FX66 66 /* Siemens FX66 microcontroller */ -#define EM_ST9PLUS 67 /* STMicroelectronics ST9+ 8/16 mc */ -#define EM_ST7 68 /* STmicroelectronics ST7 8 bit mc */ -#define EM_68HC16 69 /* Motorola MC68HC16 microcontroller */ -#define EM_68HC11 70 /* Motorola MC68HC11 microcontroller */ -#define EM_68HC08 71 /* Motorola MC68HC08 microcontroller */ -#define EM_68HC05 72 /* Motorola MC68HC05 microcontroller */ -#define EM_SVX 73 /* Silicon Graphics SVx */ -#define EM_ST19 74 /* STMicroelectronics ST19 8 bit mc */ -#define EM_VAX 75 /* Digital VAX */ -#define EM_CHRIS 76 /* Axis Communications embedded proc. */ -#define EM_JAVELIN 77 /* Infineon Technologies emb. proc. */ -#define EM_FIREPATH 78 /* Element 14 64-bit DSP Processor */ -#define EM_ZSP 79 /* LSI Logic 16-bit DSP Processor */ -#define EM_MMIX 80 /* Donald Knuth's edu 64-bit proc. */ -#define EM_HUANY 81 /* Harvard University mach-indep objs */ -#define EM_PRISM 82 /* SiTera Prism */ -#define EM_AVR 83 /* Atmel AVR 8-bit microcontroller */ -#define EM_FR30 84 /* Fujitsu FR30 */ -#define EM_D10V 85 /* Mitsubishi DV10V */ -#define EM_D30V 86 /* Mitsubishi DV30V */ -#define EM_V850 87 /* NEC v850 */ -#define EM_M32R 88 /* Mitsubishi M32R */ -#define EM_MN10300 89 /* Matsushita MN10200 */ -#define EM_MN10200 90 /* Matsushita MN10200 */ -#define EM_PJ 91 /* picoJava */ -#define EM_NUM 92 /* number of machine types */ - -/* Version */ -#define EV_NONE 0 /* Invalid */ -#define EV_CURRENT 1 /* Current */ -#define EV_NUM 2 /* number of versions */ - -/* Section Header */ -typedef struct { - Elf32_Word sh_name; /* name - index into section header - string table section */ - Elf32_Word sh_type; /* type */ - Elf32_Word sh_flags; /* flags */ - Elf32_Addr sh_addr; /* address */ - Elf32_Off sh_offset; /* file offset */ - Elf32_Word sh_size; /* section size */ - Elf32_Word sh_link; /* section header table index link */ - Elf32_Word sh_info; /* extra information */ - Elf32_Word sh_addralign; /* address alignment */ - Elf32_Word sh_entsize; /* section entry size */ -} Elf32_Shdr; - -/* Special Section Indexes */ -#define SHN_UNDEF 0 /* undefined */ -#define SHN_LORESERVE 0xff00 /* lower bounds of reserved indexes */ -#define SHN_LOPROC 0xff00 /* reserved range for processor */ -#define SHN_HIPROC 0xff1f /* specific section indexes */ -#define SHN_LOOS 0xff20 /* reserved range for operating */ -#define SHN_HIOS 0xff3f /* specific semantics */ -#define SHN_ABS 0xfff1 /* absolute value */ -#define SHN_COMMON 0xfff2 /* common symbol */ -#define SHN_XINDEX 0xffff /* Index is an extra table */ -#define SHN_HIRESERVE 0xffff /* upper bounds of reserved indexes */ - -/* sh_type */ -#define SHT_NULL 0 /* inactive */ -#define SHT_PROGBITS 1 /* program defined information */ -#define SHT_SYMTAB 2 /* symbol table section */ -#define SHT_STRTAB 3 /* string table section */ -#define SHT_RELA 4 /* relocation section with addends*/ -#define SHT_HASH 5 /* symbol hash table section */ -#define SHT_DYNAMIC 6 /* dynamic section */ -#define SHT_NOTE 7 /* note section */ -#define SHT_NOBITS 8 /* no space section */ -#define SHT_REL 9 /* relation section without addends */ -#define SHT_SHLIB 10 /* reserved - purpose unknown */ -#define SHT_DYNSYM 11 /* dynamic symbol table section */ -#define SHT_INIT_ARRAY 14 /* Array of constructors */ -#define SHT_FINI_ARRAY 15 /* Array of destructors */ -#define SHT_PREINIT_ARRAY 16 /* Array of pre-constructors */ -#define SHT_GROUP 17 /* Section group */ -#define SHT_SYMTAB_SHNDX 18 /* Extended section indeces */ -#define SHT_NUM 19 /* number of section types */ -#define SHT_LOOS 0x60000000 /* Start OS-specific */ -#define SHT_HIOS 0x6fffffff /* End OS-specific */ -#define SHT_LOPROC 0x70000000 /* reserved range for processor */ -#define SHT_HIPROC 0x7fffffff /* specific section header types */ -#define SHT_LOUSER 0x80000000 /* reserved range for application */ -#define SHT_HIUSER 0xffffffff /* specific indexes */ - -/* Section names */ -#define ELF_BSS ".bss" /* uninitialized data */ -#define ELF_COMMENT ".comment" /* version control information */ -#define ELF_DATA ".data" /* initialized data */ -#define ELF_DATA1 ".data1" /* initialized data */ -#define ELF_DEBUG ".debug" /* debug */ -#define ELF_DYNAMIC ".dynamic" /* dynamic linking information */ -#define ELF_DYNSTR ".dynstr" /* dynamic string table */ -#define ELF_DYNSYM ".dynsym" /* dynamic symbol table */ -#define ELF_FINI ".fini" /* termination code */ -#define ELF_FINI_ARRAY ".fini_array" /* Array of destructors */ -#define ELF_GOT ".got" /* global offset table */ -#define ELF_HASH ".hash" /* symbol hash table */ -#define ELF_INIT ".init" /* initialization code */ -#define ELF_INIT_ARRAY ".init_array" /* Array of constuctors */ -#define ELF_INTERP ".interp" /* Pathname of program interpreter */ -#define ELF_LINE ".line" /* Symbolic line numnber information */ -#define ELF_NOTE ".note" /* Contains note section */ -#define ELF_PLT ".plt" /* Procedure linkage table */ -#define ELF_PREINIT_ARRAY ".preinit_array" /* Array of pre-constructors */ -#define ELF_REL_DATA ".rel.data" /* relocation data */ -#define ELF_REL_FINI ".rel.fini" /* relocation termination code */ -#define ELF_REL_INIT ".rel.init" /* relocation initialization code */ -#define ELF_REL_DYN ".rel.dyn" /* relocaltion dynamic link info */ -#define ELF_REL_RODATA ".rel.rodata" /* relocation read-only data */ -#define ELF_REL_TEXT ".rel.text" /* relocation code */ -#define ELF_RODATA ".rodata" /* read-only data */ -#define ELF_RODATA1 ".rodata1" /* read-only data */ -#define ELF_SHSTRTAB ".shstrtab" /* section header string table */ -#define ELF_STRTAB ".strtab" /* string table */ -#define ELF_SYMTAB ".symtab" /* symbol table */ -#define ELF_SYMTAB_SHNDX ".symtab_shndx"/* symbol table section index */ -#define ELF_TBSS ".tbss" /* thread local uninit data */ -#define ELF_TDATA ".tdata" /* thread local init data */ -#define ELF_TDATA1 ".tdata1" /* thread local init data */ -#define ELF_TEXT ".text" /* code */ - -/* Section Attribute Flags - sh_flags */ -#define SHF_WRITE 0x1 /* Writable */ -#define SHF_ALLOC 0x2 /* occupies memory */ -#define SHF_EXECINSTR 0x4 /* executable */ -#define SHF_MERGE 0x10 /* Might be merged */ -#define SHF_STRINGS 0x20 /* Contains NULL terminated strings */ -#define SHF_INFO_LINK 0x40 /* sh_info contains SHT index */ -#define SHF_LINK_ORDER 0x80 /* Preserve order after combining*/ -#define SHF_OS_NONCONFORMING 0x100 /* Non-standard OS specific handling */ -#define SHF_GROUP 0x200 /* Member of section group */ -#define SHF_TLS 0x400 /* Thread local storage */ -#define SHF_MASKOS 0x0ff00000 /* OS specific */ -#define SHF_MASKPROC 0xf0000000 /* reserved bits for processor */ - /* specific section attributes */ - -/* Section Group Flags */ -#define GRP_COMDAT 0x1 /* COMDAT group */ -#define GRP_MASKOS 0x0ff00000 /* Mask OS specific flags */ -#define GRP_MASKPROC 0xf0000000 /* Mask processor specific flags */ - -/* Symbol Table Entry */ -typedef struct elf32_sym { - Elf32_Word st_name; /* name - index into string table */ - Elf32_Addr st_value; /* symbol value */ - Elf32_Word st_size; /* symbol size */ - unsigned char st_info; /* type and binding */ - unsigned char st_other; /* 0 - no defined meaning */ - Elf32_Half st_shndx; /* section header index */ -} Elf32_Sym; - -/* Symbol table index */ -#define STN_UNDEF 0 /* undefined */ - -/* Extract symbol info - st_info */ -#define ELF32_ST_BIND(x) ((x) >> 4) -#define ELF32_ST_TYPE(x) (((unsigned int) x) & 0xf) -#define ELF32_ST_INFO(b,t) (((b) << 4) + ((t) & 0xf)) -#define ELF32_ST_VISIBILITY(x) ((x) & 0x3) - -/* Symbol Binding - ELF32_ST_BIND - st_info */ -#define STB_LOCAL 0 /* Local symbol */ -#define STB_GLOBAL 1 /* Global symbol */ -#define STB_WEAK 2 /* like global - lower precedence */ -#define STB_NUM 3 /* number of symbol bindings */ -#define STB_LOOS 10 /* reserved range for operating */ -#define STB_HIOS 12 /* system specific symbol bindings */ -#define STB_LOPROC 13 /* reserved range for processor */ -#define STB_HIPROC 15 /* specific symbol bindings */ - -/* Symbol type - ELF32_ST_TYPE - st_info */ -#define STT_NOTYPE 0 /* not specified */ -#define STT_OBJECT 1 /* data object */ -#define STT_FUNC 2 /* function */ -#define STT_SECTION 3 /* section */ -#define STT_FILE 4 /* file */ -#define STT_NUM 5 /* number of symbol types */ -#define STT_TLS 6 /* Thread local storage symbol */ -#define STT_LOOS 10 /* reserved range for operating */ -#define STT_HIOS 12 /* system specific symbol types */ -#define STT_LOPROC 13 /* reserved range for processor */ -#define STT_HIPROC 15 /* specific symbol types */ - -/* Symbol visibility - ELF32_ST_VISIBILITY - st_other */ -#define STV_DEFAULT 0 /* Normal visibility rules */ -#define STV_INTERNAL 1 /* Processor specific hidden class */ -#define STV_HIDDEN 2 /* Symbol unavailable in other mods */ -#define STV_PROTECTED 3 /* Not preemptible, not exported */ - - -/* Relocation entry with implicit addend */ -typedef struct -{ - Elf32_Addr r_offset; /* offset of relocation */ - Elf32_Word r_info; /* symbol table index and type */ -} Elf32_Rel; - -/* Relocation entry with explicit addend */ -typedef struct -{ - Elf32_Addr r_offset; /* offset of relocation */ - Elf32_Word r_info; /* symbol table index and type */ - Elf32_Sword r_addend; -} Elf32_Rela; - -/* Extract relocation info - r_info */ -#define ELF32_R_SYM(i) ((i) >> 8) -#define ELF32_R_TYPE(i) ((unsigned char) (i)) -#define ELF32_R_INFO(s,t) (((s) << 8) + (unsigned char)(t)) - -/* Program Header */ -typedef struct { - Elf32_Word p_type; /* segment type */ - Elf32_Off p_offset; /* segment offset */ - Elf32_Addr p_vaddr; /* virtual address of segment */ - Elf32_Addr p_paddr; /* physical address - ignored? */ - Elf32_Word p_filesz; /* number of bytes in file for seg. */ - Elf32_Word p_memsz; /* number of bytes in mem. for seg. */ - Elf32_Word p_flags; /* flags */ - Elf32_Word p_align; /* memory alignment */ -} Elf32_Phdr; - -/* Segment types - p_type */ -#define PT_NULL 0 /* unused */ -#define PT_LOAD 1 /* loadable segment */ -#define PT_DYNAMIC 2 /* dynamic linking section */ -#define PT_INTERP 3 /* the RTLD */ -#define PT_NOTE 4 /* auxiliary information */ -#define PT_SHLIB 5 /* reserved - purpose undefined */ -#define PT_PHDR 6 /* program header */ -#define PT_TLS 7 /* Thread local storage template */ -#define PT_NUM 8 /* Number of segment types */ -#define PT_LOOS 0x60000000 /* reserved range for operating */ -#define PT_HIOS 0x6fffffff /* system specific segment types */ -#define PT_LOPROC 0x70000000 /* reserved range for processor */ -#define PT_HIPROC 0x7fffffff /* specific segment types */ - -/* Segment flags - p_flags */ -#define PF_X 0x1 /* Executable */ -#define PF_W 0x2 /* Writable */ -#define PF_R 0x4 /* Readable */ -#define PF_MASKOS 0x0ff00000 /* OS specific segment flags */ -#define PF_MASKPROC 0xf0000000 /* reserved bits for processor */ - /* specific segment flags */ -/* Dynamic structure */ -typedef struct -{ - Elf32_Sword d_tag; /* controls meaning of d_val */ - union - { - Elf32_Word d_val; /* Multiple meanings - see d_tag */ - Elf32_Addr d_ptr; /* program virtual address */ - } d_un; -} Elf32_Dyn; - -extern Elf32_Dyn _DYNAMIC[]; - -/* Dynamic Array Tags - d_tag */ -#define DT_NULL 0 /* marks end of _DYNAMIC array */ -#define DT_NEEDED 1 /* string table offset of needed lib */ -#define DT_PLTRELSZ 2 /* size of relocation entries in PLT */ -#define DT_PLTGOT 3 /* address PLT/GOT */ -#define DT_HASH 4 /* address of symbol hash table */ -#define DT_STRTAB 5 /* address of string table */ -#define DT_SYMTAB 6 /* address of symbol table */ -#define DT_RELA 7 /* address of relocation table */ -#define DT_RELASZ 8 /* size of relocation table */ -#define DT_RELAENT 9 /* size of relocation entry */ -#define DT_STRSZ 10 /* size of string table */ -#define DT_SYMENT 11 /* size of symbol table entry */ -#define DT_INIT 12 /* address of initialization func. */ -#define DT_FINI 13 /* address of termination function */ -#define DT_SONAME 14 /* string table offset of shared obj */ -#define DT_RPATH 15 /* string table offset of library - search path */ -#define DT_SYMBOLIC 16 /* start sym search in shared obj. */ -#define DT_REL 17 /* address of rel. tbl. w addends */ -#define DT_RELSZ 18 /* size of DT_REL relocation table */ -#define DT_RELENT 19 /* size of DT_REL relocation entry */ -#define DT_PLTREL 20 /* PLT referenced relocation entry */ -#define DT_DEBUG 21 /* bugger */ -#define DT_TEXTREL 22 /* Allow rel. mod. to unwritable seg */ -#define DT_JMPREL 23 /* add. of PLT's relocation entries */ -#define DT_BIND_NOW 24 /* Process relocations of object */ -#define DT_INIT_ARRAY 25 /* Array with addresses of init fct */ -#define DT_FINI_ARRAY 26 /* Array with addresses of fini fct */ -#define DT_INIT_ARRAYSZ 27 /* Size in bytes of DT_INIT_ARRAY */ -#define DT_FINI_ARRAYSZ 28 /* Size in bytes of DT_FINI_ARRAY */ -#define DT_RUNPATH 29 /* Library search path */ -#define DT_FLAGS 30 /* Flags for the object being loaded */ -#define DT_ENCODING 32 /* Start of encoded range */ -#define DT_PREINIT_ARRAY 32 /* Array with addresses of preinit fct*/ -#define DT_PREINIT_ARRAYSZ 33 /* size in bytes of DT_PREINIT_ARRAY */ -#define DT_NUM 34 /* Number used. */ -#define DT_LOOS 0x60000000 /* reserved range for OS */ -#define DT_HIOS 0x6fffffff /* specific dynamic array tags */ -#define DT_LOPROC 0x70000000 /* reserved range for processor */ -#define DT_HIPROC 0x7fffffff /* specific dynamic array tags */ - -/* Dynamic Tag Flags - d_un.d_val */ -#define DF_ORIGIN 0x01 /* Object may use DF_ORIGIN */ -#define DF_SYMBOLIC 0x02 /* Symbol resolutions starts here */ -#define DF_TEXTREL 0x04 /* Object contains text relocations */ -#define DF_BIND_NOW 0x08 /* No lazy binding for this object */ -#define DF_STATIC_TLS 0x10 /* Static thread local storage */ - -/* Standard ELF hashing function */ -unsigned long elf_hash(const unsigned char *name); - -#define ELF_TARG_VER 1 /* The ver for which this code is intended */ - -/* - * XXX - PowerPC defines really don't belong in here, - * but we'll put them in for simplicity. - */ - -/* Values for Elf32/64_Ehdr.e_flags. */ -#define EF_PPC_EMB 0x80000000 /* PowerPC embedded flag */ - -/* Cygnus local bits below */ -#define EF_PPC_RELOCATABLE 0x00010000 /* PowerPC -mrelocatable flag*/ -#define EF_PPC_RELOCATABLE_LIB 0x00008000 /* PowerPC -mrelocatable-lib - flag */ - -/* PowerPC relocations defined by the ABIs */ -#define R_PPC_NONE 0 -#define R_PPC_ADDR32 1 /* 32bit absolute address */ -#define R_PPC_ADDR24 2 /* 26bit address, 2 bits ignored. */ -#define R_PPC_ADDR16 3 /* 16bit absolute address */ -#define R_PPC_ADDR16_LO 4 /* lower 16bit of absolute address */ -#define R_PPC_ADDR16_HI 5 /* high 16bit of absolute address */ -#define R_PPC_ADDR16_HA 6 /* adjusted high 16bit */ -#define R_PPC_ADDR14 7 /* 16bit address, 2 bits ignored */ -#define R_PPC_ADDR14_BRTAKEN 8 -#define R_PPC_ADDR14_BRNTAKEN 9 -#define R_PPC_REL24 10 /* PC relative 26 bit */ -#define R_PPC_REL14 11 /* PC relative 16 bit */ -#define R_PPC_REL14_BRTAKEN 12 -#define R_PPC_REL14_BRNTAKEN 13 -#define R_PPC_GOT16 14 -#define R_PPC_GOT16_LO 15 -#define R_PPC_GOT16_HI 16 -#define R_PPC_GOT16_HA 17 -#define R_PPC_PLTREL24 18 -#define R_PPC_COPY 19 -#define R_PPC_GLOB_DAT 20 -#define R_PPC_JMP_SLOT 21 -#define R_PPC_RELATIVE 22 -#define R_PPC_LOCAL24PC 23 -#define R_PPC_UADDR32 24 -#define R_PPC_UADDR16 25 -#define R_PPC_REL32 26 -#define R_PPC_PLT32 27 -#define R_PPC_PLTREL32 28 -#define R_PPC_PLT16_LO 29 -#define R_PPC_PLT16_HI 30 -#define R_PPC_PLT16_HA 31 -#define R_PPC_SDAREL16 32 -#define R_PPC_SECTOFF 33 -#define R_PPC_SECTOFF_LO 34 -#define R_PPC_SECTOFF_HI 35 -#define R_PPC_SECTOFF_HA 36 -/* Keep this the last entry. */ -#define R_PPC_NUM 37 - -/* The remaining relocs are from the Embedded ELF ABI, and are not - in the SVR4 ELF ABI. */ -#define R_PPC_EMB_NADDR32 101 -#define R_PPC_EMB_NADDR16 102 -#define R_PPC_EMB_NADDR16_LO 103 -#define R_PPC_EMB_NADDR16_HI 104 -#define R_PPC_EMB_NADDR16_HA 105 -#define R_PPC_EMB_SDAI16 106 -#define R_PPC_EMB_SDA2I16 107 -#define R_PPC_EMB_SDA2REL 108 -#define R_PPC_EMB_SDA21 109 /* 16 bit offset in SDA */ -#define R_PPC_EMB_MRKREF 110 -#define R_PPC_EMB_RELSEC16 111 -#define R_PPC_EMB_RELST_LO 112 -#define R_PPC_EMB_RELST_HI 113 -#define R_PPC_EMB_RELST_HA 114 -#define R_PPC_EMB_BIT_FLD 115 -#define R_PPC_EMB_RELSDA 116 /* 16 bit relative offset in SDA */ - -/* Diab tool relocations. */ -#define R_PPC_DIAB_SDA21_LO 180 /* like EMB_SDA21, but lower 16 bit */ -#define R_PPC_DIAB_SDA21_HI 181 /* like EMB_SDA21, but high 16 bit */ -#define R_PPC_DIAB_SDA21_HA 182 /* like EMB_SDA21, adjusted high 16 */ -#define R_PPC_DIAB_RELSDA_LO 183 /* like EMB_RELSDA, but lower 16 bit */ -#define R_PPC_DIAB_RELSDA_HI 184 /* like EMB_RELSDA, but high 16 bit */ -#define R_PPC_DIAB_RELSDA_HA 185 /* like EMB_RELSDA, adjusted high 16 */ - -/* This is a phony reloc to handle any old fashioned TOC16 references - that may still be in object files. */ -#define R_PPC_TOC16 255 - -#endif /* _ELF_H */ - diff --git a/source/elfloader.c b/source/elfloader.c deleted file mode 100644 index 651a1eef..00000000 --- a/source/elfloader.c +++ /dev/null @@ -1,103 +0,0 @@ -/* - * Copyright (c) 2001 William L. Pitts - * Modifications (c) 2004 Felix Domke - * All rights reserved. - * - * Redistribution and use in source and binary forms are freely - * permitted provided that the above copyright notice and this - * paragraph and the following disclaimer are duplicated in all - * such forms. - * - * This software is provided "AS IS" and without any express or - * implied warranties, including, without limitation, the implied - * warranties of merchantability and fitness for a particular - * purpose. - */ - -#include -#include - -#include - -#include "elf_abi.h" - -/* ====================================================================== - * Determine if a valid ELF image exists at the given memory location. - * First looks at the ELF header magic field, the makes sure that it is - * executable and makes sure that it is for a PowerPC. - * ====================================================================== */ -s32 valid_elf_image (void *addr) -{ - Elf32_Ehdr *ehdr; /* Elf header structure pointer */ - - ehdr = (Elf32_Ehdr *) addr; - - if (!IS_ELF (*ehdr)) - return 0; - - if (ehdr->e_type != ET_EXEC) - return -1; - - if (ehdr->e_machine != EM_PPC) - return -1; - - return 1; -} - - -/* ====================================================================== - * A very simple elf loader, assumes the image is valid, returns the - * entry point address. - * ====================================================================== */ -u32 load_elf_image (void *addr) -{ - Elf32_Ehdr *ehdr; - Elf32_Shdr *shdr; - u8 *strtab = 0; - u8 *image; - int i; - - ehdr = (Elf32_Ehdr *) addr; - /* Find the section header string table for output info */ - shdr = (Elf32_Shdr *) (addr + ehdr->e_shoff + - (ehdr->e_shstrndx * sizeof (Elf32_Shdr))); - - if (shdr->sh_type == SHT_STRTAB) - strtab = (u8 *) (addr + shdr->sh_offset); - - /* Load each appropriate section */ - for (i = 0; i < ehdr->e_shnum; ++i) { - shdr = (Elf32_Shdr *) (addr + ehdr->e_shoff + - (i * sizeof (Elf32_Shdr))); - - if (!(shdr->sh_flags & SHF_ALLOC) - || shdr->sh_addr == 0 || shdr->sh_size == 0) { - continue; - } - - shdr->sh_addr &= 0x3FFFFFFF; - shdr->sh_addr |= 0x80000000; - - if (strtab) { - /*printf ("%sing section %s @ 0x%08x (0x%08x bytes)\n", - (shdr->sh_type == SHT_NOBITS) ? - "clear" : "load", - &strtab[shdr->sh_name], - (u32) shdr->sh_addr, - (u32) shdr->sh_size);*/ - } - - if (shdr->sh_type == SHT_NOBITS) { - memset ((void *) shdr->sh_addr, 0, shdr->sh_size); - } else { - image = (u8 *) addr + shdr->sh_offset; - memcpy ((void *) shdr->sh_addr, - (const void *) image, - shdr->sh_size); - } - DCFlushRangeNoSync ((void *) shdr->sh_addr, shdr->sh_size); - } - - return (ehdr->e_entry & 0x3FFFFFFF) | 0x80000000; -} - diff --git a/source/elfloader.h b/source/elfloader.h deleted file mode 100644 index d378fe45..00000000 --- a/source/elfloader.h +++ /dev/null @@ -1,18 +0,0 @@ -#ifndef _ELFLOADER_H_ -#define _ELFLOADER_H_ - -#include - -#ifdef __cplusplus -extern "C" -{ -#endif - -s32 valid_elf_image (void *addr); -u32 load_elf_image (void *addr); - -#ifdef __cplusplus -} -#endif - -#endif diff --git a/source/fatmounter.c b/source/fatmounter.c deleted file mode 100644 index 0009cefa..00000000 --- a/source/fatmounter.c +++ /dev/null @@ -1,184 +0,0 @@ -#include -#include -#include -#include -#include -#include -#include -#include -#include - -//these are the only stable and speed is good -#define CACHE 8 -#define SECTORS 64 - -enum BPB { - BPB_jmpBoot = 0x00, - BPB_OEMName = 0x03, - // BIOS Parameter Block - BPB_bytesPerSector = 0x0B, - BPB_sectorsPerCluster = 0x0D, - BPB_reservedSectors = 0x0E, - BPB_numFATs = 0x10, - BPB_rootEntries = 0x11, - BPB_numSectorsSmall = 0x13, - BPB_mediaDesc = 0x15, - BPB_sectorsPerFAT = 0x16, - BPB_sectorsPerTrk = 0x18, - BPB_numHeads = 0x1A, - BPB_numHiddenSectors = 0x1C, - BPB_numSectors = 0x20, - // Ext BIOS Parameter Block for FAT16 - BPB_FAT16_driveNumber = 0x24, - BPB_FAT16_reserved1 = 0x25, - BPB_FAT16_extBootSig = 0x26, - BPB_FAT16_volumeID = 0x27, - BPB_FAT16_volumeLabel = 0x2B, - BPB_FAT16_fileSysType = 0x36, - // Bootcode - BPB_FAT16_bootCode = 0x3E, - // FAT32 extended block - BPB_FAT32_sectorsPerFAT32 = 0x24, - BPB_FAT32_extFlags = 0x28, - BPB_FAT32_fsVer = 0x2A, - BPB_FAT32_rootClus = 0x2C, - BPB_FAT32_fsInfo = 0x30, - BPB_FAT32_bkBootSec = 0x32, - // Ext BIOS Parameter Block for FAT32 - BPB_FAT32_driveNumber = 0x40, - BPB_FAT32_reserved1 = 0x41, - BPB_FAT32_extBootSig = 0x42, - BPB_FAT32_volumeID = 0x43, - BPB_FAT32_volumeLabel = 0x47, - BPB_FAT32_fileSysType = 0x52, - // Bootcode - BPB_FAT32_bootCode = 0x5A, - BPB_bootSig_55 = 0x1FE, - BPB_bootSig_AA = 0x1FF -}; - -static const char FAT_SIG[3] = {'F', 'A', 'T'}; - -#define BYTES_PER_READ 512 - -static bool _FAT_partition_isFAT(const DISC_INTERFACE* disc, sec_t startSector) -{ - uint8_t sectorBuffer[BYTES_PER_READ] = {0}; - if (!disc->readSectors(startSector, 1, sectorBuffer)) { - return false; - } - // Make sure it is a valid BPB - if ( (sectorBuffer[BPB_bootSig_55] != 0x55) || (sectorBuffer[BPB_bootSig_AA] != 0xAA)) { - return false; - } - - // Now verify that this is indeed a FAT partition - if (memcmp(sectorBuffer + BPB_FAT16_fileSysType, FAT_SIG, sizeof(FAT_SIG)) && - memcmp(sectorBuffer + BPB_FAT32_fileSysType, FAT_SIG, sizeof(FAT_SIG))) - { - return false; - } - - // check again for the last two cases to make sure that we really have a FAT filesystem here - // and won't corrupt any data - if(memcmp(sectorBuffer + BPB_FAT16_fileSysType, "FAT", 3) != 0 && memcmp(sectorBuffer + BPB_FAT32_fileSysType, "FAT32", 5) != 0) - { - return false; - } - return true; -} -static inline uint32_t u8array_to_u32 (const uint8_t* item, int offset) { - return ( item[offset] | (item[offset + 1] << 8) | (item[offset + 2] << 16) | (item[offset + 3] << 24)); -} - -sec_t GetFATPartition(const DISC_INTERFACE* disc) -{ - int i; - uint8_t sectorBuffer[BYTES_PER_READ] = {0}; - sec_t startSector = 0; - - if(!disc->startup()) - return 0; - - // Read first sector of disc - if (!disc->readSectors(0, 1, sectorBuffer)) - startSector = 0; - - // Make sure it is a valid MBR or boot sector - if ( (sectorBuffer[BPB_bootSig_55] != 0x55) || (sectorBuffer[BPB_bootSig_AA] != 0xAA)) - startSector = 0; - - if (!memcmp(sectorBuffer + BPB_FAT16_fileSysType, FAT_SIG, sizeof(FAT_SIG))) - { - // Check if there is a FAT string, which indicates this is a boot sector - startSector = 0; - } - else if (!memcmp(sectorBuffer + BPB_FAT32_fileSysType, FAT_SIG, sizeof(FAT_SIG))) - { - // Check for FAT32 - startSector = 0; - } - else - { - // This is an MBR - // Find first valid partition from MBR - // First check for an active partition - for (i=0x1BE; (i < 0x1FE) && (sectorBuffer[i] != 0x80); i+= 0x10); - // If it find an active partition, check for FAT-Partition - if ( i != 0x1FE && !_FAT_partition_isFAT(disc, u8array_to_u32(sectorBuffer, 0x8 + i)) ) - i = 0x1FE; - - // If it didn't find an active partition, search for any valid partition - if (i == 0x1FE) - { - for (i=0x1BE; i < 0x1FE; i+= 0x10) - { - if ( sectorBuffer[i+0x04] != 0x00 && _FAT_partition_isFAT(disc, u8array_to_u32(sectorBuffer, 0x8 + i)) ) - break; - } - } - if (i != 0x1FE) - startSector = u8array_to_u32(sectorBuffer, 0x8 + i);; - } - disc->shutdown(); - return startSector; -} - -int USBDevice_Init() -{ - //closing all open Files write back the cache and then shutdown em! - fatUnmount("USB:/"); - //right now mounts first FAT-partition - if (fatMount("USB", &__io_usbstorage, GetFATPartition(&__io_usbstorage), CACHE, SECTORS)) { - //try first mount with libogc - return 1; - } - return -1; -} - -void USBDevice_deInit() -{ - //closing all open Files write back the cache and then shutdown em! - fatUnmount("USB:/"); -} - -int isSdInserted() -{ - return __io_wiisd.isInserted(); -} - -int SDCard_Init() -{ - //closing all open Files write back the cache and then shutdown em! - fatUnmount("SD:/"); - //right now mounts first FAT-partition - if (fatMount("SD", &__io_wiisd, GetFATPartition(&__io_wiisd), CACHE, SECTORS)) - return 1; - return -1; -} - -void SDCard_deInit() -{ - //closing all open Files write back the cache and then shutdown em! - fatUnmount("SD:/"); -} diff --git a/source/filelist.h b/source/filelist.h index e0af26c3..6bc5a69b 100644 --- a/source/filelist.h +++ b/source/filelist.h @@ -1,20 +1,20 @@ -/**************************************************************************** - * libwiigui Template - * Tantric 2009 - * - * imagelist.h - * Contains a list of all of the files in the images, fonts, sounds folders - ***************************************************************************/ - -#ifndef _FILELIST_H_ -#define _FILELIST_H_ - -#include - -extern const u8 background_png[]; -extern const u32 background_png_size; - -extern const u8 background169_png[]; -extern const u32 background169_png_size; - -#endif +/**************************************************************************** + * filelist.h + * Contains a list of all of the files in the images, fonts, sounds folders + ***************************************************************************/ + +#ifndef _FILELIST_H_ +#define _FILELIST_H_ + +#include + +extern const u8 app_booter_dol[]; +extern const u32 app_booter_dol_size; + +extern const u8 background_png[]; +extern const u32 background_png_size; + +extern const u8 background169_png[]; +extern const u32 background169_png_size; + +#endif diff --git a/source/main.c b/source/main.c new file mode 100644 index 00000000..f87f3131 --- /dev/null +++ b/source/main.c @@ -0,0 +1,205 @@ + /**************************************************************************** + * Copyright 2009 The Lemon Man and thanks to luccax, Wiipower, Aurelio and crediar + * Copyright 2010 Dimok + * + * Original forwarder source by + * + * 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 +#include +#include + +#include "video.h" +#include "background_image.h" +#include "dolloader.h" +#include "filelist.h" +#include "devicemounter.h" +#include "cfg.h" + +void __exception_setreload(int t); + +static FILE * open_file(const char * dev, char * filepath) +{ + sprintf(filepath, "%s:/apps/usbloader_gx/boot.dol", dev); + + FILE * exeFile = fopen(filepath ,"rb"); + if (exeFile == NULL) + { + sprintf(filepath, "%s:/apps/usbloader_gx/boot.dol", dev); + exeFile = fopen(filepath ,"rb"); + } + if (exeFile == NULL) + { + sprintf(filepath, "%s:/apps/usbloadergx/boot.dol", dev); + exeFile = fopen(filepath ,"rb"); + } + + return exeFile; +} + +static bool FindConfigPath(const char * device, char *cfgpath) +{ + bool result = false; + sprintf(cfgpath, "%s:/config/GXGlobal.cfg", device); + result = cfg_parsefile(cfgpath, &cfg_set); + + if(!result) + { + sprintf(cfgpath, "%s:/apps/usbloader_gx/GXGlobal.cfg", device); + result = cfg_parsefile(cfgpath, &cfg_set); + } + if(!result) + { + sprintf(cfgpath, "%s:/apps/usbloadergx/GXGlobal.cfg", device); + result = cfg_parsefile(cfgpath, &cfg_set); + } + + return result; +} + +int main(int argc, char **argv) +{ + u32 cookie; + FILE *exeFile = NULL; + void * exeBuffer = (void *)EXECUTABLE_MEM_ADDR; + u32 exeSize = 0; + u32 exeEntryPointAddress = 0; + entrypoint exeEntryPoint; + __exception_setreload(0); + + /* int videomod */ + InitVideo(); + /* get imagedata */ + u8 * imgdata = GetImageData(); + fadein(imgdata); + + char filepath[200]; + + // try SD Card First + SDCard_Init(); + + if(FindConfigPath(DeviceName[SD], filepath)) + { + strcat(filepath, "boot.dol"); + exeFile = fopen(filepath, "rb"); + } + if(!exeFile) + exeFile = open_file(DeviceName[SD], filepath); + // if app not found on SD Card try USB + if (exeFile == NULL) + { + USBDevice_Init(); + int dev; + for(dev = USB1; dev < MAXDEVICES; ++dev) + { + if(FindConfigPath(DeviceName[dev], filepath)) + { + strcat(filepath, "boot.dol"); + exeFile = fopen(filepath, "rb"); + } + if(!exeFile) + exeFile = open_file(DeviceName[dev], filepath); + } + } + + // if nothing found exiting + if (exeFile == NULL) + { + fadeout(imgdata); + fclose (exeFile); + SDCard_deInit(); + USBDevice_deInit(); + StopGX(); + free(imgdata); + SYS_ResetSystem(SYS_RETURNTOMENU, 0, 0); + } + + fseek (exeFile, 0, SEEK_END); + exeSize = ftell(exeFile); + rewind (exeFile); + + if(fread (exeBuffer, 1, exeSize, exeFile) != exeSize) + { + fadeout(imgdata); + fclose (exeFile); + SDCard_deInit(); + USBDevice_deInit(); + StopGX(); + free(imgdata); + SYS_ResetSystem(SYS_RETURNTOMENU, 0, 0); + } + fclose (exeFile); + + /* load entry point */ + struct __argv args; + bzero(&args, sizeof(args)); + args.argvMagic = ARGV_MAGIC; + args.length = strlen(filepath) + 2; + args.commandLine = (char*)malloc(args.length); + if (!args.commandLine) SYS_ResetSystem(SYS_RETURNTOMENU, 0, 0); + strcpy(args.commandLine, filepath); + args.commandLine[args.length - 1] = '\0'; + args.argc = 1; + args.argv = &args.commandLine; + args.endARGV = args.argv + 1; + + u8 * appboot_buff = (u8 *) malloc(app_booter_dol_size); + if(!appboot_buff) + { + fadeout(imgdata); + SDCard_deInit(); + USBDevice_deInit(); + StopGX(); + free(imgdata); + SYS_ResetSystem(SYS_RETURNTOMENU, 0, 0); + } + + memcpy(appboot_buff, app_booter_dol, app_booter_dol_size); + + exeEntryPointAddress = load_dol_image(appboot_buff, &args); + + if(appboot_buff) + free(appboot_buff); + + fadeout(imgdata); + SDCard_deInit(); + USBDevice_deInit(); + StopGX(); + free(imgdata); + + //! Reset HBC stub so we can leave correct from usb loader + memset((char *) 0x80001804, 0, 8); + + if (exeEntryPointAddress == 0) + SYS_ResetSystem(SYS_RETURNTOMENU, 0, 0); + + exeEntryPoint = (entrypoint) exeEntryPointAddress; + /* cleaning up and load dol */ + SYS_ResetSystem(SYS_SHUTDOWN, 0, 0); + _CPU_ISR_Disable (cookie); + __exception_closeall (); + exeEntryPoint (); + _CPU_ISR_Restore (cookie); + return 0; +} diff --git a/source/main.cpp b/source/main.cpp deleted file mode 100644 index 7750d15c..00000000 --- a/source/main.cpp +++ /dev/null @@ -1,233 +0,0 @@ -/* - * loadMii loader v0.3 - * main.c - * http://code.google.com/p/loadmii - * - * Copyright 2009 The Lemon Man - * Thanks to luccax, Wiipower, Aurelio and crediar - * GX added by dimok (thanks to Tantric) - * usbGecko powered by Nuke - * - * This program is free software; you can redistribute it and/or modify - * it under the terms of the GNU General Public License as published by - * the Free Software Foundation; either version 2 of the License, or - * (at your option) any later version. - * - * This program is distributed in the hope that it will be useful, - * but WITHOUT ANY WARRANTY; without even the implied warranty of - * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the - * GNU General Public License for more details. - * - * You should have received a copy of the GNU General Public License - * along with this program; if not, write to the Free Software - * Foundation, Inc., 51 Franklin Street, Fifth Floor, Boston, - * MA 02110-1301, USA. - */ - -#include -#include -#include -#include -#include -#include - -#include "pngu/pngu.h" -#include "video.h" -#include "filelist.h" -#include "dolloader.h" -#include "elfloader.h" -#include "cfg.h" -#include "fatmounter.h" - - -static PNGUPROP imgProp; -static IMGCTX ctx; - - -u8 * GetImageData(void) { - - u8 * data = NULL; - - int ret; - - if (CONF_GetAspectRatio()) { - ctx = PNGU_SelectImageFromBuffer(background169_png); - if (!ctx) - return NULL; - } else { - ctx = PNGU_SelectImageFromBuffer(background_png); - if (!ctx) - return NULL; - } - - ret = PNGU_GetImageProperties(ctx, &imgProp); - if (ret != PNGU_OK) - return NULL; - - int len = imgProp.imgWidth * imgProp.imgHeight * 4; - - if(len%32) len += (32-len%32); - data = (u8 *)memalign (32, len); - ret = PNGU_DecodeTo4x4RGBA8 (ctx, imgProp.imgWidth, imgProp.imgHeight, data, 255); - DCFlushRange(data, len); - - PNGU_ReleaseImageContext(ctx); - - return data; -} - -void Background_Show(int x, int y, int z, u8 * data, int angle, int scaleX, int scaleY, int alpha) -{ - /* Draw image */ - Menu_DrawImg(x, y, z, imgProp.imgWidth, imgProp.imgHeight, data, angle, scaleX, scaleY, alpha); -} - - -int main(int argc, char **argv) -{ - u32 cookie; - FILE *exeFile = NULL; - void *exeBuffer = (void *)EXECUTABLE_MEM_ADDR; - int exeSize = 0; - u32 exeEntryPointAddress = 0; - entrypoint exeEntryPoint; - /* int videomod */ - InitVideo(); - /* get imagedata */ - u8 * imgdata = GetImageData(); - /* fadein of image */ - for(int i = 0; i < 255; i = i+10) - { - if(i>255) i = 255; - Background_Show(0, 0, 0, imgdata, 0, 1, 1, i); - Menu_Render(); - } - /* check devices */ - SDCard_Init(); - USBDevice_Init(); - - char cfgpath[256]; - bool result = false; - - sprintf(cfgpath, "SD:/config/GXGlobal.cfg"); - result = cfg_parsefile(cfgpath, &cfg_set); - if(!result) //no cfg-File on SD: try USB: - { - sprintf(cfgpath, "USB:/config/GXGlobal.cfg"); - result = cfg_parsefile(cfgpath, &cfg_set); - } - - if(result) - { - sprintf(cfgpath, "%sboot.dol", update_path); - /* Open dol File and check exist */ - exeFile = fopen (cfgpath, "rb"); - if (exeFile==NULL) - { - sprintf(cfgpath, "%sboot.elf", update_path); - exeFile = fopen (cfgpath,"rb"); - } - if (exeFile==NULL) - result = false; - else - result = true; - } - - if(!result) // non cfg-File loaded or update_path not set - { - /* Open dol File and check exist */ - sprintf(cfgpath, "SD:/apps/usbloader_gx/boot.dol"); - exeFile = fopen (cfgpath ,"rb"); - if (exeFile==NULL) - { - sprintf(cfgpath, "SD:/apps/usbloader_gx/boot.elf"); - exeFile = fopen (cfgpath ,"rb"); - } - if (exeFile==NULL) - { - sprintf(cfgpath, "USB:/apps/usbloader_gx/boot.dol"); - exeFile = fopen (cfgpath ,"rb"); - } - if (exeFile==NULL) - { - sprintf(cfgpath, "USB:/apps/usbloader_gx/boot.elf"); - exeFile = fopen (cfgpath ,"rb"); - } - // if nothing found exiting - if (exeFile==NULL) { - printf("\n\n\t\tCan't find DOL File...\n"); - Menu_Render(); - sleep(3); - fclose (exeFile); - SDCard_deInit(); - USBDevice_deInit(); - StopGX(); - SYS_ResetSystem(SYS_RETURNTOMENU, 0, 0); - } - } - - fseek (exeFile, 0, SEEK_END); - exeSize = ftell(exeFile); - fseek (exeFile, 0, SEEK_SET); - - if(fread (exeBuffer, 1, exeSize, exeFile) != (unsigned int) exeSize) - { - printf("\n\n\t\tCan't open DOL File...\n"); - Menu_Render(); - fclose (exeFile); - sleep(3); - SDCard_deInit(); - USBDevice_deInit(); - StopGX(); - SYS_ResetSystem(SYS_RETURNTOMENU, 0, 0); - } - fclose (exeFile); - - /* load entry point */ - struct __argv args; - bzero(&args, sizeof(args)); - args.argvMagic = ARGV_MAGIC; - args.length = strlen(cfgpath) + 2; - args.commandLine = (char*)malloc(args.length); - if (!args.commandLine) SYS_ResetSystem(SYS_RETURNTOMENU, 0, 0); - strcpy(args.commandLine, cfgpath); - args.commandLine[args.length - 1] = '\0'; - args.argc = 1; - args.argv = &args.commandLine; - args.endARGV = args.argv + 1; - - int ret = valid_elf_image(exeBuffer); - if (ret == 1) - exeEntryPointAddress = load_elf_image(exeBuffer); - else - exeEntryPointAddress = load_dol_image(exeBuffer, &args); - - /* fadeout of image */ - for(int i = 255; i > 1; i = i-7) - { - if(i < 0) i = 0; - Background_Show(0, 0, 0, imgdata, 0, 1, 1, i); - Menu_Render(); - } - SDCard_deInit(); - USBDevice_deInit(); - StopGX(); - if (exeEntryPointAddress == 0) { - printf("EntryPointAddress failed...\n"); - Menu_Render(); - sleep(3); - fclose (exeFile); - SDCard_deInit(); - USBDevice_deInit(); - StopGX(); - SYS_ResetSystem(SYS_RETURNTOMENU, 0, 0);; - } - exeEntryPoint = (entrypoint) exeEntryPointAddress; - /* cleaning up and load dol */ - SYS_ResetSystem(SYS_SHUTDOWN, 0, 0); - _CPU_ISR_Disable (cookie); - __exception_closeall (); - exeEntryPoint (); - _CPU_ISR_Restore (cookie); - return 0; -} diff --git a/source/pngu.c b/source/pngu.c new file mode 100644 index 00000000..16cefd91 --- /dev/null +++ b/source/pngu.c @@ -0,0 +1,1306 @@ +/******************************************************************************************** + +PNGU Version : 0.2a + +Coder : frontier + +More info : http://frontier-dev.net + +********************************************************************************************/ +#include +#include +#include "pngu.h" +#include "png.h" + + +// Constants +#define PNGU_SOURCE_BUFFER 1 +#define PNGU_SOURCE_DEVICE 2 + +#define _SHIFTL(v, s, w) \ + ((PNGU_u32) (((PNGU_u32)(v) & ((0x01 << (w)) - 1)) << (s))) +#define _SHIFTR(v, s, w) \ + ((PNGU_u32)(((PNGU_u32)(v) >> (s)) & ((0x01 << (w)) - 1))) + +// 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; +} + +// Coded by Tantric for libwiigui (http://code.google.com/p/libwiigui) +int PNGU_EncodeFromRGB (IMGCTX ctx, PNGU_u32 width, PNGU_u32 height, void *buffer, PNGU_u32 stride) +{ + png_uint_32 rowbytes; + PNGU_u32 y; + + // 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 >>2) + 1) <<2; // Add extra padding so each row starts in a 4 byte boundary + + ctx->img_data = malloc(rowbytes * height); + memset(ctx->img_data, 0, 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); + memset(ctx->row_pointers, 0, 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; + } + + for (y = 0; y < height; ++y) + { + ctx->row_pointers[y] = buffer + (y * rowbytes); + } + + // 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 ctx->cursor; +} + +// Coded by Tantric for libwiigui (http://code.google.com/p/libwiigui) +int PNGU_EncodeFromGXTexture (IMGCTX ctx, PNGU_u32 width, PNGU_u32 height, void *buffer, PNGU_u32 stride) +{ + int res; + PNGU_u32 x,y, tmpy1, tmpy2, tmpyWid, tmpxy; + + unsigned char * ptr = (unsigned char*)buffer; + unsigned char * tmpbuffer = (unsigned char *)malloc(width*height*3); + memset(tmpbuffer, 0, width*height*3); + png_uint_32 offset; + + for(y=0; y < height; y++) + { + tmpy1 = y * 640*3; + tmpy2 = y%4 << 2; + tmpyWid = (((y >> 2)<<4)*width); + + for(x=0; x < width; x++) + { + offset = tmpyWid + ((x >> 2)<<6) + ((tmpy2+ x%4 ) << 1); + tmpxy = x * 3 + tmpy1; + + tmpbuffer[tmpxy ] = ptr[offset+1]; // R + tmpbuffer[tmpxy+1] = ptr[offset+32]; // G + tmpbuffer[tmpxy+2] = ptr[offset+33]; // B + } + } + + res = PNGU_EncodeFromRGB (ctx, width, height, tmpbuffer, stride); + free(tmpbuffer); + return res; +} + +// Coded by Crayon for GRRLIB (http://code.google.com/p/grrlib) +int PNGU_EncodeFromEFB (IMGCTX ctx, PNGU_u32 width, PNGU_u32 height, PNGU_u32 stride) +{ + int res; + PNGU_u32 x,y, tmpy, tmpxy, regval, val; + unsigned char * tmpbuffer = (unsigned char *)malloc(width*height*3); + memset(tmpbuffer, 0, width*height*3); + + for(y=0; y < height; y++) + { + tmpy = y * 640*3; + for(x=0; x < width; x++) + { + regval = 0xc8000000|(_SHIFTL(x,2,10)); + regval = (regval&~0x3FF000)|(_SHIFTL(y,12,10)); + val = *(PNGU_u32*)regval; + tmpxy = x * 3 + tmpy; + tmpbuffer[tmpxy ] = _SHIFTR(val,16,8); // R + tmpbuffer[tmpxy+1] = _SHIFTR(val,8,8); // G + tmpbuffer[tmpxy+2] = val&0xff; // B + } + } + + res = PNGU_EncodeFromRGB (ctx, width, height, tmpbuffer, stride); + free(tmpbuffer); + return res; +} + + +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/pngu.h b/source/pngu.h new file mode 100644 index 00000000..e1e20b6d --- /dev/null +++ b/source/pngu.h @@ -0,0 +1,175 @@ +/******************************************************************************************** + +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); + +int PNGU_EncodeFromRGB (IMGCTX ctx, PNGU_u32 width, PNGU_u32 height, void *buffer, PNGU_u32 stride); +int PNGU_EncodeFromGXTexture (IMGCTX ctx, PNGU_u32 width, PNGU_u32 height, void *buffer, PNGU_u32 stride); +int PNGU_EncodeFromEFB (IMGCTX ctx, PNGU_u32 width, PNGU_u32 height, 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/video.cpp b/source/video.c similarity index 87% rename from source/video.cpp rename to source/video.c index 7dac715c..80ae461e 100644 --- a/source/video.cpp +++ b/source/video.c @@ -20,6 +20,8 @@ static int whichfb = 0; // Switch static GXRModeObj *vmode; // Menu video mode static unsigned char gp_fifo[DEFAULT_FIFO_SIZE] ATTRIBUTE_ALIGN (32); static Mtx GXmodelView2D; +int screenwidth = 640; +int screenheight = 480; /**************************************************************************** * StartGX @@ -105,7 +107,7 @@ ResetVideo_Menu() guMtxTransApply (GXmodelView2D, GXmodelView2D, 0.0F, 0.0F, -200.0F); GX_LoadPosMtxImm(GXmodelView2D,GX_PNMTX0); - guOrtho(p,0,479,0,639,0,300); + guOrtho(p,0,screenheight-1,0,screenwidth-1,0,300); GX_LoadProjectionMtx(p, GX_ORTHOGRAPHIC); GX_SetViewport(0,0,vmode->fbWidth,vmode->efbHeight,0,1); @@ -126,15 +128,54 @@ InitVideo () VIDEO_Init(); vmode = VIDEO_GetPreferredMode(NULL); // get default video mode + bool pal = false; + + if (vmode == &TVPal528IntDf) + pal = true; + + if (CONF_GetAspectRatio() == CONF_ASPECT_16_9) + { + screenwidth = 720; + vmode->fbWidth = 640; + vmode->efbHeight = 456; + vmode->viWidth = 686; + + if (pal) + { + vmode->xfbHeight = 542; + vmode->viHeight = 542; + } + else + { + vmode->xfbHeight = 456; + vmode->viHeight = 456; + } + } + else + { + if (pal) + vmode = &TVPal574IntDfScale; + + vmode->viWidth = 672; + } + + if (pal) + { + vmode->viXOrigin = (VI_MAX_WIDTH_PAL - vmode->viWidth) / 2; + vmode->viYOrigin = (VI_MAX_HEIGHT_PAL - vmode->viHeight) / 2; + } + else + { + vmode->viXOrigin = (VI_MAX_WIDTH_NTSC - vmode->viWidth) / 2; + vmode->viYOrigin = (VI_MAX_HEIGHT_NTSC - vmode->viHeight) / 2; + } + VIDEO_Configure (vmode); // Allocate the video buffers xfb[0] = (u32 *) MEM_K0_TO_K1 (SYS_AllocateFramebuffer (vmode)); xfb[1] = (u32 *) MEM_K0_TO_K1 (SYS_AllocateFramebuffer (vmode)); - // A console is always useful while debugging - console_init (xfb[0], 20, 64, vmode->fbWidth, vmode->xfbHeight, vmode->fbWidth * 2); - // Clear framebuffers etc. VIDEO_ClearFrameBuffer (vmode, xfb[0], COLOR_BLACK); VIDEO_ClearFrameBuffer (vmode, xfb[1], COLOR_BLACK); @@ -237,4 +278,4 @@ void Menu_DrawImg(f32 xpos, f32 ypos, f32 zpos, u16 width, u16 height, u8 data[] GX_SetTevOp (GX_TEVSTAGE0, GX_PASSCLR); GX_SetVtxDesc (GX_VA_TEX0, GX_NONE); -} +}