commit bb1e653309a614cc2f19e79043780b06c3f525b4 Author: Jay Oster Date: Sat Jun 28 22:10:11 2014 -0700 Initial Commit: v0.186 diff --git a/.gitignore b/.gitignore new file mode 100644 index 0000000..cea8197 --- /dev/null +++ b/.gitignore @@ -0,0 +1,15 @@ +## Build files +*.v64 +*.elf +*.o +*.bin +*.dfs + +## OSX junk +.DS_Store +.Trashes +._* + +## Temporary files +*.tmp +*~ diff --git a/ALT64.INI b/ALT64.INI new file mode 100644 index 0000000..9b9ed3e --- /dev/null +++ b/ALT64.INI @@ -0,0 +1,32 @@ +; alt64 config file + +[ed64] ; Menu config +build=18 ; Release build nr +border_color_1=FFFFFFFF ; 0x00000080 RGBT +border_color_2=3F3F3FFF ; 0x3F3F3FFF RGBT 00000060 w light +box_color=000000B6 ; 0x00000080 RGBT +selection_color=80008070 ; 80008070 RGBT 6495ED60 +list_font_color=CDC9C940 ; 80008070 RGBT 6495ED60 +list_dir_font_color=FFFFE040 ; 80008070 RGBT 6495ED60 +selection_font_color=FFB90FFF ; 80008070 RGBT 6495ED60 +text_offset=0 ; shift menu horizontal e.g. -1 +cd_behaviour=1 ; 0=first entry 1=last entry +scroll_behaviour=0 ; 0=page-system 1=classic +quick_boot=1 ; 'START' boots last rom +sound_on=1 ; sounds 1=on 0=off +page_display=1 ; display page +tv_mode=1 ; 1=ntsc 2=pal 3=mpal 0=force_off +enable_colored_list=1 ; 1=enable 0=disalbe +ext_type=0 ; 0=classic 1=OS64 +sd_speed=2 ; 1=25MHz 2=50MHz +background_image=background.png ; backgrund png image 320x240 32bit +hide_sysfolder=0 ; 1=hide 0=don't hide +mempak_path=/MEMPAKS/ ; surround with slashes +save_path=SDSAVE ; save directory inside ED64 + +[user] +name = saturnu ; Username + +[gblite] +save_path=/ED64/SDSAVE/ ; save directory surround with slashes +tv_mode=0 ; 1=ntsc 2=pal 3=mpal 0=force_off diff --git a/Makefile b/Makefile new file mode 100644 index 0000000..a82f20b --- /dev/null +++ b/Makefile @@ -0,0 +1,38 @@ +ROOTDIR = $(N64_INST) +GCCN64PREFIX = $(ROOTDIR)/bin/mips64-elf- +CHKSUM64PATH = $(ROOTDIR)/bin/chksum64 +MKDFSPATH = $(ROOTDIR)/bin/mkdfs +HEADERPATH = $(ROOTDIR)/lib +N64TOOL = $(ROOTDIR)/bin/n64tool +HEADERNAME = header + + +LINK_FLAGS = -G4 -L$(ROOTDIR)/toolchain_font/lib -L$(ROOTDIR)/mips64-elf/lib -ldragon -L$(ROOTDIR)/toolchain_font/lib -lmikmod -lmad -lc -lm -ldragonsys -lnosys $(LIBS) -Tn64ld.x +PROG_NAME = menu +CFLAGS = -std=gnu99 -march=vr4300 -mtune=vr4300 -G4 -I$(ROOTDIR)/toolchain_font/include -I$(ROOTDIR)/mips64-elf/include -lpthread -lrt -D_REENTRANT -DUSE_TRUETYPE +ASFLAGS = -mtune=vr4300 -march=vr4300 +CC = $(GCCN64PREFIX)gcc +AS = $(GCCN64PREFIX)as +LD = $(GCCN64PREFIX)ld +OBJCOPY = $(GCCN64PREFIX)objcopy +OBJS = $(PROG_NAME).o everdrive.o fat.o disk.o mem.o sys.o ini.o strlib.o utils.o sram.o stb_image.o chksum64.o mp3.o + +$(PROG_NAME).v64: $(PROG_NAME).elf test.dfs + $(OBJCOPY) $(PROG_NAME).elf $(PROG_NAME).bin -O binary + rm -f $(PROG_NAME).v64 + $(N64TOOL) -l 4M -t "EverDrive OS" -h ./header.ed64 -o OS64.v64 $(PROG_NAME).bin -s 1M test.dfs + $(CHKSUM64PATH) OS64.v64 + +$(PROG_NAME).elf : $(OBJS) + $(LD) -o $(PROG_NAME).elf $(OBJS) $(LINK_FLAGS) + +copy: $(PROG_NAME).v64 + sh upload.sh + +test.dfs: + $(MKDFSPATH) test.dfs ./filesystem/ + +all: $(PROG_NAME).v64 + +clean: + rm -f *.v64 *.elf *.o *.bin *.dfs diff --git a/README b/README new file mode 100644 index 0000000..eaae5f3 --- /dev/null +++ b/README @@ -0,0 +1,56 @@ +alt64 +===== + +alternative everdrive64 menu + + +Kuroneko! + + :\ /; _ + ; \___/ ; ; ; + ,:-"' `"-:. / ; +_ /,---. ,---.\ _ _; / +_:>(( | ) ( | ))<:_ ,-""_," + \````` `````/""""",-"" + '-.._ v _..-' ) + / ___ ____,.. \ + / / | | | ( \. \ +ctr / / | | | | \ \ + `" `" `" `" + +nyannyannyannyannyannyannyannyannyannyannyannyannyannyannyannyannyan + + +hello here is my menu for the everdrive64. +if you don't have one you should visit krikzzs homepage +for ordering information. ^^ + +if you want to build the menu, you need a n64 toolchain +you can get one free from + +http://www.dragonminded.com/n64dev/libdragon/ +https://github.com/DragonMinded/libdragon + +i used the libmikmod linked on his site too for the menu +http://www.dragonminded.com/n64dev/libmikmod-3.2.0-beta2.tgz + + +if you are on linux, try out the build script +https://github.com/DragonMinded/libdragon/tree/master/tools/build + +here are a few setting hints +export INSTALL_PATH=/usr/mips64-elf +set this to your home directory if you don't have root access + +i was in need to alter the cxxflags to G0 to lowercase +CXXFLAGS="-g0 -O2" + +it's a good idea to the latest versions for the toolchain +export BINUTILS_V=2.23.52 +export GCC_V=4.8.0 +export NEWLIB_V=1.20.0 + + +optional: +get libn64-hkz to get more into flashram savegames +http://sourceforge.net/projects/n64dev/ diff --git a/chksum64.c b/chksum64.c new file mode 100644 index 0000000..b2e0a6c --- /dev/null +++ b/chksum64.c @@ -0,0 +1,106 @@ +/* + checksum rom in psram, based on + + chksum64 V1.2, a program to calculate the ROM checksum of Nintendo64 ROMs. + Copyright (C) 1997 Andreas Sterbenz (stan@sbox.tu-graz.ac.at) + + This program is free software; you can redistribute it and/or modify + it under the terms of the GNU General Public License as published by + the Free Software Foundation; either version 2 of the License, or + (at your option) any later version. + + This program is distributed in the hope that it will be useful, + but WITHOUT ANY WARRANTY; without even the implied warranty of + MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the + GNU General Public License for more details. + + You should have received a copy of the GNU General Public License + along with this program; if not, write to the Free Software + Foundation, Inc., 59 Temple Place - Suite 330, Boston, MA 02111-1307, USA +*/ +#include +#include +#include + + +#define BUFSIZE 0x8000 +#define SDRAM_START 0xb0000000 + +#define CHECKSUM_START 0x1000 +#define CHECKSUM_LENGTH 0x100000L +#define CHECKSUM_HEADERPOS 0x10 +#define CHECKSUM_END (CHECKSUM_START + CHECKSUM_LENGTH) + +#define CHECKSUM_STARTVALUE 0xf8ca4ddc + +#define ROL(i, b) (((i)<<(b)) | ((i)>>(32-(b)))) + +#define BYTES2LONG(b) ( (((b)[0] & 0xffL) << 24) | \ + (((b)[1] & 0xffL) << 16) | \ + (((b)[2] & 0xffL) << 8) | \ + (((b)[3] & 0xffL)) ) + +#define LONG2BYTES(l, b) (b)[0] = ((l)>>24)&0xff; \ + (b)[1] = ((l)>>16)&0xff; \ + (b)[2] = ((l)>> 8)&0xff; \ + (b)[3] = ((l) )&0xff; + + +static unsigned char __attribute__((aligned(16))) buffer1[BUFSIZE]; + + +void checksum_sdram(void) +{ + unsigned int sum1, sum2, offs; + + { + unsigned int i; + unsigned int c1, k1, k2; + unsigned int t1, t2, t3, t4; + unsigned int t5, t6; + unsigned int n; + unsigned int clen = CHECKSUM_LENGTH; + + t1 = CHECKSUM_STARTVALUE; + t2 = CHECKSUM_STARTVALUE; + t3 = CHECKSUM_STARTVALUE; + t4 = CHECKSUM_STARTVALUE; + t5 = CHECKSUM_STARTVALUE; + t6 = CHECKSUM_STARTVALUE; + + offs = CHECKSUM_START; + + for( ;; ) { + n = (BUFSIZE < clen) ? BUFSIZE : clen; + dma_read_s(buffer1, SDRAM_START+offs, n); + data_cache_hit_writeback_invalidate(buffer1,n); + + offs += n; + + for( i=0; i + +#include "disk.h" +#include "mem.h" +#include "everdrive.h" +#include "errors.h" +#include "sys.h" +#include "usb.h" + +#define CMD0 0x40 // software reset +#define CMD1 0x41 // brings card out of idle state +#define CMD8 0x48 // Reserved +#define CMD12 0x4C // stop transmission on multiple block read +#define CMD17 0x51 // read single block +#define CMD18 0x52 // read multiple block +#define CMD58 0x7A // reads the OCR register +#define CMD55 0x77 +#define CMD41 0x69 +#define CMD24 0x58 // writes a single block +#define CMD25 0x59 // writes a multi block +#define ACMD41 0x69 +#define ACMD6 0x46 +#define SD_V2 2 +#define SD_HC 1 + + +#define CMD2 0x42 //read cid +#define CMD3 0x43 //read rca +#define CMD7 0x47 +#define CMD9 0x49 +#define CMD6 0x46 + +#define R1 1 +#define R2 2 +#define R3 3 +#define R6 6 +#define R7 7 + +u8 card_type; +u8 sd_resp_buff[18]; +u32 disk_interface; + + + +unsigned int diskCrc7(unsigned char *buff, unsigned int len); +void diskCrc16SD(u8 *data, u16 *crc_out, u16 len); +u8 diskGetRespTypeSD(u8 cmd); +u8 diskCmdSD(u8 cmd, u32 arg); + +u8 diskInitSD(); +u8 diskReadSD(u32 saddr, void *buff, u16 slen); +u8 diskWriteSD(u32 saddr, u8 *buff, u16 slen); +u8 diskStopRwSD(); + +u8 diskCmdSPI(u8 cmd, u32 arg); +u8 diskInitSPI(); +u8 diskReadSPI(u32 saddr, void *buff, u16 slen); +u8 diskWriteSPI(u32 saddr, u8 *buff, u16 slen); + + + +const u16 sd_crc16_table[] = { + 0x0000, 0x1021, 0x2042, 0x3063, 0x4084, 0x50A5, 0x60C6, 0x70E7, + 0x8108, 0x9129, 0xA14A, 0xB16B, 0xC18C, 0xD1AD, 0xE1CE, 0xF1EF, + 0x1231, 0x0210, 0x3273, 0x2252, 0x52B5, 0x4294, 0x72F7, 0x62D6, + 0x9339, 0x8318, 0xB37B, 0xA35A, 0xD3BD, 0xC39C, 0xF3FF, 0xE3DE, + 0x2462, 0x3443, 0x0420, 0x1401, 0x64E6, 0x74C7, 0x44A4, 0x5485, + 0xA56A, 0xB54B, 0x8528, 0x9509, 0xE5EE, 0xF5CF, 0xC5AC, 0xD58D, + 0x3653, 0x2672, 0x1611, 0x0630, 0x76D7, 0x66F6, 0x5695, 0x46B4, + 0xB75B, 0xA77A, 0x9719, 0x8738, 0xF7DF, 0xE7FE, 0xD79D, 0xC7BC, + 0x48C4, 0x58E5, 0x6886, 0x78A7, 0x0840, 0x1861, 0x2802, 0x3823, + 0xC9CC, 0xD9ED, 0xE98E, 0xF9AF, 0x8948, 0x9969, 0xA90A, 0xB92B, + 0x5AF5, 0x4AD4, 0x7AB7, 0x6A96, 0x1A71, 0x0A50, 0x3A33, 0x2A12, + 0xDBFD, 0xCBDC, 0xFBBF, 0xEB9E, 0x9B79, 0x8B58, 0xBB3B, 0xAB1A, + 0x6CA6, 0x7C87, 0x4CE4, 0x5CC5, 0x2C22, 0x3C03, 0x0C60, 0x1C41, + 0xEDAE, 0xFD8F, 0xCDEC, 0xDDCD, 0xAD2A, 0xBD0B, 0x8D68, 0x9D49, + 0x7E97, 0x6EB6, 0x5ED5, 0x4EF4, 0x3E13, 0x2E32, 0x1E51, 0x0E70, + 0xFF9F, 0xEFBE, 0xDFDD, 0xCFFC, 0xBF1B, 0xAF3A, 0x9F59, 0x8F78, + 0x9188, 0x81A9, 0xB1CA, 0xA1EB, 0xD10C, 0xC12D, 0xF14E, 0xE16F, + 0x1080, 0x00A1, 0x30C2, 0x20E3, 0x5004, 0x4025, 0x7046, 0x6067, + 0x83B9, 0x9398, 0xA3FB, 0xB3DA, 0xC33D, 0xD31C, 0xE37F, 0xF35E, + 0x02B1, 0x1290, 0x22F3, 0x32D2, 0x4235, 0x5214, 0x6277, 0x7256, + 0xB5EA, 0xA5CB, 0x95A8, 0x8589, 0xF56E, 0xE54F, 0xD52C, 0xC50D, + 0x34E2, 0x24C3, 0x14A0, 0x0481, 0x7466, 0x6447, 0x5424, 0x4405, + 0xA7DB, 0xB7FA, 0x8799, 0x97B8, 0xE75F, 0xF77E, 0xC71D, 0xD73C, + 0x26D3, 0x36F2, 0x0691, 0x16B0, 0x6657, 0x7676, 0x4615, 0x5634, + 0xD94C, 0xC96D, 0xF90E, 0xE92F, 0x99C8, 0x89E9, 0xB98A, 0xA9AB, + 0x5844, 0x4865, 0x7806, 0x6827, 0x18C0, 0x08E1, 0x3882, 0x28A3, + 0xCB7D, 0xDB5C, 0xEB3F, 0xFB1E, 0x8BF9, 0x9BD8, 0xABBB, 0xBB9A, + 0x4A75, 0x5A54, 0x6A37, 0x7A16, 0x0AF1, 0x1AD0, 0x2AB3, 0x3A92, + 0xFD2E, 0xED0F, 0xDD6C, 0xCD4D, 0xBDAA, 0xAD8B, 0x9DE8, 0x8DC9, + 0x7C26, 0x6C07, 0x5C64, 0x4C45, 0x3CA2, 0x2C83, 0x1CE0, 0x0CC1, + 0xEF1F, 0xFF3E, 0xCF5D, 0xDF7C, 0xAF9B, 0xBFBA, 0x8FD9, 0x9FF8, + 0x6E17, 0x7E36, 0x4E55, 0x5E74, 0x2E93, 0x3EB2, 0x0ED1, 0x1EF0 +}; + +void diskSetInterface(u32 interface) { + + disk_interface = interface; +} + +u8 diskGetInterface() { + + return disk_interface; +} + +u8 diskInit() { + + if (disk_interface == DISK_IFACE_SD) { + return diskInitSD(); + } else { + return diskInitSPI(); + } +} + +u8 diskRead(u32 saddr, void *buff, u16 slen) { + + if (disk_interface == DISK_IFACE_SD) { + return diskReadSD(saddr, buff, slen); + } else { + return diskReadSPI(saddr, buff, slen); + } +} + +u8 diskWrite(u32 saddr, u8 *buff, u16 slen) { + + if (disk_interface == DISK_IFACE_SD) { + return diskWriteSD(saddr, buff, slen); + } else { + return diskWriteSPI(saddr, buff, slen); + } +} + +void diskCrc16SD(u8 *data, u16 *crc_out, u16 len) { + + ///u16 len = 512; + u16 i, tmp1, u; + u8 dat[4]; + u8 dat_tmp; + u16 crc_buff[4]; + + + for (i = 0; i < 4; i++)crc_buff[i] = 0; + for (i = 0; i < 4; i++)crc_out[i] = 0; + //u16 dptr = 0; + while (len) { + len -= 4; + for (i = 0; i < 4; i++)dat[i] = 0; + + for (u = 0; u < 4; u++) { + dat_tmp = data[3 - u]; + //dat_tmp = 0xff; + for (i = 0; i < 4; i++) { + dat[i] >>= 1; + dat[i] |= (dat_tmp & 1) << 7; + dat_tmp >>= 1; + } + + for (i = 0; i < 4; i++) { + dat[i] >>= 1; + dat[i] |= (dat_tmp & 1) << 7; + dat_tmp >>= 1; + } + } + + data += 4; + + for (u = 0; u < 4; u++) { + tmp1 = crc_buff[u]; + crc_buff[u] = sd_crc16_table[(tmp1 >> 8) ^ dat[u]]; + crc_buff[u] = crc_buff[u] ^ (tmp1 << 8); + } + + } + + for (i = 0; i < 4 * 16; i++) { + + crc_out[3 - i / 16] >>= 1; + crc_out[3 - i / 16] |= (crc_buff[i % 4] & 1) << 15; + crc_buff[i % 4] >>= 1; + } + +} + +unsigned int diskCrc7(unsigned char *buff, unsigned int len) { + + unsigned int a, crc = 0; + + while (len--) { + crc ^= *buff++; + a = 8; + do { + crc <<= 1; + if (crc & (1 << 8)) crc ^= 0x12; + } while (--a); + } + return (crc & 0xfe); +} + +u8 diskGetRespTypeSD(u8 cmd) { + + switch (cmd) { + case CMD3: + return R6; + case CMD8: + return R7; + case CMD2: + case CMD9: + return R2; + case CMD58: + case CMD41: + return R3; + + default: return R1; + } + +} + +u8 diskCmdSD(u8 cmd, u32 arg) { + + u8 resp_type = diskGetRespTypeSD(cmd); + u8 p = 0; + u8 buff[6]; + volatile u8 resp; + volatile u32 i = 0; + + u8 resp_len = resp_type == R2 ? 17 : 6; + u8 crc; + buff[p++] = cmd; + buff[p++] = (arg >> 24); + buff[p++] = (arg >> 16); + buff[p++] = (arg >> 8); + buff[p++] = (arg >> 0); + crc = diskCrc7(buff, 5) | 1; + + evd_SDcmdWriteMode(0); + + mem_spi(0xff); + mem_spi(cmd); + mem_spi(arg >> 24); + mem_spi(arg >> 16); + mem_spi(arg >> 8); + mem_spi(arg); + mem_spi(crc); + evd_SDcmdReadMode(1); + + i = 0; + resp = 0xff; + while ((resp & 192) != 0) { + resp = mem_spi(resp); + if (i++ == WAIT)return SD_CMD_TIMEOUT; + } + evd_SDcmdReadMode(0); + + + sd_resp_buff[0] = resp; + + for (i = 1; i < resp_len; i++) { + sd_resp_buff[i] = mem_spi(0xff); + } + + if (resp_type != R3) { + + + + if (resp_type == R2) { + crc = diskCrc7(sd_resp_buff + 1, resp_len - 2) | 1; + } else { + crc = diskCrc7(sd_resp_buff, resp_len - 1) | 1; + } + if (crc != sd_resp_buff[resp_len - 1])return SD_CMD_CRC_ERROR; + } + + return 0; +} + +u8 diskInitSD() { + + u16 i; + volatile u8 resp = 0; + u32 rca; + u32 wait_len = WAIT; + + card_type = 0; + evd_enableSDMode(); + memSpiSSOff(); + memSpiSetSpeed(SPI_SPEED_INIT); + + evd_SDcmdWriteMode(0); + + for (i = 0; i < 40; i++)mem_spi(0xff); + resp = diskCmdSD(CMD0, 0x1aa); + for (i = 0; i < 40; i++)mem_spi(0xff); + + + resp = diskCmdSD(CMD8, 0x1aa); + if (resp != 0 && resp != SD_CMD_TIMEOUT) { + return SD_INIT_ERROR + 0; + } + if (resp == 0)card_type |= SD_V2; + + + if (card_type == SD_V2) { + + for (i = 0; i < wait_len; i++) { + + resp = diskCmdSD(CMD55, 0); + if (resp)return SD_INIT_ERROR + 1; + if ((sd_resp_buff[3] & 1) != 1)continue; + resp = diskCmdSD(CMD41, 0x40300000); + if ((sd_resp_buff[1] & 128) == 0)continue; + + break; + } + } else { + + i = 0; + do { + resp = diskCmdSD(CMD55, 0); + if (resp)return SD_INIT_ERROR + 2; + resp = diskCmdSD(CMD41, 0x40300000); + if (resp)return SD_INIT_ERROR + 3; + + } while (sd_resp_buff[1] < 1 && i++ < wait_len); + + } + + if (i == wait_len)return SD_INIT_ERROR + 4; + + if ((sd_resp_buff[1] & 64) && card_type != 0)card_type |= SD_HC; + + + resp = diskCmdSD(CMD2, 0); + if (resp)return SD_INIT_ERROR + 5; + + resp = diskCmdSD(CMD3, 0); + if (resp)return SD_INIT_ERROR + 6; + + resp = diskCmdSD(CMD7, 0); + //if (resp)return resp; + + rca = (sd_resp_buff[1] << 24) | (sd_resp_buff[2] << 16) | (sd_resp_buff[3] << 8) | (sd_resp_buff[4] << 0); + + + + + resp = diskCmdSD(CMD9, rca); //get csd + if (resp)return SD_INIT_ERROR + 7; + + + resp = diskCmdSD(CMD7, rca); + if (resp)return SD_INIT_ERROR + 8; + + + resp = diskCmdSD(CMD55, rca); + if (resp)return SD_INIT_ERROR + 9; + + + resp = diskCmdSD(CMD6, 2); + if (resp)return SD_INIT_ERROR + 10; + + + memSpiSetSpeed(SPI_SPEED_25); + + return 0; + +} + +u8 diskReadSD(u32 saddr, void *buff, u16 slen) { + + u8 resp; + + + if (!(card_type & 1))saddr *= 512; + resp = diskCmdSD(CMD18, saddr); + if (resp)return DISK_ERR_RD1; + + + resp = memSpiRead(buff, slen); + if (resp)return resp; + + //console_printf("drd: %0X\n", saddr); + resp = diskStopRwSD(); + + return resp; +} + +u8 diskStopRwSD() { + + u8 resp; + u16 i; + resp = diskCmdSD(CMD12, 0); + if (resp)return DISK_ERR_CLOSE_RW1; + evd_SDdatReadMode(0); + mem_spi(0xff); + + i = 65535; + while (i--) { + if (mem_spi(0xff) == 0xff)break; + if (mem_spi(0xff) == 0xff)break; + if (mem_spi(0xff) == 0xff)break; + if (mem_spi(0xff) == 0xff)break; + } + if (i == 0)return DISK_ERR_CLOSE_RW2; + + return 0; +} + +u8 diskWriteSD(u32 saddr, u8 *buff, u16 slen) { + + u8 resp; + u16 crc16[5]; + u16 i; + u16 u; + u8 ram_buff[512]; + u8 *buff_ptr; + + + if (!(card_type & 1))saddr *= 512; + resp = diskCmdSD(CMD25, saddr); + if (resp)return DISK_ERR_WR1; + + evd_SDdatWriteMode(0); + + + while (slen--) { + + if ((u32) buff >= ROM_ADDR && (u32) buff < ROM_END_ADDR) { + dma_read_s(ram_buff, (u32) buff, 512); + buff_ptr = ram_buff; + } else { + buff_ptr = buff; + } + + diskCrc16SD(buff_ptr, crc16, 512); + evd_SDdatWriteMode(0); + + + mem_spi(0xff); + mem_spi(0xf0); + + + memSpiWrite(buff_ptr); + + for (i = 0; i < 4; i++) { + mem_spi(crc16[i] >> 8); + mem_spi(crc16[i] & 0xff); + } + + buff += 512; + + evd_SDdatWriteMode(1); + mem_spi(0xff); + evd_SDdatReadMode(1); + + i = 1024; + while ((mem_spi(0xff) & 1) != 0 && i-- != 0); + if (i == 0)return DISK_WR_SB_TOUT; + resp = 0; + + for (i = 0; i < 3; i++) { + resp <<= 1; + u = mem_spi(0xff); + resp |= u & 1; + } + resp &= 7; + + if (resp != 0x02) { + //console_printf("error blia: %0X\n", resp); + // joyWait(); + if (resp == 5)return DISK_ERR_WR_CRC; + return DISK_ERR_WRX; + } + + evd_SDdatReadMode(0); + mem_spi(0xff); + i = 65535; + while (i--) { + + if (mem_spi(0xff) == 0xff)break; + if (mem_spi(0xff) == 0xff)break; + } + if (i == 0)return DISK_ERR_WR2; + } + + resp = diskStopRwSD(); + if (resp)return resp; + + return 0; +} + +u8 diskCmdSPI(u8 cmd, u32 arg) { + + + u8 crc; + u8 p = 0; + u8 buff[6]; + buff[p++] = cmd; + buff[p++] = (arg >> 24); + buff[p++] = (arg >> 16); + buff[p++] = (arg >> 8); + buff[p++] = (arg >> 0); + + crc = diskCrc7(buff, 5) | 1; + + volatile u32 i = 0; + volatile u8 resp; + memSpiBusy(); + memSpiSSOff(); + memSpiSSOn(); + //mem_spi(0x1); + mem_spi(0xff); + mem_spi(cmd); + mem_spi(arg >> 24); + mem_spi(arg >> 16); + mem_spi(arg >> 8); + mem_spi(arg); + mem_spi(crc); + mem_spi(0xff); + resp = mem_spi(0xff); + + // memSpiSSOn(); + + while (resp == 0xff) { + resp = mem_spi(0xff); + if (i++ == WAIT)break; + } + + memSpiSSOff(); + return resp; +} + +u8 diskInitSPI() { + + u16 i; + u32 u; + + volatile u8 resp = 0; + u8 cmd; + u32 wait_len = WAIT; + + card_type = 0; + + evd_enableSPIMode(); + //return evd_mmcInit(); + + memSpiSetSpeed(SPI_SPEED_INIT); + + for (u = 0; u < 4; u++) { + for (i = 0; i < 40; i++)mem_spi(0xff); + resp = diskCmdSPI(CMD0, 0); + if (resp == 1)break; + } + + if (resp != 1) return DISK_ERR_INIT + 0; + //mmcCmdCrc(0x7b, 0, 0x91); + resp = diskCmdSPI(CMD8, 0x1aa); + for (i = 0; i < 5; i++)mem_spi(0xff); + if (resp == 0xff)return DISK_ERR_INIT + 1; + if (resp != 5)card_type |= SD_V2; + + if (card_type == 2) { + + for (i = 0; i < wait_len; i++) { + + + resp = diskCmdSPI(CMD55, 0xffff); + if (resp == 0xff)return DISK_ERR_INIT + 2; + if (resp != 1)continue; + + resp = diskCmdSPI(CMD41, 0x40300000); + if (resp == 0xff)return DISK_ERR_INIT + 3; + if (resp != 0)continue; + + break; + } + if (i == wait_len)return DISK_ERR_INIT + 4; + + resp = diskCmdSPI(CMD58, 0); + if (resp == 0xff)return DISK_ERR_INIT + 5; + memSpiSSOn(); + resp = mem_spi(0xff); + for (i = 0; i < 3; i++)mem_spi(0xff); + if ((resp & 0x40))card_type |= 1; + } else { + + + i = 0; + + resp = diskCmdSPI(CMD55, 0); + if (resp == 0xff)return DISK_ERR_INIT + 6; + resp = diskCmdSPI(CMD41, 0); + if (resp == 0xff)return DISK_ERR_INIT + 7; + cmd = resp; + + for (i = 0; i < wait_len; i++) { + if (resp < 1) { + + resp = diskCmdSPI(CMD55, 0); + if (resp == 0xff)return DISK_ERR_INIT + 8; + if (resp != 1)continue; + + resp = diskCmdSPI(CMD41, 0); + if (resp == 0xff)return DISK_ERR_INIT + 9; + if (resp != 0)continue; + + } else { + + resp = diskCmdSPI(CMD1, 0); + if (resp != 0)continue; + } + + break; + + } + + if (i == wait_len)return DISK_ERR_INIT + 10; + + } + + memSpiSetSpeed(SPI_SPEED_25); + + return 0; +} + +u8 diskReadSPI(u32 saddr, void *buff, u16 slen) { + + + u8 resp; + + if (!(card_type & 1))saddr *= 512; + resp = diskCmdSPI(CMD18, saddr); + if (resp != 0)return DISK_ERR_RD1; + memSpiSSOn(); + + resp = memSpiRead(buff, slen); + + memSpiSSOff(); + diskCmdSPI(CMD12, 0); + + return resp; +} + +u8 diskWriteSPI(u32 saddr, u8 *buff, u16 slen) { + + u8 resp; + u16 i; + + if (!(card_type & 1))saddr *= 512; + resp = diskCmdSPI(CMD25, saddr); + if (resp != 0)return DISK_ERR_WR1; + memSpiSSOn(); + + + while (slen--) { + + mem_spi(0xff); + mem_spi(0xff); + mem_spi(0xfc); + + + memSpiWrite(buff); + + mem_spi(0xff); + mem_spi(0xff); + resp = mem_spi(0xff); + //resp = mem_spi(0xff); + + buff += 512; + + + + if ((resp & 0x1f) != 0x05) { + memSpiSSOff(); + return DISK_ERR_WRX; + } + + + for (i = 0; i < 65535; i++) { + + if (mem_spi(0xff) == 0xff)break; + if (mem_spi(0xff) == 0xff)break; + if (mem_spi(0xff) == 0xff)break; + if (mem_spi(0xff) == 0xff)break; + } + + + if (i == 65535) { + memSpiSSOff(); + return DISK_ERR_WR2; + } + + } + + mem_spi(0xfd); + mem_spi(0xff); + + for (i = 0; i < 65535; i++) { + if ((mem_spi(0xff) & 1) != 0)break; + if ((mem_spi(0xff) & 1) != 0)break; + if ((mem_spi(0xff) & 1) != 0)break; + if ((mem_spi(0xff) & 1) != 0)break; + } + memSpiSSOff(); + if (i == 65535) return DISK_ERR_WR3; + + return 0; +} diff --git a/disk.h b/disk.h new file mode 100644 index 0000000..247a0d1 --- /dev/null +++ b/disk.h @@ -0,0 +1,29 @@ +/* + * File: disk.h + * Author: krik + * + * Created on 2 Èþíü 2011 ã., 4:07 + */ + +#ifndef _DISK_H +#define _DISK_H + +#include "types.h" + +u8 diskGetInterface(); +u8 diskInit(); +u8 diskRead(u32 saddr, void *buff, u16 slen); +u8 diskWrite(u32 saddr, u8 *buff, u16 slen); +void diskSetInterface(u32 interface); + + + +#define WAIT 1024 + +#define DISK_IFACE_SPI 0 +#define DISK_IFACE_SD 1 + + + +#endif /* _DISK_H */ + diff --git a/doc/functions.txt b/doc/functions.txt new file mode 100644 index 0000000..ad4bc78 --- /dev/null +++ b/doc/functions.txt @@ -0,0 +1,104 @@ +beta9 + +rom loading function flow: + +main() + +//init n64, fs, controller + + //joypad loop + //button (A) -> select rom + + + loadrom(disp,name_file); + //readout rom header + //check if swapped + evd_mmcSetDmaSwap(1); + + //dma read file to 0xb0000000 cartspace + diskRead(); + + //change key mapping + + //if (START) + bootRom(); + + //save LAST.CRT for reboot cart-save + + //set fpga to eeprom, sram, flashram + evd_setSaveType(boot_save); + + //do pif simulation to boot the rom + simulate_boot(boot_cic, bios_cic); + + + +cart-save overview: + +before pif simulation the cart_id and cart_savetype is stored in +LAST.CRT [maybe it's a better solution to use rom filenames +'cause the cart_id includes no game-version] +-> bootRom(); + +at every start of the menu a function is called that checks +LAST.CRT, if there is something todo and disable it for next boot +by setting a flag in that file +-> backupSaveData(disp); + +if it's a warm boot (reset) the fpga savegame is still there +-> save_cfg_stat = evd_readReg(REG_SAV_CFG); + +if it's a reset reboot to backup a savegame into a file +saveTypeToSd(disp, cartID, save_t) +is called + +it's only for some file operations and calls +utils.c: getSaveFromCart(stype, cartsave_data) +to store the savegame into a buffer, which could +be saved into a file + + +some notes: +eeprom is connected directly to the pif (i2c?) +and could be read out stable with the libdragon function + +sram/flashram is a serial device that is mapped into +memory +it has to be accessed with dma to rdram through the pif + +it's a domain2 device which needs other timing than the cart + +timings: + +PI_BSD_DOM2_XXX_REG + +sram/fram: + +val XXX desc +05 LAT Latency +OC PWD Pulse Width + +//sram - 0D +//fram Animal Forest - 0F +OD PGS Page Size + +02 RLS Release Duration + + +carts/dev-carts: +40 Latency +12 Pulse Width +07 Page Size +03 Release Duration + +IPL at 0xA6000000: +40 Latency +07 Pulse Width +07 Page Size +02 Release Duration + +cartidge: +PI_BSD_DOM1_XXX_REG + +is set with the first word in the header +e.g. 0x[80]371240 -> 3.7.12.40 ~ 40,12,07,03 diff --git a/errors.h b/errors.h new file mode 100644 index 0000000..1ce6d90 --- /dev/null +++ b/errors.h @@ -0,0 +1,70 @@ +/* + * File: errors.h + * Author: KRIK + * + * Created on 14 Ìàé 2011 ã., 7:17 + */ + +#ifndef _ERRORS_H +#define _ERRORS_H + + +#define EVD_ERROR_FIFO_TIMEOUT 90; +#define EVD_ERROR_MMC_TIMEOUT 91; + +#define BOOT_UPD_ERR_WRONG_SIZE 95 +#define BOOT_UPD_ERR_HDR 96 +#define BOOT_UPD_ERR_CMP 97 +#define BOOT_UPD_ERR_CIC_DTCT 98 + +#define FAT_ERR_NOT_EXIST 100 +#define FAT_ERR_EXIST 101 +#define FAT_ERR_NAME 102 +#define FAT_ERR_OUT_OF_FILE 103 +#define FAT_ERR_BAD_BASE_CLUSTER 104; +#define FAT_ERR_NO_FRE_SPACE 105 +#define FAT_ERR_NOT_FILE 106 +#define FAT_ERR_FILE_MODE 107 +#define FAT_ERR_ROT_OVERFLOW 108 +#define FAT_ERR_OUT_OF_TABLE 109 +#define FAT_ERR_INIT 110 +#define FAT_LFN_BUFF_OVERFLOW 111 +#define FAT_DISK_NOT_READY 112 +#define FAT_ERR_SIZE 113 +#define FAT_ERR_RESIZE 114 + +#define ERR_FILE8_TOO_BIG 140 +#define ERR_FILE16_TOO_BIG 141 +#define ERR_WRON_OS_SIZE 142 +#define ERR_OS_VERIFY 143 +#define ERR_OS_VERIFY2 144 +#define ERR_EMU_NOT_FOUND 145 +#define ERR_SAVE_FORMAT 146 +#define ERR_EEPROM 147 +#define ERR_NO_FAV_SPACE 150 + + + +#define DISK_ERR_INIT 50 + +#define DISK_ERR_RD1 62 +#define DISK_ERR_RD2 63 + +#define DISK_ERR_WR1 64 +#define DISK_ERR_WR2 65 +#define DISK_ERR_WR3 66 +#define DISK_ERR_WRX 67 +#define DISK_WR_SB_TOUT 68 +#define DISK_ERR_WR_CRC 69 + +#define DISK_RD_FE_TOUT 70 +#define DISK_ERR_CLOSE_RW1 71 +#define DISK_ERR_CLOSE_RW2 72 + +#define SD_CMD_TIMEOUT 75 +#define SD_CMD_CRC_ERROR 76 + +#define SD_INIT_ERROR 80 + +#endif /* _ERRORS_H */ + diff --git a/everdrive.c b/everdrive.c new file mode 100644 index 0000000..7b62c7c --- /dev/null +++ b/everdrive.c @@ -0,0 +1,596 @@ + +#include "types.h" +#include "everdrive.h" +#include +#include +#include "sys.h" +#include "errors.h" +//#include "rom.h" +#include "disk.h" + +#define CMD0 0x40 // software reset +#define CMD1 0x41 // brings card out of idle state +#define CMD2 0x42 // not used in SPI mode +#define CMD3 0x43 // not used in SPI mode +#define CMD4 0x44 // not used in SPI mode +#define CMD5 0x45 // Reserved +#define CMD6 0x46 // Reserved +#define CMD7 0x47 // not used in SPI mode +#define CMD8 0x48 // Reserved +#define CMD9 0x49 // ask card to send card speficic data (CSD) +#define CMD10 0x4A // ask card to send card identification (CID) +#define CMD11 0x4B // not used in SPI mode +#define CMD12 0x4C // stop transmission on multiple block read +#define CMD13 0x4D // ask the card to send it's status register +#define CMD14 0x4E // Reserved +#define CMD15 0x4F // not used in SPI mode +#define CMD16 0x50 // sets the block length used by the memory card +#define CMD17 0x51 // read single block +#define CMD18 0x52 // read multiple block +#define CMD19 0x53 // Reserved +#define CMD20 0x54 // not used in SPI mode +#define CMD21 0x55 // Reserved +#define CMD22 0x56 // Reserved +#define CMD23 0x57 // Reserved +#define CMD24 0x58 // writes a single block +#define CMD25 0x59 // writes multiple blocks +#define CMD26 0x5A // not used in SPI mode +#define CMD27 0x5B // change the bits in CSD +#define CMD28 0x5C // sets the write protection bit +#define CMD29 0x5D // clears the write protection bit +#define CMD30 0x5E // checks the write protection bit +#define CMD31 0x5F // Reserved +#define CMD32 0x60 // Sets the address of the first sector of the erase group +#define CMD33 0x61 // Sets the address of the last sector of the erase group +#define CMD34 0x62 // removes a sector from the selected group +#define CMD35 0x63 // Sets the address of the first group +#define CMD36 0x64 // Sets the address of the last erase group +#define CMD37 0x65 // removes a group from the selected section +#define CMD38 0x66 // erase all selected groups +#define CMD39 0x67 // not used in SPI mode +#define CMD40 0x68 // not used in SPI mode +#define CMD41 0x69 // Reserved +#define CMD42 0x6A // locks a block +// CMD43 ... CMD57 are Reserved +#define CMD58 0x7A // reads the OCR register +#define CMD59 0x7B // turns CRC off +// CMD60 ... CMD63 are not used in SPI mode + + + + + + +#define ED_STATE_DMA_BUSY 0 +#define ED_STATE_DMA_TOUT 1 +#define ED_STATE_TXE 2 +#define ED_STATE_RXF 3 +#define ED_STATE_SPI 4 + +#define SPI_CFG_SPD0 0 +#define SPI_CFG_SPD1 1 +#define SPI_CFG_SS 2 +#define SPI_CFG_RD 3 +#define SPI_CFG_DAT 4 +#define SPI_CFG_1BIT 5 + +#define SAV_EEP_ON 0 +#define SAV_SRM_ON 1 +#define SAV_EEP_SIZE 2 +#define SAV_SRM_SIZE 3 + +//was missing +//#define BI_SPI_SPD_LO 0 + +#define BI_SPI_SPD_LO 2 // around 200khz (only for sd initialization) +#define BI_SPI_SPD_25 1 +#define BI_SPI_SPD_50 0 + + +void evd_setSpiSpeed(u8 speed); +u8 evd_mmcCmd(u8 cmd, u32 arg); + +u8 sd_mode; +volatile u8 spi_cfg; +volatile u8 evd_cfg; +u8 sd_type; +volatile u32 *regs_ptr = (u32 *) 0xA8040000; + +/* +result[2] <= ad[15:8] == {ad[6], ad[1], ad[0], ad[7], ad[5], ad[4], ad[3], ad[2]} ^ 8'h37 ^ prv[7:0]; + prv[7:0] <= ad[15:8]; + */ +void (*dma_busy_callback)(); + + +void evd_setDmaAddr(u32 addr) { + +} + + +inline u32 bi_reg_rd(u32 reg) { + + *(vu32 *) (REGS_BASE); + return *(vu32 *) (REGS_BASE + reg * 4); +} + +inline void bi_reg_wr(u32 reg, u32 data) { + + *(vu32 *) (REGS_BASE); + *(vu32 *) (REGS_BASE + reg * 4) = data; +} + + +void bi_init() { + + evd_cfg = ED_CFG_SDRAM_ON; + spi_cfg = 0 | BI_SPI_SPD_LO; + bi_reg_wr(REG_KEY, 0x1234); + bi_reg_wr(REG_CFG, evd_cfg); + bi_reg_wr(REG_SPI_CFG, spi_cfg); +} + +void bi_speed50() { + + spi_cfg = 0 | BI_SPI_SPD_50; + bi_reg_wr(REG_KEY, 0x1234); + bi_reg_wr(REG_SPI_CFG, spi_cfg); +} + +void bi_speed25() { + + spi_cfg = 0 | BI_SPI_SPD_25; + bi_reg_wr(REG_KEY, 0x1234); + bi_reg_wr(REG_SPI_CFG, spi_cfg); +} + +void bi_load_firmware(u8 *firm) { + + u32 i; + u16 f_ctr = 0; + + + evd_cfg &= ~ED_CFG_SDRAM_ON; + bi_reg_wr(REG_CFG, evd_cfg); + + bi_reg_wr(REG_CFG_CNT, 0); + sleep(10); + bi_reg_wr(REG_CFG_CNT, 1); + sleep(10); + + i = 0; + for (;;) { + + bi_reg_wr(REG_CFG_DAT, *(u16 *) & firm[i]); + while ((bi_reg_rd(REG_CFG_CNT) & 8) != 0); + + f_ctr = firm[i++] == 0xff ? f_ctr + 1 : 0; + if (f_ctr >= 47)break; + f_ctr = firm[i++] == 0xff ? f_ctr + 1 : 0; + if (f_ctr >= 47)break; + } + + + while ((bi_reg_rd(REG_CFG_CNT) & 4) == 0) { + bi_reg_wr(REG_CFG_DAT, 0xffff); + while ((bi_reg_rd(REG_CFG_CNT) & 8) != 0); + } + + + sleep(20); + + bi_init(); +} + + +void evd_init() { + + volatile u8 val; + sd_mode = 0; + dma_busy_callback = 0; + + sleep(1); + val = regs_ptr[0]; + + spi_cfg = (0 << SPI_CFG_SPD0) | (1 << SPI_CFG_SPD1) | (1 << SPI_CFG_SS); + evd_cfg = (1 << ED_CFG_SDRAM_ON); + + val = regs_ptr[0]; + regs_ptr[REG_KEY] = 0x1234; + + val = regs_ptr[0]; + regs_ptr[REG_CFG] = evd_cfg; + val = regs_ptr[0]; + regs_ptr[REG_SPI_CFG] = spi_cfg; + + evd_fifoRxf(); + if (!evd_fifoRxf()) { + + val = regs_ptr[0]; + regs_ptr[REG_DMA_LEN] = 7; //clean 16k + val = regs_ptr[0]; + regs_ptr[REG_DMA_RAM_ADDR] = (ROM_LEN - 0x200000) / 2048; + val = regs_ptr[0]; + regs_ptr[REG_DMA_CFG] = DCFG_FIFO_TO_RAM; + while (evd_isDmaBusy()); + } + + +} +void evd_ulockRegs(){ + volatile u8 val; + val = regs_ptr[0]; + regs_ptr[REG_KEY] = 0x1234; +} + +void evd_lockRegs() { + volatile u8 val; + val = regs_ptr[0]; + regs_ptr[REG_KEY] = 0; +} + +u8 evd_fifoRxf() { + + u16 val; + //regs_ptr[REG_STATE]++; + val = regs_ptr[REG_STATUS]; + return (val >> ED_STATE_RXF) & 1; +} + +u8 evd_fifoTxe() { + + u16 val; + //regs_ptr[REG_STATE]++; + val = regs_ptr[REG_STATUS]; + return (val >> ED_STATE_TXE) & 1; +} + +u8 evd_isDmaBusy() { + + u16 val; + //volatile u32 i; + sleep(1); + if(dma_busy_callback != 0)dma_busy_callback(); + //regs_ptr[REG_STATE]++; + val = regs_ptr[REG_STATUS]; + return (val >> ED_STATE_DMA_BUSY) & 1; +} + +u8 evd_isDmaTimeout() { + + u16 val; + //regs_ptr[REG_STATE]++; + val = regs_ptr[REG_STATUS]; + + return (val >> ED_STATE_DMA_TOUT) & 1; +} + +u8 evd_fifoRdToCart(u32 cart_addr, u16 blocks) { + + volatile u8 val; + cart_addr /= 2048; + + + val = regs_ptr[0]; + regs_ptr[REG_DMA_LEN] = (blocks - 1); + val = regs_ptr[0]; + regs_ptr[REG_DMA_RAM_ADDR] = cart_addr; + val = regs_ptr[0]; + regs_ptr[REG_DMA_CFG] = DCFG_FIFO_TO_RAM; + + while (evd_isDmaBusy()); + if (evd_isDmaTimeout())return EVD_ERROR_FIFO_TIMEOUT; + + + return 0; +} + +u8 evd_fifoWrFromCart(u32 cart_addr, u16 blocks) { + + volatile u8 val; + cart_addr /= 2048; + + + val = regs_ptr[0]; + regs_ptr[REG_DMA_LEN] = (blocks - 1); + val = regs_ptr[0]; + regs_ptr[REG_DMA_RAM_ADDR] = cart_addr; + val = regs_ptr[0]; + regs_ptr[REG_DMA_CFG] = DCFG_RAM_TO_FIFO; + + while (evd_isDmaBusy()); + if (evd_isDmaTimeout())return EVD_ERROR_FIFO_TIMEOUT; + + return 0; +} + +u8 evd_fifoRd(void *buff, u16 blocks) { + + volatile u8 val; + u32 len = blocks == 0 ? 65536 * 512 : blocks * 512; + u32 ram_buff_addr = DMA_BUFF_ADDR / 2048; //(ROM_LEN - len - 65536 * 4) / 2048; + + + val = regs_ptr[0]; + regs_ptr[REG_DMA_LEN] = (blocks - 1); + val = regs_ptr[0]; + regs_ptr[REG_DMA_RAM_ADDR] = ram_buff_addr; + val = regs_ptr[0]; + regs_ptr[REG_DMA_CFG] = DCFG_FIFO_TO_RAM; + + while (evd_isDmaBusy()); + dma_read_s(buff, (0xb0000000 + ram_buff_addr * 2048), len); + if (evd_isDmaTimeout())return EVD_ERROR_FIFO_TIMEOUT; + + + + return 0; +} + + + +u8 evd_fifoWr(void *buff, u16 blocks) { + + volatile u8 val; + u32 len = blocks == 0 ? 65536 * 512 : blocks * 512; + u32 ram_buff_addr = DMA_BUFF_ADDR / 2048; //(ROM_LEN - len - 65536 * 4) / 2048; + + dma_write_s(buff, (0xb0000000 + ram_buff_addr * 1024 * 2), len); + + val = regs_ptr[0]; + regs_ptr[REG_DMA_LEN] = (blocks - 1); + val = regs_ptr[0]; + regs_ptr[REG_DMA_RAM_ADDR] = ram_buff_addr; + val = regs_ptr[0]; + regs_ptr[REG_DMA_CFG] = DCFG_RAM_TO_FIFO; + + while (evd_isDmaBusy()); + if (evd_isDmaTimeout())return EVD_ERROR_FIFO_TIMEOUT; + + return 0; +} + +u8 evd_isSpiBusy() { + + volatile u16 val; + regs_ptr[REG_STATUS]; + val = regs_ptr[REG_STATUS]; + return (val >> ED_STATE_SPI) & 1; +} + +u8 evd_SPI(u8 dat) { + + + volatile u8 val; + val = regs_ptr[0]; + regs_ptr[REG_SPI] = dat; + while (evd_isSpiBusy()); + //osInvalICache((u32*) & regs_ptr[REG_SPI], 1); + val = regs_ptr[REG_SPI]; + return val; + +} + +void evd_spiSSOn() { + + volatile u8 val; + if (sd_mode)return; + spi_cfg &= ~(1 << SPI_CFG_SS); + val = regs_ptr[0]; + regs_ptr[REG_SPI_CFG] = spi_cfg; + +} + +void evd_spiSSOff() { + + volatile u8 val; + spi_cfg |= (1 << SPI_CFG_SS); + val = regs_ptr[0]; + regs_ptr[REG_SPI_CFG] = spi_cfg; + +} + +void evd_enableSDMode() { + sd_mode = 1; +} + +void evd_enableSPIMode() { + sd_mode = 0; +} + +u8 evd_isSDMode() { + + return sd_mode; +} + +void evd_SDcmdWriteMode(u8 bit1_mode) { + + volatile u8 val; + if (!sd_mode)return; + spi_cfg &= ~((1 << SPI_CFG_RD) | (1 << SPI_CFG_DAT)); + if (bit1_mode) { + spi_cfg |= (1 << SPI_CFG_1BIT); + } else { + spi_cfg &= ~(1 << SPI_CFG_1BIT); + } + val = regs_ptr[0]; + regs_ptr[REG_SPI_CFG] = spi_cfg; + +} + +void evd_SDcmdReadMode(u8 bit1_mode) { + + + volatile u8 val; + if (!sd_mode)return; + spi_cfg |= (1 << SPI_CFG_RD); + spi_cfg &= ~(1 << SPI_CFG_DAT); + if (bit1_mode) { + spi_cfg |= (1 << SPI_CFG_1BIT); + } else { + spi_cfg &= ~(1 << SPI_CFG_1BIT); + } + val = regs_ptr[0]; + regs_ptr[REG_SPI_CFG] = spi_cfg; +} + +void evd_SDdatWriteMode(u8 bit4_mode) { + + + volatile u8 val; + if (!sd_mode)return; + spi_cfg &= ~(1 << SPI_CFG_RD); + spi_cfg |= (1 << SPI_CFG_DAT); + if (bit4_mode) { + spi_cfg |= (1 << SPI_CFG_1BIT); + } else { + spi_cfg &= ~(1 << SPI_CFG_1BIT); + } + val = regs_ptr[0]; + regs_ptr[REG_SPI_CFG] = spi_cfg; +} + +void evd_SDdatReadMode(u8 bit4_mode) { + + + volatile u8 val; + if (!sd_mode)return; + spi_cfg |= (1 << SPI_CFG_RD) | (1 << SPI_CFG_DAT); + if (bit4_mode) { + spi_cfg |= (1 << SPI_CFG_1BIT); + } else { + spi_cfg &= ~(1 << SPI_CFG_1BIT); + } + val = regs_ptr[0]; + regs_ptr[REG_SPI_CFG] = spi_cfg; +} + +void evd_setSpiSpeed(u8 speed) { + + + volatile u8 val; + spi_cfg &= ~3; //((1 << SPI_CFG_SPD0) | (1 << SPI_CFG_SPD1)); + spi_cfg |= speed & 3; + val = regs_ptr[0]; + regs_ptr[REG_SPI_CFG] = spi_cfg; + + +} + +u8 evd_mmcReadToCart(u32 cart_addr, u32 len) { + + volatile u8 val; + cart_addr /= 2048; + + + val = regs_ptr[0]; + regs_ptr[REG_DMA_LEN] = (len - 1); + val = regs_ptr[0]; + regs_ptr[REG_DMA_RAM_ADDR] = cart_addr; + val = regs_ptr[0]; + regs_ptr[REG_DMA_CFG] = DCFG_SD_TO_RAM; + + while (evd_isDmaBusy()); + if (evd_isDmaTimeout())return EVD_ERROR_MMC_TIMEOUT; + + return 0; +} + +void evd_setCfgBit(u8 option, u8 state) { + + volatile u8 val; + + if (state)evd_cfg |= (1 << option); + else + evd_cfg &= ~(1 << option); + + val = regs_ptr[0]; + regs_ptr[REG_CFG] = evd_cfg; + val = regs_ptr[0]; +} + +u16 evd_readReg(u8 reg) { + + volatile u32 tmp; + + tmp = regs_ptr[0]; + + return regs_ptr[reg]; +} + +void evd_setSaveType(u8 type) { + + + u8 eeprom_on, sram_on, eeprom_size, sram_size; + eeprom_on = 0; + sram_on = 0; + eeprom_size = 0; + sram_size = 0; + + switch (type) { + case SAVE_TYPE_EEP16k: + eeprom_on = 1; + eeprom_size = 1; + break; + case SAVE_TYPE_EEP4k: + eeprom_on = 1; + break; + case SAVE_TYPE_SRAM: + sram_on = 1; + break; + case SAVE_TYPE_SRAM128: + sram_on = 1; + sram_size = 1; + break; + case SAVE_TYPE_FLASH: + sram_on = 0; + sram_size = 1; + break; + default: + sram_on = 0; + sram_size = 0; + break; + } + + + volatile u8 val; + val = regs_ptr[0]; + regs_ptr[REG_SAV_CFG] = (eeprom_on << SAV_EEP_ON | sram_on << SAV_SRM_ON | eeprom_size << SAV_EEP_SIZE | sram_size << SAV_SRM_SIZE); + +} + +void evd_writeReg(u8 reg, u16 val) { + + volatile u8 tmp; + tmp = regs_ptr[0]; + regs_ptr[reg] = val; + +} + +void evd_mmcSetDmaSwap(u8 state) { + + evd_setCfgBit(ED_CFG_SWAP, state); +} + +void evd_writeMsg(u8 dat) { + + evd_writeReg(REG_MSG, dat); + +} + +u8 evd_readMsg() { + return evd_readReg(REG_MSG); +} + +u16 evd_getFirmVersion() { + + return evd_readReg(REG_VER); + +} + + +void evd_setDmaCallback(void (*callback)()) { + + + dma_busy_callback = callback; +} diff --git a/everdrive.h b/everdrive.h new file mode 100644 index 0000000..f3f0e31 --- /dev/null +++ b/everdrive.h @@ -0,0 +1,125 @@ +/* + * File: everdrive.h + * Author: KRIK + * + * Created on 22 Àïðåëü 2011 ã., 20:46 + */ + +#ifndef _EVERDRIVE_H +#define _EVERDRIVE_H + +#include "types.h" + +#define OS_VER "1.29" + +#define ROM_LEN 0x4000000 +#define ROM_ADDR 0xb0000000 +#define ROM_END_ADDR (0xb0000000 + 0x4000000) + + +#define SAVE_TYPE_OFF 0 +#define SAVE_TYPE_SRAM 1 +#define SAVE_TYPE_SRAM128 2 +#define SAVE_TYPE_EEP4k 3 +#define SAVE_TYPE_EEP16k 4 +#define SAVE_TYPE_FLASH 5 + +#define DMA_BUFF_ADDR (ROM_LEN - 0x100000) + +#define REG_CFG 0 +#define REG_STATUS 1 +#define REG_DMA_LEN 2 +#define REG_DMA_RAM_ADDR 3 +#define REG_MSG 4 +#define REG_DMA_CFG 5 +#define REG_SPI 6 +#define REG_SPI_CFG 7 +#define REG_KEY 8 +#define REG_SAV_CFG 9 +#define REG_SEC 10 +#define REG_VER 11 + + +#define REG_CFG_CNT 16 +#define REG_CFG_DAT 17 +#define REG_MAX_MSG 18 +#define REG_CRC 19 + + +#define DCFG_SD_TO_RAM 1 +#define DCFG_RAM_TO_SD 2 +#define DCFG_FIFO_TO_RAM 3 +#define DCFG_RAM_TO_FIFO 4 + +#define ED_CFG_SDRAM_ON 0 +#define ED_CFG_SWAP 1 +#define ED_CFG_WR_MOD 2 +#define ED_CFG_WR_ADDR_MASK 3 + +//new firmware load +#define REGS_BASE 0xA8040000 +//-- + +void evd_setCfgBit(u8 option, u8 state); + +void evd_init(); +u8 evd_fifoRxf(); +u8 evd_fifoTxe(); +u8 evd_isDmaBusy(); +u8 evd_isDmaTimeout(); +u8 evd_fifoRd(void *buff, u16 blocks); +u8 evd_fifoWr(void *buff, u16 blocks); +u8 evd_fifoRdToCart(u32 cart_addr, u16 blocks); +u8 evd_fifoWrFromCart(u32 cart_addr, u16 blocks); + +u8 evd_SPI(u8 dat); +void evd_mmcSetDmaSwap(u8 state); +u8 evd_mmcReadToCart(u32 cart_addr, u32 len); + +void evd_lockRegs(); +u16 evd_readReg(u8 reg); +void evd_writeReg(u8 reg, u16 val); +void evd_setSaveType(u8 type); + + +u8 romLoadSettingsFromSD(); +u8 romSaveInfoToLog(); +void evd_writeMsg(u8 dat); +u8 evd_readMsg(); +u16 evd_getFirmVersion(); + +void evd_spiSSOn(); +void evd_spiSSOff(); +u8 evd_isSpiBusy(); +void evd_setSpiSpeed(u8 speed); + +void evd_SDcmdWriteMode(u8 bit1_mode); +void evd_SDcmdReadMode(u8 bit1_mode); +void evd_SDdatWriteMode(u8 bit4_mode); +void evd_SDdatReadMode(u8 bit4_mode); +void evd_enableSDMode(); +void evd_enableSPIMode(); +u8 evd_isSDMode(); +void evd_setDmaCallback(void (*callback)()); + + +//firmware upload +extern u32 bi_reg_rd(u32 reg); +extern void bi_reg_wr(u32 reg, u32 data); +void bi_init(); +void bi_load_firmware(u8 *firm); +void bi_speed25(); +void bi_speed50(); + +/* +u8 evd_mmcWriteNextBlock(void *dat); +u8 evd_mmcOpenWrite(u32 addr); +u8 evd_mmcWriteBlock(void *dat, u32 addr); +u8 evd_mmcInit(); +u8 evd_mmcReadBlock(void *dat, u32 addr); +u8 evd_mmcOpenRead(u32 addr); +u8 evd_mmcReadNextBlock(void *dat); +void evd_mmcCloseRW(); + */ +#endif /* _EVERDRIVE_H */ + diff --git a/fat.c b/fat.c new file mode 100644 index 0000000..d01ee1c --- /dev/null +++ b/fat.c @@ -0,0 +1,1931 @@ + + +#include +#include + +#include "fat.h" +#include "disk.h" +#include "mem.h" +#include "everdrive.h" +#include "sys.h" +#include + + + + + + + + + + +//FatRecordHdr rec_hdr; + + + + +u8 fatLoadLfnPart(u8 *name_ptr, u8 *lfn_buff); +u8 fatCacheLoadTable(u32 sector, u8 save_before_load); +u8 fatCacheLoadData(u32 sector, u8 save_befor_load); + +u8 fatGetNextCluster(u32 *cluster); +u8 fatGetTableRecord(u32 cluster, u32 *val); +u8 fatSetTableRecord(u32 cluster, u32 next_cluster); +u8 fatCacheApplyTable(); +u8 fatGetFreeRecord(FatRecord *dir_rec, u8 len); +void fatMakeLfnBlock(u8 *name, u8 *lfn_block, u8 block_idx, u8 crc); +u32 fatSectorToCluster(u32 sector); +void strhicase(u8 *str, u8 len); +u8 fatClearClusters(u32 cluster, u8 len); + +u8 fatReadCluster(void *dst); +u8 fatOccupyClusters(u32 *base_cluster, u16 len); +void fatOpenDir(u32 cluster); +u8 fatReadDir(FatRecord *rec, u16 max_name_len); +u32 fatClusterToSector(u32 cluster); +u8 fatInitRoot(); +u8 fatSkipCluster(); +//u8 fatReadMultiClustes(void *dst, u32 max_sectors, u32 *readrd_sectors); +u8 fatMultiClustRW(void *buff, u32 max_sectors, u32 *readrd_sectors, u16 write); +u8 fatResizeFile(FatRecord *rec, u32 req_sec_size); + +Dir *dir; +Fat current_fat; +File file; +FatCache *fat_cache; +u16 kot; +u32 *occupy_buff; +vu16 table_changed; + +//FatRecord *rec_tmp; +//#define CACHE_SIZE 16 + +void fatInitRam() { + + u16 i; + //rec_tmp = malloc() + dir = malloc(sizeof (Dir)); + + for (i = 0; i < FAT_MAX_DIR_SIZE; i++) { + dir->rec[i] = malloc(sizeof (FatRecord)); + } + + dir->path = malloc(1024); + + fat_cache = malloc(sizeof (FatCache)); + + fat_cache->data_sector = malloc(512); + fat_cache->table_sector = malloc(512); + + occupy_buff = malloc(16384); + + // current_cache->table_frame = current_cache->table_buff; + + //cache = &ch; +} + + +u8 fatInitRoot() { + + // u8 resp = 0; + + // resp = fatCreateRecIfNotExist(ED_ROOT, 1); + // if (resp)return resp; + +// resp = fatCreateRecIfNotExist(ED_SAVE, 1); + // if (resp)return resp; + + // resp = fatCreateRecIfNotExist(ED_ROM_CFG, 0); + // if (resp)return resp; + + return 0; +} + +/* +u8 fatInitRoot() { + + u8 resp = 0; + + resp = fatCreateRecIfNotExist(ED_ROOT, 1); + if (resp)return resp; + resp = fatCreateRecIfNotExist(ED_SAVE, 1); + if (resp)return resp; + resp = fatCreateRecIfNotExist(ED_ROM_CFG, 0); + if (resp)return resp; + resp = fatCreateRecIfNotExist(ED_OPTIONS, 0); + if (resp)return resp; + + return 0; +}*/ + +u8 fatGetFullName(u8 *name, u32 dir_entry, u32 rec_entry) { + + u16 i; + u8 resp; + FatRecord rec_tmp; + fatOpenDir(dir_entry); + for (;;) { + + resp = fatReadDir(&rec_tmp, FAT_MAX_NAME_LEN_S); + if (resp)return resp; + if (rec_tmp.name[0] == 0)return FAT_ERR_NOT_EXIST; + if (rec_entry == rec_tmp.entry_cluster) { + for (i = 0; i < FAT_MAX_NAME_LEN_S; i++) { + *name++ = rec_tmp.name[i]; + if (rec_tmp.name[i] == 0)return 0; + } + } + + } + + return 0; +} + +u8 fatCreateRecIfNotExist(u8 *name, u8 is_dir) { + + u8 resp; + FatRecord rec_tmp; + resp = fatFindRecord(name, &rec_tmp, is_dir); + if (resp == FAT_ERR_NOT_EXIST) { + resp = fatCreateRecord(name, is_dir, 0); + if (resp)return resp; + } else + if (resp)return resp; + + return 0; +} + +u8 fatInit() { + + + u8 resp; + + u32 reserved_sectors; + resp = diskInit(); + if (resp)return resp; + fat_cache->data_sec_idx = 0xffffffff; + fat_cache->table_sec_idx = 0xffffffff; + current_fat.pbr_entry = 0; + table_changed = 0; + //rec_tmp.name = rec_tmp_name; + + //for (i = 0; i < FAT_MAX_DIR_SIZE; i++)dir.rec[i].name = (u8 *) & rec_names[i * (FAT_MAX_NAME_LEN + 4) / 4]; + //for (i = 0; i < sizeof (rec_names); i++)((u8 *) rec_names)[i] = 0; + resp = fatCacheLoadData(current_fat.pbr_entry, 0); + if (resp)return resp; + fatCacheLoadData(0, 0); + + + if (!streql("FAT16", &fat_cache->data_sector[0x36], 5) && !streql("FAT32", &fat_cache->data_sector[0x52], 5)) { + + current_fat.pbr_entry = (u32) fat_cache->data_sector[0x1c6] | (u32) fat_cache->data_sector[0x1c7] << 8 | (u32) fat_cache->data_sector[0x1c8] << 16 | (u32) fat_cache->data_sector[0x1c9] << 24; + resp = fatCacheLoadData(current_fat.pbr_entry, 0); + //console_printf("dat: %s\n", &cache->buff[0x36]); + if (resp)return resp; + if (!streql("FAT16", &fat_cache->data_sector[0x36], 5) && !streql("FAT32", &fat_cache->data_sector[0x52], 5)) { + return FAT_ERR_INIT; + } + } + + + if (streql("FAT16", &fat_cache->data_sector[0x36], 5)) { + current_fat.type = FAT_TYPE_16; + current_fat.sectors_per_fat = (u32) fat_cache->data_sector[0x16] | (u32) fat_cache->data_sector[0x17] << 8; + } else { + current_fat.type = FAT_TYPE_32; + current_fat.sectors_per_fat = (u32) fat_cache->data_sector[0x24] | (u32) fat_cache->data_sector[0x25] << 8 | (u32) fat_cache->data_sector[0x26] << 16 | (u32) fat_cache->data_sector[0x27] << 24; + } + reserved_sectors = (u32) fat_cache->data_sector[0x0e] | (u32) fat_cache->data_sector[0x0f] << 8; + + current_fat.cluster_size = fat_cache->data_sector[0x0d]; + current_fat.root_entry = current_fat.sectors_per_fat * 2 + current_fat.pbr_entry + reserved_sectors; + current_fat.fat_entry = current_fat.pbr_entry + reserved_sectors; + current_fat.data_entry = current_fat.root_entry; + if (current_fat.type == FAT_TYPE_16) { + current_fat.data_entry += 16384 / 512; + } + current_fat.cluster_byte_size = current_fat.cluster_size * 512; + + //fatCacheLoadBuff(fat.root_entry, 0); + //VDP_drawText(APLAN, &cache->buff[0], 0, 2, 20); + + //resp = fatCacheLoadTable(0, 0); + //if (resp)return resp; + + //debug + resp = fatInitRoot(); + if (resp)return resp; + + return 0; +} + +u8 fatOpenDirByName(u8 *name) { + + u8 resp; + FatRecord rec_tmp; + resp = fatFindRecord(name, &rec_tmp, 1); + if (resp)return resp; + fatOpenDir(rec_tmp.entry_cluster); + return 0; +} + +void fatOpenDir(u32 cluster) { + + + if (cluster == 0) { + dir->entry_sector = current_fat.root_entry; + dir->is_root = 1; + if (current_fat.type == FAT_TYPE_32)cluster = 2; + } else { + dir->entry_sector = fatClusterToSector(cluster); + dir->is_root = 0; + } + + dir->entry_cluster = cluster; + dir->current_cluster = cluster; + dir->in_cluster_ptr = 0; + +} + +u8 fatLoadDirByName(u8 *name) { + + u8 resp; + + if (streq(name, "/")) { + fatOpenDir(0); + resp = fatLoadDir(dir->entry_cluster); + if (resp)return resp; + } else { + resp = fatOpenDirByName(name); + if (resp)return resp; + resp = fatLoadDir(dir->entry_cluster); + if (resp)return resp; + } + + + + return 0; +} + +u8 fatLoadDir(u32 cluster) { + + u8 resp; + + fatOpenDir(cluster); + + for (dir->size = 0; dir->size < FAT_MAX_DIR_SIZE; dir->size++) { + + resp = fatReadDir(dir->rec[dir->size], FAT_MAX_NAME_LEN); + if (resp)return resp; + if (dir->rec[dir->size]->name[0] == 0)break; + } + + fatSortRecords(); + return 0; +} + +void fatSortRecords() { + + + FatRecord *rec_buff; + s32 i; + u8 flag0; + u8 flag1; + u64 name0; + u64 name1; + u32 u; + + // VDP_drawText(APLAN, "1", 0, 2, cy++); + for (i = 0; i < dir->size; i++) { + dir->s_records[i] = dir->rec[i]; + } + + s32 stop, swap, limit; + s32 x = (s32) (dir->size / 2) - 1; + while (x > 0) { + stop = 0; + limit = dir->size - x; + while (stop == 0) { + swap = 0; + for (i = 0; i < limit; i++) { + + name0 = 0; + name1 = 0; + for (u = 0; u < 8; u++) { + name0 <<= 8; + name1 <<= 8; + name0 |= dir->s_records[i]->name[u]; + name1 |= dir->s_records[i + x]->name[u]; + } + + name0 |= 0x2020202020202020; + name1 |= 0x2020202020202020; + if (name0 > name1) { + + rec_buff = dir->s_records[i]; + dir->s_records[i] = dir->s_records[i + x]; + dir->s_records[i + x] = rec_buff; + swap = i; + + } + } + limit = swap - x; + if (swap == 0) + stop = 1; + } + x = (int) (x / 2); + } + + //VDP_drawText(APLAN, "2", 0, 2, cy++); + /* + for (i = 1; i < dir->size;) { + + name0 = 0; + name1 = 0; + for (u = 0; u < 8; u++) { + name0 <<= 8; + name1 <<= 8; + name0 |= dir->s_records[i - 1]->name[u]; + name1 |= dir->s_records[i]->name[u]; + } + // name0 = *((u64 *) dir->s_records[i - 1]->name); + //VDP_drawText(APLAN, "--", 0, 2, cy++); + //name1 = *((u64 *) dir->s_records[i]->name); + + if ((name1 | 0x2020202020202020) < (name0 | 0x2020202020202020)) { + + rec_buff = dir->s_records[i]; + dir->s_records[i] = dir->s_records[i - 1]; + dir->s_records[i - 1] = rec_buff; + if (i > 1)i--; + + } else { + i++; + } + } + */ + + // VDP_drawText(APLAN, "3", 0, 2, cy++); + + for (i = 1; i < dir->size;) { + + flag0 = dir->s_records[i - 1]->is_dir; + flag1 = dir->s_records[i]->is_dir; + + if (flag1 && !flag0) { + rec_buff = dir->s_records[i]; + dir->s_records[i] = dir->s_records[i - 1]; + dir->s_records[i - 1] = rec_buff; + if (i > 1)i--; + } else { + i++; + } + } + //VDP_drawText(APLAN, "4", 0, 2, cy++); + +} + +u8 fatReadDir(FatRecord *rec, u16 max_name_len) { + + u16 lfn_len = 0; + u16 in_sector_ptr = 0; + u32 current_sector = 0; + u16 i = 0; + u8 *ptr = 0; + u8 resp; + u32 base_sector; // = dir.entry_sector == dir.is_root ? fat.root_entry : fatClusterToSector(dir.current_cluster); + u8 lfn_buff[FAT_MAX_NAME_LEN_S]; + + //u8 attr; + // u8 attrib; + + //cache->buff_sector = ~0; + rec->is_dir = 0; + rec->entry_cluster = 0; + rec->size = 0; + rec->name[0] = 0; + + if (current_fat.type == FAT_TYPE_16 && dir->is_root) { + base_sector = current_fat.root_entry; + } else { + base_sector = fatClusterToSector(dir->current_cluster); + } + + for (;;) { + + current_sector = base_sector + dir->in_cluster_ptr / 512; + + if (dir->is_root && current_fat.type == FAT_TYPE_16 && dir->in_cluster_ptr >= 16384) { + + return 0; + } + + if (dir->in_cluster_ptr >= current_fat.cluster_byte_size && (!dir->is_root || current_fat.type == FAT_TYPE_32)) { + + //return 0; + //if(dir->current_cluster == 0)dir->current_cluster = 2; + resp = fatGetNextCluster(&dir->current_cluster); + if (resp)return resp; + if (dir->current_cluster == 0) return 0; + base_sector = fatClusterToSector(dir->current_cluster); + current_sector = base_sector; // + dir->in_cluster_ptr / 512; + dir->in_cluster_ptr = 0; + } + + in_sector_ptr = dir->in_cluster_ptr & 511; + resp = fatCacheLoadData(current_sector, 0); + if (resp)return resp; + + if (fat_cache->data_sector[in_sector_ptr] == 0) { + + rec->is_dir = 0; + rec->entry_cluster = 0; + rec->size = 0; + rec->name[0] = 0; + return 0; + //dir->in_cluster_ptr += 32; + // continue; + } + if (fat_cache->data_sector[in_sector_ptr] == 0xe5 || fat_cache->data_sector[in_sector_ptr] == 0x2e || fat_cache->data_sector[in_sector_ptr] == '.') { + dir->in_cluster_ptr += 32; + continue; + } + + if (fat_cache->data_sector[in_sector_ptr + 0x0b] == 0x0f) { + + i = (fat_cache->data_sector[in_sector_ptr] & 0x0f); + + if (i * 13 >= sizeof (lfn_buff) || i == 0) { + dir->in_cluster_ptr += 32; //i * 32; //cache.buff[in_sector_ptr] * 32; + lfn_len = 0; + //VDP_drawText(APLAN, "xxx", 0, 20, 8); + } else { + + //while (i--) { + + resp = fatLoadLfnPart(&fat_cache->data_sector[in_sector_ptr], lfn_buff); + if (resp)return resp; + + dir->in_cluster_ptr += 32; + in_sector_ptr = dir->in_cluster_ptr & 511; + current_sector = base_sector + dir->in_cluster_ptr / 512; + resp = fatCacheLoadData(current_sector, 0); + if (resp)return resp; + lfn_len += 13; + //} + + } + + } else { + + if (lfn_len == 0) { + ptr = &fat_cache->data_sector[in_sector_ptr]; + for (i = 0; i < 8; i++)rec->name[i] = *ptr++; + + for (i = 7; rec->name[i] == ' '; i--) { + rec->name[i] = 0; + if (i == 0)break; + } + i++; + + rec->name[i] = 0; + + if (ptr[0] != ' ' || ptr[1] != ' ' || ptr[2] != ' ') { + rec->name[i++] = '.'; + rec->name[i++] = *ptr++; + rec->name[i++] = *ptr++; + rec->name[i++] = *ptr++; + rec->name[i] = 0; + } + + } else { + + if (lfn_buff[0] == '.') { + lfn_buff[0] = 0; + dir->in_cluster_ptr += 32; //i * 32; //cache.buff[in_sector_ptr] * 32; + lfn_len = 0; + continue; + } + + ptr = lfn_buff; + if (lfn_len > max_name_len)lfn_len = max_name_len; + for (i = 0; i < lfn_len; i++)rec->name[i] = *ptr++; + rec->name[i] = 0; + } + + lfn_len = 0; + ptr = &fat_cache->data_sector[in_sector_ptr]; + rec->is_dir = (ptr[0x0b] & 0x10) == 0x10 ? 1 : 0; + rec->entry_cluster = (u32) ptr[0x1a] | (u32) ptr[0x1b] << 8 | (u32) ptr[0x14] << 16 | (u32) ptr[0x15] << 24; + rec->size = (u32) ptr[0x1c] | (u32) ptr[0x1d] << 8 | (u32) ptr[0x1e] << 16 | (u32) ptr[0x1f] << 24; + rec->hdr_idx = (dir->in_cluster_ptr & 511) / 32; + rec->hdr_sector = current_sector; + + dir->in_cluster_ptr += 32; + + return 0; + /* + } else { + + dir->in_cluster_ptr += 32; + }*/ + } + } + + + return 1; +} + +u8 fatLoadLfnPart(u8 *buff_ptr, u8 *lfn_buff) { + + u8 i; + u8 offset = ((*buff_ptr & 0x0f) - 1) * 13; + u8 *lfn_ptr = &lfn_buff[offset]; + + if (offset + 13 >= FAT_MAX_NAME_LEN_S)return FAT_LFN_BUFF_OVERFLOW; + + buff_ptr++; + for (i = 0; i < 5; i++) { + *lfn_ptr++ = *buff_ptr++; + buff_ptr++; + } + + buff_ptr += 3; + + + for (i = 0; i < 6; i++) { + + *lfn_ptr++ = *buff_ptr++; + buff_ptr++; + } + + buff_ptr += 2; + + *lfn_ptr++ = *buff_ptr++; + buff_ptr++; + *lfn_ptr++ = *buff_ptr++; + + return 0; +} + +u8 fatCacheLoadData(u32 sector, u8 save_befor_load) { + + u8 resp; + //if(cache->grobovik != 333)console_printf("b: %u\n", cache->grobovik); + //console_printf("a: %u\n", cache->buff_sector); + if (fat_cache->data_sec_idx == sector)return 0; + + if (save_befor_load) { + resp = diskWrite(fat_cache->data_sec_idx, fat_cache->data_sector, 1); + } + fat_cache->data_sec_idx = sector; + + //addr=addr*512, buff, lenght 1 + resp = diskRead(fat_cache->data_sec_idx, fat_cache->data_sector, 1); + + //console_printf("c: %u\n", cache->buff_sector); + //joyWait(); + + return resp; + +} + +u8 fatCacheSaveData() { + + return diskWrite(fat_cache->data_sec_idx, fat_cache->data_sector, 1); +} + +u8 fatCacheLoadTable(u32 sector, u8 save_before_load) { + + + u8 resp; + sector += current_fat.fat_entry; + if (fat_cache->table_sec_idx == sector)return 0; + if (save_before_load) { + resp = fatCacheApplyTable(); + if (resp)return resp; + } + + fat_cache->table_sec_idx = sector; + + resp = diskRead(fat_cache->table_sec_idx, fat_cache->table_sector, 1); + + return resp; + +} + +u8 fatCacheApplyTable() { + + u8 resp; + + resp = diskWrite(fat_cache->table_sec_idx, fat_cache->table_sector, 1); + if (resp)return resp; + resp = diskWrite(fat_cache->table_sec_idx + current_fat.sectors_per_fat, fat_cache->table_sector, 1); + + table_changed = 0; + return resp; +} + +u32 fatClusterToSector(u32 cluster) { + + return (cluster - 2) * current_fat.cluster_size + current_fat.data_entry; +} + +u32 fatSectorToCluster(u32 sector) { + + return current_fat.data_entry + sector / current_fat.cluster_size + 2; +} + +u8 fatGetNextCluster(u32 *cluster) { + + u8 resp; + + + resp = fatGetTableRecord(*cluster, cluster); + if (resp)return resp; + + if (current_fat.type == FAT_TYPE_16) { + if (*cluster == 0xffff) { + *cluster = 0; + } + } else { + if (*cluster == 0x0FFFFFFF) { + + *cluster = 0; + } + } + return 0; + +} + +u8 fatOpenFileByeName(u8 *name, u32 wr_sectors) { + + //u16 i; + u8 resp; + FatRecord rec; + //rec.name = fat_name; + + + resp = fatFindRecord(name, &rec, 0); + if (resp)return resp; + if (rec.is_dir)return FAT_ERR_NOT_FILE; + resp = fatOpenFile(&rec, wr_sectors); + if (resp)return resp; + + return 0; +} + +u8 fatFindRecord(u8 *name, FatRecord *rec, u8 is_dir) { + + u8 resp; + u8 *sub_name_ptr; + u8 sub_name[FAT_MAX_NAME_LEN_S]; + u8 name_len; + //u8 cy = 2; + //u16 i; + + + + rec->entry_cluster = 0; + + for (;;) { + + if (*name == '/')name++; + sub_name_ptr = sub_name; + name_len = 0; + while (*name != 0 && *name != '/') { + *sub_name_ptr++ = *name++; + name_len++; + } + + *sub_name_ptr = 0; + + //for (i = 0; sub_name[i] != 0; i++)if (sub_name[i] == ' ')sub_name[i] = '*'; + //VDP_drawText(APLAN, sub_name, 0, 20, cy++); + fatOpenDir(rec->entry_cluster); + for (;;) { + + + resp = fatReadDir(rec, FAT_MAX_NAME_LEN_S); + //VDP_drawText(APLAN, rec->name, 0, 20, cy++); + if (resp)return resp; + if (rec->name[0] == 0) { + return FAT_ERR_NOT_EXIST; + } + + + + if (streq(sub_name, rec->name)) { + + if (*name != 0)break; + if (*name == 0 && ((is_dir && rec->is_dir) || (!is_dir && !rec->is_dir)))break; + } + } + //& ((is_dir && rec->is_dir) || (!is_dir && !rec->is_dir)) + + if (*name == 0) { + //VDP_drawText(APLAN, "record found+++++", 0, 20, cy++); + //VDP_drawText(APLAN, sub_name, 0, 20, cy++); + + return 0; + } + + } + + return 0; + +} + +//FatRecordHdr hdr; + +/* +void fatGetNameExt(u8 *filename, u8 *name, u8 *ext){ + +} + */ + + +void fatSetClustVal(FatRecordHdr *hdr, u32 clust_val) { + + ((u8 *) & hdr->cluster_lo)[0] = (u32) clust_val & 0xff; + ((u8 *) & hdr->cluster_lo)[1] = (u32) clust_val >> 8; + //hdr.cluster_lo = cluster & 0xffff; + if (current_fat.type == FAT_TYPE_32) { + ((u8 *) & hdr->cluster_hi)[0] = (u32) clust_val >> 16; + ((u8 *) & hdr->cluster_hi)[1] = (u32) clust_val >> 24; + } + +} + + +#define fatSlen slen +#define fatStrHiCase strhicase + +u8 fatCreateRecord(u8 *name, u8 is_dir, u8 check_exist) { + + //u8 cy = 10; + u16 i; + FatRecordHdr hdr; + FatRecord rec; + u8 patch[256]; + u8 *file_name; + u8 *ptr; + u8 lfn_blocks = 0; + u8 resp; + u8 sum = 0; + u8 nam_len; + u8 ext_len; + u32 cluster; + u8 lfn_block[32]; + u32 sub_dir_cluster = 0; + + //rec.name = rec_name; + fat_cache->data_sec_idx = ~0; + + if (*name != '/')return FAT_ERR_NAME; + if (fatSlen(name) < 2)return FAT_ERR_NAME; + i = fatSlen(name) - 1; + if (name[i] == '.' || name[i] == ' ')return FAT_ERR_NAME; + + + if (check_exist) { + resp = fatFindRecord(name, &rec, is_dir); + if (resp != FAT_ERR_NOT_EXIST) { + if (resp)return resp; + return FAT_ERR_EXIST; + } + } + + + + memfill(&hdr, 0, 32); + + + ptr = &name[fatSlen(name)]; + + i = 0; + while (*ptr != '/')ptr--; + + i = 0; + while (name != ptr)patch[i++] = *name++; + patch[i] = 0; + + ptr++; + file_name = ptr; + //i = 0; + //while (*ptr != 0)file_name[i++] = *ptr++; + //file_name[i] = 0; + + + + if (fatSlen(file_name) < 1)return FAT_ERR_NAME; + + + i = fatSlen(file_name); + while (i != 0 && file_name[i] != '.')i--; + if (i == 0)i = fatSlen(file_name); + nam_len = i; + ext_len = fatSlen(file_name); + if (ext_len == nam_len) { + ext_len = 0; + } else { + ext_len -= (nam_len + 1); + } + + + + if (nam_len > 8 || ext_len > 3) { + + lfn_blocks = fatSlen(file_name); + if (lfn_blocks % 13 != 0)lfn_blocks += 13; + lfn_blocks /= 13; + for (i = 0; i < 8; i++)hdr.name[i] = file_name[i]; + } else { + for (i = 0; i < 8 && file_name[i] != 0 && file_name[i] != '.'; i++)hdr.name[i] = file_name[i]; + for (; i < 8; i++)hdr.name[i] = ' '; + } + //VDP_drawText(APLAN, file_name, 0, 2, cy++); + + ptr = 0; + if (ext_len != 0)ptr = &file_name[nam_len + 1]; + + + if (ptr != 0) { + hdr.ext[0] = *ptr == 0 ? 0x20 : *ptr++; + hdr.ext[1] = *ptr == 0 ? 0x20 : *ptr++; + hdr.ext[2] = *ptr == 0 ? 0x20 : *ptr++; + } else { + for (i = 0; i < 3; i++)hdr.ext[i] = ' '; + } + fatStrHiCase(hdr.name, 8); + fatStrHiCase(hdr.ext, 3); + hdr.attrib = is_dir ? 0x10 : 0x20; + + + + + if (patch[0] != 0) { + resp = fatFindRecord(patch, &rec, 1); + sub_dir_cluster = rec.entry_cluster; + //if (resp == FAT_ERR_NOT_EXIST)VDP_drawText(APLAN, "cant find patch", 0, 2, cy++); + if (resp)return resp; + } else { + rec.entry_cluster = 0; + } + + + resp = fatGetFreeRecord(&rec, lfn_blocks + 1); + if (resp)return resp; + + //rec.hdr_sector = fat->root_entry; + //rec.hdr_idx + + + resp = fatCacheLoadData(rec.hdr_sector,0); + if (resp)return resp; + + if (lfn_blocks != 0) { + + hdr.name[7] = '1'; + hdr.name[6] = '~'; + hdr.name[5] = (rec.hdr_idx + rec.hdr_sector / 32) & 15; + hdr.name[4] = ((rec.hdr_idx + rec.hdr_sector / 32) >> 4) & 15; + hdr.name[5] += hdr.name[5] <= 10 ? 48 : 55; + hdr.name[4] += hdr.name[4] <= 10 ? 48 : 55; + for (sum = i = 0; i < 11; i++) { + if (i < 8) { + sum = (((sum & 1) << 7) | ((sum & 0xfe) >> 1)) + hdr.name[i]; + } else { + sum = (((sum & 1) << 7) | ((sum & 0xfe) >> 1)) + hdr.ext[i - 8]; + } + } + } + + for (i = 0; i < lfn_blocks; i++) { + + fatMakeLfnBlock(file_name, lfn_block, lfn_blocks - i - 1, sum); + if (i == 0)lfn_block[0] |= 0x40; + memcopy(lfn_block, &fat_cache->data_sector[rec.hdr_idx * 32], 32); + rec.hdr_idx++; + if (rec.hdr_idx == 16) { + rec.hdr_sector++; + rec.hdr_idx = 0; + resp = fatCacheSaveData(); + if (resp)return resp; + //resp = fatCacheLoadData(rec.hdr_sector); + resp = fatCacheLoadData(rec.hdr_sector, 0); + if (resp)return resp; + } + } + /* + if (lfn_blocks != 0) { + resp = diskWriteSector(cache->buff_sector, cache->buff); + if (resp)return resp; + } + */ + + if (is_dir) { + + //ocuppy function may destroy cache buff data + if (lfn_blocks != 0) { + resp = diskWrite(fat_cache->data_sec_idx, fat_cache->data_sector, 1); + if (resp)return resp; + } + cluster = 0; + resp = fatOccupyClusters(&cluster, 1); + if (resp)return resp; + + fatSetClustVal(&hdr, cluster); + + fat_cache->data_sec_idx = ~0; + resp = fatCacheLoadData(rec.hdr_sector, 0); + if (resp)return resp; + } + + hdr.date = 0x5d11; + hdr.time = 0; + + memcopy(&hdr, &fat_cache->data_sector[rec.hdr_idx * 32], 32); + + //resp = diskWriteSector(current_cache->data_sec_idx, current_cache->data_sector); + resp = fatCacheSaveData(); + if (resp)return resp; + + + + if (is_dir) { + + u32 sector = fatClusterToSector(cluster); + resp = fatClearClusters(cluster, 1); + if (resp)return resp; + + resp = fatCacheLoadData(sector, 0); + if (resp)return resp; + + memfill(&hdr, 0, 32); + //memcopy((void *)rawData, &hdr, 32); + hdr.attrib = 0x10; + ptr = (u8 *) & hdr; + for (i = 0; i < 11; i++)ptr[i] = 0x20; + ptr[0] = 0x2e; + + fatSetClustVal(&hdr, cluster); + + memcopy(&hdr, fat_cache->data_sector, 32); + + fatSetClustVal(&hdr, sub_dir_cluster); + ptr[1] = 0x2e; + + memcopy(&hdr, fat_cache->data_sector + 32, 32); + + fatCacheSaveData(); + //fatCacheLoadData() + } + + return 0; +} + +u8 fatCreateRecord2(u8 *name, u8 is_dir, u8 check_exist) { + + //u8 cy = 10; + u16 i; + FatRecordHdr hdr; + FatRecord rec; + u8 patch[FAT_MAX_NAME_LEN_S]; + u8 *file_name; //[FAT_MAX_NAME_LEN_S]; + u8 *ptr; + u8 lfn_blocks = 0; + u8 resp; + u8 sum = 0; + u8 nam_len; + u8 ext_len; + u32 cluster; + u8 lfn_block[32]; + u32 sub_dir_cluster = 0; + + //rec.name = rec_name; + fat_cache->data_sec_idx = ~0; + + if (*name != '/')return FAT_ERR_NAME; + if (slen(name) < 2)return FAT_ERR_NAME; + i = slen(name) - 1; + if (name[i] == '.' || name[i] == ' ')return FAT_ERR_NAME; + + + if (check_exist) { + resp = fatFindRecord(name, &rec, is_dir); + if (resp != FAT_ERR_NOT_EXIST) { + if (resp)return resp; + return FAT_ERR_EXIST; + } + } + + + memfill(&hdr, 0, 32); + + + ptr = &name[slen(name)]; + + i = 0; + while (*ptr != '/')ptr--; + + i = 0; + while (name != ptr)patch[i++] = *name++; + patch[i] = 0; + + ptr++; + file_name = ptr; + //i = 0; + //while (*ptr != 0)file_name[i++] = *ptr++; + //file_name[i] = 0; + + + + if (slen(file_name) < 1)return FAT_ERR_NAME; + + + i = slen(file_name); + while (i != 0 && file_name[i] != '.')i--; + if (i == 0)i = slen(file_name); + nam_len = i; + ext_len = slen(file_name); + if (ext_len == nam_len) { + ext_len = 0; + } else { + ext_len -= (nam_len + 1); + } + + + + if (nam_len > 8 || ext_len > 3) { + + lfn_blocks = slen(file_name); + if (lfn_blocks % 13 != 0)lfn_blocks += 13; + lfn_blocks /= 13; + for (i = 0; i < 8; i++)hdr.name[i] = file_name[i]; + } else { + for (i = 0; i < 8 && file_name[i] != 0 && file_name[i] != '.'; i++)hdr.name[i] = file_name[i]; + for (; i < 8; i++)hdr.name[i] = ' '; + } + //VDP_drawText(APLAN, file_name, 0, 2, cy++); + + ptr = 0; + if (ext_len != 0)ptr = &file_name[nam_len + 1]; + + + if (ptr != 0) { + hdr.ext[0] = *ptr == 0 ? 0x20 : *ptr++; + hdr.ext[1] = *ptr == 0 ? 0x20 : *ptr++; + hdr.ext[2] = *ptr == 0 ? 0x20 : *ptr++; + } else { + for (i = 0; i < 3; i++)hdr.ext[i] = ' '; + } + strhicase(hdr.name, 8); + strhicase(hdr.ext, 3); + hdr.attrib = is_dir ? 0x10 : 0x20; + + + + + if (patch[0] != 0) { + resp = fatFindRecord(patch, &rec, 1); + sub_dir_cluster = rec.entry_cluster; + //if (resp == FAT_ERR_NOT_EXIST)VDP_drawText(APLAN, "cant find patch", 0, 2, cy++); + if (resp)return resp; + } else { + rec.entry_cluster = 0; + } + + + resp = fatGetFreeRecord(&rec, lfn_blocks + 1); + if (resp)return resp; + + //rec.hdr_sector = fat.root_entry; + //rec.hdr_idx + + + resp = fatCacheLoadData(rec.hdr_sector, 0); + if (resp)return resp; + + if (lfn_blocks != 0) { + + hdr.name[7] = '1'; + hdr.name[6] = '~'; + hdr.name[5] = (rec.hdr_idx + rec.hdr_sector / 32) & 15; + hdr.name[4] = ((rec.hdr_idx + rec.hdr_sector / 32) >> 4) & 15; + hdr.name[5] += hdr.name[5] <= 10 ? 48 : 55; + hdr.name[4] += hdr.name[4] <= 10 ? 48 : 55; + for (sum = i = 0; i < 11; i++) { + if (i < 8) { + sum = (((sum & 1) << 7) | ((sum & 0xfe) >> 1)) + hdr.name[i]; + } else { + sum = (((sum & 1) << 7) | ((sum & 0xfe) >> 1)) + hdr.ext[i - 8]; + } + } + } + + for (i = 0; i < lfn_blocks; i++) { + + fatMakeLfnBlock(file_name, lfn_block, lfn_blocks - i - 1, sum); + if (i == 0)lfn_block[0] |= 0x40; + memcopy(lfn_block, &fat_cache->data_sector[rec.hdr_idx * 32], 32); + rec.hdr_idx++; + if (rec.hdr_idx == 16) { + rec.hdr_sector++; + rec.hdr_idx = 0; + resp = fatCacheLoadData(rec.hdr_sector, 1); + if (resp)return resp; + } + } + /* + if (lfn_blocks != 0) { + resp = diskWriteSector(cache->buff_sector, cache->buff); + if (resp)return resp; + } + */ + + if (is_dir) { + + //ocuppy function may destroy cache buff data + if (lfn_blocks != 0) { + resp = diskWrite(fat_cache->data_sec_idx, fat_cache->data_sector, 1); + if (resp)return resp; + } + cluster = 0; + resp = fatOccupyClusters(&cluster, 1); + if (resp)return resp; + ((u8 *) & hdr.cluster_lo)[0] = (u32) cluster & 0xff; + ((u8 *) & hdr.cluster_lo)[1] = (u32) cluster >> 8; + //hdr.cluster_lo = cluster & 0xffff; + if (current_fat.type == FAT_TYPE_32) { + ((u8 *) & hdr.cluster_hi)[0] = (u32) cluster >> 16; + ((u8 *) & hdr.cluster_hi)[1] = (u32) cluster >> 24; + } + + fat_cache->data_sec_idx = ~0; + resp = fatCacheLoadData(rec.hdr_sector, 0); + if (resp)return resp; + } + + hdr.date = 0x5d11; + hdr.time = 0; + + memcopy(&hdr, &fat_cache->data_sector[rec.hdr_idx * 32], 32); + + resp = diskWrite(fat_cache->data_sec_idx, fat_cache->data_sector, 1); + if (resp)return resp; + + if (is_dir) { + + u32 sector = fatClusterToSector(cluster); + resp = fatClearClusters(cluster, 1); + if (resp)return resp; + + resp = fatCacheLoadData(sector, 0); + if (resp)return resp; + + memfill(&hdr, 0, 32); + //memcopy((void *)rawData, &hdr, 32); + hdr.attrib = 0x10; + ptr = (u8 *) & hdr; + for (i = 0; i < 11; i++)ptr[i] = 0x20; + ptr[0] = 0x2e; + + fatSetClustVal(&hdr, cluster); + + memcopy(&hdr, fat_cache->data_sector, 32); + + fatSetClustVal(&hdr, sub_dir_cluster); + ptr[1] = 0x2e; + + memcopy(&hdr, fat_cache->data_sector + 32, 32); + + fatCacheSaveData(); + //fatCacheLoadData() + } + + return 0; +} + +void fatMakeLfnBlock(u8 *name, u8 *lfn_block, u8 block_idx, u8 crc) { + + u8 i; + name += block_idx * 13; + for (i = 0; i < 13; i++) { + if (name[i] == 0)break; + } + i++; + for (; i < 13; i++) { + name[i] = 0xff; + } + + *lfn_block++ = block_idx + 1; + + for (i = 0; i < 5; i++) { + *lfn_block++ = *name == 0xff ? 0xff : *name; + *lfn_block++ = *name == 0xff ? 0xff : 0; + name++; + } + *lfn_block++ = 0x0f; + *lfn_block++ = 0; + *lfn_block++ = crc; + + for (i = 0; i < 6; i++) { + + *lfn_block++ = *name == 0xff ? 0xff : *name; + *lfn_block++ = *name == 0xff ? 0xff : 0; + name++; + } + + *lfn_block++ = 0; + *lfn_block++ = 0; + + *lfn_block++ = *name == 0xff ? 0xff : *name; + *lfn_block++ = *name == 0xff ? 0xff : 0; + name++; + *lfn_block++ = *name == 0xff ? 0xff : *name++; + *lfn_block++ = *name == 0xff ? 0xff : 0; + name++; + +} + +u8 fatGetFreeRecord(FatRecord *dir_rec, u8 len) { + + u8 len_ctr = 0; + u32 sector; + u8 resp; + u32 current_cluster = dir_rec->entry_cluster; + u32 next_cluster; + u16 in_cluster_ptr = 0; + u16 i; + u8 is_root = 0; + fat_cache->data_sec_idx = ~0; + + if (current_cluster == 0 && current_fat.type == FAT_TYPE_32)current_cluster = 2; + + if (current_cluster == 0) { + //current_cluster = fatSectorToCluster(fat->root_entry); + sector = current_fat.root_entry; + is_root = 1; + } else { + sector = fatClusterToSector(current_cluster); + } + + //VDP_drawText(APLAN, "get rec", 0, 15, cy++); + + while (len_ctr != len) { + + + + resp = fatCacheLoadData(sector,0); + if (resp)return resp; + + for (i = 0; i < 512; i += 32) { + + if (fat_cache->data_sector[i] == 0 || fat_cache->data_sector[i] == 0xe5) { + if (len_ctr == 0) { + dir_rec->hdr_sector = sector; + dir_rec->hdr_idx = i / 32; + } + len_ctr++; + } else { + len_ctr = 0; + dir_rec->hdr_sector = 0; + } + if (len_ctr == len)return 0; + } + + sector++; + in_cluster_ptr++; + + if (is_root && current_fat.type == FAT_TYPE_16 && in_cluster_ptr >= 16384 / 512) { + //VDP_drawText(APLAN, "root overflow", 0, 15, cy++); + return FAT_ERR_ROT_OVERFLOW; + } + + if (current_fat.type == FAT_TYPE_16 && is_root)continue; + + if (in_cluster_ptr == current_fat.cluster_size) { + + //VDP_drawText(APLAN, "cluster end", 0, 15, cy++); + next_cluster = current_cluster; + resp = fatGetNextCluster(&next_cluster); + if (resp)return resp; + if (next_cluster == 0) { + //VDP_drawText(APLAN, "need to ocupy cluster for dir", 0, 15, cy++); + resp = fatOccupyClusters(¤t_cluster, 1); + if (resp)return resp; + resp = fatClearClusters(current_cluster, 1); + if (resp)return resp; + //VDP_drawText(APLAN, "ocupied!!!!!", TILE_ATTR(cy % 3, 0, 0, 0), 15, cy++); + //cy %= 27; + } else { + current_cluster = next_cluster; + } + sector = fatClusterToSector(current_cluster); + in_cluster_ptr = 0; + } + } + + return 0; +} + +u8 fatClearClusters(u32 cluster, u8 len) { + + u32 i; + u8 resp; + memfill(fat_cache->data_sector, 0x00, 512); + + while (len--) { + fat_cache->data_sec_idx = fatClusterToSector(cluster++); + for (i = 0; i < current_fat.cluster_size; i++) { + resp = diskWrite(fat_cache->data_sec_idx++, fat_cache->data_sector, 1); + + if (resp)return resp; + } + } + return 0; +} + +u8 fatOccupyClusters(u32 *base_cluster, u16 len) { + + + if ((current_fat.type == FAT_TYPE_16 && *base_cluster == 0xffff) || (current_fat.type == FAT_TYPE_32 && *base_cluster == 0x0FFFFFFF))return FAT_ERR_BAD_BASE_CLUSTER; + u32 *ptr32; + u16 *ptr16; + u16 i; + u32 fat_table_sector = 0; + u16 len_ctr = 0; + u32 first_free_cluster = 0; + u32 cluster_ctr = 0; + u16 fat_page_len = current_fat.type == FAT_TYPE_16 ? 8192 : 4096; + u8 resp = 0; + + + kot = 0; + //gDrawString("okupy 1", 32, 32); + + while (len_ctr < len) { + + + //memcopy(current_cache->table_frame, occupy_buff, 512); + resp = diskRead(current_fat.fat_entry + fat_table_sector, (void *) ROM_ADDR, 32); + //resp = diskRead(current_fat.fat_entry + fat_table_sector, occupy_buff, 1); + if (resp)return resp; + dma_read(occupy_buff, ROM_ADDR, 32 * 512); + + + if (current_fat.type == FAT_TYPE_16) { + + ptr16 = (u16 *) occupy_buff; + + + for (i = 0; i < fat_page_len; i++) { + + if (*ptr16++ == 0) { + if (first_free_cluster == 0)first_free_cluster = cluster_ctr; + len_ctr++; + if (len_ctr == len)break; + } else if (len_ctr != 0) { + len_ctr = 0; + first_free_cluster = 0; + } + cluster_ctr++; + + } + + if (cluster_ctr == 0x10000)return FAT_ERR_NO_FRE_SPACE; + + } else { + + ptr32 = (u32 *) occupy_buff; + for (i = 0; i < fat_page_len; i++) { + + + if (*ptr32++ == 0) { + if (first_free_cluster == 0)first_free_cluster = cluster_ctr; + len_ctr++; + if (len_ctr == len)break; + } else if (len_ctr != 0) { + len_ctr = 0; + first_free_cluster = 0; + } + cluster_ctr++; + + } + } + + + + fat_table_sector += 32; + } + + + /* + gConsDrawString("oc: "); + gAppendNum(first_free_cluster); + */ + + //cache current fact sector to avoid dummy rewrite + /* + cluster = *base_cluster != 0 ? *base_cluster : first_free_cluster; + table_sector = current_fat.type == FAT_TYPE_16 ? cluster / 256 : cluster / 128; + resp = fatCacheLoadTable(table_sector, 0); + if (resp)return resp;*/ + + if (*base_cluster != 0) { + resp = fatSetTableRecord(*base_cluster, first_free_cluster); + if (resp)return resp; + } + + *base_cluster = first_free_cluster; + + while (len--) { + + if (len == 0) { + resp = fatSetTableRecord(first_free_cluster, 0x0FFFFFFF); + } else { + resp = fatSetTableRecord(first_free_cluster, first_free_cluster + 1); + } + if (resp)return resp; + first_free_cluster++; + } + + fatCacheApplyTable(); + if (resp)return resp; + + return 0; +} + +u8 fatGetTableRecord(u32 cluster, u32 *val) { + + u8 resp; + u8 *ptr; + + u32 table_sector = current_fat.type == FAT_TYPE_16 ? cluster / 256 : cluster / 128; + resp = fatCacheLoadTable(table_sector, 0); + + if (resp)return resp; + + if (current_fat.type == FAT_TYPE_16) { + ptr = &fat_cache->table_sector[(cluster & 255) * 2]; + *val = (u32) ptr[0] | (u32) ptr[1] << 8; + } else { + + ptr = &fat_cache->table_sector[(cluster & 127) * 4]; + *val = (u32) ptr[0] | (u32) ptr[1] << 8 | (u32) ptr[2] << 16 | (u32) ptr[3] << 24; + } + + + return 0; + +} + +u8 fatSetTableRecord(u32 cluster, u32 next_cluster) { + + u8 resp; + u32 table_sector = current_fat.type == FAT_TYPE_16 ? cluster / 256 : cluster / 128; + u8 *ptr; + + resp = fatCacheLoadTable(table_sector, table_changed); + + if (resp)return resp; + + if (current_fat.type == FAT_TYPE_16) { + ptr = &fat_cache->table_sector[(cluster & 255) * 2]; + ptr[0] = (u32) next_cluster >> 0; + ptr[1] = (u32) next_cluster >> 8; + } else { + + ptr = &fat_cache->table_sector[(cluster & 127) * 4]; + ptr[0] = (u32) next_cluster >> 0; + ptr[1] = (u32) next_cluster >> 8; + ptr[2] = (u32) next_cluster >> 16; + ptr[3] = (u32) next_cluster >> 24; + } + + table_changed = 1; + + return 0; +} + +/* +u8 fatWriteFile(void *src, u32 sectors) { + + u8 resp; + u8 *ptr8 = (u8 *) src; + + if (file.mode != FILE_MODE_WR)return FAT_ERR_FILE_MODE; + if (sectors > file.sec_available)return FAT_ERR_OUT_OF_FILE; + while (sectors--) { + + resp = diskWrite(file.sector, ptr8, 1); + if (resp)return resp; + ptr8 += 512; + resp = fatSkipSectors(1); + if (resp)return resp; + } + + + if (file.sec_available == 0)file.mode = 0; + + return 0; +}*/ + + +u8 fatWriteFile(void *src, u32 sectors) { + + u8 resp; + u8 *ptr8 = (u8 *) src; + u32 len; + u32 readed; + + if (file.mode != FILE_MODE_WR)return FAT_ERR_FILE_MODE; + if (sectors > file.sec_available)return FAT_ERR_OUT_OF_FILE; + + while (sectors) { + + if (file.in_cluster_ptr == 0 && sectors >= current_fat.cluster_size) { + resp = fatMultiClustRW(src, sectors, &readed, 1); + sectors -= readed; + //console_printf("len: %d\n", readed); + //joyWait(); + + } else { + + len = sectors + file.in_cluster_ptr < current_fat.cluster_size ? sectors : 1; + + //console_printf("len: %d\n", len); + //joyWait(); + resp = diskWrite(file.sector, ptr8, len); + if (resp)return resp; + ptr8 += 512 * len; + resp = fatSkipSectors(len); + if (resp)return resp; + sectors -= len; + } + + + } + + + if (file.sec_available == 0)file.mode = 0; + + return 0; +} + +u8 fatReadFile(void *dst, u32 sectors) { + + u8 resp; + u32 readed; + u32 len; + + if (file.mode != FILE_MODE_RD)return FAT_ERR_FILE_MODE; + if (sectors > file.sec_available)return FAT_ERR_OUT_OF_FILE; + + while (sectors) { + + if (sectors < current_fat.cluster_size || file.in_cluster_ptr != 0) { + + len = current_fat.cluster_size - file.in_cluster_ptr; + if (len > sectors)len = sectors; + + resp = diskRead(file.sector, dst, len); + + if (resp)return resp; + dst += 512 * len; + resp = fatSkipSectors(len); + if (resp)return resp; + sectors = sectors >= len ? sectors - len : 0; + } else { + + resp = fatMultiClustRW(dst, sectors, &readed, 0); + if (resp)return resp; + dst += readed * 512; + sectors -= readed; + } + } + + if (file.sec_available == 0)file.mode = 0; + + return 0; +} + +/* +u8 fatReadPartialFile(void *dst, u32 sectors, int b_offset) { + +//1048576 * 8 sectors + + //secotrs = datei gesamt-sektoranzahl + u8 resp; + u32 readed; + u32 len; + + if (file.mode != FILE_MODE_RD)return FAT_ERR_FILE_MODE; + if (end_sector > file.sec_available || offset_sector > file.sec_available)return FAT_ERR_OUT_OF_FILE; + + +//first run b_offset = 0 -> 1Mb lesen +//also einmal tmp_sectors +//zweiter run b_offset = 1 -> 1Mb lesen +//aber ab offset 1024*1024 * 1 + + tmp_sectors = 1024*1024; //1048576 + //for(int i=0; i < ;i++){ //1 mb + while (tmp_sectors) { + + //gesamt < 4096 (8*512) oder c_ptr!=0 -> >first run? + if (sectors < current_fat.cluster_size || file.in_cluster_ptr != 0) { + + //rest sectores + len = current_fat.cluster_size - file.in_cluster_ptr; + if (len > sectors)len = sectors; + + resp = diskRead(file.sector, dst, len); //len in sectors + if (resp)return resp; + dst += 512 * len; + resp = fatSkipSectors(len); + if (resp)return resp; + sectors = sectors >= len ? sectors - len : 0; + } else { + //anfang liest ganze 512 cluster + //sectors sollte offset+1Mb + resp = fatMultiClustRW(dst, sectors, &readed, 0); + if (resp)return resp; + dst += readed * 512; + sectors -= readed; //zieht großen block ab + } + } + + if (file.sec_available == 0)file.mode = 0; + + return 0; +} +*/ + +u8 fatMultiClustRW(void *buff, u32 max_sectors, u32 *readrd_sectors, u16 write) { + + u8 resp; + u32 clust = max_sectors / current_fat.cluster_size; + if (max_sectors % current_fat.cluster_size != 0)clust++; + u32 begin_sect = file.sector; + u32 len = 0; + u32 prev_clust = file.cluster; + u32 max_len = 0x2000000 / current_fat.cluster_byte_size; + + while (clust-- && len < max_len) { + len++; + resp = fatSkipCluster(); + if (resp)return resp; + if (prev_clust + 1 != file.cluster)break; + prev_clust = file.cluster; + } + + len *= current_fat.cluster_size; + if (len > max_sectors)len = max_sectors; + *readrd_sectors = len; + + if (write) { + resp = diskWrite(begin_sect, buff, len); + } else { + resp = diskRead(begin_sect, buff, len); + } + + return resp; + +} + +u8 fatSkipCluster() { + + u8 resp; + + resp = fatGetNextCluster(&file.cluster); + if (resp)return resp; + file.sec_available = file.sec_available < current_fat.cluster_size ? 0 : file.sec_available - current_fat.cluster_size; + if (file.sec_available != 0 && file.cluster == 0)return FAT_ERR_OUT_OF_TABLE; + file.sector = fatClusterToSector(file.cluster); + file.in_cluster_ptr = 0; + + if (file.sec_available == 0)file.mode = 0; + + return 0; +} + +u8 fatReadCluster(void *dst) { + + u8 resp; + if (file.mode != FILE_MODE_RD)return FAT_ERR_FILE_MODE; + + resp = diskRead(file.sector, dst, current_fat.cluster_size); + if (resp)return resp; + dst += current_fat.cluster_byte_size; + resp = fatGetNextCluster(&file.cluster); + //file.cluster ++; + if (resp)return resp; + file.sec_available = file.sec_available < current_fat.cluster_size ? 0 : file.sec_available - current_fat.cluster_size; + if (file.sec_available != 0 && file.cluster == 0)return FAT_ERR_OUT_OF_TABLE; + file.sector = fatClusterToSector(file.cluster); + file.in_cluster_ptr = 0; + + if (file.sec_available == 0)file.mode = 0; + + return 0; +} + +u8 fatSkipSectors(u32 sectors) { + + u8 resp; + + if (sectors == 0)return 0; + + if (sectors > file.sec_available)sectors = file.sec_available; + while (sectors--) { + + file.sector++; + file.sec_available--; + file.in_cluster_ptr++; + if (file.in_cluster_ptr == current_fat.cluster_size) { + resp = fatGetNextCluster(&file.cluster); + if (resp)return resp; + + if (file.cluster == 0 && sectors != 0)return FAT_ERR_OUT_OF_TABLE; + file.sector = fatClusterToSector(file.cluster); + file.in_cluster_ptr = 0; + } + } + + + return 0; +} + +u8 fatOpenFile(FatRecord *rec, u32 wr_sectors) { + + + u8 resp; + file.sec_available = rec->size / 512; + if ((rec->size & 511) != 0)file.sec_available++; + file.cluster = rec->entry_cluster; + file.in_cluster_ptr = 0; + file.mode = wr_sectors == 0 ? FILE_MODE_RD : FILE_MODE_WR; + + + + if (wr_sectors > file.sec_available) { + + resp = fatResizeFile(rec, wr_sectors); + if (resp)return resp; + if (file.cluster == 0)file.cluster = rec->entry_cluster; + file.sec_available = wr_sectors; + } + + file.sector = fatClusterToSector(file.cluster); + + + return 0; + +} + +/* +u8 fatOpenFile(FatRecord *rec, u32 wr_sectors) { + + u8 resp; + u32 clusters_need; + FatRecordHdr hdr; + u32 clust; + + u32 size; + u32 clusters_available; + u32 wr_clusters_req; + + + current_cache->data_sector = ~0; + memcopy(rec, &file.rec, sizeof (FatRecord)); + + + file.sec_available = file.rec.size / 512; + if ((file.rec.size & 511) != 0)file.sec_available++; + file.cluster = file.rec.entry_cluster; + file.in_cluster_ptr = 0; + file.mode = wr_sectors == 0 ? FILE_MODE_RD : FILE_MODE_WR; + + clusters_available = file.sec_available / current_fat.cluster_size; + if ((file.sec_available & (current_fat.cluster_size - 1)) != 0)clusters_available++; + wr_clusters_req = wr_sectors / current_fat.cluster_size; + if ((wr_sectors & (current_fat.cluster_size - 1)) != 0)wr_clusters_req++; + + if (wr_sectors > file.sec_available) { + + + if (clusters_available == 0 || clusters_available < wr_clusters_req) { + //VDP_drawText(APLAN, "ned to get some clusters", 0, 2, cy++); + clusters_need = (wr_sectors - file.sec_available); + if ((clusters_need & (current_fat.cluster_size - 1)) != 0)clusters_need += current_fat.cluster_size; + clusters_need /= current_fat.cluster_size; + clust = file.cluster; + if (clusters_available != 0)clust += clusters_available - 1; + + resp = fatOccupyClusters(&clust, clusters_need); + + + if (resp)return resp; + if (file.cluster == 0)file.cluster = clust; + } + + file.sec_available = wr_sectors; + current_cache->data_sector = ~0; + fatCacheLoadBuff(file.rec.hdr_sector, 0); + memcopy(¤t_cache->data_frame[file.rec.hdr_idx * 32], &hdr, 32); + size = file.sec_available * 512; + //hdr.size = 0x12345; + ((u8 *) & hdr.size)[0] = (u32) size; + ((u8 *) & hdr.size)[1] = (u32) size >> 8; + ((u8 *) & hdr.size)[2] = (u32) size >> 16; + ((u8 *) & hdr.size)[3] = (u32) size >> 24; + + ((u8 *) & hdr.cluster_lo)[0] = (u32) file.cluster & 0xff; + ((u8 *) & hdr.cluster_lo)[1] = (u32) file.cluster >> 8; + //hdr.cluster_lo = cluster & 0xffff; + if (current_fat.type == FAT_TYPE_32) { + ((u8 *) & hdr.cluster_hi)[0] = (u32) file.cluster >> 16; + ((u8 *) & hdr.cluster_hi)[1] = (u32) file.cluster >> 24; + } + + + memcopy(&hdr, ¤t_cache->data_frame[file.rec.hdr_idx * 32], 32); + + resp = diskWrite(current_cache->data_sector, current_cache->data_frame, 1); + + if (resp)return resp; + } + + file.sector = fatClusterToSector(file.cluster); + + return 0; +} + */ +u8 fatResizeFile(FatRecord *rec, u32 req_sec_size) { + + u8 resp; + u32 sec_available; + u32 wr_clusters_req; + u32 clusters_available; + u32 clusters_need; + u32 clust; + u16 i; + FatRecordHdr *hdr; + u32 size; + + + + sec_available = rec->size / 512; + if ((rec->size & 511) != 0)sec_available += 512; + + if (req_sec_size <= sec_available)return FAT_ERR_RESIZE; + + + clusters_available = sec_available / current_fat.cluster_size; + if ((sec_available % current_fat.cluster_size) != 0)clusters_available++; + wr_clusters_req = req_sec_size / current_fat.cluster_size; + if (req_sec_size % current_fat.cluster_size != 0)wr_clusters_req++; + + if (clusters_available < wr_clusters_req) { + + clusters_need = (wr_clusters_req - clusters_available); + clust = rec->entry_cluster; + if (clusters_available != 0) { + for (i = 0; i < clusters_available - 1; i++) { + fatGetNextCluster(&clust); + } + } + + resp = fatOccupyClusters(&clust, clusters_need); + + if (rec->entry_cluster == 0)rec->entry_cluster = clust; + if (resp)return resp; + } + + //resp = fatCacheLoadData(rec->hdr_sector); + resp = fatCacheLoadData(rec->hdr_sector, 0); + if (resp)return resp; + hdr = (FatRecordHdr *) & fat_cache->data_sector[rec->hdr_idx * 32]; + + size = req_sec_size * 512; + ((u8 *) & hdr->size)[0] = (u32) size; + ((u8 *) & hdr->size)[1] = (u32) size >> 8; + ((u8 *) & hdr->size)[2] = (u32) size >> 16; + ((u8 *) & hdr->size)[3] = (u32) size >> 24; + + + ((u8 *) & hdr->cluster_lo)[0] = (u32) rec->entry_cluster & 0xff; + ((u8 *) & hdr->cluster_lo)[1] = (u32) rec->entry_cluster >> 8; + if (current_fat.type == FAT_TYPE_32) { + ((u8 *) & hdr->cluster_hi)[0] = (u32) rec->entry_cluster >> 16; + ((u8 *) & hdr->cluster_hi)[1] = (u32) rec->entry_cluster >> 24; + } + + resp = fatCacheSaveData(); + if (resp)return resp; + + + return 0; +} diff --git a/fat.h b/fat.h new file mode 100644 index 0000000..c8e1522 --- /dev/null +++ b/fat.h @@ -0,0 +1,128 @@ +/* + * File: fat.h + * Author: krik + * + * Created on 22 Ìàé 2011 ã., 1:06 + */ + +#ifndef _FAT_H +#define _FAT_H + +#include "types.h" +#include "errors.h" + + + + +#define FAT_MAX_DIR_SIZE 1024 +#define FAT_MAX_NAME_LEN 128 +#define FAT_MAX_NAME_LEN_S 256 + +#define FAT_TYPE_16 1 +#define FAT_TYPE_32 2 + +typedef struct { + u8 name[FAT_MAX_NAME_LEN]; + u32 entry_cluster; + u32 size; + u32 hdr_sector; + u16 hdr_idx; + u16 fav_idx; + u8 is_dir; +} FatRecord; + +typedef struct { + u8 name[8]; + u8 ext[3]; + u8 attrib; + u8 x[8]; + u16 cluster_hi; + u16 time; + u16 date; + u16 cluster_lo; + u32 size; +} FatRecordHdr; + +typedef struct { + FatRecord * rec[FAT_MAX_DIR_SIZE]; + FatRecord * s_records[FAT_MAX_DIR_SIZE]; + u8 *path; + u16 size; + u32 entry_cluster; + u32 entry_sector; + u32 in_cluster_ptr; + u32 current_cluster; + u8 is_root; +} Dir; + +typedef struct { + u32 pbr_entry; + u32 root_entry; + u32 fat_entry; + u32 data_entry; + u8 type; + u8 cluster_size; + u32 loaded_sector; + u32 cluster_byte_size; + u32 sectors_per_fat; +} Fat; + +typedef struct { + u8 *table_buff; + u8 *table_sector; + u8 *data_sector; + u32 data_sec_idx; + u32 table_sec_idx; +} FatCache; + +typedef struct { + u32 sector; + u32 cluster; + u32 sec_available; + u16 in_cluster_ptr; + FatRecord rec; + u8 mode; +} File; + + + +extern Dir *dir; +//extern Dir dir_o; +extern File file; +extern FatCache *fat_cache; +//extern FatRecord rec_tmp; +#define ED_ROOT "/ED64" +#define ED_SAVE "/ED64/SDSAVE" +#define ED_ROM_CFG "/ED64/SDSAVE/LAST.CRT" +//#define ED_TEST_CFG "/MISC/test.dat" +//#define ED_OPTIONS "/ED64/options.dat" +//#define ED_FAVOR "/ED64/favor.dat" + +//#define ED_SAVE_DMP "/EDMD/sav-dmp.bin" +#define FILE_MODE_RD 1 +#define FILE_MODE_WR 2 + +u8 fatInit(); +u8 fatLoadDir(u32 cluster); +u8 fatLoadDirByName(u8 *name); + +u8 fatWriteFile(void *src, u32 sectors); +u8 fatReadFile(void *dst, u32 sectors); +u8 fatReadPartialFile(void *dst, u32 sectors, int b_offset); +u8 fatCreateRecord(u8 *name, u8 is_dir, u8 check_exist); +u8 fatOpenFileByeName(u8 *name, u32 wr_sectors); +u8 fatFindRecord(u8 *name, FatRecord *rec, u8 is_dir); +u8 fatOpenFile(FatRecord *rec, u32 wr_sectors); + +u8 fatSkipSectors(u32 sectors); +u8 fatCreateRecIfNotExist(u8 *name, u8 is_dir); +u8 fatGetFullName(u8 *name, u32 dir_entry, u32 rec_entry); +u8 fatOpenDirByName(u8 *name); +void fatInitRam(); + +u8 streq(u8 *str1, u8 *str2); +u8 streql(u8 *str1, u8 *str2, u8 len); +u8 slen(u8 *str); +void fatSortRecords(); +#endif /* _FAT_H */ + diff --git a/filesystem/bamboo.wav b/filesystem/bamboo.wav new file mode 100644 index 0000000..dad6dbc Binary files /dev/null and b/filesystem/bamboo.wav differ diff --git a/filesystem/done.wav b/filesystem/done.wav new file mode 100644 index 0000000..6051ac7 Binary files /dev/null and b/filesystem/done.wav differ diff --git a/filesystem/ed64_mono.wav b/filesystem/ed64_mono.wav new file mode 100644 index 0000000..74d4dcb Binary files /dev/null and b/filesystem/ed64_mono.wav differ diff --git a/filesystem/n64controller.sprite b/filesystem/n64controller.sprite new file mode 100644 index 0000000..437d2b8 Binary files /dev/null and b/filesystem/n64controller.sprite differ diff --git a/filesystem/sprites/background.sprite b/filesystem/sprites/background.sprite new file mode 100644 index 0000000..d4ccae7 Binary files /dev/null and b/filesystem/sprites/background.sprite differ diff --git a/filesystem/sprites/splash.sprite b/filesystem/sprites/splash.sprite new file mode 100644 index 0000000..7681071 Binary files /dev/null and b/filesystem/sprites/splash.sprite differ diff --git a/filesystem/warning.wav b/filesystem/warning.wav new file mode 100644 index 0000000..53b3d4b Binary files /dev/null and b/filesystem/warning.wav differ diff --git a/font_patch/font.h b/font_patch/font.h new file mode 100644 index 0000000..3a18596 --- /dev/null +++ b/font_patch/font.h @@ -0,0 +1,275 @@ + +/** + * @file font.h + * @brief Font Data + * @ingroup graphics + */ +#ifndef __LIBDRAGON_FONT +#define __LIBDRAGON_FONT + +/** + * @brief saturnu future font + * @ingroup graphics + */ + + +static unsigned char __font_data[2048] = { + 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, // Char 000 (.) + 0x7E, 0x81, 0xA5, 0x81, 0xBD, 0x99, 0x81, 0x7E, // Char 001 (.) + 0x7E, 0xFF, 0xDB, 0xFF, 0xC3, 0xE7, 0xFF, 0x7E, // Char 002 (.) + 0x6C, 0xFE, 0xFE, 0xFE, 0x7C, 0x38, 0x10, 0x00, // Char 003 (.) + 0x10, 0x38, 0x7C, 0xFE, 0x7C, 0x38, 0x10, 0x00, // Char 004 (.) + 0x3C, 0x3C, 0x18, 0xFF, 0xE7, 0x18, 0x3C, 0x00, // Char 005 (.) + 0x10, 0x38, 0x7C, 0xFE, 0xEE, 0x10, 0x38, 0x00, // Char 006 (.) + 0x00, 0x00, 0x18, 0x3C, 0x3C, 0x18, 0x00, 0x00, // Char 007 (.) + 0xFF, 0xFF, 0xE7, 0xC3, 0xC3, 0xE7, 0xFF, 0xFF, // Char 008 (.) + 0x00, 0x3C, 0x66, 0x42, 0x42, 0x66, 0x3C, 0x00, // Char 009 (.) + 0xFF, 0xC3, 0x99, 0xBD, 0xBD, 0x99, 0xC3, 0xFF, // Char 010 (.) + 0x0F, 0x07, 0x0F, 0x7D, 0xCC, 0xCC, 0xCC, 0x78, // Char 011 (.) + 0x3C, 0x66, 0x66, 0x66, 0x3C, 0x18, 0x7E, 0x18, // Char 012 (.) + 0x08, 0x0C, 0x0A, 0x0A, 0x08, 0x78, 0xF0, 0x00, // Char 013 (.) + 0x18, 0x14, 0x1A, 0x16, 0x72, 0xE2, 0x0E, 0x1C, // Char 014 (.) + 0x10, 0x54, 0x38, 0xEE, 0x38, 0x54, 0x10, 0x00, // Char 015 (.) + 0x80, 0xE0, 0xF8, 0xFE, 0xF8, 0xE0, 0x80, 0x00, // Char 016 (.) + 0x02, 0x0E, 0x3E, 0xFE, 0x3E, 0x0E, 0x02, 0x00, // Char 017 (.) + 0x18, 0x3C, 0x5A, 0x18, 0x5A, 0x3C, 0x18, 0x00, // Char 018 (.) + 0x66, 0x66, 0x66, 0x66, 0x66, 0x00, 0x66, 0x00, // Char 019 (.) + 0x7F, 0xDB, 0xDB, 0xDB, 0x7B, 0x1B, 0x1B, 0x00, // Char 020 (.) + 0x1C, 0x22, 0x38, 0x44, 0x44, 0x38, 0x88, 0x70, // Char 021 (.) + 0x00, 0x00, 0x00, 0x00, 0x7E, 0x7E, 0x7E, 0x00, // Char 022 (.) + 0x18, 0x3C, 0x5A, 0x18, 0x5A, 0x3C, 0x18, 0x7E, // Char 023 (.) + 0x18, 0x3C, 0x5A, 0x18, 0x18, 0x18, 0x18, 0x00, // Char 024 (.) + 0x18, 0x18, 0x18, 0x18, 0x5A, 0x3C, 0x18, 0x00, // Char 025 (.) + 0x00, 0x18, 0x0C, 0xFE, 0x0C, 0x18, 0x00, 0x00, // Char 026 (.) + 0x00, 0x30, 0x60, 0xFE, 0x60, 0x30, 0x00, 0x00, // Char 027 (.) + 0x00, 0x00, 0xC0, 0xC0, 0xC0, 0xFE, 0x00, 0x00, // Char 028 (.) + 0x00, 0x24, 0x42, 0xFF, 0x42, 0x24, 0x00, 0x00, // Char 029 (.) + 0x00, 0x10, 0x38, 0x7C, 0xFE, 0xFE, 0x00, 0x00, // Char 030 (.) + 0x00, 0xFE, 0xFE, 0x7C, 0x38, 0x10, 0x00, 0x00, // Char 031 (.) + 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, // Char 032 ( ) + 0x3C, 0x3C, 0x3C, 0x38, 0x38, 0x00, 0x38, 0x00, // Char 033 (!) + 0x6C, 0x24, 0x24, 0x00, 0x00, 0x00, 0x00, 0x00, // Char 034 (") + 0x6C, 0x6C, 0xFE, 0x6C, 0xFE, 0xFE, 0x6C, 0x00, // Char 035 (#) + 0x10, 0x7C, 0xD0, 0x7C, 0x16, 0xFC, 0x10, 0x00, // Char 036 ($) + 0x00, 0x66, 0xAC, 0xD8, 0x36, 0x6A, 0xCC, 0x00, // Char 037 (%) + 0x38, 0x4C, 0x38, 0x78, 0xCE, 0xCC, 0x7A, 0x00, // Char 038 (&) + 0x30, 0x10, 0x20, 0x00, 0x00, 0x00, 0x00, 0x00, // Char 039 (') + 0x38, 0x38, 0x60, 0x60, 0x60, 0x38, 0x38, 0x00, // Char 040 (() + 0x60, 0x78, 0x18, 0x18, 0x18, 0x78, 0x60, 0x00, // Char 041 ()) + 0x00, 0x66, 0x3C, 0xFF, 0x3C, 0x66, 0x00, 0x00, // Char 042 (*) + 0x00, 0x30, 0x30, 0xFC, 0x30, 0x30, 0x00, 0x00, // Char 043 (+) + 0x00, 0x00, 0x00, 0x00, 0x00, 0x30, 0x10, 0x20, // Char 044 (,) + 0x00, 0x00, 0x00, 0xFC, 0x00, 0x00, 0x00, 0x00, // Char 045 (-) + 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x30, 0x00, // Char 046 (.) + 0x02, 0x06, 0x0C, 0x18, 0x30, 0x60, 0xC0, 0x00, // Char 047 (/) + 0xFC, 0xE6, 0xFE, 0xF6, 0xE6, 0xE6, 0xFE, 0x00, // Char 048 (0) + 0x3C, 0x7C, 0x7C, 0x1C, 0x1C, 0x1C, 0x1C, 0x00, // Char 049 (1) + 0xFE, 0xE6, 0x06, 0x1E, 0xF0, 0xFE, 0xFE, 0x00, // Char 050 (2) + 0xFE, 0xFE, 0x06, 0x3E, 0x06, 0xFE, 0xFE, 0x00, // Char 051 (3) + 0xFC, 0xFC, 0x9C, 0x9C, 0xFE, 0x1C, 0x1C, 0x00, // Char 052 (4) + 0xFE, 0xC0, 0xFE, 0x0E, 0x0E, 0xEE, 0xFE, 0x00, // Char 053 (5) + 0xFE, 0xE0, 0xE0, 0xFE, 0xE6, 0xE6, 0xFE, 0x00, // Char 054 (6) + 0xFE, 0xCE, 0x0E, 0x0E, 0x0E, 0x0E, 0x0E, 0x00, // Char 055 (7) + 0xFE, 0xE6, 0xE6, 0xFE, 0xE6, 0xE6, 0xFE, 0x00, // Char 056 (8) + 0xFE, 0xE6, 0xE6, 0xFE, 0x06, 0x06, 0xFE, 0x00, // Char 057 (9) + 0x00, 0x30, 0x30, 0x00, 0x00, 0x30, 0x30, 0x00, // Char 058 (:) + 0x00, 0x30, 0x30, 0x00, 0x00, 0x30, 0x30, 0x20, // Char 059 (;) + 0x0C, 0x18, 0x30, 0x60, 0x30, 0x18, 0x0C, 0x00, // Char 060 (<) + 0x00, 0x00, 0x7E, 0x70, 0x00, 0x7E, 0x7E, 0x00, // Char 061 (=) + 0x60, 0x30, 0x18, 0x0C, 0x18, 0x30, 0x60, 0x00, // Char 062 (>) + 0xFC, 0xCC, 0x0C, 0x3C, 0x3C, 0x00, 0x3C, 0x00, // Char 063 (?) + 0xFE, 0xC2, 0xDE, 0xD6, 0xDE, 0xC0, 0xFE, 0x00, // Char 064 (@) + 0xFC, 0xEE, 0xE6, 0xFE, 0xE6, 0xE6, 0xE6, 0x00, // Char 065 (A) + 0xFC, 0xE4, 0xE4, 0xFE, 0xE6, 0xE6, 0xFE, 0x00, // Char 066 (B) + 0xFE, 0xE0, 0xE0, 0xE0, 0xE0, 0xFE, 0xFE, 0x00, // Char 067 (C) + 0xFC, 0xE6, 0xE6, 0xE6, 0xE6, 0xE6, 0xFE, 0x00, // Char 068 (D) + 0xFE, 0xE0, 0xE0, 0xFC, 0xE0, 0xFE, 0xFE, 0x00, // Char 069 (E) + 0xFE, 0xE0, 0xE0, 0xFC, 0xFC, 0xE0, 0xE0, 0x00, // Char 070 (F) + 0xFE, 0xE2, 0xE2, 0xE0, 0xEE, 0xE2, 0xFE, 0x00, // Char 071 (G) + 0xE6, 0xE6, 0xE6, 0xFE, 0xE6, 0xE6, 0xE6, 0x00, // Char 072 (H) + 0x38, 0x38, 0x3C, 0x3C, 0x3C, 0x3C, 0x3C, 0x00, // Char 073 (I) + 0x0E, 0x0E, 0x0E, 0x0E, 0x0E, 0xFE, 0xFE, 0x00, // Char 074 (J) + 0xE8, 0xE8, 0xE8, 0xFE, 0xE6, 0xE6, 0xE6, 0x00, // Char 075 (K) + 0xE0, 0xE0, 0xE0, 0xE0, 0xE0, 0xFE, 0xFE, 0x00, // Char 076 (L) + 0xF6, 0xFE, 0xEA, 0xEA, 0xE2, 0xE2, 0xE2, 0x00, // Char 077 (M) + 0xE6, 0xE6, 0xF6, 0xFE, 0xEE, 0xE6, 0xE6, 0x00, // Char 078 (N) + 0x7C, 0xE6, 0xE6, 0xE6, 0xE6, 0xE6, 0xFC, 0x00, // Char 079 (O) + 0xFE, 0xE6, 0xE6, 0xFE, 0xE0, 0xE0, 0xE0, 0x00, // Char 080 (P) + 0xFE, 0xE6, 0xE6, 0xE6, 0xF6, 0xEE, 0xFE, 0x00, // Char 081 (Q) + 0xFE, 0xE6, 0xE6, 0xFC, 0xE6, 0xE6, 0xE6, 0x00, // Char 082 (R) + 0xFE, 0xE6, 0xE0, 0xFE, 0x06, 0xE6, 0xFE, 0x00, // Char 083 (S) + 0x7E, 0x7E, 0x38, 0x38, 0x38, 0x38, 0x38, 0x00, // Char 084 (T) + 0xE6, 0xE6, 0xE6, 0xE6, 0xE6, 0xE6, 0xFE, 0x00, // Char 085 (U) + 0xE6, 0xE6, 0xE6, 0xE6, 0xEE, 0xFC, 0xF8, 0x00, // Char 086 (V) + 0xE2, 0xE2, 0xEA, 0xFE, 0xEE, 0xE6, 0xE2, 0x00, // Char 087 (W) + 0xEE, 0xEE, 0x78, 0x78, 0x78, 0xEE, 0xEE, 0x00, // Char 088 (X) + 0xE6, 0xE6, 0xFE, 0x3E, 0x38, 0x38, 0x38, 0x00, // Char 089 (Y) + 0xFE, 0xE6, 0xEE, 0x18, 0x30, 0xE6, 0xFE, 0x00, // Char 090 (Z) + 0xF8, 0xE0, 0xE0, 0xE0, 0xE0, 0xE0, 0xF8, 0x00, // Char 091 ([) + 0xE0, 0x70, 0x38, 0x1C, 0x0E, 0x06, 0x02, 0x00, // Char 092 (\) + 0x7C, 0x1C, 0x1C, 0x1C, 0x1C, 0x1C, 0x7C, 0x00, // Char 093 (]) + 0x38, 0x7C, 0xEE, 0xC6, 0x00, 0x00, 0x00, 0x00, // Char 094 (^) + 0x00, 0x00, 0x00, 0x00, 0x00, 0xFF, 0xFF, 0x00, // Char 095 (_) + 0x30, 0x30, 0x30, 0x00, 0x00, 0x00, 0x00, 0x00, // Char 096 (`) + 0x00, 0x00, 0x7C, 0x0C, 0x7C, 0x6E, 0x7E, 0x00, // Char 097 (a) + 0xE0, 0xE0, 0x60, 0x7E, 0x66, 0x66, 0x7E, 0x00, // Char 098 (b) + 0x00, 0x00, 0x7E, 0x66, 0x60, 0x66, 0x7E, 0x00, // Char 099 (c) + 0x0C, 0x0C, 0x0C, 0x7C, 0x6C, 0x6E, 0x7E, 0x00, // Char 100 (d) + 0x00, 0x00, 0x7E, 0x66, 0x7E, 0x60, 0x7E, 0x00, // Char 101 (e) + 0x1C, 0x3C, 0x30, 0x38, 0x30, 0x30, 0x30, 0x00, // Char 102 (f) + 0x00, 0x00, 0x7E, 0x62, 0x62, 0x7E, 0x02, 0x7E, // Char 103 (g) + 0x60, 0x60, 0x7E, 0x7E, 0x62, 0x62, 0x62, 0x00, // Char 104 (h) + 0x18, 0x00, 0x18, 0x18, 0x18, 0x18, 0x18, 0x00, // Char 105 (i) + 0x00, 0x0C, 0x00, 0x0C, 0x0C, 0x0C, 0x7C, 0x00, // Char 106 (j) + 0x60, 0x60, 0x66, 0x6E, 0x7C, 0x7E, 0x66, 0x00, // Char 107 (k) + 0x30, 0x30, 0x30, 0x30, 0x30, 0x3C, 0x3C, 0x00, // Char 108 (l) + 0x00, 0x00, 0x7C, 0x7E, 0x6A, 0x6A, 0x6A, 0x00, // Char 109 (m) + 0x00, 0x00, 0x78, 0x6C, 0x64, 0x64, 0x64, 0x00, // Char 110 (n) + 0x00, 0x00, 0x7C, 0x46, 0x46, 0x46, 0x7E, 0x00, // Char 111 (o) + 0x00, 0x00, 0x7E, 0x66, 0x66, 0x7C, 0x60, 0x60, // Char 112 (p) + 0x00, 0x00, 0x7C, 0x6C, 0x6C, 0x3C, 0x0C, 0x0C, // Char 113 (q) + 0x00, 0x00, 0x7E, 0x76, 0x70, 0x70, 0x70, 0x00, // Char 114 (r) + 0x00, 0x00, 0x7C, 0x60, 0x7C, 0x04, 0x7C, 0x00, // Char 115 (s) + 0x00, 0x30, 0x78, 0x30, 0x30, 0x30, 0x38, 0x00, // Char 116 (t) + 0x00, 0x00, 0x6C, 0x6C, 0x6C, 0x6C, 0x3C, 0x00, // Char 117 (u) + 0x00, 0x00, 0x64, 0x64, 0x6C, 0x78, 0x70, 0x00, // Char 118 (v) + 0x00, 0x00, 0x56, 0x56, 0x56, 0x7E, 0x7E, 0x00, // Char 119 (w) + 0x00, 0x00, 0xEE, 0x6C, 0x38, 0x6C, 0xEE, 0x00, // Char 120 (x) + 0x00, 0x00, 0x4C, 0x4C, 0x4C, 0x7C, 0x0C, 0x7C, // Char 121 (y) + 0x00, 0x00, 0x7E, 0x18, 0x30, 0x60, 0x7E, 0x00, // Char 122 (z) + 0x0E, 0x18, 0x18, 0x30, 0x18, 0x18, 0x0E, 0x00, // Char 123 ({) + 0x18, 0x18, 0x18, 0x00, 0x18, 0x18, 0x18, 0x00, // Char 124 (|) + 0xE0, 0x30, 0x30, 0x18, 0x30, 0x30, 0xE0, 0x00, // Char 125 (}) + 0x76, 0xDC, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, // Char 126 (~) + 0x00, 0x10, 0x38, 0x6C, 0xC6, 0xC6, 0xFE, 0x00, // Char 127 (.) + 0x7C, 0xC6, 0xC0, 0xC0, 0xC6, 0x7C, 0x18, 0x70, // Char 128 (.) + 0xCC, 0x00, 0xCC, 0xCC, 0xCC, 0xCC, 0x76, 0x00, // Char 129 (.) + 0x0E, 0x10, 0x7C, 0xC6, 0xFE, 0xC0, 0x7C, 0x00, // Char 130 (.) + 0x7C, 0x82, 0x7C, 0x0C, 0x7C, 0x6E, 0x7E, 0x00, // Char 131 (.) + 0x6C, 0x00, 0x7C, 0x0C, 0x7C, 0x6E, 0x7E, 0x00, // Char 132 (.) + 0xE0, 0x10, 0x78, 0x0C, 0x7C, 0xCC, 0x76, 0x00, // Char 133 (.) + 0x30, 0x30, 0x78, 0x0C, 0x7C, 0xCC, 0x76, 0x00, // Char 134 (.) + 0x00, 0x00, 0x7C, 0xC0, 0xC0, 0x7C, 0x18, 0x70, // Char 135 (.) + 0x7C, 0x82, 0x7C, 0xC6, 0xFE, 0xC0, 0x7C, 0x00, // Char 136 (.) + 0x66, 0x00, 0x7E, 0x66, 0x7E, 0x60, 0x7E, 0x00, // Char 137 (.) + 0xE0, 0x10, 0x7C, 0xC6, 0xFE, 0xC0, 0x7C, 0x00, // Char 138 (.) + 0x66, 0x00, 0x38, 0x38, 0x38, 0x38, 0x3C, 0x00, // Char 139 (.) + 0x7C, 0x82, 0x38, 0x18, 0x18, 0x18, 0x3C, 0x00, // Char 140 (.) + 0xE0, 0x10, 0x38, 0x18, 0x18, 0x18, 0x3C, 0x00, // Char 141 (.) + 0xC6, 0x00, 0x7C, 0xC6, 0xFE, 0xC6, 0xC6, 0x00, // Char 142 (.) + 0x38, 0x38, 0x7C, 0xC6, 0xFE, 0xC6, 0xC6, 0x00, // Char 143 (.) + 0x0E, 0x10, 0xFE, 0x60, 0x78, 0x60, 0xFE, 0x00, // Char 144 (.) + 0x00, 0x00, 0x7C, 0x12, 0x7E, 0xD0, 0x7E, 0x00, // Char 145 (.) + 0x7E, 0xC8, 0xC8, 0xFE, 0xC8, 0xC8, 0xCE, 0x00, // Char 146 (.) + 0x7C, 0x82, 0x7C, 0xC6, 0xC6, 0xC6, 0x7C, 0x00, // Char 147 (.) + 0x66, 0x00, 0x7C, 0x46, 0x46, 0x46, 0x7E, 0x00, // Char 148 (.) + 0xE0, 0x10, 0x7C, 0xC6, 0xC6, 0xC6, 0x7C, 0x00, // Char 149 (.) + 0x7C, 0x82, 0xCC, 0xCC, 0xCC, 0xCC, 0x76, 0x00, // Char 150 (.) + 0xE0, 0x10, 0xCC, 0xCC, 0xCC, 0xCC, 0x76, 0x00, // Char 151 (.) + 0xCC, 0x00, 0xCC, 0xCC, 0xCC, 0x7C, 0x0C, 0xF8, // Char 152 (.) + 0xC6, 0x7C, 0xC6, 0xC6, 0xC6, 0xC6, 0x7C, 0x00, // Char 153 (.) + 0xC6, 0x00, 0xC6, 0xC6, 0xC6, 0xC6, 0x7C, 0x00, // Char 154 (.) + 0x18, 0x7C, 0xD6, 0xD0, 0xD6, 0x7C, 0x18, 0x00, // Char 155 (.) + 0x38, 0x6C, 0x60, 0xF0, 0x60, 0xF2, 0xDC, 0x00, // Char 156 (.) + 0x66, 0x3C, 0x18, 0x7E, 0x18, 0x7E, 0x18, 0x00, // Char 157 (.) + 0xF8, 0xCC, 0xF8, 0xC4, 0xCC, 0xDE, 0xCC, 0x06, // Char 158 (.) + 0x0E, 0x1B, 0x18, 0x3C, 0x18, 0x18, 0xD8, 0x70, // Char 159 (.) + 0x0E, 0x10, 0x78, 0x0C, 0x7C, 0xCC, 0x76, 0x00, // Char 160 (.) + 0x0E, 0x10, 0x38, 0x18, 0x18, 0x18, 0x3C, 0x00, // Char 161 (.) + 0x0E, 0x10, 0x7C, 0xC6, 0xC6, 0xC6, 0x7C, 0x00, // Char 162 (.) + 0x0E, 0x10, 0xCC, 0xCC, 0xCC, 0xCC, 0x76, 0x00, // Char 163 (.) + 0x66, 0x98, 0xDC, 0x66, 0x66, 0x66, 0x66, 0x00, // Char 164 (.) + 0x66, 0x98, 0xE6, 0xF6, 0xDE, 0xCE, 0xC6, 0x00, // Char 165 (.) + 0x38, 0x0C, 0x3C, 0x34, 0x00, 0x7E, 0x00, 0x00, // Char 166 (.) + 0x38, 0x6C, 0x6C, 0x38, 0x00, 0x7C, 0x00, 0x00, // Char 167 (.) + 0x30, 0x00, 0x30, 0x60, 0xC6, 0xC6, 0x7C, 0x00, // Char 168 (.) + 0x00, 0x00, 0x00, 0xFC, 0xC0, 0xC0, 0x00, 0x00, // Char 169 (.) + 0x00, 0x00, 0x00, 0xFC, 0x0C, 0x0C, 0x00, 0x00, // Char 170 (.) + 0xC0, 0xC8, 0xD0, 0xFE, 0x46, 0x8C, 0x1E, 0x00, // Char 171 (.) + 0xC0, 0xC8, 0xD0, 0xEC, 0x5C, 0xBE, 0x0C, 0x00, // Char 172 (.) + 0x18, 0x00, 0x18, 0x18, 0x3C, 0x3C, 0x18, 0x00, // Char 173 (.) + 0x00, 0x36, 0x6C, 0xD8, 0x6C, 0x36, 0x00, 0x00, // Char 174 (.) + 0x00, 0xD8, 0x6C, 0x36, 0x6C, 0xD8, 0x00, 0x00, // Char 175 (.) + 0x22, 0x88, 0x22, 0x88, 0x22, 0x88, 0x22, 0x88, // Char 176 (.) + 0x55, 0xAA, 0x55, 0xAA, 0x55, 0xAA, 0x55, 0xAA, // Char 177 (.) + 0xDB, 0x77, 0xDB, 0xEE, 0xDB, 0x77, 0xDB, 0xEE, // Char 178 (.) + 0x18, 0x18, 0x18, 0x18, 0x18, 0x18, 0x18, 0x18, // Char 179 (.) + 0x18, 0x18, 0x18, 0x18, 0xF8, 0x18, 0x18, 0x18, // Char 180 (.) + 0x18, 0x18, 0xF8, 0x18, 0xF8, 0x18, 0x18, 0x18, // Char 181 (.) + 0x36, 0x36, 0x36, 0x36, 0xF6, 0x36, 0x36, 0x36, // Char 182 (.) + 0x00, 0x00, 0x00, 0x00, 0xFE, 0x36, 0x36, 0x36, // Char 183 (.) + 0x00, 0x00, 0xF8, 0x18, 0xF8, 0x18, 0x18, 0x18, // Char 184 (.) + 0x36, 0x36, 0xF6, 0x06, 0xF6, 0x36, 0x36, 0x36, // Char 185 (.) + 0x36, 0x36, 0x36, 0x36, 0x36, 0x36, 0x36, 0x36, // Char 186 (.) + 0x00, 0x00, 0xFE, 0x06, 0xF6, 0x36, 0x36, 0x36, // Char 187 (.) + 0x36, 0x36, 0xF6, 0x06, 0xFE, 0x00, 0x00, 0x00, // Char 188 (.) + 0x36, 0x36, 0x36, 0x36, 0xFE, 0x00, 0x00, 0x00, // Char 189 (.) + 0x18, 0x18, 0xF8, 0x18, 0xF8, 0x00, 0x00, 0x00, // Char 190 (.) + 0x00, 0x00, 0x00, 0x00, 0xF8, 0x18, 0x18, 0x18, // Char 191 (.) + 0x18, 0x18, 0x18, 0x18, 0x1F, 0x00, 0x00, 0x00, // Char 192 (.) + 0x18, 0x18, 0x18, 0x18, 0xFF, 0x00, 0x00, 0x00, // Char 193 (.) + 0x00, 0x00, 0x00, 0x00, 0xFF, 0x18, 0x18, 0x18, // Char 194 (.) + 0x18, 0x18, 0x18, 0x18, 0x1F, 0x18, 0x18, 0x18, // Char 195 (.) + 0x00, 0x00, 0x00, 0x00, 0xFF, 0x00, 0x00, 0x00, // Char 196 (.) + 0x18, 0x18, 0x18, 0x18, 0xFF, 0x18, 0x18, 0x18, // Char 197 (.) + 0x18, 0x18, 0x1F, 0x18, 0x1F, 0x18, 0x18, 0x18, // Char 198 (.) + 0x36, 0x36, 0x36, 0x36, 0x37, 0x36, 0x36, 0x36, // Char 199 (.) + 0x36, 0x36, 0x37, 0x30, 0x3F, 0x00, 0x00, 0x00, // Char 200 (.) + 0x00, 0x00, 0x3F, 0x30, 0x37, 0x36, 0x36, 0x36, // Char 201 (.) + 0x36, 0x36, 0xF7, 0x00, 0xFF, 0x00, 0x00, 0x00, // Char 202 (.) + 0x00, 0x00, 0xFF, 0x00, 0xF7, 0x36, 0x36, 0x36, // Char 203 (.) + 0x36, 0x36, 0x37, 0x30, 0x37, 0x36, 0x36, 0x36, // Char 204 (.) + 0x00, 0x00, 0xFF, 0x00, 0xFF, 0x00, 0x00, 0x00, // Char 205 (.) + 0x36, 0x36, 0xF7, 0x00, 0xF7, 0x36, 0x36, 0x36, // Char 206 (.) + 0x18, 0x18, 0xFF, 0x00, 0xFF, 0x00, 0x00, 0x00, // Char 207 (.) + 0x36, 0x36, 0x36, 0x36, 0xFF, 0x00, 0x00, 0x00, // Char 208 (.) + 0x00, 0x00, 0xFF, 0x00, 0xFF, 0x18, 0x18, 0x18, // Char 209 (.) + 0x00, 0x00, 0x00, 0x00, 0xFF, 0x36, 0x36, 0x36, // Char 210 (.) + 0x36, 0x36, 0x36, 0x36, 0x3F, 0x00, 0x00, 0x00, // Char 211 (.) + 0x18, 0x18, 0x1F, 0x18, 0x1F, 0x00, 0x00, 0x00, // Char 212 (.) + 0x00, 0x00, 0x1F, 0x18, 0x1F, 0x18, 0x18, 0x18, // Char 213 (.) + 0x00, 0x00, 0x00, 0x00, 0x3F, 0x36, 0x36, 0x36, // Char 214 (.) + 0x36, 0x36, 0x36, 0x36, 0xFF, 0x36, 0x36, 0x36, // Char 215 (.) + 0x18, 0x18, 0xFF, 0x18, 0xFF, 0x18, 0x18, 0x18, // Char 216 (.) + 0x18, 0x18, 0x18, 0x18, 0xF8, 0x00, 0x00, 0x00, // Char 217 (.) + 0x00, 0x00, 0x00, 0x00, 0x1F, 0x18, 0x18, 0x18, // Char 218 (.) + 0xFF, 0xFF, 0xFF, 0xFF, 0xFF, 0xFF, 0xFF, 0xFF, // Char 219 (.) + 0x00, 0x00, 0x00, 0x00, 0xFF, 0xFF, 0xFF, 0xFF, // Char 220 (.) + 0xF0, 0xF0, 0xF0, 0xF0, 0xF0, 0xF0, 0xF0, 0xF0, // Char 221 (.) + 0x0F, 0x0F, 0x0F, 0x0F, 0x0F, 0x0F, 0x0F, 0x0F, // Char 222 (.) + 0xFF, 0xFF, 0xFF, 0xFF, 0x00, 0x00, 0x00, 0x00, // Char 223 (.) + 0x00, 0x00, 0x74, 0xCC, 0xC8, 0xDC, 0x76, 0x00, // Char 224 (.) + 0x78, 0xCC, 0xD8, 0xCC, 0xC6, 0xC6, 0xDC, 0x40, // Char 225 (.) + 0xFE, 0x62, 0x60, 0x60, 0x60, 0x60, 0xF0, 0x00, // Char 226 (.) + 0x00, 0x02, 0x7E, 0xEC, 0x6C, 0x6C, 0x48, 0x00, // Char 227 (.) + 0xFE, 0x62, 0x30, 0x18, 0x30, 0x62, 0xFE, 0x00, // Char 228 (.) + 0x00, 0x00, 0x7E, 0xD0, 0xC8, 0xC8, 0x70, 0x00, // Char 229 (.) + 0x00, 0x00, 0xCC, 0xCC, 0xCC, 0xCC, 0xF8, 0x80, // Char 230 (.) + 0x00, 0x00, 0x7E, 0xD8, 0x18, 0x18, 0x10, 0x00, // Char 231 (.) + 0x38, 0x10, 0x7C, 0xD6, 0xD6, 0x7C, 0x10, 0x38, // Char 232 (.) + 0x7C, 0xC6, 0xC6, 0xFE, 0xC6, 0xC6, 0x7C, 0x00, // Char 233 (.) + 0x7C, 0xC6, 0xC6, 0xC6, 0x6C, 0x28, 0xEE, 0x00, // Char 234 (.) + 0x3C, 0x22, 0x18, 0x7C, 0xCC, 0xCC, 0x78, 0x00, // Char 235 (.) + 0x00, 0x00, 0x66, 0x99, 0x99, 0x66, 0x00, 0x00, // Char 236 (.) + 0x00, 0x06, 0x7C, 0x9E, 0xF2, 0x7C, 0xC0, 0x00, // Char 237 (.) + 0x00, 0x00, 0x7C, 0xC0, 0xF8, 0xC0, 0x7C, 0x00, // Char 238 (.) + 0x00, 0x7C, 0xC6, 0xC6, 0xC6, 0xC6, 0xC6, 0x00, // Char 239 (.) + 0x00, 0xFE, 0x00, 0xFE, 0x00, 0xFE, 0x00, 0x00, // Char 240 (.) + 0x18, 0x18, 0x7E, 0x18, 0x18, 0x00, 0x7E, 0x00, // Char 241 (.) + 0x30, 0x18, 0x0C, 0x18, 0x30, 0x00, 0x7C, 0x00, // Char 242 (.) + 0x18, 0x30, 0x60, 0x30, 0x18, 0x00, 0x7C, 0x00, // Char 243 (.) + 0x0E, 0x1B, 0x1B, 0x18, 0x18, 0x18, 0x18, 0x18, // Char 244 (.) + 0x18, 0x18, 0x18, 0x18, 0x18, 0xD8, 0xD8, 0x70, // Char 245 (.) + 0x00, 0x18, 0x00, 0x7E, 0x00, 0x18, 0x00, 0x00, // Char 246 (.) + 0x00, 0x76, 0xDC, 0x00, 0x76, 0xDC, 0x00, 0x00, // Char 247 (.) + 0x38, 0x6C, 0x38, 0x00, 0x00, 0x00, 0x00, 0x00, // Char 248 (.) + 0x00, 0x00, 0x00, 0x18, 0x18, 0x00, 0x00, 0x00, // Char 249 (.) + 0x00, 0x00, 0x00, 0x00, 0x18, 0x00, 0x00, 0x00, // Char 250 (.) + 0x0F, 0x0C, 0x0C, 0x0C, 0xEC, 0x6C, 0x3C, 0x00, // Char 251 (.) + 0xD8, 0x6C, 0x6C, 0x6C, 0x00, 0x00, 0x00, 0x00, // Char 252 (.) + 0xF0, 0x30, 0xC0, 0xF0, 0x00, 0x00, 0x00, 0x00, // Char 253 (.) + 0x00, 0x00, 0x3C, 0x3C, 0x3C, 0x3C, 0x00, 0x00, // Char 254 (.) + 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00 // Char 255 (.) +}; + +#endif diff --git a/font_patch/info.txt b/font_patch/info.txt new file mode 100644 index 0000000..51a638d --- /dev/null +++ b/font_patch/info.txt @@ -0,0 +1 @@ +libdragon: include/font.h \ No newline at end of file diff --git a/header.ed64 b/header.ed64 new file mode 100644 index 0000000..988a7e0 Binary files /dev/null and b/header.ed64 differ diff --git a/ini.c b/ini.c new file mode 100644 index 0000000..a100222 --- /dev/null +++ b/ini.c @@ -0,0 +1,301 @@ +/* inih -- simple .INI file parser + +inih is released under the New BSD license (see LICENSE.txt). Go to the project +home page for more info: + +http://code.google.com/p/inih/ + +*/ + +#include +#include +#include + + +#include "ini.h" + +#if !INI_USE_STACK +#include +#endif + +#define MAX_SECTION 50 +#define MAX_NAME 50 + + +/* Strip whitespace chars off end of given string, in place. Return s. */ +static char* rstrip(char* s) +{ + char* p = s + strlen(s); + while (p > s && isspace((unsigned char)(*--p))) + *p = '\0'; + return s; +} + +/* Return pointer to first non-whitespace char in given string. */ +static char* lskip(const char* s) +{ + while (*s && isspace((unsigned char)(*s))) + s++; + return (char*)s; +} + +/* Return pointer to first char c or ';' comment in given string, or pointer to + null at end of string if neither found. ';' must be prefixed by a whitespace + character to register as a comment. */ +static char* find_char_or_comment(const char* s, char c) +{ + int was_whitespace = 0; + while (*s && *s != c && !(was_whitespace && *s == ';')) { + was_whitespace = isspace((unsigned char)(*s)); + s++; + } + return (char*)s; +} + +/* Version of strncpy that ensures dest (size bytes) is null-terminated. */ +static char* strncpy0(char* dest, const char* src, size_t size) +{ + strncpy(dest, src, size); + dest[size - 1] = '\0'; + return dest; +} + + +/* See documentation in header file. */ +int ini_parse_str(char* ini_string, + int (*handler)(void*, const char*, const char*, + const char*), + void* user) +{ + /* Uses a fair bit of stack (use heap instead if you need to) */ +#if INI_USE_STACK + char line[INI_MAX_LINE]; +#else + char* line; +#endif + char section[MAX_SECTION] = ""; + char prev_name[MAX_NAME] = ""; + + char* start; + char* end; + char* name; + char* value; + int lineno = 0; + int error = 0; + +#if !INI_USE_STACK + line = (char*)malloc(INI_MAX_LINE); + if (!line) { + return -2; + } +#endif + + + int i=1; + char *ptr; + +// initialisieren und ersten Abschnitt erstellen + ptr = strtok(ini_string, "\n"); + + + /* Scan through file line by line */ + while(ptr != NULL) { + + sprintf(line, "%s\n",ptr); + ptr = strtok(NULL, "\n"); + // while (line=gets( ini_string) != NULL) { + lineno++; + + start = line; +#if INI_ALLOW_BOM + if (lineno == 1 && (unsigned char)start[0] == 0xEF && + (unsigned char)start[1] == 0xBB && + (unsigned char)start[2] == 0xBF) { + start += 3; + } +#endif + start = lskip(rstrip(start)); + + if (*start == ';' || *start == '#') { + /* Per Python ConfigParser, allow '#' comments at start of line */ + } +#if INI_ALLOW_MULTILINE + else if (*prev_name && *start && start > line) { + /* Non-black line with leading whitespace, treat as continuation + of previous name's value (as per Python ConfigParser). */ + if (!handler(user, section, prev_name, start) && !error) + error = lineno; + } +#endif + else if (*start == '[') { + /* A "[section]" line */ + end = find_char_or_comment(start + 1, ']'); + if (*end == ']') { + *end = '\0'; + strncpy0(section, start + 1, sizeof(section)); + *prev_name = '\0'; + } + else if (!error) { + /* No ']' found on section line */ + error = lineno; + } + } + else if (*start && *start != ';') { + /* Not a comment, must be a name[=:]value pair */ + end = find_char_or_comment(start, '='); + if (*end != '=') { + end = find_char_or_comment(start, ':'); + } + if (*end == '=' || *end == ':') { + *end = '\0'; + name = rstrip(start); + value = lskip(end + 1); + end = find_char_or_comment(value, '\0'); + if (*end == ';') + *end = '\0'; + rstrip(value); + + /* Valid name[=:]value pair found, call handler */ + strncpy0(prev_name, name, sizeof(prev_name)); + if (!handler(user, section, name, value) && !error) + error = lineno; + } + else if (!error) { + /* No '=' or ':' found on name[=:]value line */ + error = lineno; + } + } + } + +#if !INI_USE_STACK + free(line); +#endif + + return error; +} + + + +/* See documentation in header file. */ +int ini_parse_file(FILE* file, + int (*handler)(void*, const char*, const char*, + const char*), + void* user) +{ + /* Uses a fair bit of stack (use heap instead if you need to) */ +#if INI_USE_STACK + char line[INI_MAX_LINE]; +#else + char* line; +#endif + char section[MAX_SECTION] = ""; + char prev_name[MAX_NAME] = ""; + + char* start; + char* end; + char* name; + char* value; + int lineno = 0; + int error = 0; + +#if !INI_USE_STACK + line = (char*)malloc(INI_MAX_LINE); + if (!line) { + return -2; + } +#endif + + /* Scan through file line by line */ + while (fgets(line, INI_MAX_LINE, file) != NULL) { + lineno++; + + start = line; +#if INI_ALLOW_BOM + if (lineno == 1 && (unsigned char)start[0] == 0xEF && + (unsigned char)start[1] == 0xBB && + (unsigned char)start[2] == 0xBF) { + start += 3; + } +#endif + start = lskip(rstrip(start)); + + if (*start == ';' || *start == '#') { + /* Per Python ConfigParser, allow '#' comments at start of line */ + } +#if INI_ALLOW_MULTILINE + else if (*prev_name && *start && start > line) { + /* Non-black line with leading whitespace, treat as continuation + of previous name's value (as per Python ConfigParser). */ + if (!handler(user, section, prev_name, start) && !error) + error = lineno; + } +#endif + else if (*start == '[') { + /* A "[section]" line */ + end = find_char_or_comment(start + 1, ']'); + if (*end == ']') { + *end = '\0'; + strncpy0(section, start + 1, sizeof(section)); + *prev_name = '\0'; + } + else if (!error) { + /* No ']' found on section line */ + error = lineno; + } + } + else if (*start && *start != ';') { + /* Not a comment, must be a name[=:]value pair */ + end = find_char_or_comment(start, '='); + if (*end != '=') { + end = find_char_or_comment(start, ':'); + } + if (*end == '=' || *end == ':') { + *end = '\0'; + name = rstrip(start); + value = lskip(end + 1); + end = find_char_or_comment(value, '\0'); + if (*end == ';') + *end = '\0'; + rstrip(value); + + /* Valid name[=:]value pair found, call handler */ + strncpy0(prev_name, name, sizeof(prev_name)); + if (!handler(user, section, name, value) && !error) + error = lineno; + } + else if (!error) { + /* No '=' or ':' found on name[=:]value line */ + error = lineno; + } + } + } + +#if !INI_USE_STACK + free(line); +#endif + + return error; +} + +/* See documentation in header file. */ +int ini_parse(const char* filename, + int (*handler)(void*, const char*, const char*, const char*), + void* user) +{ + FILE* file; + int error; + + // file = fopen(filename, "r"); + + + fputs(filename, file); + + + + + if (!file) + return -1; + error = ini_parse_file(file, handler, user); + fclose(file); + return error; +} diff --git a/ini.h b/ini.h new file mode 100644 index 0000000..10965c2 --- /dev/null +++ b/ini.h @@ -0,0 +1,77 @@ +/* inih -- simple .INI file parser + +inih is released under the New BSD license (see LICENSE.txt). Go to the project +home page for more info: + +http://code.google.com/p/inih/ + +*/ + +#ifndef __INI_H__ +#define __INI_H__ + +/* Make this header file easier to include in C++ code */ +#ifdef __cplusplus +extern "C" { +#endif + +#include + +/* Parse given INI-style file. May have [section]s, name=value pairs + (whitespace stripped), and comments starting with ';' (semicolon). Section + is "" if name=value pair parsed before any section heading. name:value + pairs are also supported as a concession to Python's ConfigParser. + + For each name=value pair parsed, call handler function with given user + pointer as well as section, name, and value (data only valid for duration + of handler call). Handler should return nonzero on success, zero on error. + + Returns 0 on success, line number of first error on parse error (doesn't + stop on first error), -1 on file open error, or -2 on memory allocation + error (only when INI_USE_STACK is zero). +*/ +int ini_parse(const char* filename, + int (*handler)(void* user, const char* section, + const char* name, const char* value), + void* user); + +/* Same as ini_parse(), but takes a FILE* instead of filename. This doesn't + close the file when it's finished -- the caller must do that. */ +int ini_parse_file(FILE* file, + int (*handler)(void* user, const char* section, + const char* name, const char* value), + void* user); + +int ini_parse_str(char* ini_string, + int (*handler)(void*, const char*, const char*, + const char*), + void* user); + +/* Nonzero to allow multi-line value parsing, in the style of Python's + ConfigParser. If allowed, ini_parse() will call the handler with the same + name for each subsequent line parsed. */ +#ifndef INI_ALLOW_MULTILINE +#define INI_ALLOW_MULTILINE 1 +#endif + +/* Nonzero to allow a UTF-8 BOM sequence (0xEF 0xBB 0xBF) at the start of + the file. See http://code.google.com/p/inih/issues/detail?id=21 */ +#ifndef INI_ALLOW_BOM +#define INI_ALLOW_BOM 1 +#endif + +/* Nonzero to use stack, zero to use heap (malloc/free). */ +#ifndef INI_USE_STACK +#define INI_USE_STACK 1 +#endif + +/* Maximum line length for any line in INI file. */ +#ifndef INI_MAX_LINE +#define INI_MAX_LINE 200 +#endif + +#ifdef __cplusplus +} +#endif + +#endif /* __INI_H__ */ diff --git a/mem.c b/mem.c new file mode 100644 index 0000000..87ed68b --- /dev/null +++ b/mem.c @@ -0,0 +1,160 @@ + +#include "types.h" +#include "everdrive.h" +#include "sys.h" + +u8 spi_dma; +#include "errors.h" +#include "mem.h" + +u8 memSpiReadDma(void *dst, u16 slen); +u8 memSpiReadPio(void *dst, u16 slen); +u8 mem_buff[512]; + +void memSpiSetDma(u8 mode) { + spi_dma = mode; +} + +void memcopy(void *src, void *dst, u16 len) { + + u8 *s = (u8 *) src; + u8 *d = (u8 *) dst; + while (len--)*d++ = *s++; +} + +void memfill(void *dst, u8 val, u16 len) { + u8 *d = (u8 *) dst; + while (len--)*d++ = val; +} + + +// buff len +u8 memSpiRead(void *dst, u16 slen) { + + u8 copy_to_rom = 0; + u32 addr = (u32) dst; //if ROM_ADDR 0xb0000000 + if (addr >= ROM_ADDR && addr < ROM_END_ADDR)copy_to_rom = 1; + + //if (copy_to_rom || spi_dma) { + if ((copy_to_rom || spi_dma) && addr % 4 == 0) { + return memSpiReadDma(dst, slen); + } else { + return memSpiReadPio(dst, slen); + + } + +} + +u8 memSpiReadPio(void *dst, u16 slen) { + + u16 i; + u16 u; + u8 *ptr8 = (u8 *) dst; + + + + for (i = 0; i < slen; i++) { + + evd_SDdatReadMode(1); + for (u = 0; u < 65535; u++)if ((evd_SPI(0xff) & 0xf1) == 0xf0)break; + evd_SDdatReadMode(0); + if (u == 65535) { + evd_spiSSOff(); + return DISK_RD_FE_TOUT; + } + + for (u = 0; u < 512; u++)*ptr8++ = evd_SPI(0xff); + + u = evd_isSDMode() ? 8 : 2; + + while (u--) { + // console_printf("XRC: %02X", evd_SPI(0xff)); + //console_printf("%02X\n", evd_SPI(0xff)); + u--; + evd_SPI(0xff); + evd_SPI(0xff); + } + //evd_SPI(0xff); + //evd_SPI(0xff); + } + + + return 0; +} + +u8 memSpiReadDma(void *dst, u16 slen) { + + u8 resp = 0; + u8 copy_to_rom = 0; + u32 addr = (u32) dst; + + evd_SDdatReadMode(0); + // console_printf("dma\n"); + if (addr >= ROM_ADDR && addr < ROM_END_ADDR)copy_to_rom = 1; + + if (copy_to_rom) { + + return evd_mmcReadToCart(addr, slen); + + } else { + + resp = evd_mmcReadToCart(0, slen); + dma_read_s(dst, ROM_ADDR, slen * 512); + } + + + return resp; +} + +u8 memSpiWrite(void *src) { + + u16 i; + + u8 *ptr8 = (u8 *) src; + + if ((u32) src >= ROM_ADDR && (u32) src < ROM_END_ADDR) { + dma_read_s(mem_buff, (u32) src, 512); + for (i = 0; i < 512; i++)mem_spi(mem_buff[i]); + } else { + for (i = 0; i < 512; i++)mem_spi(*ptr8++); + } + + + return 0; +} + +void memSpiBusy() { + + while (evd_isSpiBusy()); + +} + +void memRomWrite32(u32 addr, u32 val) { + + vu32 *ptr = (u32 *) (addr + ROM_ADDR); + vu8 tmp; + + tmp = *ptr; + *ptr = val; + tmp = *ptr; +} + +u32 memRomRead32(u32 addr) { + + vu32 *ptr = (u32 *) (addr + ROM_ADDR); + vu8 tmp; + vu32 val; + + tmp = *ptr; + val = *ptr; + + return val; +} + + +/* +u8 mem_spi(u8 dat) { + return evd_SPI(dat); +} + + */ diff --git a/mem.h b/mem.h new file mode 100644 index 0000000..89f0a28 --- /dev/null +++ b/mem.h @@ -0,0 +1,39 @@ +/* + * File: mem.h + * Author: krik + * + * Created on 2 Èþíü 2011 ã., 4:07 + */ + +#ifndef _MEM_H +#define _MEM_H +#include "types.h" + +#define SPI_SPEED_INIT 2 +#define SPI_SPEED_25 1 +#define SPI_SPEED_50 0 + +#define mem_spi evd_SPI +#define memSpiSetSpeed evd_setSpiSpeed +#define memSpiIsBusy evd_isSpiBusy +#define memSpiSSOff evd_spiSSOff +#define memSpiSSOn evd_spiSSOn + + +void memSpiSSOn(); +void memSpiSSOff(); +void memSpiBusy(); +u8 memSpiIsBusy(); +void memSpiSetSpeed(u8 speed); +void spiReadBlock(void *dat); +void spiWriteBlock(void *dat); +u8 memSpiRead(void *dst, u16 slen); +u8 memSpiWrite(void *src); +//u8 mem_spi(u8 dat); +void memfill(void *dst, u8 val, u16 len); +void memcopy(void *src, void *dst, u16 len); +void memSpiSetDma(u8 mode); +void memRomWrite32(u32 addr, u32 val); +u32 memRomRead32(u32 addr); +#endif /* _MEM_H */ + diff --git a/menu.c b/menu.c new file mode 100644 index 0000000..5c3208e --- /dev/null +++ b/menu.c @@ -0,0 +1,5784 @@ +/* + * ALT64 Menu v0.1.8.6 - 2013 + * a menu for the Everdrive64 + * based on libdragon, Neo64Menu, ED64IO, libn64-hkz, libmikmod + * by saturnu + */ + + +#include +#include +#include +#include +#include +#include + +//libdragon n64 lib +#include + +//everdrive system libs +#include "types.h" +#include "sys.h" +#include "everdrive.h" +#include "everdrive.h" + +//filesystem +#include "disk.h" +#include "fat.h" + +//utils +#include "utils.h" + +//config file on sd +#include "ini.h" +#include "strlib.h" + +//menu header +#include "menu.h" + +//sound +#include + +//cheats +struct gscEntry { + char *description; + char *gscodes; + u16 count; + u16 state; + u16 mask; + u16 value; +}; +typedef struct gscEntry gscEntry_t; + +gscEntry_t gGSCodes[256]; + +#ifdef USE_TRUETYPE +#define STB_TRUETYPE_IMPLEMENTATION +#include "stb_truetype.h" + +struct glyph { + int xoff; + int yoff; + int adv; + int lsb; + float scale; + unsigned int color; + unsigned char *alpha; + sprite_t sprite; +}; +typedef struct glyph glyph_t; + +glyph_t sans[96]; +glyph_t serif[96]; +#endif + + +typedef struct +{ + uint32_t type; + uint32_t color; + char filename[MAX_FILENAME_LEN+1]; +} direntry_t; + + +//ini file +typedef struct +{ + int version; + const char* name; + char* background_image; + char* border_color_1; + char* border_color_2; + char* box_color; + char* selection_color; + char* list_font_color; + char* list_dir_font_color; + char* selection_font_color; + char* mempak_path; + char* save_path; + int sound_on; + int page_display; + int tv_mode; + int quick_boot; + int enable_colored_list; + int ext_type; + int cd_behaviour; + int scroll_behaviour; + int text_offset; + int hide_sysfolder; + int sd_speed; + +} configuration; + + + + + +volatile u32 *romaddr_ptr = (u32 *) ROM_ADDR; +unsigned int gBootCic=CIC_6102; +u8 debug=DBG_MODE; + +static uint8_t mempak_data[128 * 256]; +static void *bg_buffer; +void *__safe_bg_buffer; + +#define __get_buffer( x ) __safe_buffer[(x)-1] +extern uint32_t __bitdepth; +extern uint32_t __width; +extern uint32_t __height; +extern void *__safe_buffer[]; + +char firmware_str[64]; +int firm_found=0; +char rom_config[10]; + +display_context_t lockVideo(int wait); + +int select_mode = 0; +int toplist_reload=1; +int toplist_cursor=0; +char toplist15[15][256]; + +int fat_initialized=0; +int exit_ok = 0; +int boot_cic=0; +int boot_save=0; +int cursor_line=0; +int cursor_lastline=0; +u16 cursor_history[32]; +u16 cursor_history_pos=0; +u8 empty=0; +u8 playing=0; +u8 gb_load_y=0; + +//start with filebrowser menu key settings +int input_mapping=1; +/* 1=filemanager + * 2=mempak-menu + * 3=charinput + * 4=rom loaded + * 5=format mpk + * 6=restore mpk + * 7=rom config box + * 8=toplist + * 9=mpk choice + * 10=mpk quick-backup + * 11=mp3 + * 98= A/B abort screen + */ + + +//holds the string of the character input screen result +int text_input_on=0; + +//char input vars +int x,y,position,set; + +unsigned char input_text[32]; +uint32_t chr_forecolor; +uint32_t chr_backcolor; + + +//save type still set - save todo after reboot +int save_reboot=0; + +//cart id from rom header +unsigned char cartID[4]; +char curr_dirname[64]; +char pwd[64]; +char rom_filename[128]; + +u32 rom_buff[128]; //rom buffer +u8 *rom_buff8; //rom buffer + +u8 *firmware; +u8 gbload=0; + +int cheats_on=0; +int checksum_fix_on=0; +short int gCheats = 0; /* 0 = off, 1 = select, 2 = all */ +short int force_tv = 0; +short int boot_country=0; + + +static resolution_t res = RESOLUTION_320x240; + +//background sprites +sprite_t *loadPng(u8 *png_filename); +sprite_t *background; //background +sprite_t *background0; //splash screen + +//config file theme settings +u32 border_color_1=0xFFFFFFFF; //hex 0xRRGGBBAA AA=transparenxy +u32 border_color_2=0x3F3F3FFF; +u32 box_color=0x00000080; +u32 selection_color=0x6495ED60; +u32 selection_font_color=0xFFFFFFFF; +u32 list_font_color=0xFFFFFFFF; +u32 list_dir_font_color=0xFFFFFFFF; + +char* border_color_1_s; +char* border_color_2_s; +char* box_color_s; +char* selection_color_s; +char* selection_font_color_s; +char* list_font_color_s; +char* list_dir_font_color_s; + +char* mempak_path; +char* save_path; + + +u8 sound_on=0; +u8 page_display=0; +int text_offset=0; +u8 tv_mode=0; // 1=ntsc 2=pal 3=mpal 0=default automatic +u8 quick_boot=0; +u8 enable_colored_list=0; +u8 cd_behaviour=0; //0=first entry 1=last entry +u8 scroll_behaviour=0; //1=classic 0=new page-system +u8 ext_type=0; //0=classic 1=org os +u8 sd_speed=1; // 1=25Mhz 2=50Mhz +u8 hide_sysfolder=0; +char* background_image; + +//toplist helper +int list_pos_backup[3]; +char list_pwd_backup[256]; + +char dirz[512] = "rom://"; + +short int gCursorX; +short int gCursorY; + +int count = 0; +int page = 0; +int cursor = 0; +direntry_t *list; + + + + +int filesize( FILE *pFile ) +{ + fseek( pFile, 0, SEEK_END ); + int lSize = ftell( pFile ); + rewind( pFile ); + + return lSize; +} + +sprite_t *read_sprite( const char * const spritename ) +{ + FILE *fp = fopen( spritename, "r" ); + + if( fp ) + { + sprite_t *sp = malloc( filesize( fp ) ); + fread( sp, 1, filesize( fp ), fp ); + fclose( fp ); + + return sp; + } + else + { + return 0; + } +} + + +void drawSelection(display_context_t disp,int p){ + + +int s_x=23+(text_offset*8); + + if(scroll_behaviour){ + + + if(select_mode){ + + if(cursor_lastline>cursor && cursor_line > 0) { + cursor_line--; + } + + if(cursor_lastlinetype == DT_DIR && second->type != DT_DIR) + { + /* First should be first */ + return -1; + } + + if(first->type != DT_DIR && second->type == DT_DIR) + { + /* First should be second */ + return 1; + } + + return strcmp(first->filename, second->filename); +} + +int compare_int (const void *a, const void *b) +{ + const int *ia = (const int *)a; // casting pointer types + const int *ib = (const int *)b; + return *ia - *ib; +} + +int compare_int_reverse (const void *a, const void *b) +{ + const int *ia = (const int *)a; // casting pointer types + const int *ib = (const int *)b; + return *ib - *ia; +} + +/* +direntry_t *populate_dir(int *count) +{ + //* Grab a slot + direntry_t *list = malloc(sizeof(direntry_t)); + *count = 1; + + //* Grab first + dir_t buf; + int ret = dir_findfirst(dirz, &buf); //replace + + if( ret != 0 ) + { + //* Free stuff + free(list); + *count = 0; + + //* Dir was bad! + return 0; + } + + //* Copy in loop + while( ret == 0 ) + { + list[(*count)-1].type = buf.d_type; + strcpy(list[(*count)-1].filename, buf.d_name); + + // Grab next + ret = dir_findnext(dirz,&buf); //replace + + if( ret == 0 ) + { + (*count)++; + list = realloc(list, sizeof(direntry_t) * (*count)); + } + } + + if(*count > 0) + { + /*Should sort! + qsort(list, *count, sizeof(direntry_t), compare); + } + + return list; +} +*/ + +void free_dir(direntry_t *dirz) +{ + if(dirz) { free(dirz); } +} + +void new_scroll_pos(int *cursor, int *page, int max, int count) +{ + /* Make sure windows too small can be calculated right */ + if(max > count) { max = count; } + + /* Bounds checking */ + if(*cursor >= count) + { + *cursor = count-1; + } + + if(*cursor < 0) + { + *cursor = 0; + } + + /* Scrolled up? */ + if(*cursor < *page) + { + *page = *cursor; + return; + } + + /* Scrolled down/ */ + if(*cursor >= (*page + max)) + { + *page = (*cursor - max) + 1; + return; + } + + /* Nothing here, should be good! */ +} + +void display_dir(direntry_t *list, int cursor, int page, int max, int count, display_context_t disp) +{ + + //system color + uint32_t forecolor=0; + uint32_t forecolor_menu=0; + uint32_t backcolor; + backcolor = graphics_make_color(0x00, 0x00, 0x00, 0xFF); //bg + forecolor_menu = graphics_make_color(0xFF, 0xFF, 0xFF, 0xFF); //fg + + graphics_set_color (list_font_color, backcolor); + + u8 c_pos[MAX_LIST+1]; + int c_pos_counter=0; + + c_pos[c_pos_counter++]=0; + + u8 c_dirname[64]; + + if(page_display){ + + u8 pi = page/20; + u8 ci=0; + + if(count%20==0) + ci = (count-1)/20; + else + ci = count/20; + sprintf(c_dirname, "%i/%i SD:/%s", pi+1, ci+1, pwd); + } + else{ + sprintf(c_dirname, "SD:/%s", pwd); + } + char sel_str[128]; + + + + //printText("Directory: ", 3, 4, disp); + printText(c_dirname, 3, 4, disp); + + int firstrun=1; + /* Page bounds checking */ + if(max > count) //cound = dir ectrys starting at 1 + { + max = count; + } + + /* Cursor bounds checking */ + if(cursor >= (page + max)) + { + cursor = (page + max) - 1; + } + + if(cursor < page) + { + cursor = page; + } + + if( max == 0 ) + { + printText("dir empty...", 3, 6, disp); + sprintf(sel_str, "dir empty..."); + empty=1; + }else{ + empty=0; + } + + + + //strcpy(list[(*count)-1].filename, buf.d_name); + + //last page anti ghosting entries + if(page == (count/20)*20) + max=count%20; + + for(int i = page; i < (page + max); i++) //from page to page + max + { + + if(list[i].type == DT_DIR) + { + char tmpdir[(CONSOLE_WIDTH-5)+1]; + strncpy(tmpdir, list[i].filename, CONSOLE_WIDTH-5); + + tmpdir[CONSOLE_WIDTH-5] = 0; + + + + //char dir_str[128]; + char *dir_str; + dir_str = malloc(slen(tmpdir)+3); + + + if(i == cursor){ + sprintf(dir_str, " [%s]", tmpdir); + sprintf(sel_str, " [%s]", tmpdir); + // if(cd_behaviour==0) + if(scroll_behaviour) + drawSelection(disp,i); + + c_pos[c_pos_counter++]=1; + + } + else{ + sprintf(dir_str, "[%s]", tmpdir); + c_pos[c_pos_counter++]=0; + } + graphics_set_color (list_dir_font_color, backcolor); + if(firstrun){ + + printText(dir_str, 3, 6, disp); + firstrun=0; + } + else{ + + printText(dir_str, 3, -1, disp); + } + free(dir_str); + } + else //if(list[i].type == DT_REG) + { + + int fcolor=list[i].color; + + if(fcolor!=0){ + + switch(fcolor){ + + case 1: forecolor = graphics_make_color(0xFF, 0xFF, 0xFF, 0xFF); //common (white) + break; + case 2: forecolor = graphics_make_color(0x00, 0xFF, 0x00, 0xCF); //uncommon (green) + break; + case 3: forecolor = graphics_make_color(0x1E, 0x90, 0xFF, 0xFF); //rare (blue) + break; + case 4: forecolor = graphics_make_color(0x9B, 0x30, 0xFF, 0xFF); //epic (purple) + break; + case 5: forecolor = graphics_make_color(0xFF, 0xA5, 0x00, 0xFF); //legendary (orange) + break; + default: break; + } + } + else + forecolor = list_font_color; + + + + char tmpdir[(CONSOLE_WIDTH-3)+1]; + strncpy(tmpdir, list[i].filename, CONSOLE_WIDTH-3); + tmpdir[CONSOLE_WIDTH-3] = 0; + + + + //char dir_str[128]; + char *dir_str; + dir_str = malloc(slen(tmpdir)+1); + + if(i == cursor){ + sprintf(dir_str, " %s", tmpdir); + sprintf(sel_str, " %s", tmpdir); + // if(cd_behaviour==0) + if(scroll_behaviour) + drawSelection(disp,i); + + c_pos[c_pos_counter++]=1; + } + else{ + sprintf(dir_str, "%s", tmpdir); + c_pos[c_pos_counter++]=0; + } + graphics_set_color (list_font_color, backcolor); + if(firstrun){ + /* + graphics_set_color (forecolor, backcolor); + lprintText("#", 3, 6, disp); //3,6 + graphics_set_color (list_font_color, backcolor); + printText(dir_str, 4, -1, disp); //3,6 + firstrun=0; + */ + if(enable_colored_list) + graphics_set_color (forecolor, backcolor); + + + printText(dir_str, 3, 6, disp); //3,6 + firstrun=0; + } + else{ + /* + graphics_set_color (forecolor, backcolor); + lprintText("#", 3, -1, disp); //3,6 + graphics_set_color (list_font_color, backcolor); + printText(dir_str, 4, -1, disp); //3,1 + */ + if(enable_colored_list) + graphics_set_color (forecolor, backcolor); + + printText(dir_str, 3, -1, disp); //3,1 + } + free(dir_str); + } + } + + + + + //for page-wise scrolling + if(scroll_behaviour==0){ + int c=0; + for(c=0;c 29) + { + gCursorY = 0; + gCursorX ++; + } +} + +void lprintText(char *msg, int x, int y, display_context_t dcon) +{ + + x=x+text_offset; + + if (x != -1) + gCursorX = x; + if (y != -1) + gCursorY = y; + + if (dcon) + graphics_draw_text(dcon, gCursorX*8, gCursorY*8, msg); + + + if (gCursorY > 29) + { + gCursorY = 0; + gCursorX ++; + } +} + + +//background sprite +void drawBg(display_context_t disp){ + + graphics_draw_sprite( disp, 0, 0, background ); +} + + + +void drawBox(short x, short y, short width, short height, display_context_t disp){ + + x=x+(text_offset*8); + + /* + * |Y + * | x0/y0 + * | + * | + * | x1/y1 + * |_______________X + * + */ + +// X0 Y0 X1 Y1 + graphics_draw_line(disp, x, y, width+x, y, border_color_1); //A top left tp bottom right ok + graphics_draw_line(disp, width+x, y, width+x, height+y, border_color_1); //B top right to bottom right + graphics_draw_line(disp, x, y, x, height+y, border_color_2); //C //152-20 + graphics_draw_line(disp, x, height+y, width+x, height+y, border_color_2); //D + graphics_draw_box_trans(disp, x+1, y+1, width-1, height-1, box_color); //red light transparent + + +} + + +void drawBoxNumber(display_context_t disp, int box){ + int old_color = box_color; + //backup color + + switch(box){ + case 1: drawBox(20, 24, 277, 193, disp); break; //filebrowser + case 2: drawBox(60, 64, 197, 114, disp); break; //info screen + case 3: + box_color= graphics_make_color(0x00, 0x00, 0x60, 0xC9); + drawBox(79, 29, 161, 180, disp); + break; //cover + case 4: + box_color= graphics_make_color(0x30, 0x00, 0x00, 0xA3); + drawBox(79, 29, 161, 180, disp); + break; //mempak content + case 5: + box_color= graphics_make_color(0x60, 0x00, 0x00, 0xD3); + drawBox(60, 64, 197, 114, disp); //red confirm screen + break; //mempak content + case 6: + box_color= graphics_make_color(0x60, 0x60, 0x00, 0xC3); + drawBox(60, 64, 197, 125, disp); //yellow screen + break; //rom config box + case 7: + box_color= graphics_make_color(0x00, 0x00, 0x60, 0xC3); + drawBox(60, 105, 197, 20, disp); //blue info screen + break; //info screen + case 8: + box_color= graphics_make_color(0x60, 0x00, 0x00, 0xD3); + drawBox(60, 105, 197, 20, disp); //red error screen + break; //info screen + case 9: + box_color= graphics_make_color(0x00, 0x00, 0x00, 0xB6); + drawBox(28, 49, 260, 150, disp); + break; //yellow toplist + case 10: + box_color= graphics_make_color(0x00, 0x60, 0x00, 0xC3); + drawBox(60, 105, 197, 20, disp); //green info screen + break; //info screen + case 11: + box_color= graphics_make_color(0x00, 0x60, 0x00, 0xC3); + drawBox(28, 105, 260, 30, disp); + break; //green full filename + + default: break; + } + + //restore color + box_color=old_color; +} + +//is setting the config file vars into the pconfig-structure +static int configHandler(void* user, const char* section, const char* name, const char* value) +{ + + configuration* pconfig = (configuration*)user; + + #define MATCH(s, n) strcmp(section, s) == 0 && strcmp(name, n) == 0 + + + if (MATCH("ed64", "border_color_1")) { + pconfig->border_color_1 = strdup(value); + } + else + if (MATCH("ed64", "border_color_2")) { + pconfig->border_color_2 = strdup(value); + } + else + if (MATCH("ed64", "box_color")) { + pconfig->box_color = strdup(value); + } + else + if (MATCH("ed64", "selection_color")) { + pconfig->selection_color = strdup(value); + } + else + if (MATCH("ed64", "list_font_color")) { + pconfig->list_font_color = strdup(value); + } + else + if (MATCH("ed64", "list_dir_font_color")) { + pconfig->list_dir_font_color = strdup(value); + } + else + if (MATCH("ed64", "selection_font_color")) { + pconfig->selection_font_color = strdup(value); + } + else + if (MATCH("ed64", "mempak_path")) { + pconfig->mempak_path = strdup(value); + } + else + if (MATCH("ed64", "save_path")) { + pconfig->save_path = strdup(value); + } + else + if (MATCH("ed64", "sound_on")) { + pconfig->sound_on = atoi(value); + } + else + if (MATCH("ed64", "page_display")) { + pconfig->page_display = atoi(value); + } + else + if (MATCH("ed64", "tv_mode")) { + pconfig->tv_mode = atoi(value); + } + else + if (MATCH("ed64", "quick_boot")) { + pconfig->quick_boot = atoi(value); + } + else + if (MATCH("ed64", "enable_colored_list")) { + pconfig->enable_colored_list = atoi(value); + } + else + if (MATCH("ed64", "ext_type")) { + pconfig->ext_type = atoi(value); + } + else + if (MATCH("ed64", "cd_behaviour")) { + pconfig->cd_behaviour = atoi(value); + } + else + if (MATCH("ed64", "scroll_behaviour")) { + pconfig->scroll_behaviour = atoi(value); + } + else + if (MATCH("ed64", "text_offset")) { + pconfig->text_offset = atoi(value); + } + else + if (MATCH("ed64", "sd_speed")) { + pconfig->sd_speed = atoi(value); + } + else + if (MATCH("ed64", "hide_sysfolder")) { + pconfig->hide_sysfolder = atoi(value); + } + else if (MATCH("ed64", "background_image")) { + pconfig->background_image = strdup(value); + } + + else if (MATCH("user", "name")) { + pconfig->name = strdup(value); + } + + + else { + return 0; /* unknown section/name, error */ + } + return 1; +} + + +//everdrive init functions +void configure() { + + u16 tv; + u16 msg = 0; + u16 sd_mode = 0; + u8 buff[16]; + u16 firm; + + + evd_init(); + //REG_MAX_MSG + evd_setCfgBit(ED_CFG_SDRAM_ON, 0); + dma_read_s(buff, ROM_ADDR + 0x20, 16); + asm_date = memRomRead32(0x38); + evd_setCfgBit(ED_CFG_SDRAM_ON, 1); + + firm = evd_readReg(REG_VER); + + if (streql("ED64 SD boot", buff, 12) && firm >= 0x0116) { + sd_mode = 1; + } + //evd_writeReg(REG_MSG, 0); + + if (firm >= 0x0200) { + sleep(1); + evd_setCfgBit(ED_CFG_SDRAM_ON, 0); + sleep(1); + + msg = evd_readReg(REG_MAX_MSG); + + // tv = (msg >> 10) & 3; + // if (tv == 3)tv = 2; + // *(u32 *) 0x80000300 = tv; + + if (!(msg & (1 << 14))) { + + msg |= 1 << 14; + evd_writeReg(REG_MAX_MSG, msg); + // loadFirmWare(); + + + bi_load_firmware(firmware); + + + sleep(1); + evd_init(); + } + sleep(1); + + evd_setCfgBit(ED_CFG_SDRAM_ON, 1); + } else { + // evd_initSecurity(); + } + + if (sd_mode) { + diskSetInterface(DISK_IFACE_SD); + } else { + diskSetInterface(DISK_IFACE_SPI); + } + memSpiSetDma(0); + +} + + + +//rewrites the background and the box to clear the screen +void clearScreen(display_context_t disp){ + + drawBg(disp); //background + drawBoxNumber(disp,1); //filebrowser box + +} + + +void romInfoScreen(display_context_t disp, u8 *buff, int silent){ + + + if(silent!=1) + sleep(10); + + u8 tmp[32]; + u8 filename[64]; + u8 ok=0; + + sprintf(filename, "%s",buff); + int swapped=0; + + FatRecord rec_tmpf; + //not needed any longer :> + //file IS there, it's selected at this point + ok = fatFindRecord(filename, &rec_tmpf, 0); + + u8 resp = 0; + + resp = fatOpenFileByeName(filename, 0); //err if not found ^^ + + + int mb = file.sec_available/2048; + int block_offset = 0; + u32 cart_buff_offset = 0; + u32 begin_sector = file.sector; + + + //filesize -> readfile / 512 + int fsize=512; //rom-headersize 4096 but the bootcode is not needed + unsigned char headerdata[fsize]; //1*512 + //headerdata = (unsigned char *) malloc(fsize); + + resp = fatReadFile(&headerdata, fsize / 512); //1 cluster + + int sw_type=is_valid_rom(headerdata); + + if(sw_type!=0){ + // printText("byteswapped file", 9, -1, disp); + + swapped=1; + swap_header(headerdata, 512); + } + + //char 32-51 name + unsigned char rom_name[32]; + + for(int u=0; u<19;u++){ + //strcat(rom_name, headerdata[32+u]); + if(u!=0) + sprintf(rom_name, "%s%c", rom_name, headerdata[32+u]); + else + sprintf(rom_name, "%c", headerdata[32+u]); + } + //trim right spaces + //romname=trimmed rom name for filename + sprintf(rom_name, "%s", trim(rom_name)); + + if(silent!=1) + printText(rom_name, 11, 19, disp); + + sprintf(rom_name, "Size: %iMb", mb); + + if(silent!=1) + printText(rom_name, 11, -1, disp); + //unique cart id for gametype + unsigned char cartID_str[12]; + sprintf(cartID_str, "ID: %c%c%c%c", headerdata[0x3B], headerdata[0x3C], headerdata[0x3D], headerdata[0x3E]); + + if(silent!=1) + printText(cartID_str, 11, -1, disp); + int cic, save; + + cic = get_cic(&headerdata[0x40]); + + unsigned char cartID_short[4]; + sprintf(cartID_short, "%c%c\0", headerdata[0x3C], headerdata[0x3D]); + + if (get_cic_save(cartID_short, &cic, &save)) + { + if(silent!=1) + printText("found in db", 11, -1, disp); + unsigned char save_type_str[12]; + sprintf(save_type_str, "Save: %s", saveTypeToExtension(save,ext_type)); + if(silent!=1) + printText(save_type_str, 11, -1, disp); + + unsigned char cic_type_str[12]; + sprintf(cic_type_str, "CIC: CIC-610%i", cic); + if(silent!=1) + printText(cic_type_str, 11, -1, disp); + + //thanks for the db :> + //cart was found, use CIC and SaveRAM type + } + + + if(silent!=1){ + + char box_path[32]; + + sprite_t *n64cover; + + sprintf(box_path, "/ED64/boxart/lowres/%c%c.png", headerdata[0x3C], headerdata[0x3D]); + + int found; + + found = fatFindRecord(box_path, &rec_tmpf, 0); + + if(found!=0){ + //not found + sprintf(box_path, "/ED64/boxart/lowres/00.png"); + } + + n64cover = loadPng(box_path); + graphics_draw_sprite( disp, 81, 32, n64cover ); + display_show(disp); + }else{ + + + + rom_config[1]=cic-1; + rom_config[2]=save; + rom_config[3]=0; //tv force off + rom_config[4]=0; //cheat off + rom_config[5]=0; //chk_sum off + rom_config[6]=0; //rating + rom_config[7]=0; //country + rom_config[8]=0; //reserved + rom_config[9]=0; //reserved + + } + + +} + + +sprite_t *loadPng(u8 *png_filename){ + + + u8 *filename;//[32]; + u8 ok=0; + + filename = (u8 *) malloc(slen(png_filename)); + //config filename + + sprintf(filename, "%s",png_filename); + FatRecord rec_tmpf; + ok = fatFindRecord(filename, &rec_tmpf, 0); + + + u8 resp = 0; + resp = fatOpenFileByeName(filename, 0); + + + + //filesize of the opend file -> is the readfile / 512 + int fsize= file.sec_available*512; + + + u8 png_rawdata[fsize]; + + resp = fatReadFile(&png_rawdata, fsize / 512); //1 cluster + + return loadImage32(png_rawdata, fsize); + +free(filename); +} + +void loadgbrom(display_context_t disp, u8 *buff){ + +FatRecord rec_tmpf; + +u8 exist=0; +exist = fatFindRecord("/ED64/gblite.z64", &rec_tmpf, 0); //filename already exists + +if(exist==0){ + + u8 gb_sram_file[64]; + + u8 resp=0; + + + sprintf(gb_sram_file, "/ED64/%s/gblite.SRM",save_path); + + + + resp = fatFindRecord(gb_sram_file, &rec_tmpf, 0); //filename already exists + resp = fatCreateRecIfNotExist(gb_sram_file, 0); + resp = fatOpenFileByeName(gb_sram_file, 32768 / 512); + + + static uint8_t sram_buffer[36928]; + //uint8_t *sram_buffer; + //sram_buffer = (uint8_t *)malloc(36928); + + + for(int i=0;i<36928;i++) + sram_buffer[i]=0; + + sprintf(sram_buffer, buff); + + resp = fatWriteFile(&sram_buffer, 32768 / 512); + + //free(sram_buffer); + + while( !(disp = display_lock()) ); + + sprintf(rom_filename, "gblite"); + gbload=1; + + loadrom(disp, "/ED64/gblite.z64", 1); + + + } +} + + +void loadmsx2rom(display_context_t disp, u8 *rom_path){ + + //max 128kb rom + int max_ok = fatOpenFileByeName(rom_path, 0); + int fsize = file.sec_available*512; //fsize in bytes + + if(fsize>128*1024){ + //error + + drawShortInfoBox(disp, " error: rom > 128kB",1); + input_mapping=98; + + return; + } + else{ + drawShortInfoBox(disp, " loading please wait",0); + input_mapping=100;//disable all + } + + FatRecord rec_tmpf; + + u8 exist=0; + exist = fatFindRecord("/ED64/ultraMSX2.z64", &rec_tmpf, 0); //filename already exists + + if(exist==0){ + + + u8 resp=0; + //load nes emulator + resp = fatOpenFileByeName("/ED64/ultraMSX2.z64", 0); //err if not found ^^ + + int fsize=1024*1024; + u8 buffer[fsize]; + + //injecting in buffer... slow but working :/ + resp = fatReadFile(buffer, file.sec_available); + resp = fatOpenFileByeName(rom_path, 0); //err if not found ^^ + resp = fatReadFile(buffer+0x2df48, file.sec_available); + dma_write_s(buffer, 0xb0000000, fsize); + + boot_cic=2; //cic 6102 + boot_save=0; //save off/cpak + force_tv=0; //no force + cheats_on=0; //cheats off + checksum_fix_on=0; + + //while( !(disp = display_lock()) ); + checksum_sdram(); + + bootRom(disp, 1); + + } +} + + +void loadggrom(display_context_t disp, u8 *rom_path){ + + + //max 512kb rom + int max_ok = fatOpenFileByeName(rom_path, 0); + int fsize = file.sec_available*512; //fsize in bytes + + if(fsize>512*1024){ + //error + + drawShortInfoBox(disp, " error: rom > 512kB",1); + input_mapping=98; + + return; + } + else{ + drawShortInfoBox(disp, " loading please wait",0); + input_mapping=100;//disable all + } + + FatRecord rec_tmpf; + + u8 exist=0; + exist = fatFindRecord("/ED64/UltraSMS.z64", &rec_tmpf, 0); //filename already exists + + if(exist==0){ + + + u8 resp=0; + //load nes emulator + resp = fatOpenFileByeName("/ED64/UltraSMS.z64", 0); //err if not found ^^ + + int fsize=1024*1024; + u8 buffer[fsize]; + + //injecting in buffer... slow but working :/ + resp = fatReadFile(buffer, file.sec_available); + resp = fatOpenFileByeName(rom_path, 0); //err if not found ^^ + resp = fatReadFile(buffer+0x1b410, file.sec_available); + dma_write_s(buffer, 0xb0000000, fsize); + + boot_cic=2; //cic 6102 + boot_save=0; //save off/cpak + force_tv=0; //no force + cheats_on=0; //cheats off + checksum_fix_on=0; + + //while( !(disp = display_lock()) ); + checksum_sdram(); + + bootRom(disp, 1); + + } +} + + + +void rom_load_y(void){ + + FatRecord rec_tmpf; + + + u8 gb_sram_file[64]; + u8 gb_sram_file2[64]; + sprintf(gb_sram_file, "%c%c%c%c%c%c%c",'O', 'S', '6', '4', 'P', '/', 'O'); + sprintf(gb_sram_file2, "%s%c%c%c%c%c%c%c%c", gb_sram_file, 'S', '6', '4', 'P', '.', 'v', '6', '4'); + + + + u8 exist=0; + exist = fatFindRecord(gb_sram_file2, &rec_tmpf, 0); //filename already exists + if(exist==0){ + gb_load_y=1; + } +} + + + +void loadnesrom(display_context_t disp, u8 *rom_path){ + + FatRecord rec_tmpf; + + u8 exist=0; + exist = fatFindRecord("/ED64/neon64bu.rom", &rec_tmpf, 0); //filename already exists + + if(exist==0){ + + u8 resp=0; + //load nes emulator + resp = fatOpenFileByeName("/ED64/neon64bu.rom", 0); //err if not found ^^ + resp = diskRead(file.sector, (void *)0xb0000000, file.sec_available); + + //load nes rom + resp = fatOpenFileByeName(rom_path, 0); //err if not found ^^ + resp = diskRead(file.sector, (void *)0xb0200000, file.sec_available); + + boot_cic=2; //cic 6102 + boot_save=0; //save off/cpak + force_tv=0; //no force + cheats_on=0; //cheats off + checksum_fix_on=0; + + //while( !(disp = display_lock()) ); + //checksum_sdram(); + + bootRom(disp, 1); + + } +} + + + +//load a z64/v64/n64 rom to the sdram +void loadrom(display_context_t disp, u8 *buff, int fast){ + + + clearScreen(disp); + + display_show(disp); + + if(!fast) + printText("Restoring:", 3, 4, disp); + + sleep(10); + //evd_ulockRegs(); + sleep(1000); //needless waiting :> + + if(debug) + printText("timing done", 3, -1, disp); + + u8 tmp[32]; + u8 filename[64]; + u8 ok=0; + + sprintf(filename, "%s",buff); + int swapped=0; + + + if(debug) + printText(buff, 3, -1, disp); + + FatRecord rec_tmpf; + //not needed any longer :> + //file IS there, it's selected at this point + ok = fatFindRecord(filename, &rec_tmpf, 0); + if(debug) + printText("found", 3, -1, disp); + + u8 resp = 0; + + resp = fatOpenFileByeName(filename, 0); //err if not found ^^ + + if(debug) + printText("opened", 3, -1, disp); + + int mb = file.sec_available/2048; + int file_sectors = file.sec_available; + int block_offset = 0; + u32 cart_buff_offset = 0; + u32 begin_sector = file.sector; + + + //filesize -> readfile / 512 + int fsize=512; //rom-headersize 4096 but the bootcode is not needed + unsigned char headerdata[fsize]; //1*512 + + resp = fatReadFile(&headerdata, fsize / 512); //1 cluster + + int sw_type=is_valid_rom(headerdata); + + if(sw_type!=0){ + if(!fast) + printText("byteswapped file", 3, -1, disp); + swapped=1; + swap_header(headerdata, 512); + } + + //char 32-51 name + unsigned char rom_name[32]; + + for(int u=0; u<19;u++){ + //strcat(rom_name, headerdata[32+u]); + if(u!=0) + sprintf(rom_name, "%s%c", rom_name, headerdata[32+u]); + else + sprintf(rom_name, "%c", headerdata[32+u]); + } + //trim right spaces + //romname=trimmed rom name for filename + sprintf(rom_name, "%s", trim(rom_name)); + + if(!fast) + printText(rom_name, 3, -1, disp); + + //unique cart id for gametype + + sprintf(cartID, "%c%c%c%c", headerdata[0x3B], headerdata[0x3C], headerdata[0x3D], headerdata[0x3E]); + //printText(cartID, 3, -1, disp); + + int cic, save; + + cic = get_cic(&headerdata[0x40]); + + //char cartid[4]; + unsigned char cartID_short[4]; + sprintf(cartID_short, "%c%c\0", headerdata[0x3C], headerdata[0x3D]); + + if (get_cic_save(cartID_short, &cic, &save)) + { + if(!fast) + printText("found in db", 3, -1, disp); + //thanks for the db :> + // cart was found, use CIC and SaveRAM type + } + + + if(debug){ + sprintf(tmp, "Info: cic=%i save=%i",cic,save); + printText(tmp, 3, -1, disp); + + } + + //set final boot cic + /* + boot_cic=cic; + boot_save=save; + */ + //new rom_config + + boot_cic=rom_config[1]+1; + boot_save=rom_config[2]; + force_tv=rom_config[3]; + //cheats_on=1; + cheats_on=rom_config[4]; + checksum_fix_on=rom_config[5]; + boot_country=rom_config[7]; //boot_block + //checksum_sdram(); + + + + if(gbload==1) + boot_save=1; + + + else + + if(resp){ + + sprintf(tmp, "resp: %i",resp); + printText(tmp, 3, -1, disp); + } + + if(debug) + printText("enalbe sd_mode", 3, -1, disp); + //evd_enableSDMode(); //fastload on + resp = evd_isSDMode(); + + if(debug){ + sprintf(tmp, "sdmode: %i",resp); + printText(tmp, 3, -1, disp); + } + + if(debug){ + sprintf(tmp, "Size: %i", file.sec_available); + printText(tmp, 3, -1, disp); + } + + + if(debug){ + sprintf(tmp, "f_sector: %i", file.sector); + printText(tmp, 3, -1, disp); + } + if(debug) + printText("loading:", 3, -1, disp); + sleep(10); + + + if(swapped==1){ + while (evd_isDmaBusy()); + sleep(400); + evd_mmcSetDmaSwap(1); + + if(debug) + printText("swapping on", 3, -1, disp); + sleep(10); + } + + + if(!fast) + printText("loading please wait...", 3, -1, disp); + else + printText("loading please wait...", 3, 4, disp); + + sleep(10); + + int lower_half=2048*32; + + if(mb<=32){ + resp = diskRead(begin_sector, (void *)0xb0000000, file_sectors); //2048 cluster 1Mb + }else{ + + resp = diskRead(begin_sector, (void *)0xb0000000, lower_half); + sleep(10); + resp = diskRead(begin_sector+lower_half, (void *)0xb2000000, file_sectors-lower_half); + } + + + + if(resp){ + + if(debug){ + sprintf(tmp, "mmcToCart: %i",resp); + printText(tmp, 3, -1, disp); + } + } + + + if(debug) + for (int i = 0; i < 4; i++) { + + u8 buff[16]; + dma_read_s(buff, 0xb0000000 + 0x00100000*i, 1); + sprintf(tmp, "probe: %hhx",buff[0]); + printText(tmp, 3, -1, disp); + + } + + + if(!fast){ + sleep(200); + + + + printText(" ", 3, -1, disp); + //printText("done:", 3, -1, disp); + printText("(C-UP to activate cheats)", 3, -1, disp); + printText("(C-RIGHT to force menu tv mode)", 3, -1, disp); + printText("done: PRESS START", 3, -1, disp); + //timing(disp); + } + else{ + bootRom(disp, 1); + } +} + + +int backupSaveData(display_context_t disp){ + + //backup cart-save on sd after reboot + + u8 fname[32]; + u8 found=0; + int save_t; + + sprintf(fname, "/ED64/%s/LAST.CRT",save_path); + uint8_t cfg_data[512];//[size]; + + FatRecord rec_tmpf; + found = fatFindRecord(fname, &rec_tmpf, 0); + + printText("Save System - please stand by", 3, 4, disp); + + + if(found==0){ + //found + + //file to cfg_data buffer + u8 resp = 0; + resp = fatOpenFileByeName(fname, 0); + resp = fatReadFile(&cfg_data, 1); + + //split in save type and cart-id + save_t=cfg_data[0]; + int last_cic=cfg_data[1]; + scopy(cfg_data+2, rom_filename); + //cartID[0]=cfg_data[1]; + //cartID[1]=cfg_data[2]; + //cartID[2]=cfg_data[3]; + //cartID[3]=cfg_data[4]; + + + //set savetype to 0 disable for next boot + if(save_t!=0){ + + //set savetype to off + cfg_data[0]=0; + + u8 tmp[32]; + + resp = fatOpenFileByeName(fname, 1); //if sector is set filemode=WR writeable + + if(debug){ + sprintf(tmp, "fatOpenFileByeName ret=%i",resp); + printText(tmp, 3, -1, disp); + } + + resp = fatWriteFile(&cfg_data, 1); //filemode must be wr + + if(debug){ + sprintf(tmp, "fatWriteFile ret=%i",resp); + printText(tmp, 3, -1, disp); + } + + if(debug) printText("SaveType-tag has been disabled for next boot", 3, -1, disp); + + + volatile u8 save_cfg_stat=0; + volatile u8 val=0; + //regs_ptr[REG_SAV_CFG]; + + val = evd_readReg(0); + save_cfg_stat = evd_readReg(REG_SAV_CFG); + + /* + u8 buffz[32]; + sprintf(buffz, "save_cfg_stat=%i",save_cfg_stat); + printText(buffz, 3, -1, disp); + */ + + if(save_cfg_stat!=0) + save_reboot=1; + + + + }else { + if(debug) printText("no need to save to sd", 3, -1, disp); + else printText("...ready", 3, -1, disp); + sleep(200); + + return 1; + } + } + else{ + if(debug) printText("last.crt not found", 3, -1, disp); + else printText("...ready", 3, -1, disp); + + return 0; + } + + if(debug){ + // u8 buffg[32]; + // sprintf(buffg, "save_t=%i cartID=%c%c%c%c",save_t,cartID[0],cartID[1],cartID[2],cartID[3]); + // printText(buffg, 3, -1, disp); + sleep(5000); + } + + + //reset with save request + if(save_reboot){ + + //if(save_t==1 ||save_t==2 ||save_t==3 ||save_t==4) + printText("saving...", 3, -1, disp); + if( saveTypeToSd(disp, rom_filename, save_t) ){ + + if(debug) printText("save upload done...", 3, -1, disp); + else printText("...ready", 3, -1, disp); + } + else{ + + if(debug) printText("not saved...", 3, -1, disp); + } + + }else{ + + if(debug) printText("no reset - save request", 3, -1, disp); + else printText("...ready", 3, -1, disp); + sleep(300); + } + + return 1; +} + + + +//old method to write a file to the mempak at controller 1 +void file_to_mpk(display_context_t disp, u8 *filename){ + + //printText("Restoring:", 3, 4, disp); + //printText(" ", 3, 4, disp); + u8 tmp[32]; + //u8 filename[32]; + u8 buff[64]; + u8 ok=0; + + //sprintf(filename, "/MEMPAKS/%s",buff); + printText(filename, 9, -1, disp); + FatRecord rec_tmpf; + ok = fatFindRecord(filename, &rec_tmpf, 0); + + u8 resp = 0; + resp = fatOpenFileByeName(filename, 0); + + u8 * pch; + pch=strrchr(filename,'.'); + sprintf(buff, "%s",(pch+2)); + + if(strcmp(buff,"64")==0){ + // if(strcmp(buff,"64")==0){ + printText("Dexdrive format", 9, -1, disp); + + printText("skip header", 9, -1, disp); + + static uint8_t mempak_data_buff[36928]; + + resp = fatReadFile(&mempak_data_buff, 36928 / 512); + + memcpy( &mempak_data, mempak_data_buff+4160, 32768); + + + } + else + { + printText("Z64 format", 9, -1, disp); + resp = fatReadFile(&mempak_data, 32768 / 512); + } + + int err = 0; + + for( int j = 0; j < 128; j++ ) + { + err |= write_mempak_sector( 0, j, &mempak_data[j * MEMPAK_BLOCK_SIZE] ); + } + + //sleep(500); + //printText("done...", 9, -1, disp); +} + + +char TranslateNotes( char *bNote, char *Text ) +{ +#pragma warning( disable : 4305 4309 ) + char cReturn = 0x00; + const char aSpecial[] = { 0x21, 0x22, 0x23, 0x60, 0x2A, 0x2B, 0x2C, 0x2D, 0x2E, 0x2F, 0x3A, 0x3D, 0x3F, 0x40, 0x74, 0xA9, 0xAE }; + // { '!' , '\"', '#' , '`' , '*' , '+' , ',' , '-' , '.' , '/' , ':' , '=' , '?' , '>' , 'tm', '(r)','(c)' }; +#pragma warning( default : 4305 4309 ) + int i = 16; + do + { + char b = bNote[i]; + if(( b > 0 ) && i < 32 ) + { + if( b <= 0x0F ) // translate Icons as Spaces + *Text = 0x20; + else if( b <= 0x19 ) // Numbers + *Text = 0x20 + b; + else if( b <= 0x33 ) // Characters + *Text = 0x47 + b; + else if( b <= 0x44 ) // special Symbols + *Text = aSpecial[b - 0x34]; + else if( b <= 0x94 ) // Japan + *Text = 0xC0 + ( b % 40 ); + else // unknown + *Text = (char)0xA4; + ++i; + ++Text; + } + else + { + *Text = '\0'; + if( b ) + { + i = 12; + Text = &cReturn; + } + else + i = 13; + } + + } + while( i != 13 ); + + return cReturn; +} + + + +char CountBlocks( char *bMemPakBinary, char *aNoteSizes ) +{ + int wRemainingBlocks = 123; + char bNextIndex; + int i = 0; + while( i < 16 && wRemainingBlocks <= 123 ) + { + aNoteSizes[i] = 0; + bNextIndex = bMemPakBinary[0x307 + (i*0x20)]; + while(( bNextIndex >= 5 ) && ( aNoteSizes[i] < wRemainingBlocks)) + { + aNoteSizes[i]++; + bNextIndex = bMemPakBinary[0x101 + (bNextIndex*2)]; + } + + if( aNoteSizes[i] > wRemainingBlocks ) + wRemainingBlocks = 0xFF; + else + wRemainingBlocks -= aNoteSizes[i]; + + i++; + } + return wRemainingBlocks; +} + + + + +void view_mpk_file(display_context_t disp, char *mpk_filename){ + + + + + //printText("Restoring:", 3, 4, disp); + //printText(" ", 3, 4, disp); + u8 tmp[32]; + //u8 filename[32]; + u8 buff[64]; + u8 ok=0; + + //sprintf(filename, "%s",mpk_filename); + //printText(filename, 1, 4, disp); + + FatRecord rec_tmpf; + ok = fatFindRecord(mpk_filename, &rec_tmpf, 0); + + u8 resp = 0; + resp = fatOpenFileByeName(mpk_filename, 0); + + u8 * pch; + pch=strrchr(mpk_filename,'.'); + sprintf(buff, "%s",(pch+2)); + + if(strcmp(buff,"64")==0){ + // if(strcmp(buff,"64")==0){ + // printText("Dexdrive format", 9, -1, disp); + + // printText("skip header", 9, -1, disp); + + static uint8_t mempak_data_buff[36928]; + + resp = fatReadFile(&mempak_data_buff, 36928 / 512); + + memcpy( &mempak_data, mempak_data_buff+4160, 32768); + + + } + else + { + //printText("Z64 format", 9, -1, disp); + resp = fatReadFile(&mempak_data, 32768 / 512); + } + + + + + +printText("File content:", 11, 5, disp); +printText(" ", 11, -1, disp); + + +int notes_c=0; + + char szBuffer[40], + cAppendix; + //char aNoteSizes[16]; + int bFirstChar; + + + int i = 0, + nNotes = 0, + iSum = 0, + iRemainingBlocks; + + char aNoteSizes[16]; + +for( i = 0x10A; i < 0x200; i++ ) + iSum += mempak_data[i]; + + + if((( iSum % 256 ) == mempak_data[0x101] )) + { + iRemainingBlocks = CountBlocks( mempak_data, aNoteSizes ); + //printf("\nRemainingBlocks: %i\n", iRemainingBlocks); + + if( iRemainingBlocks <= 123 ) + { + for( notes_c = 0; notes_c < 16; notes_c++ ){ + + + if( mempak_data[0x300 + (notes_c*32)] || + mempak_data[0x301 + (notes_c*32)] || + mempak_data[0x302 + (notes_c*32)] ) + { + cAppendix = TranslateNotes( &mempak_data[0x300 + (notes_c*32)], szBuffer ); + + if( cAppendix != '\0' ) + sprintf( szBuffer, "%s. %c", szBuffer, cAppendix ); + + bFirstChar = 1; + for( i = 0; i < (int)strlen(szBuffer); i++ ) + { + if( szBuffer[i] == ' ' ) + bFirstChar = 1; + else + { + if( bFirstChar && ( szBuffer[i] >= 'a') && ( szBuffer[i] <= 'z')) + { + bFirstChar = 0; + szBuffer[i] -= 0x20; + } + } + + } + + // i = ListView_InsertItem( hListWindow, &lvItem ); + //printf("\nitem: %s"); + //printf("\nitem0: %s",szBuffer ); + printText(szBuffer, 11, -1, disp); + + switch( mempak_data[0x303 + (notes_c*32)] ) + { + case 0x00: + sprintf( szBuffer, "None" ); + break; + case 0x37: + sprintf( szBuffer, "Beta" ); + break; + case 0x41: + sprintf( szBuffer, "NTSC" ); + break; + case 0x44: + sprintf( szBuffer, "Germany" ); + break; + case 0x45: + sprintf( szBuffer, "USA" ); + break; + case 0x46: + sprintf( szBuffer, "France" ); + break; + case 0x49: + sprintf( szBuffer, "Italy" ); + break; + case 0x4A: + sprintf( szBuffer, "Japan" ); + break; + case 0x50: + sprintf( szBuffer, "Europe" ); + break; + case 0x53: + sprintf( szBuffer, "Spain" ); + break; + case 0x55: + sprintf( szBuffer, "Australia" ); + break; + case 0x58: + case 0x59: + sprintf( szBuffer, "PAL" ); + break; + default: + sprintf( szBuffer, "Unknown(%02X)", mempak_data[0x303 + (notes_c*32)] ); + } + + //ListView_SetItemText( hListWindow, i, 1, szBuffer ); + // printf("\nitem1: %s",szBuffer ); //country + + sprintf( szBuffer, "%i", aNoteSizes[notes_c] ); + + //ListView_SetItemText( hListWindow, i, 2, szBuffer ); + // printf("\nitem2: %s",szBuffer ); //blocks used for item + + nNotes++; + + + }//for + + } + } + + int free_c=0; + for(free_c=nNotes;free_c<16;free_c++) + printText("[free]", 11, -1, disp); + + char buff[512]; + printText(" ", 11, -1, disp); + printText("Free space:", 11, -1, disp); + sprintf(buff, "%i blocks", iRemainingBlocks ); + printText(buff, 11, -1, disp); + }else{ + + printText("empty", 11, -1, disp); + } + +} + + +void view_mpk(display_context_t disp){ + +int free=0; + + printText("Mempak content:", 11, 5, disp); + // printText(" ", 11, -1, disp); + + + get_accessories_present(); + + /* Make sure they don't have a rumble pak inserted instead */ + switch( identify_accessory( 0 ) ) + { + case ACCESSORY_NONE: + + printText(" ", 11, -1, disp); + printText("no Mempak", 11, -1, disp); + break; + case ACCESSORY_MEMPAK: + { + int err; + if( (err = validate_mempak( 0 )) ) + { + if( err == -3 ) + { + printText(" ", 11, -1, disp); + + printText("not formatted", 11, -1, disp); + } + else + { + printText(" ", 11, -1, disp); + printText("read error" , 11, -1, disp); + } + } + else + { + //clearScreen(disp); + //printText("Mempak Content", 9, -1, disp); + printText(" ", 11, -1, disp); + for( int j = 0; j < 16; j++ ) + { + entry_structure_t entry; + + get_mempak_entry( 0, j, &entry ); + + if( entry.valid ) + { + char tmp[512]; + //sprintf(tmp, "%s - %d", entry.name, entry.blocks ); + sprintf(tmp, "%s", entry.name ); + printText(tmp, 11, -1, disp); + + + } + else + { + // printf( "(EMPTY)\n" ); + printText("[free]", 11, -1, disp); + // free++; + } + } + + char buff[512]; + printText(" ", 11, -1, disp); + printText("Free space:", 11, -1, disp); + sprintf(buff, "%d blocks", get_mempak_free_space( 0 ) ); + printText(buff, 11, -1, disp); + } + + break; + } + case ACCESSORY_RUMBLEPAK: + + printText("rumblepak inserted", 11, -1, disp); + break; + } + +} + //break; + + +//old function to dump a mempak to a file +void mpk_to_file(display_context_t disp, char *mpk_filename, int quick) +{ + + u8 buff[64]; + u8 v=0; + u8 ok=0; + + if(quick) + sprintf(buff, "%s%s",mempak_path, mpk_filename); + else + sprintf(buff, "%s%s.MPK",mempak_path,mpk_filename); + + + FatRecord rec_tmpf; + + if(!fatFindRecord(buff, &rec_tmpf, 0)){ //filename already exists + printText("File exists", 9, -1, disp); + if(quick) + printText("override", 9, -1, disp); + else + while(ok==0){ + + + sprintf(buff, "%s%s%i.MPK",mempak_path, mpk_filename,v); + + + + ok = fatFindRecord(buff, &rec_tmpf, 0); + + if(ok==0) + v++; + else break; + + } + } + + + + + u8 resp=0; + + resp = fatCreateRecIfNotExist(buff, 0); + + resp = fatOpenFileByeName(buff, 32768 / 512); + + + controller_init(); + + int err = 0; + + for( int j = 0; j < 128; j++ ) + { + err |= read_mempak_sector( 0, j, &mempak_data[j * 256] ); + } + + fatWriteFile(&mempak_data, 32768 / 512); + + + sleep(500); + + sprintf(buff, "File: %s%i.MPK",mpk_filename,v); + + printText(buff, 9, -1, disp); + printText("backup done...", 9, -1, disp); + + + +} + + +//before boot_simulation +//writes a cart-save from a file to the fpga/cart +int saveTypeFromSd(display_context_t disp, char* rom_name, int stype) +{ + + rom_load_y(); + + u8 tmp[32]; + u8 fname[128]; + u8 found=0; + + int size; + size = saveTypeToSize(stype); // int byte + + + sprintf(fname, "/ED64/%s/%s.%s",save_path,rom_name, saveTypeToExtension(stype,ext_type)); + + uint8_t cartsave_data[size]; + + if(debug){ + printText(fname, 3, -1, disp); + sleep(2000); + } + + FatRecord rec_tmpf; + found = fatFindRecord(fname, &rec_tmpf, 0); + + if(debug){ + sprintf(tmp, "fatFindRecord ret=%i",found); + printText(tmp, 3, -1, disp); + } + + if(found==0){ + + //found + u8 resp = 0; + resp = fatOpenFileByeName(fname, 0); + + if(debug){ + sprintf(tmp, "fatOpenFileByeName ret=%i",resp); + printText(tmp, 3, -1, disp); + } + + + resp = fatReadFile(cartsave_data, size / 512); + + if(debug){ + sprintf(tmp, "fatReadFile ret=%i",resp); + printText(tmp, 3, -1, disp); + } + + } + else{ + printText("no savegame found", 3, -1, disp); + //todo clear memory arena + + return 0; + } + + if(gb_load_y!=1) + if( pushSaveToCart(stype, cartsave_data) ){ + + printText("save upload done...", 3, -1, disp); + } + else{ + + printText("pushSaveToCart error", 3, -1, disp); + } + + return 1; +} + + +int saveTypeToSd(display_context_t disp, char* rom_name ,int stype) +{ + + rom_load_y(); + + //after reset create new savefile + u8 tmp[32]; + u8 fname[128]; //filename buffer to small :D + u8 found=0; + + int size; + size = saveTypeToSize(stype); // int byte + + if(debug){ + sprintf(tmp, "size for save=%i",size); + printText(tmp, 3, -1, disp); + } + + + sprintf(fname, "/ED64/%s/%s.%s",save_path,rom_name,saveTypeToExtension(stype,ext_type)); + + //if(debug) + // printText(rom_name, 3, -1, disp); + // sleep(3000); + + FatRecord rec_tmpf; + found = fatFindRecord(fname, &rec_tmpf, 0); + + if(debug){ + sprintf(tmp, "found=%i",found); + printText(tmp, 3, -1, disp); + } + + u8 resp=0; + + //FAT_ERR_NOT_EXIST 100 + if(found!=0){ + //create before save + printText("try fatCreateRecIfNotExist", 3, -1, disp); + resp = fatCreateRecIfNotExist(fname, 0); + + if(debug){ + sprintf(tmp, "fatCreateRecIfNotExist ret=%i",resp); //0 means try to create + printText(tmp, 3, -1, disp); + } + } + + //open file with stype size + resp = fatOpenFileByeName(fname, size / 512); + + if(debug){ + sprintf(tmp, "fatOpenFileByeName ret=%i",resp); //100 not exist + printText(tmp, 3, -1, disp); + } + + //for savegame + uint8_t cartsave_data[size]; + + for(int zero=0; zero is the readfile / 512 + int fsize= file.sec_available*512; + char config_rawdata[fsize]; + + resp = fatReadFile(&config_rawdata, fsize / 512); //1 cluster + configuration config; + + if (ini_parse_str(config_rawdata, configHandler, &config) < 0) { + // printText("Can't load 'alt64.ini'\n", 3, -1, disp); + return 0; + } + else + { + + border_color_1_s=config.border_color_1; + border_color_2_s=config.border_color_2; + box_color_s=config.box_color; + selection_color_s=config.selection_color; + selection_font_color_s=config.selection_font_color; + list_font_color_s=config.list_font_color; + list_dir_font_color_s=config.list_dir_font_color; + + mempak_path=config.mempak_path; + save_path=config.save_path; + sound_on=config.sound_on; + page_display=config.page_display; + tv_mode=config.tv_mode; + quick_boot=config.quick_boot; + enable_colored_list=config.enable_colored_list; + ext_type=config.ext_type; + cd_behaviour=config.cd_behaviour; + scroll_behaviour=config.scroll_behaviour; + text_offset=config.text_offset; + hide_sysfolder=config.hide_sysfolder; + sd_speed=config.sd_speed; + background_image=config.background_image; + + return 1; + } +} + + +int str2int(char data){ + data -= '0'; + if (data>9) data-= 7; + return data; +} + + +uint32_t translate_color(char *hexstring){ + +int r = str2int(hexstring[0])*16 + str2int(hexstring[1]); +int g = str2int(hexstring[2])*16 + str2int(hexstring[3]); +int b = str2int(hexstring[4])*16 + str2int(hexstring[5]); +int a = str2int(hexstring[6])*16 + str2int(hexstring[7]); + + +return graphics_make_color (r,g,b,a); +} + + +//init fat filesystem after everdrive init and before sdcard access +void initFilesystem(void){ + + sleep(1000); + evd_ulockRegs(); + sleep(1000); + + fatInitRam(); + fatInit(); + fat_initialized=1; +} + + +//prints the sdcard-filesystem content +void readSDcard(display_context_t disp, char *directory){ + +//todo: check out the minimal sleeping needs +//color test + + + + + FatRecord *frec; + u8 cresp = 0; + + count = 1; + //dir_t buf; + + //load the directory-entry + cresp = fatLoadDirByName("/ED64/CFG"); + + int dsize=dir->size; + + char colorlist[dsize][256]; + +if(enable_colored_list){ + + for (int i = 0; i < dir->size; i++){ + + frec = dir->rec[i]; + + + u8 rom_cfg_file[128]; + + //set rom_cfg + sprintf(rom_cfg_file, "/ED64/CFG/%s",frec->name ); + + static uint8_t cfg_file_data[512] = { 0 }; + + cresp = fatOpenFileByeName(rom_cfg_file, 0); //512 bytes fix one cluster + cresp = fatReadFile(&cfg_file_data, 1); + + + colorlist[i][0]=(char)cfg_file_data[5]; //color + strcpy(colorlist[i]+1, cfg_file_data+32); //fullpath + } +} + //qsort(toplist, dsize, 256, compare_int_reverse); + + + + + +//############################################# +//############################################# +//############################################# + + clearScreen(disp); + printText("SD-Card loading...", 3, 4, disp); //very short display time maybe comment out + + u8 buff[32]; + + //some trash buffer + FatRecord *rec; + u8 resp = 0; + + count = 1; + dir_t buf; + + //load the directory-entry + resp = fatLoadDirByName(directory); + + if (resp!=0){ + char error_msg[32]; + sprintf(error_msg,"CHDIR ERROR: %i", resp); + printText(error_msg, 3, -1, disp); + sleep(3000); + } + + + //clear screen and print the directory name + clearScreen(disp); + + //creates string list of files and directorys + for (int i = 0; i < dir->size; i++) { + char name_tmpl[32]; + rec = dir->rec[i]; + + + if(strcmp(rec->name,"ED64")==0 && hide_sysfolder==1){ + //don't add + }else{ + + + if(rec->is_dir){ + list[count-1].type = DT_DIR; //2 is dir +1 + } + else{ + list[count-1].type = DT_REG; //1 is file +1 + } + + strcpy(list[count-1].filename, rec->name); //+1 + + + //new color test + list[count-1].color = 0; + + if(enable_colored_list){ + for(int c=0; c 0) + { + /* Should sort! */ + qsort(list, count, sizeof(direntry_t), compare); + } + + + //print directory + display_dir(list, cursor, page, MAX_LIST, count, disp); +} + + +//void readCheatFile(char *filename, display_context_t disp){ +int readCheatFile(char *filename){ + + //var file readin + u8 tmp[32]; + u8 ok=0; + u8 firstr=1; + + char *cp, *vp, *dp; + char buffer[512]; + char temp[42]; + char buff[512]; + + int i = 0, j, type, offset = 0; + + int curr_cheat = -1; + + FatRecord rec_tmpf; + ok = fatFindRecord(filename, &rec_tmpf, 0); + + if(ok!=0){ + return -1; //err file not found + } + else{ + u8 resp = 0; + resp = fatOpenFileByeName(filename, 0); + + //filesize of the opend file -> is the readfile / 512 + int fsize= file.sec_available*512; + char cheatfile_rawdata[fsize]; + + resp = fatReadFile(&cheatfile_rawdata, fsize / 512); //1 cluster + + + int line_chr=0; + long c=0; + + for(c=0;c 3C1A A0XX 375A YYYY 241B 00ZZ A35B 0000 + if (gGSCodes[curr_cheat].gscodes) + gGSCodes[curr_cheat].gscodes = realloc(gGSCodes[curr_cheat].gscodes, offset + 16); + else + gGSCodes[curr_cheat].gscodes = malloc(16); + memcpy(&gGSCodes[curr_cheat].gscodes[offset], cp, 9); + memcpy(&gGSCodes[curr_cheat].gscodes[offset+9], vp, 5); + gGSCodes[curr_cheat].count++; + offset += 16; + break; + case 0xA1: + // write 16-bit value to (uncached) ram continuously + // A1XXYYYY ZZZZ => 3C1A A0XX 375A YYYY 241B ZZZZ A75B 0000 + if (gGSCodes[curr_cheat].gscodes) + gGSCodes[curr_cheat].gscodes = realloc(gGSCodes[curr_cheat].gscodes, offset + 16); + else + gGSCodes[curr_cheat].gscodes = malloc(16); + memcpy(&gGSCodes[curr_cheat].gscodes[offset], cp, 9); + memcpy(&gGSCodes[curr_cheat].gscodes[offset+9], vp, 5); + gGSCodes[curr_cheat].count++; + offset += 16; + break; + case 0xCC: + // deactivate expansion ram using 3rd method + // CC000000 0000 + if (gGSCodes[curr_cheat].gscodes) + gGSCodes[curr_cheat].gscodes = realloc(gGSCodes[curr_cheat].gscodes, offset + 16); + else + gGSCodes[curr_cheat].gscodes = malloc(16); + memcpy(&gGSCodes[curr_cheat].gscodes[offset], cp, 9); + memcpy(&gGSCodes[curr_cheat].gscodes[offset+9], vp, 5); + gGSCodes[curr_cheat].count++; + offset += 16; + break; + case 0xD0: + // do next gs code if ram location is equal to 8-bit value + // D0XXYYYY 00ZZ + if (gGSCodes[curr_cheat].gscodes) + gGSCodes[curr_cheat].gscodes = realloc(gGSCodes[curr_cheat].gscodes, offset + 16); + else + gGSCodes[curr_cheat].gscodes = malloc(16); + memcpy(&gGSCodes[curr_cheat].gscodes[offset], cp, 9); + memcpy(&gGSCodes[curr_cheat].gscodes[offset+9], vp, 5); + gGSCodes[curr_cheat].count++; + offset += 16; + break; + case 0xD1: + // do next gs code if ram location is equal to 16-bit value + // D1XXYYYY ZZZZ + if (gGSCodes[curr_cheat].gscodes) + gGSCodes[curr_cheat].gscodes = realloc(gGSCodes[curr_cheat].gscodes, offset + 16); + else + gGSCodes[curr_cheat].gscodes = malloc(16); + memcpy(&gGSCodes[curr_cheat].gscodes[offset], cp, 9); + memcpy(&gGSCodes[curr_cheat].gscodes[offset+9], vp, 5); + gGSCodes[curr_cheat].count++; + offset += 16; + break; + case 0xD2: + // do next gs code if ram location is not equal to 8-bit value + // D2XXYYYY 00ZZ + if (gGSCodes[curr_cheat].gscodes) + gGSCodes[curr_cheat].gscodes = realloc(gGSCodes[curr_cheat].gscodes, offset + 16); + else + gGSCodes[curr_cheat].gscodes = malloc(16); + memcpy(&gGSCodes[curr_cheat].gscodes[offset], cp, 9); + memcpy(&gGSCodes[curr_cheat].gscodes[offset+9], vp, 5); + gGSCodes[curr_cheat].count++; + offset += 16; + break; + case 0xD3: + // do next gs code if ram location is not equal to 16-bit value + // D1XXYYYY ZZZZ + if (gGSCodes[curr_cheat].gscodes) + gGSCodes[curr_cheat].gscodes = realloc(gGSCodes[curr_cheat].gscodes, offset + 16); + else + gGSCodes[curr_cheat].gscodes = malloc(16); + memcpy(&gGSCodes[curr_cheat].gscodes[offset], cp, 9); + memcpy(&gGSCodes[curr_cheat].gscodes[offset+9], vp, 5); + gGSCodes[curr_cheat].count++; + offset += 16; + break; + case 0xDD: + // deactivate expansion ram using 2nd method + // DD000000 0000 + if (gGSCodes[curr_cheat].gscodes) + gGSCodes[curr_cheat].gscodes = realloc(gGSCodes[curr_cheat].gscodes, offset + 16); + else + gGSCodes[curr_cheat].gscodes = malloc(16); + memcpy(&gGSCodes[curr_cheat].gscodes[offset], cp, 9); + memcpy(&gGSCodes[curr_cheat].gscodes[offset+9], vp, 5); + gGSCodes[curr_cheat].count++; + offset += 16; + break; + case 0xDE: + // set game boot address + // DEXXXXXX 0000 + if (gGSCodes[curr_cheat].gscodes) + gGSCodes[curr_cheat].gscodes = realloc(gGSCodes[curr_cheat].gscodes, offset + 16); + else + gGSCodes[curr_cheat].gscodes = malloc(16); + memcpy(&gGSCodes[curr_cheat].gscodes[offset], cp, 9); + memcpy(&gGSCodes[curr_cheat].gscodes[offset+9], vp, 5); + gGSCodes[curr_cheat].count++; + offset += 16; + break; + case 0xEE: + // deactivate expansion ram using 1st method + // EE000000 0000 + if (gGSCodes[curr_cheat].gscodes) + gGSCodes[curr_cheat].gscodes = realloc(gGSCodes[curr_cheat].gscodes, offset + 16); + else + gGSCodes[curr_cheat].gscodes = malloc(16); + memcpy(&gGSCodes[curr_cheat].gscodes[offset], cp, 9); + memcpy(&gGSCodes[curr_cheat].gscodes[offset+9], vp, 5); + gGSCodes[curr_cheat].count++; + offset += 16; + break; + case 0xF0: + // write 8-bit value to (cached) ram before boot + // F0XXXXXX 00YY + if (gGSCodes[curr_cheat].gscodes) + gGSCodes[curr_cheat].gscodes = realloc(gGSCodes[curr_cheat].gscodes, offset + 16); + else + gGSCodes[curr_cheat].gscodes = malloc(16); + memcpy(&gGSCodes[curr_cheat].gscodes[offset], cp, 9); + memcpy(&gGSCodes[curr_cheat].gscodes[offset+9], vp, 5); + gGSCodes[curr_cheat].count++; + offset += 16; + break; + case 0xF1: + // write 16-bit value to (cached) ram before boot + // F1XXXXXX YYYY + if (gGSCodes[curr_cheat].gscodes) + gGSCodes[curr_cheat].gscodes = realloc(gGSCodes[curr_cheat].gscodes, offset + 16); + else + gGSCodes[curr_cheat].gscodes = malloc(16); + memcpy(&gGSCodes[curr_cheat].gscodes[offset], cp, 9); + memcpy(&gGSCodes[curr_cheat].gscodes[offset+9], vp, 5); + gGSCodes[curr_cheat].count++; + offset += 16; + break; + case 0xFF: + // set code base + // FFXXXXXX 0000 + if (gGSCodes[curr_cheat].gscodes) + gGSCodes[curr_cheat].gscodes = realloc(gGSCodes[curr_cheat].gscodes, offset + 16); + else + gGSCodes[curr_cheat].gscodes = malloc(16); + memcpy(&gGSCodes[curr_cheat].gscodes[offset], cp, 9); + memcpy(&gGSCodes[curr_cheat].gscodes[offset+9], vp, 5); + gGSCodes[curr_cheat].count++; + offset += 16; + break; + } + + line_chr=0; + + + } + + + } + curr_cheat++; + gGSCodes[curr_cheat].count = 0xFFFF; // end of table + gGSCodes[curr_cheat].gscodes = NULL; + gGSCodes[curr_cheat].state = 0; + } + return 0; //ok + } + + + + + +void timing(display_context_t disp){ + + unsigned char tmp[32]; + + IO_WRITE(PI_STATUS_REG, 0x02); + + u32 pi0 = IO_READ(PI_BSD_DOM1_LAT_REG); + u32 pi1 = IO_READ(PI_BSD_DOM1_PWD_REG); + u32 pi2 = IO_READ(PI_BSD_DOM1_PGS_REG); + u32 pi3 = IO_READ(PI_BSD_DOM1_RLS_REG); + + printText("timing dom1:", 3, -1, disp); + sprintf(tmp, "lat=%x pwd=%x\npgs=%x rls=%x",pi0,pi1,pi2,pi3); + printText(tmp, 3, -1, disp); +} + + + +void bootRom(display_context_t disp, int silent){ + + + + + if(boot_cic!=0){ + + if(boot_save!=0){ + + u8 cfg_file[32]; + u8 found=0; + u8 resp=0; + u8 tmp[32]; + + + + + //set cfg file with last loaded cart info and save-type + sprintf(cfg_file, "/ED64/%s/LAST.CRT",save_path); + + resp = fatCreateRecIfNotExist(cfg_file, 0); + resp = fatOpenFileByeName(cfg_file, 1); //512 bytes fix one cluster + + //rom_filename[strlen(rom_filename)-4]='\0'; //cut extension + + static uint8_t cfg_file_data[512] = { 0 }; + cfg_file_data[0]=boot_save; + cfg_file_data[1]=boot_cic; + scopy(rom_filename, cfg_file_data+2); + //cfg_file_data[1]=cartID[0]; + //cfg_file_data[2]=cartID[1]; + //cfg_file_data[3]=cartID[2]; + //cfg_file_data[4]=cartID[3]; + + fatWriteFile(&cfg_file_data, 1); + + + sleep(500); + //set the fpga cart-save tpye + + // if(boot_save!=5) + evd_setSaveType(boot_save); + + + if(debug) printText("try to restore save from sd", 3, -1, disp); + resp = saveTypeFromSd(disp, rom_filename, boot_save); + + if(debug){ + sprintf(tmp, "saveTypeFromSd ret=%i",resp); + printText(tmp, 3, -1, disp); + } + + + + + + + } + + if(debug) + printText("Cartridge-Savetype set", 3, -1, disp); + + if(debug) + printText("information stored for reboot-save...", 3, -1, disp); + sleep(50); + + u32 cart, country; + u32 info = *(vu32 *)0xB000003C; + cart = info >> 16; + country = (info >> 8) & 0xFF; + + + + if(cheats_on){ + + gCheats=1; + printText("try to load cheat-file...", 3, -1, disp); + + char cheat_filename[64]; + //rom_filename[strlen(rom_filename)-4]='\0'; // cut extension - already cut + sprintf(cheat_filename,"/ED64/CHEATS/%s.cht",rom_filename); + + int ok = readCheatFile(cheat_filename); + + if(ok==0) + { + if(!silent) + printText("cheats found...", 3, -1, disp); + sleep(600); + }else{ + if(!silent) + printText("cheats not found...", 3, -1, disp); + gCheats=0; + sleep(2000); + } + + }else{ + gCheats=0; + + } + + disable_interrupts(); + if(!silent){ + if(gCheats) + printText("cheat boot", 3, -1, disp); + else + printText("normal boot", 3, -1, disp); + } + int bios_cic=getCicType(1); + + if(checksum_fix_on) + checksum_sdram(); + + + evd_lockRegs(); + sleep(1000); + + while( !(disp = display_lock()) ); + //blank screen to avoid glitches + graphics_fill_screen(disp, 0x000000FF); + display_show(disp); + + + simulate_boot(boot_cic, bios_cic); // boot_cic + + } +} + +void playSound(int snd){ + +//no thread support in libdragon yet, sounds pause the menu for a time :/ + + //maybe global + int v1; + + static SAMPLE *add_sfx = NULL; + if(snd==1) + add_sfx = Sample_Load("rom://ed64_mono.wav"); + + if(snd==2) + add_sfx = Sample_Load("rom://bamboo.wav"); + + if(snd==3) + add_sfx = Sample_Load("rom://warning.wav"); + + if(snd==4) + add_sfx = Sample_Load("rom://done.wav"); + + MikMod_SetNumVoices(-1, 2); + + + if (!add_sfx) { + MikMod_Exit(); + //return; + }else{ + + MikMod_EnableOutput(); + + audio_write_silence(); + audio_write_silence(); + + v1 = Sample_Play(add_sfx, 0, 0); + Voice_SetVolume(v1, 100); + + + //maybe put update function into a int/vblank callback + for(int s=0;s<50;s++){ + + MikMod_Update(); + sleep(10); + }//for + + MikMod_DisableOutput(); + Sample_Free(add_sfx); + } +} + +//draws the next char at the text input screen +void drawInputAdd(display_context_t disp, char *msg){ + + graphics_draw_box_trans(disp, 23, 5, 272, 18, 0x00000090); + position++; + sprintf(input_text, "%s%s",input_text, msg); + drawTextInput(disp,input_text); + +} + +//del the last char at the text input screen +void drawInputDel(display_context_t disp){ + + graphics_draw_box_trans(disp, 23, 5, 272, 18, 0x00000090); + if(position){ + input_text[position-1]='\0'; + drawTextInput(disp,input_text); + + position--; + } + +} + +void drawTextInput(display_context_t disp,char *msg){ + graphics_draw_text( disp, 40, 15, msg ); +} + + +void drawConfirmBox(display_context_t disp){ + + + drawBoxNumber(disp,5); + + display_show(disp); + + printText(" ", 9, 9, disp); + printText("Confirmation required:", 9, -1, disp); + printText(" ", 9, -1, disp); + printText(" ", 9, -1, disp); + printText(" Are you sure?", 9, -1, disp); + printText(" ", 9, -1, disp); + printText(" C-UP Continue ", 9, -1, disp); //set mapping 3 + printText(" ", 9, -1, disp); + printText(" B Cancel", 9, -1, disp); + + if(sound_on) playSound(3); + sleep(500); + +} + +void drawShortInfoBox(display_context_t disp, char* text, u8 mode){ + + if(mode==0) + drawBoxNumber(disp,7); + else if(mode==1) + drawBoxNumber(disp,8); + else if(mode==2) + drawBoxNumber(disp,10); + + printText(text, 9, 14, disp); + + display_show(disp); + + if(sound_on){ + if(mode==0) + playSound(4); + else if(mode==1) + playSound(3); + else if(mode==2) + playSound(4); + + } + sleep(300); + +} + + +void readRomConfig(display_context_t disp, char* short_filename, char* full_filename){ + + u8 found=0; + char cfg_filename[128]; + sprintf(rom_filename,"%s", short_filename); + rom_filename[strlen(rom_filename)-4]='\0'; // cut extension + sprintf(cfg_filename,"/ED64/CFG/%s.CFG",rom_filename); + + uint8_t rom_cfg_data[512];//[size]; + + FatRecord rec_tmpf; + found = fatFindRecord(cfg_filename, &rec_tmpf, 0); + + + if(found==0){ + //read rom-config + + u8 resp = 0; + resp = fatOpenFileByeName(cfg_filename, 0); + resp = fatReadFile(&rom_cfg_data, 1); + + + rom_config[1]=rom_cfg_data[0]; + rom_config[2]=rom_cfg_data[1]; + rom_config[3]=rom_cfg_data[2]; + rom_config[4]=rom_cfg_data[3]; + rom_config[5]=rom_cfg_data[4]; + rom_config[6]=rom_cfg_data[5]; + rom_config[7]=rom_cfg_data[6]; + rom_config[8]=rom_cfg_data[7]; + rom_config[9]=rom_cfg_data[8]; + + + + + }else{ + //preload with header data + + romInfoScreen(disp, full_filename, 1); //silent info screen with readout + + } + + + //preload cursor position 1 cic + rom_config[0]=1; + + + +} + +void alterRomConfig(int type, int mode){ + + + //mode 1 = increae mode 2 = decrease + + //cic + u8 min_cic=0; + u8 max_cic=5; + + //save + u8 min_save=0; + u8 max_save=5; + + //tv-type + u8 min_tv=0; + u8 max_tv=3; + + //cheat + u8 min_cheat=0; + u8 max_cheat=1; + + //chk fix + u8 min_chk_sum=0; + u8 max_chk_sum=1; + + //quality + u8 min_quality=0; + u8 max_quality=5; + + //country + u8 min_country=0; + u8 max_country=2; + + if(type==1){//start cic + + if(mode==1){ + //down + if(rom_config[1]min_cic) + rom_config[1]--; + if(rom_config[1]==3) rom_config[1]--; + } + }//end cic + else + if(type==2){//start save + + if(mode==1){ + //down + if(rom_config[2]min_save) + rom_config[2]--; + } + }//end save + else + if(type==3){//start tv + + if(mode==1){ + //down + if(rom_config[3]min_tv) + rom_config[3]--; + } + }//end tv + else + if(type==4){//start cheat + + if(mode==1){ + //down + if(rom_config[4]min_cheat) + rom_config[4]--; + } + }//end cheat + else + if(type==5){//start chk sum + + if(mode==1){ + //down + if(rom_config[5]min_chk_sum) + rom_config[5]--; + } + }//end chk sum + else + if(type==6){ //start quality + + if(mode==1){ + //down + if(rom_config[6]min_quality) + rom_config[6]--; + } + } + else + if(type==7){ //start country + + if(mode==1){ + //down + if(rom_config[7]min_country) + rom_config[7]--; + } + } + +} + +void drawToplistBox(display_context_t disp,int line){ + +list_pos_backup[0]=cursor; +list_pos_backup[1]=page; + +u8 list_size=0; + +if(line==0){ +//if(toplist_reload){ + + FatRecord *rec; + u8 resp = 0; + + count = 1; + dir_t buf; + + //load the directory-entry + resp = fatLoadDirByName("/ED64/CFG"); + + int dsize=dir->size; + + char toplist[dsize][256]; + + for (int i = 0; i < dir->size; i++){ + + rec = dir->rec[i]; + + + u8 rom_cfg_file[128]; + + //set rom_cfg + sprintf(rom_cfg_file, "/ED64/CFG/%s",rec->name ); + + static uint8_t cfg_file_data[512] = { 0 }; + + resp = fatOpenFileByeName(rom_cfg_file, 0); //512 bytes fix one cluster + resp = fatReadFile(&cfg_file_data, 1); + + + toplist[i][0]=(char)cfg_file_data[5]; //quality + strcpy(toplist[i]+1, cfg_file_data+32); //fullpath + + + } + + qsort(toplist, dsize, 256, compare_int_reverse); + + + if(dsize>15) + list_size=15; + else + list_size=dsize; + + for(int c = 0; c < list_size; c++) + strcpy(toplist15[c], toplist[c]); + + list_pos_backup[2]=list_size; +//toplist_reload=0; +} + + list_size=list_pos_backup[2]; + + + + u8 min=1; + u8 max=15; + u8 real_max=0; + + for(int c = 0; c < list_size; c++) + if(toplist15[c][0]!=0) + real_max++; + + max=real_max; + + //cursor line + if(line!=0){ + + if(line==1){ + //down + if(toplist_cursormin) + toplist_cursor--; + } + } + + + + + + drawBoxNumber(disp,9); //toplist +//drawToplistSelection(disp, toplist_cursor); + + printText(" -Toplist 15-", 4, 7, disp); + printText(" ", 4, -1, disp); + + uint32_t forecolor; + uint32_t backcolor; + backcolor = graphics_make_color(0x00, 0x00, 0x00, 0xFF); //bg + forecolor = graphics_make_color(0xFF, 0xFF, 0xFF, 0xFF); //fg + + + + for(int t = 0; t < 15; t++){ + + int quality_level=toplist15[t][0]; //quality + + if(quality_level!=0){ + + switch(quality_level){ + + case 1: forecolor = graphics_make_color(0xFF, 0xFF, 0xFF, 0xFF); //common (white) + break; + case 2: forecolor = graphics_make_color(0x00, 0xFF, 0x00, 0xCF); //uncommon (green) + break; + case 3: forecolor = graphics_make_color(0x1E, 0x90, 0xFF, 0xFF); //rare (blue) + break; + case 4: forecolor = graphics_make_color(0x9B, 0x30, 0xFF, 0xFF); //epic (purple) + break; + case 5: forecolor = graphics_make_color(0xFF, 0xA5, 0x00, 0xFF); //legendary (orange) + break; + default: break; + } + + graphics_set_color (forecolor, backcolor); + //max 32 chr + + u8 short_name[256]; + + sprintf(short_name, "%s", toplist15[t]+1); + + u8 * pch_s; // point-offset + pch_s=strrchr(short_name,'/'); + + /* + u8 * pch; // point-offset + pch=strrchr(short_name,'.'); + short_name[31]='\0'; + short_name[pch-short_name]='\0'; + */ + u8 * pch; // point-offset + pch=strrchr(pch_s,'.'); + pch_s[30]='\0'; //was 31 + pch_s[pch-pch_s]='\0'; + + //printText(short_name, 4, -1, disp); + if(t+1==toplist_cursor) + printText(pch_s+1, 5, -1, disp); + else + printText(pch_s+1, 4, -1, disp); + + //restore color + graphics_set_color(graphics_make_color(0xFF, 0xFF, 0xFF, 0xFF), graphics_make_color(0x00, 0x00, 0x00, 0x00)); + } + + }//for15 + +/* +if(line==0){ + if(sound_on) playSound(4); + sleep(500); +} +*/ + + +} + + + +void drawRomConfigBox(display_context_t disp,int line){ + + u8 min=1; + u8 max=7; + + //cursor line + if(line!=0){ + + if(line==1){ + //down + if(rom_config[0]min) + rom_config[0]--; + } + } + + drawBoxNumber(disp,6); + + drawConfigSelection(disp, rom_config[0]); + //display_show(disp); + + printText(" ", 9, 9, disp); + printText("Rom configuration:", 9, -1, disp); + printText(" ", 9, -1, disp); + + + + switch(rom_config[1]){ + case 0: printText(" CIC: 6101", 9, -1, disp); break; + case 1: printText(" CIC: 6102", 9, -1, disp); break; + case 2: printText(" CIC: 6103", 9, -1, disp); break; + case 3: printText(" CIC: 6104", 9, -1, disp); break; //do i really need 6104 in that list? :D + case 4: printText(" CIC: 6105", 9, -1, disp); break; + case 5: printText(" CIC: 6106", 9, -1, disp); break; + default: break; + + } + + switch(rom_config[2]){ + case 0: printText(" Save: Off/Mempak", 9, -1, disp); break; + case 1: printText(" Save: Sram 32", 9, -1, disp); break; + case 2: printText(" Save: Sram 128", 9, -1, disp); break; + case 3: printText(" Save: Eeprom 4k", 9, -1, disp); break; + case 4: printText(" Save: Eeprom 16k", 9, -1, disp); break; + case 5: printText(" Save: Flashram", 9, -1, disp); break; + default: break; + + } + + switch(rom_config[3]){ + case 0: printText(" Tv: Force off", 9, -1, disp); break; + case 1: printText(" Tv: NTSC", 9, -1, disp); break; + case 2: printText(" Tv: PAL", 9, -1, disp); break; + case 3: printText(" Tv: M-PAL", 9, -1, disp); break; + + default: break; + + + } + + switch(rom_config[4]){ + case 0: printText(" Cheat: off", 9, -1, disp); break; + case 1: printText(" Cheat: on", 9, -1, disp); break; + default: break; + + +} + + switch(rom_config[5]){ + case 0: printText("Checksum: disable fix", 9, -1, disp); break; + case 1: printText("Checksum: enable fix", 9, -1, disp); break; + default: break; + + +} + + switch(rom_config[6]){ + case 0: printText(" Rating: off", 9, -1, disp); break; + case 1: printText(" Rating: common", 9, -1, disp); break; + case 2: printText(" Rating: uncommon", 9, -1, disp); break; + case 3: printText(" Rating: rare", 9, -1, disp); break; + case 4: printText(" Rating: epic", 9, -1, disp); break; + case 5: printText(" Rating: legendary", 9, -1, disp); break; + + default: break; + + +} + + switch(rom_config[7]){ + case 0: printText(" Country: default", 9, -1, disp); break; + case 1: printText(" Country: NTSC", 9, -1, disp); break; + case 2: printText(" Country: PAL", 9, -1, disp); break; + default: break; + + +} + + + printText(" ", 9, -1, disp); + printText("B Cancel", 9, -1, disp); + printText("A Save config", 9, -1, disp); + + + + + //cursor position + // if(line=0) + // rom_config[0]=1; + +} + + + +//draws the charset for the textinputscreen +void drawSet1(display_context_t disp){ + + set=1; + uint32_t forecolor; + uint32_t backcolor; + backcolor = graphics_make_color(0x00, 0x00, 0x00, 0xFF); + + forecolor = graphics_make_color(0xFF, 0xFF, 0x00, 0xFF); //yellow + + + graphics_draw_text( disp, 80, 40, "<" ); //L + graphics_set_color (forecolor, backcolor); + graphics_draw_text( disp, 233, 40, "A" ); //R + graphics_draw_text( disp, 223, 62, "B" ); //G up + graphics_draw_text( disp, 210, 74, "C" ); //G left + graphics_draw_text( disp, 235, 74, "D" ); //G right + graphics_draw_text( disp, 193, 86, "E" ); //B + graphics_draw_text( disp, 223, 86, "F" ); //G down + graphics_draw_text( disp, 209, 100, "G" ); //A + +} + + +void drawSet2(display_context_t disp){ + + set=2; + uint32_t forecolor; + uint32_t backcolor; + backcolor = graphics_make_color(0x00, 0x00, 0x00, 0xFF); + + forecolor = graphics_make_color(0xFF, 0xFF, 0x00, 0xFF); + + + graphics_draw_text( disp, 80, 40, "<" ); + graphics_set_color (forecolor, backcolor); + graphics_draw_text( disp, 233, 40, "H" ); + graphics_draw_text( disp, 223, 62, "I" ); + graphics_draw_text( disp, 210, 74, "J" ); + graphics_draw_text( disp, 235, 74, "K" ); + graphics_draw_text( disp, 193, 86, "L" ); + graphics_draw_text( disp, 223, 86, "M" ); + graphics_draw_text( disp, 209, 100, "N" ); + +} + +void drawSet3(display_context_t disp){ + + set=3; + uint32_t forecolor; + uint32_t backcolor; + backcolor = graphics_make_color(0x00, 0x00, 0x00, 0xFF); + + forecolor = graphics_make_color(0xFF, 0xFF, 0x00, 0xFF); + + + graphics_draw_text( disp, 80, 40, "<" ); + graphics_set_color (forecolor, backcolor); + graphics_draw_text( disp, 233, 40, "O" ); + graphics_draw_text( disp, 223, 62, "P" ); + graphics_draw_text( disp, 210, 74, "Q" ); + graphics_draw_text( disp, 235, 74, "R" ); + graphics_draw_text( disp, 193, 86, "S" ); + graphics_draw_text( disp, 223, 86, "T" ); + graphics_draw_text( disp, 209, 100, "U" ); + +} + +display_context_t lockVideo(int wait) +{ + display_context_t dc; + + if (wait) + while (!(dc = display_lock())); + else + dc = display_lock(); + return dc; +} + + +void drawSet4(display_context_t disp){ + + set=4; + uint32_t forecolor; + uint32_t backcolor; + backcolor = graphics_make_color(0x00, 0x00, 0x00, 0xFF); + + forecolor = graphics_make_color(0xFF, 0xFF, 0x00, 0xFF); + + + graphics_draw_text( disp, 80, 40, "<" ); + graphics_set_color (forecolor, backcolor); + graphics_draw_text( disp, 233, 40, "V" ); + graphics_draw_text( disp, 223, 62, "W" ); + graphics_draw_text( disp, 210, 74, "X" ); + graphics_draw_text( disp, 235, 74, "Y" ); + graphics_draw_text( disp, 193, 86, "Z" ); + graphics_draw_text( disp, 223, 86, "-" ); + graphics_draw_text( disp, 209, 100, "_" ); + +} + + +//entry point +int main(void) +{ + + int fast_boot=0; + + + //reserve memory + list = malloc(sizeof(direntry_t)); + //list = malloc(sizeof(direntry_t)); + + + //dfs init for the rom-attached virtual filesystem + if(dfs_init( DFS_DEFAULT_LOCATION ) != DFS_ESUCCESS) + { + printf("Filesystem failed to start!\n"); + } + else + { + + int fpf = dfs_open("/firmware.bin"); + firmware = malloc( dfs_size( fpf ) ); + dfs_read( firmware, 1, dfs_size( fpf ), fpf ); + dfs_close( fpf ); + + + //everdirve initial function + configure(); + + + //fast boot for backup-save data + int sj = evd_readReg(0); + int save_job = evd_readReg(REG_SAV_CFG); + + if(save_job!=0) + fast_boot=1; + + + //not gamepads more or less the n64 hardware-controllers + controller_init(); + + //filesystem on + initFilesystem(); + sleep(200); + + + readConfigFile(); + //n64 initialization + + + if(tv_mode!=0){ + *(u32 *) 0x80000300 = tv_mode; + } + + + + init_interrupts(); + + + //background + display_init( res, DEPTH_32_BPP, 3, GAMMA_NONE, ANTIALIAS_RESAMPLE ); + + + //bg buffer + static display_context_t disp; + + //Grab a render buffer + while( !(disp = display_lock()) ); + //backgrounds from ramfs/libdragonfs + + if(!fast_boot){ + background0 = read_sprite( "rom://sprites/splash.sprite" ); + graphics_draw_sprite( disp, 0, 0, background0 ); //start-picture + display_show(disp); + } + + char background_path[64]; + sprintf(background_path, "/ED64/wallpaper/%s", background_image); + int found; + FatRecord rec_tmpf; + found = fatFindRecord(background_path, &rec_tmpf, 0); + if(found==0){ + + background = loadPng(background_path); + }else{ + background = read_sprite( "rom://sprites/background.sprite" ); + } + sleep(200); + + //mp3 + int buf_size; + char *buf_ptr; + + if(sound_on){ + + audio_init(44100,2); + //load soundsystem + timer_init(); + + + MikMod_RegisterAllDrivers(); + MikMod_RegisterAllLoaders(); + + md_mode = 0; + md_mode |= DMODE_16BITS; + md_mode |= DMODE_SOFT_MUSIC; + md_mode |= DMODE_SOFT_SNDFX; + + //md_mode |= DMODE_STEREO; + + md_mixfreq = audio_get_frequency(); + + MikMod_Init(""); + } + + + + if(!fast_boot){ + if(sound_on) playSound(1); + + sleep(2000); //splash screen duration + } + + + border_color_1 = translate_color(border_color_1_s); + border_color_2= translate_color(border_color_2_s); + box_color= translate_color(box_color_s); + selection_color= translate_color(selection_color_s); + selection_font_color= translate_color(selection_font_color_s); + list_font_color= translate_color(list_font_color_s); + list_dir_font_color= translate_color(list_dir_font_color_s); + + + + while( !(disp = display_lock()) ); + + drawBg(disp); //new + drawBoxNumber(disp,1); //new + + uint32_t *buffer = (uint32_t *)__get_buffer( disp ); //fg disp = 2 + + display_show(disp); //new + + backupSaveData(disp); + + + + while( !(disp = display_lock()) ); + + + sprintf(pwd,"%s", "/"); + readSDcard(disp,"/"); + + display_show(disp); + //chr input coord + x=30; + y=30; + + position=0; + set=1; + sprintf(input_text, ""); + + //sprite for chr input + int fp = dfs_open("/n64controller.sprite"); + sprite_t *contr = malloc( dfs_size( fp ) ); + dfs_read( contr, 1, dfs_size( fp ), fp ); + dfs_close( fp ); + + + + //wip font support + //init_fonts(); + + //sd card speed settings from config + if(sd_speed==2){ + bi_speed50(); + } + else{ + bi_speed25(); + } + + + //system main-loop with controller inputs-scan + while(1) + { + + if (playing==1) + playing = update_mp3(buf_ptr, buf_size); + + + + + if(input_mapping==1) + sleep(60); + + if(input_mapping==3){ + + while( !(disp = display_lock()) ); + + graphics_draw_sprite( disp, 0, 0, contr ); + /* Set the text output color */ + graphics_set_color( 0x0, 0xFFFFFFFF ); + + chr_forecolor = graphics_make_color(0xFF, 0x14, 0x94, 0xFF); //pink + graphics_set_color (chr_forecolor, chr_backcolor); + + graphics_draw_text( disp, 85, 55, "SETS" ); + graphics_draw_text( disp, 94, 70, "1" ); //u + graphics_draw_text( disp, 104, 82, "2" ); //r + graphics_draw_text( disp, 94, 93, "3" ); //d + graphics_draw_text( disp, 82, 82, "4" ); //l + + graphics_draw_text( disp, 208, 206, "press START" ); + + if(set==1) + drawSet1(disp); + if(set==2) + drawSet2(disp); + if(set==3) + drawSet3(disp); + if(set==4) + drawSet4(disp); + + drawTextInput(disp, input_text); + + /* Force backbuffer flip */ + display_show(disp); + + + } //mapping 2 chr input drawings + + + + //request controller + controller_scan(); + struct controller_data keys = get_keys_down(); + struct controller_data keys_held = get_keys_held(); + + if(keys.c[0].up || keys_held.c[0].up || keys_held.c[0].y > +25) + { + if(input_mapping==1){ + if(select_mode){ + + if(count!=0){ + + if(scroll_behaviour==1) + cursor--; + else + if(cursor!=0) + if((cursor+0)%20==0){ + + cursor--; + page-=20; + } + else{ + + cursor--; + } + + //end + while( !(disp = display_lock()) ); + + new_scroll_pos(&cursor, &page, MAX_LIST, count); + + clearScreen(disp); //part clear? + display_dir(list, cursor, page, MAX_LIST, count, disp); + + display_show(disp); + } + } + } + else + if(input_mapping==2){ + + } + else + if(input_mapping==3){ + //chr input screen + set=1; + } + else + if(input_mapping==7){ + + while( !(disp = display_lock()) ); + + new_scroll_pos(&cursor, &page, MAX_LIST, count); + clearScreen(disp); //part clear? + //display_show(disp_bg); + display_dir(list, cursor, page, MAX_LIST, count, disp); + + drawRomConfigBox(disp,2); + display_show(disp); + input_mapping=7; + sleep(80); + + } + else + if(input_mapping==8){ + + + + while( !(disp = display_lock()) ); + + /* + readSDcard(disp,list_pwd_backup); + + if(scroll_behaviour==0){ + cursor=list_pos_backup[0]; + page=list_pos_backup[1]; + } + else{ + cursor_line=0; + } + */ + //clearScreen(disp); //part clear? + drawBg(disp); //background + //display_dir(list, cursor, page, MAX_LIST, count, disp); + + // display_show(disp); + + + + drawToplistBox(disp,2); + + display_show(disp); + input_mapping=8; + sleep(80); + + } + + + } + + if(keys.c[0].down || keys_held.c[0].down || keys_held.c[0].y < -25) + { + + if(input_mapping==1){ + if(select_mode){ + + if(count!=0){ + if(scroll_behaviour==1) + cursor++; + else + if(cursor+1!=count) + if((cursor+1)%20==0){ + + cursor++; + page+=20; + } + else{ + + cursor++; + } + + + + while( !(disp = display_lock()) ); + + new_scroll_pos(&cursor, &page, MAX_LIST, count); + clearScreen(disp); //part clear? + //display_show(disp_bg); + display_dir(list, cursor, page, MAX_LIST, count, disp); + + + display_show(disp); + } + } + } + else + if(input_mapping==2){ + + } + else + if(input_mapping==3){ + //chr input screen + set=3; + } + else + if(input_mapping==7){ + + while( !(disp = display_lock()) ); + + new_scroll_pos(&cursor, &page, MAX_LIST, count); + clearScreen(disp); //part clear? + + //display_show(disp_bg); + display_dir(list, cursor, page, MAX_LIST, count, disp); + + + + + drawRomConfigBox(disp,1); + + display_show(disp); + input_mapping=7; + sleep(80); + + } + else + if(input_mapping==8){ + + + + while( !(disp = display_lock()) ); + +/* + readSDcard(disp,list_pwd_backup); + + if(scroll_behaviour==0){ + cursor=list_pos_backup[0]; + page=list_pos_backup[1]; + } + else{ + cursor_line=0; + } + */ + //clearScreen(disp); //part clear? + drawBg(disp); + //display_dir(list, cursor, page, MAX_LIST, count, disp); + + // display_show(disp); + + + + drawToplistBox(disp,1); + + display_show(disp); + input_mapping=8; + sleep(80); + + } + + } + else + if(keys.c[0].left || keys_held.c[0].left || keys_held.c[0].x < -25) + { + if(input_mapping==1){ + if(select_mode){ + + if(count!=0){ + if(scroll_behaviour==0) + if(cursor-20>=0){ + page-=20; + cursor=page; + } + } + + while( !(disp = display_lock()) ); + + new_scroll_pos(&cursor, &page, MAX_LIST, count); + clearScreen(disp); //part clear? + //display_show(disp_bg); + display_dir(list, cursor, page, MAX_LIST, count, disp); + + + display_show(disp); + } + + + } + else + if(input_mapping==2){ + + } + else + if(input_mapping==3){ + //chr input screen + set=4; + } + else + if(input_mapping==7){ + + while( !(disp = display_lock()) ); + + new_scroll_pos(&cursor, &page, MAX_LIST, count); + clearScreen(disp); //part clear? + //display_show(disp_bg); + display_dir(list, cursor, page, MAX_LIST, count, disp); + + + alterRomConfig(rom_config[0], 2); + + + drawRomConfigBox(disp,0); + display_show(disp); + input_mapping=7; + sleep(80); + + } + + } + else + if(keys.c[0].right || keys_held.c[0].right || keys_held.c[0].x > +25) + { + if(input_mapping==1){ + if(select_mode){ + + if(count!=0){ + if(scroll_behaviour==0){ + if(page+20!=count) + if(cursor+20 <= ( (count/20)*20 +19 ) ){ + page+=20; + cursor=page; + } + } + } + + while( !(disp = display_lock()) ); + + new_scroll_pos(&cursor, &page, MAX_LIST, count); + clearScreen(disp); //part clear? + //display_show(disp_bg); + display_dir(list, cursor, page, MAX_LIST, count, disp); + + + display_show(disp); + } + + + } + else + if(input_mapping==2){ + + } + else + if(input_mapping==3){ + //chr input screen + set=2; + } + else + if(input_mapping==7){ + + while( !(disp = display_lock()) ); + + new_scroll_pos(&cursor, &page, MAX_LIST, count); + clearScreen(disp); //part clear? + //display_show(disp_bg); + display_dir(list, cursor, page, MAX_LIST, count, disp); + + + alterRomConfig(rom_config[0], 1); + + drawRomConfigBox(disp,0); + display_show(disp); + input_mapping=7; + sleep(80); + + } + + } + else if( keys.c[0].start ){ + + + if(input_mapping==1){ + //quick boot + + if(quick_boot){ + + FatRecord rec_last; + uint8_t lastrom_cfg_data[512];//[size]; + + + if(fatFindRecord("/ED64/LASTROM.CFG", &rec_last, 0)==0){ + + //drawShortInfoBox(disp, " quick boot",0); + + u8 resp = 0; + resp = fatOpenFileByeName("/ED64/LASTROM.CFG", 0); + resp = fatReadFile(&lastrom_cfg_data, 1); + + + + u8 * short_s; + short_s=strrchr(lastrom_cfg_data,'/'); + + while( !(disp = display_lock()) ); + clearScreen(disp); + + sleep(100); + evd_ulockRegs(); + sleep(100); + + select_mode=9; + //short fullpath + readRomConfig(disp, short_s+1, lastrom_cfg_data); + + loadrom(disp, lastrom_cfg_data, 1); + display_show(disp); + + } + //nothing else :> + + drawShortInfoBox(disp, " rom not found",0); + + + } + else + if(list[cursor].type != DT_DIR && empty==0){ + + + char name_file[64]; + + if(strcmp(pwd,"/")==0 ) + sprintf(name_file,"/%s", list[cursor].filename); + else + sprintf(name_file,"%s/%s", pwd, list[cursor].filename); + + /*filetype + * 1 rom + * 2 mempak + * 3 background + * 4 mp3 + */ + + int ft=0; + char _upper_name_file[64]; + + strcpy(_upper_name_file, name_file); + + strhicase(_upper_name_file, strlen (_upper_name_file)); + sprintf(_upper_name_file, "%s", _upper_name_file ); + + u8 extension[4]; + u8 * pch; + pch=strrchr(_upper_name_file,'.'); //asd.n64 + + + sprintf(extension, "%s",(pch+1)); //0123456 + + + if(!strcmp(extension,"GB" )) + ft=5; + if(!strcmp(extension,"GBC" )) + ft=6; + if(!strcmp(extension,"NES" )) + ft=7; + if(!strcmp(extension,"GG" )) + ft=8; + if(!strcmp(extension,"MSX" )) + ft=9; + + if(!strcmp(extension,"MPK" )) + ft=2; + if(!strcmp(extension,"Z64") || !strcmp(extension,"V64" ) || !strcmp(extension,"N64" ) ) + ft=1; + + + + if(ft==1){ //rom + //load rom + + while( !(disp = display_lock()) ); + clearScreen(disp); + u16 msg = 0; + sleep(300); + evd_ulockRegs(); + sleep(300); + sprintf(rom_filename,"%s", list[cursor].filename); + + + select_mode=9; + + //read rom_config data + readRomConfig(disp, rom_filename, name_file); + + loadrom(disp,name_file, 1); + display_show(disp); + //display_show(disp); + + //rom loaded mapping + input_mapping=4; + + }else + if(ft==5 || ft==6){ //gb/gbc rom + //load rom + + while( !(disp = display_lock()) ); + clearScreen(disp); + u16 msg = 0; + sleep(300); + evd_ulockRegs(); + sleep(300); + sprintf(rom_filename,"%s", list[cursor].filename); + display_show(disp); + + select_mode=9; + + loadgbrom(disp,name_file); + + display_show(disp); + }else + if(ft==2){ //mempak + + + + + } + else + if(ft==7){ //nes + //load rom + + while( !(disp = display_lock()) ); + clearScreen(disp); + u16 msg = 0; + sleep(300); + evd_ulockRegs(); + sleep(300); + sprintf(rom_filename,"%s", list[cursor].filename); + display_show(disp); + + select_mode=9; + + loadnesrom(disp,name_file); + + display_show(disp); + } + else + if(ft==8){ //gg + //load gg rom + + while( !(disp = display_lock()) ); + clearScreen(disp); + u16 msg = 0; + sleep(300); + evd_ulockRegs(); + sleep(300); + sprintf(rom_filename,"%s", list[cursor].filename); + display_show(disp); + + select_mode=9; + + loadggrom(disp,name_file); + + display_show(disp); + } + else + if(ft==9){ //msx2 + //load msx2 rom + + while( !(disp = display_lock()) ); + clearScreen(disp); + u16 msg = 0; + sleep(300); + evd_ulockRegs(); + sleep(300); + sprintf(rom_filename,"%s", list[cursor].filename); + display_show(disp); + + select_mode=9; + + loadmsx2rom(disp,name_file); + + display_show(disp); + } + + + } + } + else + if(input_mapping==2){ + + + } + else + if(input_mapping==3){ + + + //better config color-set + graphics_set_color(graphics_make_color(0xFF, 0xFF, 0xFF, 0xFF), graphics_make_color(0x00, 0x00, 0x00, 0x00)); + clearScreen(disp); + display_dir(list, cursor, page, MAX_LIST, count, disp); + + if(input_text[0]!='\0'){ //input_text is set - do backup + drawBoxNumber(disp,2); + + display_show(disp); + + printText("Mempak-Backup:", 9, 9, disp); + printText(" ", 9, -1, disp); + printText("search...", 9, -1, disp); + mpk_to_file(disp, input_text, 0); + sleep(300); + + drawShortInfoBox(disp, " done",0); + //input_mapping=98; + sleep(2000); + + //reread filesystem + cursor_line=0; + readSDcard(disp,"/"); + } + + input_mapping=1; + + } + else + if(input_mapping==4){ + //rom start screen + + //normal boot + //boot the loaded rom + bootRom(disp, 0); + //never return + + } + + + + } + else if( keys.c[0].L ) + { + if(input_mapping==1){ + + input_mapping=2; + + drawBoxNumber(disp,2); + + display_show(disp); + + printText("Mempak-Subsystem:", 9, 9, disp); + printText(" ", 9, -1, disp); + printText(" ", 9, -1, disp); + printText(" Z: View content", 9, -1, disp); + printText(" ", 9, -1, disp); + printText(" A: Backup - new", 9, -1, disp); //set mapping 3 + printText(" ", 9, -1, disp); + printText(" R: Format", 9, -1, disp); + printText(" ", 9, -1, disp); + printText(" B: Abort", 9, -1, disp); + if(sound_on) playSound(2); + sleep(500); + } + else + if(input_mapping==2){ + + } + else + if(input_mapping==3){ + //chr input screen + drawInputDel(disp); + } + else + if(input_mapping==9){ + //c-up or A + drawConfirmBox(disp); + //confirm restore mpk + input_mapping=6; + } + + + //placeholder + } + else if( keys.c[0].R ) + { + if(input_mapping==1){ + + + + } + else + if(input_mapping==2){ + + //c-up or A + drawConfirmBox(disp); + //confirm format mpk + input_mapping=5; + + } + else + if(input_mapping==3){ + //chr input screen + if(set==1) + drawInputAdd(disp, "A"); //P X ) + if(set==2) + drawInputAdd(disp, "H"); + if(set==3) + drawInputAdd(disp, "O"); + if(set==4) + drawInputAdd(disp, "V"); + } + else + if(input_mapping==9){ + //c-up or A + drawConfirmBox(disp); + //confirm quick-backup + input_mapping=10; + + + } + + //placeholder + } + else if( keys.c[0].C_up ) + { + if(input_mapping==1){ + + + if(list[cursor].type != DT_DIR && empty==0){ + drawBoxNumber(disp,11); + + char *part; + + part = malloc(slen(list[cursor].filename)); + sprintf(part,"%s", list[cursor].filename); + part[31]='\0'; + + printText(part, 4, 14, disp); + + + if(slen(list[cursor].filename)>31){ + sprintf(part,"%s", list[cursor].filename+31); + part[31]='\0'; + printText(part, 4, -1, disp); + } + + free(part); + input_mapping=98; + } + + } + else + if(input_mapping==2){ + + } + else + if(input_mapping==3){ + //chr input screen + if(set==1) + drawInputAdd(disp, "B"); //P X ) + if(set==2) + drawInputAdd(disp, "I"); + if(set==3) + drawInputAdd(disp, "P"); + if(set==4) + drawInputAdd(disp, "W"); + } + else + if(input_mapping==4){ + //rom start screen + if(cheats_on==0){ + printText("cheat system activated...", 3, -1, disp); + cheats_on=1; + } + + } + else //format mpk + if(input_mapping==5){ + + drawBoxNumber(disp,2); + // writeInitText(disp); + + display_show(disp); + + printText("Mempak-Format:", 9, 9, disp); + printText(" ", 9, -1, disp); + //printText("search...", 9, -1, disp); + + printText("formating...", 9, -1, disp); + + /* Make sure they don't have a rumble pak inserted instead */ + switch( identify_accessory( 0 ) ) + { + + + case ACCESSORY_NONE: + + printText("No Mempak", 9, -1, disp); + break; + case ACCESSORY_MEMPAK: + printText("Please wait...", 9, -1, disp); + if( format_mempak( 0 ) ) + { + printText("Error formatting!", 9, -1, disp); + } + else + { + // printText("done...", 9, -1, disp); + + drawShortInfoBox(disp, " done",0); + input_mapping=98; + + } + + break; + case ACCESSORY_RUMBLEPAK: + + printText("Cannot format rumblepak!", 9, -1, disp); + break; + } + + + sleep(500); + + //reread filesystem + //readSDcard(disp,"/"); + + + input_mapping=98; + } + else //restore mpk + if(input_mapping==6){ + + + drawBoxNumber(disp,2); + + + display_show(disp); + + printText("Mempak-Restore:", 9, 9, disp); + printText(" ", 9, -1, disp); + + file_to_mpk(disp, rom_filename); + + sleep(300); + + drawShortInfoBox(disp, " done",0); + input_mapping=98; + + // input_mapping=98; + display_show(disp); + + + } + else //quick-backup + if(input_mapping==10){ + drawBoxNumber(disp,2); + + display_show(disp); + + printText("Quick-Backup:", 9, 9, disp); + printText(" ", 9, -1, disp); + printText("search...", 9, -1, disp); + mpk_to_file(disp, list[cursor].filename ,1); //quick + sleep(300); + + drawShortInfoBox(disp, " done",0); + //input_mapping=98; + sleep(500); + input_mapping=98; + //reread filesystem + // cursor_line=0; + //readSDcard(disp,"/"); + } + + + + } + else + if( keys.c[0].C_right ) + { + //placeholder + if(input_mapping==1 && list[cursor].type != DT_DIR){ + //show rom cfg screen + + char name_file[64]; + + if(strcmp(pwd,"/")==0 ) + sprintf(name_file,"/%s", list[cursor].filename); + else + sprintf(name_file,"%s/%s", pwd, list[cursor].filename); + + /*filetype + * 1 rom + */ + + int ft=0; + char _upper_name_file[64]; + + strcpy(_upper_name_file, name_file); + + strhicase(_upper_name_file, strlen (_upper_name_file)); + sprintf(_upper_name_file, "%s", _upper_name_file ); + + u8 extension[4]; + u8 * pch; + pch=strrchr(_upper_name_file,'.'); //asd.n64 + + + sprintf(extension, "%s",(pch+1)); //0123456 + + + if(!strcmp(extension,"Z64") || !strcmp(extension,"V64" ) || !strcmp(extension,"N64" ) ) + ft=1; + + if(ft==1){ //rom + //cfg rom + + sprintf(rom_filename,"%s", list[cursor].filename); + + + //preload config or file header + readRomConfig(disp, rom_filename, name_file); + + drawRomConfigBox(disp,0); + display_show(disp); + input_mapping=7; + + } + + + } + else + if(input_mapping==2){ + + } + else + if(input_mapping==3){ + //chr input screen + if(set==1) + drawInputAdd(disp, "D"); //P X ) + if(set==2) + drawInputAdd(disp, "K"); + if(set==3) + drawInputAdd(disp, "R"); + if(set==4) + drawInputAdd(disp, "Y"); + } + else + if(input_mapping==4){ + //rom start screen + + if(force_tv!=0){ + printText("force tv mode...", 3, -1, disp); + + } + + + } + + + } + else + if( keys.c[0].C_down ) + { + //placeholder + if(input_mapping==1){ + + input_mapping=99; + + //sprintf(list_pwd_backup,"%s",pwd); + scopy(pwd, list_pwd_backup); + while( !(disp = display_lock()) ); + + drawBg(disp); //background + + + toplist_cursor=1; + drawToplistBox(disp,0); //0 = load entries + + display_show(disp); + + input_mapping=8; + //input_mapping=97; + + } + else + if(input_mapping==2){ + + } + else + if(input_mapping==3){ + //chr input screen + if(set==1) + drawInputAdd(disp, "F"); //P X ) + if(set==2) + drawInputAdd(disp, "M"); + if(set==3) + drawInputAdd(disp, "T"); + if(set==4) + drawInputAdd(disp, "-"); //GR Set4 + } + } + else + if( keys.c[0].C_left ) + { + //placeholder + if(input_mapping==1){ + + if(input_mapping==1 && list[cursor].type != DT_DIR ){//open + + char name_file[64]; + + if(strcmp(pwd,"/")==0 ) + sprintf(name_file,"/%s", list[cursor].filename); + else + sprintf(name_file,"%s/%s", pwd, list[cursor].filename); + + /*filetype + * 1 rom + * 2 mempak + * 3 background + * 4 mp3 + */ + + int ft=0; + char _upper_name_file[64]; + + strcpy(_upper_name_file, name_file); + + strhicase(_upper_name_file, strlen (_upper_name_file)); + sprintf(_upper_name_file, "%s", _upper_name_file ); + + u8 extension[4]; + u8 * pch; + pch=strrchr(_upper_name_file,'.'); + + + sprintf(extension, "%s",(pch+1)); + + if(!strcmp(extension,"MPK" )) + ft=2; + if(!strcmp(extension,"Z64") || !strcmp(extension,"V64" ) || !strcmp(extension,"N64" ) ) + ft=1; + + + if(ft==1){ //rom + //load rom + + + input_mapping=99; + + drawBoxNumber(disp,3); //rominfo + + u16 msg = 0; + sleep(300); + evd_ulockRegs(); + sleep(300); + sprintf(rom_filename,"%s", list[cursor].filename); + romInfoScreen(disp, name_file, 0); + + + if(sound_on) playSound(2); + sleep(500); + input_mapping=98; + + } + if(ft==2){ //mpk file + + drawBoxNumber(disp,4); + // writeInitText(disp); + + display_show(disp); + + if(strcmp(pwd,"/")==0 ) + sprintf(rom_filename,"/%s", list[cursor].filename); + else + sprintf(rom_filename,"%s/%s", pwd, list[cursor].filename); + + + // sprintf(rom_filename,"%s", list[cursor].filename); + view_mpk_file(disp, rom_filename); + + if(sound_on) playSound(2); + sleep(500); + input_mapping=98; + + + } + + } //mapping and not dir + + } + else + if(input_mapping==2){ + + } + else + if(input_mapping==3){ + //chr input screen + if(set==1) + drawInputAdd(disp, "C"); //P X ) + if(set==2) + drawInputAdd(disp, "J"); + if(set==3) + drawInputAdd(disp, "Q"); + if(set==4) + drawInputAdd(disp, "X"); + } + } + else + if(keys.c[0].Z){ + + if(input_mapping==1){ + + input_mapping=99; + + drawBoxNumber(disp,2); + + display_show(disp); + + printText("About: ", 9, 9, disp); + printText(" ", 9, -1, disp); + printText("ALT64: v0.1.8.6", 9, -1, disp); + printText("by saturnu", 9, -1, disp); + printText(" ", 9, -1, disp); + printText("thanks to:", 9, -1, disp); + printText("Krikzz", 9, -1, disp); + printText("ChillyWilly", 9, -1, disp); + printText("ShaunTaylor", 9, -1, disp); + printText("Conle", 9, -1, disp); + + /* + char mpkp[64]; + sprintf(mpkp, "%s", mempak_path); + printText(mpkp, 9, -1, disp); + */ + + if(sound_on) playSound(2); + sleep(500); + input_mapping=98; + + + + } + else + if(input_mapping==2){ + + + drawBoxNumber(disp,4); + // writeInitText(disp); + + display_show(disp); + + + view_mpk(disp); + + if(sound_on) playSound(2); + sleep(500); + input_mapping=98; + + } + + + //placeholder + } + else + if(keys.c[0].A) //open + { + + + if(input_mapping==1){ + while( !(disp = display_lock()) ); + + if(input_mapping==1 && list[cursor].type == DT_DIR && empty==0){ + char name_dir[256]; + + /* init pwd=/ + * / + * + * cursor=ED64 + * /ED64 + * + * cursor=SAVE + * /ED64/SAVE + */ + + + if(strcmp(pwd,"/")==0 ) + sprintf(name_dir,"/%s", list[cursor].filename); + else + sprintf(name_dir,"%s/%s", pwd, list[cursor].filename); + + sprintf(curr_dirname,"%s", list[cursor].filename); + sprintf(pwd,"%s", name_dir); + + //load dir + + cursor_lastline=0; + cursor_line=0; + + + //backup tree cursor postions + + if(cd_behaviour==1){ + //if(cursor){ + cursor_history[cursor_history_pos]=cursor; + cursor_history_pos++; + //} + } + + readSDcard(disp,name_dir); + display_show(disp); + + + + }//mapping 1 and dir + else + if(input_mapping==1 && list[cursor].type != DT_DIR && empty==0){//open + + char name_file[256]; + + if(strcmp(pwd,"/")==0 ) + sprintf(name_file,"/%s", list[cursor].filename); + else + sprintf(name_file,"%s/%s", pwd, list[cursor].filename); + + /*filetype + * 1 rom + * 2 mempak + * 3 background + * 4 mp3 + */ + + int ft=0; + char _upper_name_file[256]; + + strcpy(_upper_name_file, name_file); + + strhicase(_upper_name_file, strlen (_upper_name_file)); + sprintf(_upper_name_file, "%s", _upper_name_file ); + + u8 extension[4]; + u8 * pch; + pch=strrchr(_upper_name_file,'.'); //asd.n64 + + + sprintf(extension, "%s",(pch+1)); //0123456 + + + if(!strcmp(extension,"GB" )) + ft=5; + if(!strcmp(extension,"GBC" )) + ft=6; + if(!strcmp(extension,"NES" )) + ft=7; + if(!strcmp(extension,"GG" )) + ft=8; + if(!strcmp(extension,"MSX" )) + ft=9; + if(!strcmp(extension,"MP3" )) + ft=10; + + if(!strcmp(extension,"MPK" )) + ft=2; + if(!strcmp(extension,"Z64") || !strcmp(extension,"V64" ) || !strcmp(extension,"N64" ) ) + ft=1; + + if(ft==1){ //rom + //load rom + + while( !(disp = display_lock()) ); + clearScreen(disp); + u16 msg = 0; + sleep(300); + evd_ulockRegs(); + sleep(300); + sprintf(rom_filename,"%s", list[cursor].filename); + + + if(quick_boot){ + + u8 resp=0; + resp = fatCreateRecIfNotExist("/ED64/LASTROM.CFG", 0); + resp = fatOpenFileByeName("/ED64/LASTROM.CFG", 1); //512 bytes fix one cluster + static uint8_t lastrom_file_data[512] = { 0 }; + scopy(name_file, lastrom_file_data); + + fatWriteFile(&lastrom_file_data, 1); + } + + select_mode=9; + + //read rom_config data + readRomConfig(disp, rom_filename, name_file); + + loadrom(disp,name_file, 1); + display_show(disp); + //display_show(disp); + + //rom loaded mapping + input_mapping=4; + + + }else + if(ft==5 || ft==6){ //gb/gbc rom + //load rom + + while( !(disp = display_lock()) ); + + clearScreen(disp); + u16 msg = 0; + sleep(300); + evd_ulockRegs(); + sleep(300); + sprintf(rom_filename,"%s", list[cursor].filename); + display_show(disp); + + select_mode=9; + + loadgbrom(disp,name_file); + + display_show(disp); + }else + if(ft==2){ //mempak + + + while( !(disp = display_lock()) ); + + + clearScreen(disp); //part clear? + display_dir(list, cursor, page, MAX_LIST, count, disp); + + display_show(disp); + + + + /* + //c-up or A + drawConfirmBox(disp); + //confirm format mpk + input_mapping=6; + * + */ + + drawShortInfoBox(disp, " L=Restore R=Backup", 2); + input_mapping=9; + + sprintf(rom_filename,"%s", name_file); + + + } + else + if(ft==7){ //nes + //load rom + + while( !(disp = display_lock()) ); + clearScreen(disp); + u16 msg = 0; + sleep(300); + evd_ulockRegs(); + sleep(300); + sprintf(rom_filename,"%s", list[cursor].filename); + display_show(disp); + + select_mode=9; + + loadnesrom(disp,name_file); + + display_show(disp); + } + else + if(ft==8){ //gg + //load gg rom + + while( !(disp = display_lock()) ); + clearScreen(disp); + u16 msg = 0; + sleep(300); + evd_ulockRegs(); + sleep(300); + sprintf(rom_filename,"%s", list[cursor].filename); + display_show(disp); + + select_mode=9; + + loadggrom(disp,name_file); + + display_show(disp); + } + else + if(ft==9){ //msx2 + //load msx2 rom + + while( !(disp = display_lock()) ); + clearScreen(disp); + u16 msg = 0; + sleep(300); + evd_ulockRegs(); + sleep(300); + sprintf(rom_filename,"%s", list[cursor].filename); + display_show(disp); + + select_mode=9; + + loadmsx2rom(disp,name_file); + + display_show(disp); + } + else + if(ft==10){ //mp3 + //load mp3 + buf_size = audio_get_buffer_length() * 4; + buf_ptr = malloc(buf_size); + + + while( !(disp = display_lock()) ); + clearScreen(disp); + drawShortInfoBox(disp, " playback",0); + + long long start = 0, end = 0, curr, pause = 0, samples; + int rate = 44100, last_rate = 44100, channels = 2; + + audio_init(44100,2); + + start_mp3(name_file, &samples, &rate, &channels); + playing=1; + select_mode=9; + + input_mapping=11;//mp3 stop + + display_show(disp); + + } + + + + } //mapping and not dir + + + }//mapping 1 end + else + if(input_mapping==2){ + + //open up charinput screen + input_mapping=3; + //inputText(, contr,disp); + input_text[0]='\0'; + graphics_draw_sprite( disp, 0, 0, contr ); + + + } + else + if(input_mapping==3){ + //chr input screen + + // mode = 1 - mode; + if(set==1) + drawInputAdd(disp, "G"); //P X ) + if(set==2) + drawInputAdd(disp, "N"); + if(set==3) + drawInputAdd(disp, "U"); + if(set==4) + drawInputAdd(disp, "_"); + } + else + if(input_mapping==7){ + + //save rom_cfg[] to + // /ED64/CFG/Romname.cfg if not exist create + // if exist open/write + + //print confirm msg + char name_file[256]; + + if(strcmp(pwd,"/")==0 ) + sprintf(name_file,"/%s", list[cursor].filename); + else + sprintf(name_file,"%s/%s", pwd, list[cursor].filename); + + + + u8 rom_cfg_file[128]; + + u8 resp=0; + + //set rom_cfg + sprintf(rom_cfg_file, "/ED64/CFG/%s.CFG",rom_filename ); + + resp = fatCreateRecIfNotExist(rom_cfg_file, 0); + resp = fatOpenFileByeName(rom_cfg_file, 1); //512 bytes fix one cluster + + + static uint8_t cfg_file_data[512] = { 0 }; + cfg_file_data[0]=rom_config[1]; //cic + cfg_file_data[1]=rom_config[2]; //save + cfg_file_data[2]=rom_config[3]; //tv + cfg_file_data[3]=rom_config[4]; //cheat + cfg_file_data[4]=rom_config[5]; //chksum + cfg_file_data[5]=rom_config[6]; //rating + cfg_file_data[6]=rom_config[7]; //country + cfg_file_data[7]=rom_config[8]; + cfg_file_data[8]=rom_config[9]; + + //copy full rom path to offset at 32 byte - 32 bytes reversed + scopy(name_file, cfg_file_data+32); //filename to rom_cfg file + + + + fatWriteFile(&cfg_file_data, 1); + + + sleep(200); + + drawShortInfoBox(disp, " done",0); + toplist_reload=1; + + + input_mapping=98; + + + /* + //back to list mode + while( !(disp = display_lock()) ); + + + clearScreen(disp); //part clear? + display_dir(list, cursor, page, MAX_LIST, count, disp); + + display_show(disp); + + input_mapping=1; + * + * + */ + } + else + if(input_mapping==8){ + //run from toplist + + + + u8 * pch_s; + pch_s=strrchr(toplist15[toplist_cursor-1]+1,'/'); + //drawShortInfoBox(disp,pch_s,0); + + readRomConfig(disp, pch_s+1, toplist15[toplist_cursor-1]+1); + + loadrom(disp,toplist15[toplist_cursor-1]+1, 1); + //display_show(disp); + //display_show(disp); + + //rom loaded mapping + input_mapping=4; + + } + else + if(input_mapping==98){ + //rom info screen + + while( !(disp = display_lock()) ); + + + clearScreen(disp); //part clear? + display_dir(list, cursor, page, MAX_LIST, count, disp); + + display_show(disp); + + input_mapping=1; + } + + + }//key a + + + else if(keys.c[0].B ) //go back + { + + if(input_mapping==1){ + + + if(!(strcmp(pwd,"/")==0)){ + while( !(disp = display_lock()) ); + + //replace by strstr()? :> + int slash_pos=0; + int i = 0; + while(pwd[i] != '\0'){ + if(pwd[i] == '/') + slash_pos=i; + i++; + } + char new_pwd[64]; + + int j; + for(j = 0; j < slash_pos; j++) + new_pwd[j] = pwd[j]; + + if(j==0) j++; + new_pwd[j] = '\0'; + + sprintf(pwd,"%s", new_pwd); + + cursor_lastline=0; + cursor_line=0; + + + + + readSDcard(disp,pwd); + + + if(cd_behaviour==1){ + cursor_history_pos--; + cursor=cursor_history[cursor_history_pos]; + + if(cursor_history[cursor_history_pos]>0){ + + cursor_line=cursor_history[cursor_history_pos]-1; + + if(scroll_behaviour==0){ + + int p = cursor_line/20; + page=p*20; + } + else{ + if(cursor_line>19) + cursor_line=19; + } + } + else + cursor_line=0; + } + + + + + + while( !(disp = display_lock()) ); + + new_scroll_pos(&cursor, &page, MAX_LIST, count); + + clearScreen(disp); //part clear? + display_dir(list, cursor, page, MAX_LIST, count, disp); + + display_show(disp); + + + + + + + + + display_show(disp); + }//not root + + + } + else + if(input_mapping==2){ + while( !(disp = display_lock()) ); + + graphics_set_color(graphics_make_color(0xFF, 0xFF, 0xFF, 0xFF), graphics_make_color(0x00, 0x00, 0x00, 0x00)); + clearScreen(disp); + + display_show(disp); + + display_dir(list, cursor, page, MAX_LIST, count, disp); + input_mapping=1; + display_show(disp); + } + else + if(input_mapping==3){ + //chr input screen + + /* Lazy switching */ + // mode = 1 - mode; + if(set==1) + drawInputAdd(disp, "E"); //P X ) + if(set==2) + drawInputAdd(disp, "L"); + if(set==3) + drawInputAdd(disp, "S"); + if(set==4) + drawInputAdd(disp, "Z"); + + } + else + if(input_mapping==98 || input_mapping==5 || input_mapping==6 || input_mapping==7 || input_mapping==10){ + //rom info screen + + while( !(disp = display_lock()) ); + + clearScreen(disp); //part clear? + display_dir(list, cursor, page, MAX_LIST, count, disp); + + display_show(disp); + + input_mapping=1; + } + else + if(input_mapping==8){ + //leave toplist + + while( !(disp = display_lock()) ); + + + readSDcard(disp,list_pwd_backup); + + if(scroll_behaviour==0){ + cursor=list_pos_backup[0]; + page=list_pos_backup[1]; + } + else{ + cursor_line=0; + } + + clearScreen(disp); //part clear? + display_dir(list, cursor, page, MAX_LIST, count, disp); + + display_show(disp); + + input_mapping=1; + } + else + if(input_mapping==11){ + //stop mp3 + + stop_mp3(); + playing=0; + + clearScreen(disp); //part clear? + display_dir(list, cursor, page, MAX_LIST, count, disp); + + display_show(disp); + + input_mapping=1; + } + + + + + + }//key b + //if(input_mapping==2) + sleep(10); + + } + } + + //never return + while(1); + + return 0; +} + diff --git a/menu.h b/menu.h new file mode 100644 index 0000000..30f70ed --- /dev/null +++ b/menu.h @@ -0,0 +1,108 @@ +//protos maybe some aren't necessary any longer +void strhicase(u8 *str, u8 len); +void PI_DMAWait(void); +void evd_writeReg(u8 reg, u16 val); +void bootRom(display_context_t disp, int silent); +void loadrom(display_context_t disp, u8 *buff, int fast); +void loadgbrom(display_context_t disp, u8 *buff); +void view_mpk_file(display_context_t disp, char *mpk_filename); +char TranslateNotes( char *bNote, char *Text ); +inline char CountBlocks( char *bMemPakBinary, char *aNoteSizes ); +void view_mpk(display_context_t disp); +void evd_ulockRegs(void); +u8 fatReadCluster(void *dst); +u8 fatGetNextCluster(u32 *cluster); +u8 fatSkipCluster(); +void memRomWrite32(u32 addr, u32 val); +u32 memRomRead32(u32 addr); +uint32_t translate_color(char *hexstring); +void evd_init(void); +void memSpiSetDma(u8 mode); +u16 evd_getFirmVersion(void); +u8 evd_isSDMode(void); +void dma_write_s(void * ram_address, unsigned long pi_address, unsigned long len); +void dma_read_s(void * ram_address, unsigned long pi_address, unsigned long len); +void readSDcard(display_context_t disp, char *directory); +int readConfigFile(void); +//void readCheatFile(display_context_t disp); +int readCheatFile(char *filename); +static int configHandler(void* user, const char* section, const char* name, const char* value); +int saveTypeToSd(display_context_t disp, char* save_filename ,int tpye); +int saveTypeFromSd(display_context_t disp, char* rom_name, int stype); +int backupSaveData(display_context_t disp); +void romInfoScreen(display_context_t disp, u8 *buff, int silent); +void checksum_sdram(void); +void drawShortInfoBox(display_context_t disp, char* text, u8 mode); +void drawToplistSelection(display_context_t disp,int l); +//boxes +void drawBox(short x, short y, short width, short height, display_context_t disp); +void drawBoxNumber(display_context_t disp, int box); + +void printText(char *msg, int x, int y, display_context_t dcon); +void lprintText(char *msg, int x, int y, display_context_t dcon); +void sleep(unsigned long int ms); +void clearScreen(display_context_t disp); + +//mp3 +void start_mp3(char *fname, long long *samples, int *rate, int *channels); + +//character input functions +void drawTextInput(display_context_t disp,char *msg); +void drawInputAdd(display_context_t disp, char *msg); +void drawInputDel(display_context_t disp); + +void readRomConfig(display_context_t disp, char* short_filename, char* full_filename); +void alterRomConfig(int type, int mode); +void drawConfigSelection(display_context_t disp,int l); +void drawRomConfigBox(display_context_t disp,int line); + +#define MAX_LIST 20 +#define REG_VER 11 +#define ED_CFG_SDRAM_ON 0 + +#define ishexchar(c) (((c >= '0') && (c <= '9')) || ((c >= 'A') && (c <= 'F')) || ((c >= 'a') && (c <= 'f'))) + + +//debug flag to enable some debug outputs +/////////////////////////////////////////// +#define DBG_MODE 0 +/////////////////////////////////////////// + +/** + * @brief Return the uncached memory address of a cached address + * + * @param[in] x + * The cached address + *uint32_t + * @return The uncached address + */ +#define UNCACHED_ADDR(x) ((void *)(((uint32_t)(x)) | 0xA0000000)) + +/** + * @brief Align a memory address to 16 byte offset + * + * @param[in] x + * Unaligned memory address + * + * @return An aligned address guaranteed to be >= the unaligned address + */ +#define ALIGN_16BYTE(x) ((void *)(((uint32_t)(x)+15) & 0xFFFFFFF0)) + + + + + + + + + + + + + + + + + + + diff --git a/mp3.c b/mp3.c new file mode 100644 index 0000000..f8e86c9 --- /dev/null +++ b/mp3.c @@ -0,0 +1,446 @@ +#include +#include +#include +#include +#include +#include "fat.h" + +#include + +static struct mad_stream Stream; +static struct mad_header Header; +static struct mad_frame Frame; +static struct mad_synth Synth; +static mad_timer_t Timer; + +typedef struct { + short left; + short right; +} Sample; + +static int eos; + +#define INPUT_BUFFER_SIZE 2048 +static unsigned char fileBuffer[INPUT_BUFFER_SIZE]; +static unsigned char readBuffer[INPUT_BUFFER_SIZE]; +static int useReadBuffer; +static int readPos; +static unsigned int readLen; +static int samplesRead; + +char * mp3File; +char * mp3Fd; +long mp3File_fptr=0; +int mp3File_fsize; + +extern int gBrowser; +extern char path[1024]; + +extern void c2wstrcpy(void *dst, void *src); +extern void c2wstrcat(void *dst, void *src); + + +static int mp3_seek(char* fd, int offset, int whence) +{ + +//todo filesize and mp3File_fptr; + + + long offs = 0; + // libff routine + switch (whence) + { + case SEEK_SET: + offs = offset; + break; + case SEEK_CUR: + offs = mp3File_fptr + offset; + break; + case SEEK_END: + offs = mp3File_fsize + offset; + break; + } + //f_lseek(&mp3File, offs); + mp3File_fptr=offs; + + return offs; + +} + +static int mp3_size(char* fd) +{ + FatRecord rec_tmpf; + u8 resp=0; + resp = fatOpenFileByeName(fd, 0); //err if not found ^^ + + int fsize = file.sec_available*512; //fsize in bytes + mp3File_fsize = fsize; + //todo filesize + return mp3File_fsize; + +} + +static void _f_read(char* fname, unsigned char *readBuffer, int size){ + + +/* + FatRecord rec_tmpf; + u8 resp=0; + resp = fatOpenFileByeName(fname, 0); //err if not found ^^ + + int fsize = file.sec_available*512; //fsize in bytes + mp3File_fsize = fsize; + + + + //injecting in buffer... slow but working :/ + if(file.sec_available*512>=size){ + resp = fatReadPartialFile(readBuffer, size/512, mp3File_fptr); + //resp = fatReadFile(readBuffer+mp3File_fptr, size/512);//file.sec_available); + mp3File_fptr+=size; + + } + //dma_write_s(buffer, 0xb0000000, fsize); + + +*/ + +} + +static int mp3_read(char* fd, unsigned char *ptr, int size) +{ + int ts=size; + _f_read(fd, ptr, size); + return ts; + +} + +static int id3_tag_size(unsigned char const *buf, int remaining) +{ + int size; + + if (remaining < 10) + return 0; + + if (!strncmp((char*)buf, "ID3", 3) || !strncmp((char*)buf, "ea3", 3)) //skip past id3v2 header, which can cause a false sync to be found + { + //get the real size from the syncsafe int + size = buf[6]; + size = (size<<7) | buf[7]; + size = (size<<7) | buf[8]; + size = (size<<7) | buf[9]; + + size += 10; + + if (buf[5] & 0x10) //has footer + size += 10; + } + return size; +} + +//Seek next valid frame after ID3/EA3 header +//NOTE: adapted from Music prx 0.55 source +// credit goes to joek2100. +static int MP3_SkipHdr(char* fd) +{ + int offset = 0; + unsigned char buf[1024]; + unsigned char *pBuffer; + int i; + int size = 0; + + offset = mp3_seek(fd, 0, SEEK_CUR); + mp3_read(fd, buf, sizeof(buf)); + if (!strncmp((char*)buf, "ID3", 3) || !strncmp((char*)buf, "ea3", 3)) //skip past id3v2 header, which can cause a false sync to be found + { + //get the real size from the syncsafe int + size = buf[6]; + size = (size<<7) | buf[7]; + size = (size<<7) | buf[8]; + size = (size<<7) | buf[9]; + + size += 10; + + if (buf[5] & 0x10) //has footer + size += 10; + + offset += size; + } + mp3_seek(fd, offset, SEEK_SET); + + //now seek for a sync + while(1) + { + offset = mp3_seek(fd, 0, SEEK_CUR); + size = mp3_read(fd, buf, sizeof(buf)); + + if (size <= 2)//at end of file + return -1; + + if (!strncmp((char*)buf, "EA3", 3)) //oma mp3 files have non-safe ints in the EA3 header + { + mp3_seek(fd, (buf[4]<<8)+buf[5], SEEK_CUR); + continue; + } + + pBuffer = buf; + for( i = 0; i < size; i++) + { + //if this is a valid frame sync (0xe0 is for mpeg version 2.5,2+1) + if ( (pBuffer[i] == 0xff) && ((pBuffer[i+1] & 0xE0) == 0xE0) ) + { + offset += i; + mp3_seek(fd, offset, SEEK_SET); + return offset; + } + } + //go back two bytes to catch any syncs that on the boundary + mp3_seek(fd, -2, SEEK_CUR); + } +} + +static short convertSample(mad_fixed_t Fixed) +{ + /* Clipping */ + if (Fixed >= MAD_F_ONE) + return (32767); + if (Fixed <= -MAD_F_ONE) + return (-32768); + + /* Conversion. */ + Fixed = Fixed >> (MAD_F_FRACBITS - 15); + return ((short)Fixed); +} + +static int fillFileBuffer() +{ + int leftOver = Stream.bufend - Stream.next_frame; + int want = INPUT_BUFFER_SIZE - leftOver; + + // move left-over bytes + if (leftOver > 0) + memmove(fileBuffer, fileBuffer + want, leftOver); + + // fill remainder of buffer + unsigned char* bufferPos = fileBuffer + leftOver; + while (want > 0) + { + int got = mp3_read(mp3Fd, bufferPos, want); + if (got <= 0) + return 1; // EOF + + want -= got; + bufferPos += got; + } + return 0; +} + +static void decode() +{ + while (mad_frame_decode(&Frame, &Stream) == -1) + { + if ((Stream.error == MAD_ERROR_BUFLEN) || (Stream.error == MAD_ERROR_BUFPTR)) + { + if (fillFileBuffer()) + { + eos = 1; + break; + } + mad_stream_buffer(&Stream, fileBuffer, INPUT_BUFFER_SIZE); + } + else if (Stream.error == MAD_ERROR_LOSTSYNC) + { + /* LOSTSYNC - due to ID3 tags? */ + int tagsize = id3_tag_size(Stream.this_frame, Stream.bufend - Stream.this_frame); + if (tagsize > 0) + { + mad_stream_skip (&Stream, tagsize); + continue; + } + } + } + + mad_timer_add(&Timer, Frame.header.duration); + mad_synth_frame(&Synth, &Frame); +} + +static void convertLeftSamples(Sample* first, Sample* last, const mad_fixed_t* src) +{ + for (Sample *dst = first; dst != last; ++dst) + dst->left = convertSample(*src++); +} + +static void convertRightSamples(Sample* first, Sample* last, const mad_fixed_t* src) +{ + for (Sample *dst = first; dst != last; ++dst) + dst->right = convertSample(*src++); +} + +static void MP3_Callback(void *buffer, unsigned int samplesToWrite) +{ + Sample *destination = (Sample*)buffer; + + while (samplesToWrite > 0) + { + while (!eos && (Synth.pcm.length == 0)) + decode(); + + if (eos) + { + // done + memset(destination, 0, samplesToWrite*4); + break; + } + + unsigned int samplesAvailable = Synth.pcm.length - samplesRead; + if (samplesAvailable > samplesToWrite) + { + convertLeftSamples(destination, destination + samplesToWrite, &Synth.pcm.samples[0][samplesRead]); + convertRightSamples(destination, destination + samplesToWrite, &Synth.pcm.samples[1][samplesRead]); + + samplesRead += samplesToWrite; + samplesToWrite = 0; + } + else + { + convertLeftSamples(destination, destination + samplesAvailable, &Synth.pcm.samples[0][samplesRead]); + convertRightSamples(destination, destination + samplesAvailable, &Synth.pcm.samples[1][samplesRead]); + + destination += samplesAvailable; + samplesToWrite -= samplesAvailable; + + samplesRead = 0; + decode(); + } + } +} + +static void MP3_Init() +{ + /* First the structures used by libmad must be initialized. */ + mad_stream_init(&Stream); + mad_header_init(&Header); + mad_frame_init(&Frame); + mad_synth_init(&Synth); + mad_timer_reset(&Timer); +} + +static void MP3_Exit() +{ + mad_synth_finish(&Synth); + mad_header_finish(&Header); + mad_frame_finish(&Frame); + mad_stream_finish(&Stream); +} + +static void MP3_GetInfo(long long *samples, int *rate) +{ + unsigned long FrameCount = 0; + int bufferSize = 1024*512; + unsigned char *localBuffer; + long red = 0; + double totalBitrate = 0.0; + double mediumBitrate = 0.0; + struct mad_stream stream; + struct mad_header header; + int size = mp3_size(mp3Fd); + long count = size; + + mad_stream_init (&stream); + mad_header_init (&header); + + localBuffer = (unsigned char *)malloc(bufferSize); + + for (int i=0; i<3; i++) + { + memset(localBuffer, 0, bufferSize); + + if (count > bufferSize) + red = mp3_read(mp3Fd, localBuffer, bufferSize); + else + red = mp3_read(mp3Fd, localBuffer, count); + count -= red; + if (!red) + break; // ran out of data + + mad_stream_buffer (&stream, localBuffer, red); + + while (1) + { + if (mad_header_decode(&header, &stream) == -1) + { + if (stream.buffer == NULL || stream.error == MAD_ERROR_BUFLEN) + break; + else if (MAD_RECOVERABLE(stream.error)) + { + continue; + } + else + { + break; + } + } + if (FrameCount++ == 0) + *rate = header.samplerate; + totalBitrate += header.bitrate; + } + } + + mediumBitrate = totalBitrate / (double)FrameCount; + int secs = size * 8 / mediumBitrate; + *samples = *rate * secs; + + mad_header_finish (&header); + mad_stream_finish (&stream); + + if (localBuffer) + free(localBuffer); + + mp3_seek(mp3Fd, 0, SEEK_SET); +} + + +void start_mp3(char *fname, long long *samples, int *rate, int *channels) +{ + + sprintf(mp3Fd, "%s", fname); + + + //if (mp3Fd[0]!=0) + //{ + useReadBuffer = 0; + MP3_GetInfo(samples, rate); + *channels = 2; + + MP3_Init(); + MP3_SkipHdr(mp3Fd); + eos = readLen = readPos = 0; + useReadBuffer = 1; + return; + //} + + //*samples = 0; + //return; +} + +void stop_mp3(void) +{ + MP3_Exit(); + mp3File_fptr=0; +/* + if (mp3Fd > 0) + { + if (gBrowser) + f_close(&mp3File); + else + dfs_close(mp3Fd); + } + mp3Fd = -1; + */ +} + +int update_mp3(char *buf, int bytes) +{ + MP3_Callback(buf, bytes/4); + return eos ? 0 : 1; +} diff --git a/sram.c b/sram.c new file mode 100644 index 0000000..eaee972 --- /dev/null +++ b/sram.c @@ -0,0 +1,127 @@ +#include +#include +#include +#include "sys.h" +#include "types.h" +#include "utils.h" +#include "sram.h" + + +void PI_Init(void) { + PI_DMAWait(); + IO_WRITE(PI_STATUS_REG, 0x03); +} + +// Inits PI for sram transfer +void PI_Init_SRAM(void) { + + + IO_WRITE(PI_BSD_DOM2_LAT_REG, 0x05); + IO_WRITE(PI_BSD_DOM2_PWD_REG, 0x0C); + IO_WRITE(PI_BSD_DOM2_PGS_REG, 0x0D); + IO_WRITE(PI_BSD_DOM2_RLS_REG, 0x02); + +} + +void PI_DMAWait(void) { + + + /*PI DMA wait + + 1. Read PI_STATUS_REG then AND it with 0x3, if its true... then wait until + it is not true. + */ + + while (IO_READ(PI_STATUS_REG) & (PI_STATUS_IO_BUSY | PI_STATUS_DMA_BUSY)); +} + + +void PI_DMAFromSRAM(void *dest, u32 offset, u32 size) { + + + IO_WRITE(PI_DRAM_ADDR_REG, K1_TO_PHYS(dest)); + IO_WRITE(PI_CART_ADDR_REG, (0xA8000000 + offset)); + asm volatile ("" : : : "memory"); + IO_WRITE(PI_WR_LEN_REG, (size - 1)); + asm volatile ("" : : : "memory"); + + + /* + PI_DMAWait(); + + IO_WRITE(PI_STATUS_REG, 0x03); + IO_WRITE(PI_DRAM_ADDR_REG, K1_TO_PHYS(dest)); + IO_WRITE(PI_CART_ADDR_REG, (0xA8000000 + offset)); + _data_cache_invalidate_all(); + IO_WRITE(PI_WR_LEN_REG, (size - 1)); +*/ +} + + +void PI_DMAToSRAM(void *src, u32 offset, u32 size) { //void* + PI_DMAWait(); + + IO_WRITE(PI_STATUS_REG, 2); + IO_WRITE(PI_DRAM_ADDR_REG, K1_TO_PHYS(src)); + IO_WRITE(PI_CART_ADDR_REG, (0xA8000000 + offset)); + _data_cache_invalidate_all(); + //data_cache_hit_writeback_invalidate(src,size); + + /* Write back . nusys - only writeback + osWritebackDCache((void*)buf_ptr, (s32)size); + */ + //libdragon equivalent + // data_cache_hit_writeback (src, size); + + IO_WRITE(PI_RD_LEN_REG, (size - 1)); +} + +void PI_DMAFromCart(void* dest, void* src, u32 size) { + PI_DMAWait(); + + IO_WRITE(PI_STATUS_REG, 0x03); + IO_WRITE(PI_DRAM_ADDR_REG, K1_TO_PHYS(dest)); + IO_WRITE(PI_CART_ADDR_REG, K0_TO_PHYS(src)); + //_data_cache_invalidate_all(); + IO_WRITE(PI_WR_LEN_REG, (size - 1)); +} + + +void PI_DMAToCart(void* dest, void* src, u32 size) { + PI_DMAWait(); + + IO_WRITE(PI_STATUS_REG, 0x02); + IO_WRITE(PI_DRAM_ADDR_REG, K1_TO_PHYS(src)); + IO_WRITE(PI_CART_ADDR_REG, K0_TO_PHYS(dest)); + //_data_cache_invalidate_all(); + IO_WRITE(PI_RD_LEN_REG, (size - 1)); +} + + +// Wrapper to support unaligned access to memory +void PI_SafeDMAFromCart(void *dest, void *src, u32 size) { + if (!dest || !src || !size) return; + + u32 unalignedSrc = ((u32)src) % 2; + u32 unalignedDest = ((u32)dest) % 8; + + //FIXME: Do i really need to check if size is 16bit aligned? + if (!unalignedDest && !unalignedSrc && !(size % 2)) { + PI_DMAFromCart(dest, src, size); + PI_DMAWait(); + + return; + } + + void* newSrc = (void*)(((u32)src) - unalignedSrc); + u32 newSize = (size + unalignedSrc) + ((size + unalignedSrc) % 2); + + u8 *buffer = memalign(8, newSize); + PI_DMAFromCart(buffer, newSrc, newSize); + PI_DMAWait(); + + memcpy(dest, (buffer + unalignedSrc), size); + + free(buffer); +} + diff --git a/sram.h b/sram.h new file mode 100644 index 0000000..913a238 --- /dev/null +++ b/sram.h @@ -0,0 +1,32 @@ +//sram.h +#include +#include +#include "types.h" + +void data_cache_hit_writeback_invalidate(volatile void *, unsigned long); +unsigned int CRC_Calculate(unsigned int crc, unsigned char* buf, unsigned int len); +void dma_write_sram(void* src, u32 offset, u32 size); +void dma_read_sram(void *dest, u32 offset, u32 size); +void dma_write_s(void * ram_address, unsigned long pi_address, unsigned long len); +void dma_read_s(void * ram_address, unsigned long pi_address, unsigned long len); +int writeSram(void* src, u32 size); +void setSDTiming(void); + + +void PI_Init(void); +void PI_Init_SRAM(void); +void PI_DMAWait(void); +void PI_DMAFromCart(void* dest, void* src, u32 size); +void PI_DMAToCart(void* dest, void* src, u32 size); +void PI_DMAFromSRAM(void *dest, u32 offset, u32 size); +void PI_DMAToSRAM(void* src, u32 offset, u32 size); +void PI_SafeDMAFromCart(void *dest, void *src, u32 size); + +//memory +/*** MEMORY ***/ +void *safe_memalign(size_t boundary, size_t size); +void *safe_calloc(size_t nmemb, size_t size); +void *safe_malloc(size_t size); +void safe_free(void *ptr); +void *safe_memset(void *s, int c, size_t n); +void *safe_memcpy(void *dest, const void *src, size_t n); diff --git a/stb_image.c b/stb_image.c new file mode 100644 index 0000000..918dd80 --- /dev/null +++ b/stb_image.c @@ -0,0 +1,4940 @@ +/* stbi-1.28 - public domain JPEG/PNG reader - http://nothings.org/stb_image.c + when you control the images you're loading + no warranty implied; use at your own risk + + QUICK NOTES: + Primarily of interest to game developers and other people who can + avoid problematic images and only need the trivial interface + + JPEG baseline (no JPEG progressive) + PNG 8-bit only + + TGA (not sure what subset, if a subset) + BMP non-1bpp, non-RLE + PSD (composited view only, no extra channels) + + GIF (*comp always reports as 4-channel) + HDR (radiance rgbE format) + PIC (Softimage PIC) + + - decoded from memory or through stdio FILE (define STBI_NO_STDIO to remove code) + - supports installable dequantizing-IDCT, YCbCr-to-RGB conversion (define STBI_SIMD) + + Latest revisions: + 1.28 (2010-08-01) fix bug in GIF palette transparency (SpartanJ) + 1.27 (2010-08-01) cast-to-uint8 to fix warnings (Laurent Gomila) + allow trailing 0s at end of image data (Laurent Gomila) + 1.26 (2010-07-24) fix bug in file buffering for PNG reported by SpartanJ + 1.25 (2010-07-17) refix trans_data warning (Won Chun) + 1.24 (2010-07-12) perf improvements reading from files + minor perf improvements for jpeg + deprecated type-specific functions in hope of feedback + attempt to fix trans_data warning (Won Chun) + 1.23 fixed bug in iPhone support + 1.22 (2010-07-10) removed image *writing* support to stb_image_write.h + stbi_info support from Jetro Lauha + GIF support from Jean-Marc Lienher + iPhone PNG-extensions from James Brown + warning-fixes from Nicolas Schulz and Janez Zemva + 1.21 fix use of 'uint8' in header (reported by jon blow) + 1.20 added support for Softimage PIC, by Tom Seddon + + See end of file for full revision history. + + TODO: + stbi_info support for BMP,PSD,HDR,PIC + rewrite stbi_info and load_file variations to share file handling code + (current system allows individual functions to be called directly, + since each does all the work, but I doubt anyone uses this in practice) + + + ============================ Contributors ========================= + + Image formats Optimizations & bugfixes + Sean Barrett (jpeg, png, bmp) Fabian "ryg" Giesen + Nicolas Schulz (hdr, psd) + Jonathan Dummer (tga) Bug fixes & warning fixes + Jean-Marc Lienher (gif) Marc LeBlanc + Tom Seddon (pic) Christpher Lloyd + Thatcher Ulrich (psd) Dave Moore + Won Chun + the Horde3D community + Extensions, features Janez Zemva + Jetro Lauha (stbi_info) Jonathan Blow + James "moose2000" Brown (iPhone PNG) Laurent Gomila + + If your name should be here but isn't, let Sean know. + +*/ + +#ifndef STBI_INCLUDE_STB_IMAGE_H +#define STBI_INCLUDE_STB_IMAGE_H + +// To get a header file for this, either cut and paste the header, +// or create stb_image.h, #define STBI_HEADER_FILE_ONLY, and +// then include stb_image.c from it. + +//// begin header file //////////////////////////////////////////////////// +// +// Limitations: +// - no jpeg progressive support +// - non-HDR formats support 8-bit samples only (jpeg, png) +// - no delayed line count (jpeg) -- IJG doesn't support either +// - no 1-bit BMP +// - GIF always returns *comp=4 +// +// Basic usage (see HDR discussion below): +// int x,y,n; +// unsigned char *data = stbi_load(filename, &x, &y, &n, 0); +// // ... process data if not NULL ... +// // ... x = width, y = height, n = # 8-bit components per pixel ... +// // ... replace '0' with '1'..'4' to force that many components per pixel +// stbi_image_free(data) +// +// Standard parameters: +// int *x -- outputs image width in pixels +// int *y -- outputs image height in pixels +// int *comp -- outputs # of image components in image file +// int req_comp -- if non-zero, # of image components requested in result +// +// The return value from an image loader is an 'unsigned char *' which points +// to the pixel data. The pixel data consists of *y scanlines of *x pixels, +// with each pixel consisting of N interleaved 8-bit components; the first +// pixel pointed to is top-left-most in the image. There is no padding between +// image scanlines or between pixels, regardless of format. The number of +// components N is 'req_comp' if req_comp is non-zero, or *comp otherwise. +// If req_comp is non-zero, *comp has the number of components that _would_ +// have been output otherwise. E.g. if you set req_comp to 4, you will always +// get RGBA output, but you can check *comp to easily see if it's opaque. +// +// An output image with N components has the following components interleaved +// in this order in each pixel: +// +// N=#comp components +// 1 grey +// 2 grey, alpha +// 3 red, green, blue +// 4 red, green, blue, alpha +// +// If image loading fails for any reason, the return value will be NULL, +// and *x, *y, *comp will be unchanged. The function stbi_failure_reason() +// can be queried for an extremely brief, end-user unfriendly explanation +// of why the load failed. Define STBI_NO_FAILURE_STRINGS to avoid +// compiling these strings at all, and STBI_FAILURE_USERMSG to get slightly +// more user-friendly ones. +// +// Paletted PNG, BMP, GIF, and PIC images are automatically depalettized. +// +// =========================================================================== +// +// iPhone PNG support: +// +// By default we convert iphone-formatted PNGs back to RGB; nominally they +// would silently load as BGR, except the existing code should have just +// failed on such iPhone PNGs. But you can disable this conversion by +// by calling stbi_convert_iphone_png_to_rgb(0), in which case +// you will always just get the native iphone "format" through. +// +// Call stbi_set_unpremultiply_on_load(1) as well to force a divide per +// pixel to remove any premultiplied alpha *only* if the image file explicitly +// says there's premultiplied data (currently only happens in iPhone images, +// and only if iPhone convert-to-rgb processing is on). +// +// =========================================================================== +// +// HDR image support (disable by defining STBI_NO_HDR) +// +// stb_image now supports loading HDR images in general, and currently +// the Radiance .HDR file format, although the support is provided +// generically. You can still load any file through the existing interface; +// if you attempt to load an HDR file, it will be automatically remapped to +// LDR, assuming gamma 2.2 and an arbitrary scale factor defaulting to 1; +// both of these constants can be reconfigured through this interface: +// +// stbi_hdr_to_ldr_gamma(2.2f); +// stbi_hdr_to_ldr_scale(1.0f); +// +// (note, do not use _inverse_ constants; stbi_image will invert them +// appropriately). +// +// Additionally, there is a new, parallel interface for loading files as +// (linear) floats to preserve the full dynamic range: +// +// float *data = stbi_loadf(filename, &x, &y, &n, 0); +// +// If you load LDR images through this interface, those images will +// be promoted to floating point values, run through the inverse of +// constants corresponding to the above: +// +// stbi_ldr_to_hdr_scale(1.0f); +// stbi_ldr_to_hdr_gamma(2.2f); +// +// Finally, given a filename (or an open file or memory block--see header +// file for details) containing image data, you can query for the "most +// appropriate" interface to use (that is, whether the image is HDR or +// not), using: +// +// stbi_is_hdr(char *filename); + +#ifndef STBI_NO_STDIO +#include +#endif + +#define STBI_VERSION 1 + +enum +{ + STBI_default = 0, // only used for req_comp + + STBI_grey = 1, + STBI_grey_alpha = 2, + STBI_rgb = 3, + STBI_rgb_alpha = 4, +}; + +typedef unsigned char stbi_uc; + +#ifdef __cplusplus +extern "C" { +#endif + +// PRIMARY API - works on images of any type + +// load image by filename, open file, or memory buffer +extern stbi_uc *stbi_load_from_memory(stbi_uc const *buffer, int len, int *x, int *y, int *comp, int req_comp); + +#ifndef STBI_NO_STDIO +extern stbi_uc *stbi_load (char const *filename, int *x, int *y, int *comp, int req_comp); +extern stbi_uc *stbi_load_from_file (FILE *f, int *x, int *y, int *comp, int req_comp); +// for stbi_load_from_file, file pointer is left pointing immediately after image +#endif + +#ifndef STBI_NO_HDR + extern float *stbi_loadf_from_memory(stbi_uc const *buffer, int len, int *x, int *y, int *comp, int req_comp); + + #ifndef STBI_NO_STDIO + extern float *stbi_loadf (char const *filename, int *x, int *y, int *comp, int req_comp); + extern float *stbi_loadf_from_file (FILE *f, int *x, int *y, int *comp, int req_comp); + #endif + + extern void stbi_hdr_to_ldr_gamma(float gamma); + extern void stbi_hdr_to_ldr_scale(float scale); + + extern void stbi_ldr_to_hdr_gamma(float gamma); + extern void stbi_ldr_to_hdr_scale(float scale); +#endif // STBI_NO_HDR + +// get a VERY brief reason for failure +// NOT THREADSAFE +extern const char *stbi_failure_reason (void); + +// free the loaded image -- this is just free() +extern void stbi_image_free (void *retval_from_stbi_load); + +// get image dimensions & components without fully decoding +extern int stbi_info_from_memory(stbi_uc const *buffer, int len, int *x, int *y, int *comp); +extern int stbi_is_hdr_from_memory(stbi_uc const *buffer, int len); + +#ifndef STBI_NO_STDIO +extern int stbi_info (char const *filename, int *x, int *y, int *comp); +extern int stbi_info_from_file (FILE *f, int *x, int *y, int *comp); + +extern int stbi_is_hdr (char const *filename); +extern int stbi_is_hdr_from_file(FILE *f); +#endif + +// for image formats that explicitly notate that they have premultiplied alpha, +// we just return the colors as stored in the file. set this flag to force +// unpremultiplication. results are undefined if the unpremultiply overflow. +extern void stbi_set_unpremultiply_on_load(int flag_true_if_should_unpremultiply); + +// indicate whether we should process iphone images back to canonical format, +// or just pass them through "as-is" +extern void stbi_convert_iphone_png_to_rgb(int flag_true_if_should_convert); + + +// ZLIB client - used by PNG, available for other purposes + +extern char *stbi_zlib_decode_malloc_guesssize(const char *buffer, int len, int initial_size, int *outlen); +extern char *stbi_zlib_decode_malloc(const char *buffer, int len, int *outlen); +extern int stbi_zlib_decode_buffer(char *obuffer, int olen, const char *ibuffer, int ilen); + +extern char *stbi_zlib_decode_noheader_malloc(const char *buffer, int len, int *outlen); +extern int stbi_zlib_decode_noheader_buffer(char *obuffer, int olen, const char *ibuffer, int ilen); + +// define new loaders +typedef struct +{ + int (*test_memory)(stbi_uc const *buffer, int len); + stbi_uc * (*load_from_memory)(stbi_uc const *buffer, int len, int *x, int *y, int *comp, int req_comp); + #ifndef STBI_NO_STDIO + int (*test_file)(FILE *f); + stbi_uc * (*load_from_file)(FILE *f, int *x, int *y, int *comp, int req_comp); + #endif +} stbi_loader; + +// register a loader by filling out the above structure (you must define ALL functions) +// returns 1 if added or already added, 0 if not added (too many loaders) +// NOT THREADSAFE +extern int stbi_register_loader(stbi_loader *loader); + +// define faster low-level operations (typically SIMD support) +#if STBI_SIMD +typedef void (*stbi_idct_8x8)(stbi_uc *out, int out_stride, short data[64], unsigned short *dequantize); +// compute an integer IDCT on "input" +// input[x] = data[x] * dequantize[x] +// write results to 'out': 64 samples, each run of 8 spaced by 'out_stride' +// CLAMP results to 0..255 +typedef void (*stbi_YCbCr_to_RGB_run)(stbi_uc *output, stbi_uc const *y, stbi_uc const *cb, stbi_uc const *cr, int count, int step); +// compute a conversion from YCbCr to RGB +// 'count' pixels +// write pixels to 'output'; each pixel is 'step' bytes (either 3 or 4; if 4, write '255' as 4th), order R,G,B +// y: Y input channel +// cb: Cb input channel; scale/biased to be 0..255 +// cr: Cr input channel; scale/biased to be 0..255 + +extern void stbi_install_idct(stbi_idct_8x8 func); +extern void stbi_install_YCbCr_to_RGB(stbi_YCbCr_to_RGB_run func); +#endif // STBI_SIMD + + + + +// TYPE-SPECIFIC ACCESS + +#ifdef STBI_TYPE_SPECIFIC_FUNCTIONS + +// is it a jpeg? +extern int stbi_jpeg_test_memory (stbi_uc const *buffer, int len); +extern stbi_uc *stbi_jpeg_load_from_memory(stbi_uc const *buffer, int len, int *x, int *y, int *comp, int req_comp); +extern int stbi_jpeg_info_from_memory(stbi_uc const *buffer, int len, int *x, int *y, int *comp); + +#ifndef STBI_NO_STDIO +extern stbi_uc *stbi_jpeg_load (char const *filename, int *x, int *y, int *comp, int req_comp); +extern int stbi_jpeg_test_file (FILE *f); +extern stbi_uc *stbi_jpeg_load_from_file (FILE *f, int *x, int *y, int *comp, int req_comp); + +extern int stbi_jpeg_info (char const *filename, int *x, int *y, int *comp); +extern int stbi_jpeg_info_from_file (FILE *f, int *x, int *y, int *comp); +#endif + +// is it a png? +extern int stbi_png_test_memory (stbi_uc const *buffer, int len); +extern stbi_uc *stbi_png_load_from_memory (stbi_uc const *buffer, int len, int *x, int *y, int *comp, int req_comp); +extern int stbi_png_info_from_memory (stbi_uc const *buffer, int len, int *x, int *y, int *comp); + +#ifndef STBI_NO_STDIO +extern stbi_uc *stbi_png_load (char const *filename, int *x, int *y, int *comp, int req_comp); +extern int stbi_png_info (char const *filename, int *x, int *y, int *comp); +extern int stbi_png_test_file (FILE *f); +extern stbi_uc *stbi_png_load_from_file (FILE *f, int *x, int *y, int *comp, int req_comp); +extern int stbi_png_info_from_file (FILE *f, int *x, int *y, int *comp); +#endif + +// is it a bmp? +extern int stbi_bmp_test_memory (stbi_uc const *buffer, int len); + +extern stbi_uc *stbi_bmp_load (char const *filename, int *x, int *y, int *comp, int req_comp); +extern stbi_uc *stbi_bmp_load_from_memory (stbi_uc const *buffer, int len, int *x, int *y, int *comp, int req_comp); +#ifndef STBI_NO_STDIO +extern int stbi_bmp_test_file (FILE *f); +extern stbi_uc *stbi_bmp_load_from_file (FILE *f, int *x, int *y, int *comp, int req_comp); +#endif + +// is it a tga? +extern int stbi_tga_test_memory (stbi_uc const *buffer, int len); + +extern stbi_uc *stbi_tga_load (char const *filename, int *x, int *y, int *comp, int req_comp); +extern stbi_uc *stbi_tga_load_from_memory (stbi_uc const *buffer, int len, int *x, int *y, int *comp, int req_comp); +#ifndef STBI_NO_STDIO +extern int stbi_tga_test_file (FILE *f); +extern stbi_uc *stbi_tga_load_from_file (FILE *f, int *x, int *y, int *comp, int req_comp); +#endif + +// is it a psd? +extern int stbi_psd_test_memory (stbi_uc const *buffer, int len); + +extern stbi_uc *stbi_psd_load (char const *filename, int *x, int *y, int *comp, int req_comp); +extern stbi_uc *stbi_psd_load_from_memory (stbi_uc const *buffer, int len, int *x, int *y, int *comp, int req_comp); +#ifndef STBI_NO_STDIO +extern int stbi_psd_test_file (FILE *f); +extern stbi_uc *stbi_psd_load_from_file (FILE *f, int *x, int *y, int *comp, int req_comp); +#endif + +// is it an hdr? +extern int stbi_hdr_test_memory (stbi_uc const *buffer, int len); + +extern float * stbi_hdr_load (char const *filename, int *x, int *y, int *comp, int req_comp); +extern float * stbi_hdr_load_from_memory (stbi_uc const *buffer, int len, int *x, int *y, int *comp, int req_comp); +#ifndef STBI_NO_STDIO +extern int stbi_hdr_test_file (FILE *f); +extern float * stbi_hdr_load_from_file (FILE *f, int *x, int *y, int *comp, int req_comp); +#endif + +// is it a pic? +extern int stbi_pic_test_memory (stbi_uc const *buffer, int len); + +extern stbi_uc *stbi_pic_load (char const *filename, int *x, int *y, int *comp, int req_comp); +extern stbi_uc *stbi_pic_load_from_memory (stbi_uc const *buffer, int len, int *x, int *y, int *comp, int req_comp); +#ifndef STBI_NO_STDIO +extern int stbi_pic_test_file (FILE *f); +extern stbi_uc *stbi_pic_load_from_file (FILE *f, int *x, int *y, int *comp, int req_comp); +#endif + +// is it a gif? +extern int stbi_gif_test_memory (stbi_uc const *buffer, int len); + +extern stbi_uc *stbi_gif_load (char const *filename, int *x, int *y, int *comp, int req_comp); +extern stbi_uc *stbi_gif_load_from_memory (stbi_uc const *buffer, int len, int *x, int *y, int *comp, int req_comp); +extern int stbi_gif_info_from_memory (stbi_uc const *buffer, int len, int *x, int *y, int *comp); + +#ifndef STBI_NO_STDIO +extern int stbi_gif_test_file (FILE *f); +extern stbi_uc *stbi_gif_load_from_file (FILE *f, int *x, int *y, int *comp, int req_comp); +extern int stbi_gif_info (char const *filename, int *x, int *y, int *comp); +extern int stbi_gif_info_from_file (FILE *f, int *x, int *y, int *comp); +#endif + +#endif//STBI_TYPE_SPECIFIC_FUNCTIONS + + + + +#ifdef __cplusplus +} +#endif + +// +// +//// end header file ///////////////////////////////////////////////////// +#endif // STBI_INCLUDE_STB_IMAGE_H + +#ifndef STBI_HEADER_FILE_ONLY + +#ifndef STBI_NO_HDR +#include // ldexp +#include // strcmp +#endif + +#ifndef STBI_NO_STDIO +#include +#endif +#include +#include +#include +//#include +#include +#include + +#ifndef _MSC_VER + #ifdef __cplusplus + #define __forceinline inline + #else + #define __forceinline + #endif +#endif + + +// implementation: +typedef unsigned char uint8; +typedef unsigned short uint16; +typedef signed short int16; +typedef unsigned int uint32; +typedef signed int int32; +typedef unsigned int uint; + +// should produce compiler error if size is wrong +typedef unsigned char validate_uint32[sizeof(uint32)==4 ? 1 : -1]; + +#if defined(STBI_NO_STDIO) && !defined(STBI_NO_WRITE) +#define STBI_NO_WRITE +#endif + +#define STBI_NOTUSED(v) v=v + +#ifdef _MSC_VER +#define STBI_HAS_LRTOL +#endif + +#ifdef STBI_HAS_LRTOL + #define stbi_lrot(x,y) _lrotl(x,y) +#else + #define stbi_lrot(x,y) (((x) << (y)) | ((x) >> (32 - (y)))) +#endif + +////////////////////////////////////////////////////////////////////////////// +// +// Generic API that works on all image types +// + +// deprecated functions + +// is it a jpeg? +extern int stbi_jpeg_test_memory (stbi_uc const *buffer, int len); +extern stbi_uc *stbi_jpeg_load_from_memory(stbi_uc const *buffer, int len, int *x, int *y, int *comp, int req_comp); +extern int stbi_jpeg_info_from_memory(stbi_uc const *buffer, int len, int *x, int *y, int *comp); + +#ifndef STBI_NO_STDIO +extern stbi_uc *stbi_jpeg_load (char const *filename, int *x, int *y, int *comp, int req_comp); +extern int stbi_jpeg_test_file (FILE *f); +extern stbi_uc *stbi_jpeg_load_from_file (FILE *f, int *x, int *y, int *comp, int req_comp); + +extern int stbi_jpeg_info (char const *filename, int *x, int *y, int *comp); +extern int stbi_jpeg_info_from_file (FILE *f, int *x, int *y, int *comp); +#endif + +// is it a png? +extern int stbi_png_test_memory (stbi_uc const *buffer, int len); +extern stbi_uc *stbi_png_load_from_memory (stbi_uc const *buffer, int len, int *x, int *y, int *comp, int req_comp); +extern int stbi_png_info_from_memory (stbi_uc const *buffer, int len, int *x, int *y, int *comp); + +#ifndef STBI_NO_STDIO +extern stbi_uc *stbi_png_load (char const *filename, int *x, int *y, int *comp, int req_comp); +extern int stbi_png_info (char const *filename, int *x, int *y, int *comp); +extern int stbi_png_test_file (FILE *f); +extern stbi_uc *stbi_png_load_from_file (FILE *f, int *x, int *y, int *comp, int req_comp); +extern int stbi_png_info_from_file (FILE *f, int *x, int *y, int *comp); +#endif + +// is it a bmp? +extern int stbi_bmp_test_memory (stbi_uc const *buffer, int len); + +extern stbi_uc *stbi_bmp_load (char const *filename, int *x, int *y, int *comp, int req_comp); +extern stbi_uc *stbi_bmp_load_from_memory (stbi_uc const *buffer, int len, int *x, int *y, int *comp, int req_comp); +#ifndef STBI_NO_STDIO +extern int stbi_bmp_test_file (FILE *f); +extern stbi_uc *stbi_bmp_load_from_file (FILE *f, int *x, int *y, int *comp, int req_comp); +#endif + +// is it a tga? +extern int stbi_tga_test_memory (stbi_uc const *buffer, int len); + +extern stbi_uc *stbi_tga_load (char const *filename, int *x, int *y, int *comp, int req_comp); +extern stbi_uc *stbi_tga_load_from_memory (stbi_uc const *buffer, int len, int *x, int *y, int *comp, int req_comp); +#ifndef STBI_NO_STDIO +extern int stbi_tga_test_file (FILE *f); +extern stbi_uc *stbi_tga_load_from_file (FILE *f, int *x, int *y, int *comp, int req_comp); +#endif + +// is it a psd? +extern int stbi_psd_test_memory (stbi_uc const *buffer, int len); + +extern stbi_uc *stbi_psd_load (char const *filename, int *x, int *y, int *comp, int req_comp); +extern stbi_uc *stbi_psd_load_from_memory (stbi_uc const *buffer, int len, int *x, int *y, int *comp, int req_comp); +#ifndef STBI_NO_STDIO +extern int stbi_psd_test_file (FILE *f); +extern stbi_uc *stbi_psd_load_from_file (FILE *f, int *x, int *y, int *comp, int req_comp); +#endif + +// is it an hdr? +extern int stbi_hdr_test_memory (stbi_uc const *buffer, int len); + +extern float * stbi_hdr_load (char const *filename, int *x, int *y, int *comp, int req_comp); +extern float * stbi_hdr_load_from_memory (stbi_uc const *buffer, int len, int *x, int *y, int *comp, int req_comp); +#ifndef STBI_NO_STDIO +extern int stbi_hdr_test_file (FILE *f); +extern float * stbi_hdr_load_from_file (FILE *f, int *x, int *y, int *comp, int req_comp); +#endif + +// is it a pic? +extern int stbi_pic_test_memory (stbi_uc const *buffer, int len); + +extern stbi_uc *stbi_pic_load (char const *filename, int *x, int *y, int *comp, int req_comp); +extern stbi_uc *stbi_pic_load_from_memory (stbi_uc const *buffer, int len, int *x, int *y, int *comp, int req_comp); +#ifndef STBI_NO_STDIO +extern int stbi_pic_test_file (FILE *f); +extern stbi_uc *stbi_pic_load_from_file (FILE *f, int *x, int *y, int *comp, int req_comp); +#endif + +// is it a gif? +extern int stbi_gif_test_memory (stbi_uc const *buffer, int len); + +extern stbi_uc *stbi_gif_load (char const *filename, int *x, int *y, int *comp, int req_comp); +extern stbi_uc *stbi_gif_load_from_memory (stbi_uc const *buffer, int len, int *x, int *y, int *comp, int req_comp); +extern int stbi_gif_info_from_memory (stbi_uc const *buffer, int len, int *x, int *y, int *comp); + +#ifndef STBI_NO_STDIO +extern int stbi_gif_test_file (FILE *f); +extern stbi_uc *stbi_gif_load_from_file (FILE *f, int *x, int *y, int *comp, int req_comp); +extern int stbi_gif_info (char const *filename, int *x, int *y, int *comp); +extern int stbi_gif_info_from_file (FILE *f, int *x, int *y, int *comp); +#endif + + +// this is not threadsafe +static const char *failure_reason; + +const char *stbi_failure_reason(void) +{ + return failure_reason; +} + +static int e(const char *str) +{ + failure_reason = str; + return 0; +} + +#ifdef STBI_NO_FAILURE_STRINGS + #define e(x,y) 0 +#elif defined(STBI_FAILURE_USERMSG) + #define e(x,y) e(y) +#else + #define e(x,y) e(x) +#endif + +#define epf(x,y) ((float *) (e(x,y)?NULL:NULL)) +#define epuc(x,y) ((unsigned char *) (e(x,y)?NULL:NULL)) + +void stbi_image_free(void *retval_from_stbi_load) +{ + free(retval_from_stbi_load); +} + +#define MAX_LOADERS 32 +stbi_loader *loaders[MAX_LOADERS]; +static int max_loaders = 0; + +int stbi_register_loader(stbi_loader *loader) +{ + int i; + for (i=0; i < MAX_LOADERS; ++i) { + // already present? + if (loaders[i] == loader) + return 1; + // end of the list? + if (loaders[i] == NULL) { + loaders[i] = loader; + max_loaders = i+1; + return 1; + } + } + // no room for it + return 0; +} + +#ifndef STBI_NO_HDR +static float *ldr_to_hdr(stbi_uc *data, int x, int y, int comp); +static stbi_uc *hdr_to_ldr(float *data, int x, int y, int comp); +#endif + +#ifndef STBI_NO_STDIO +unsigned char *stbi_load(char const *filename, int *x, int *y, int *comp, int req_comp) +{ + FILE *f = fopen(filename, "rb"); + unsigned char *result; + if (!f) return epuc("can't fopen", "Unable to open file"); + result = stbi_load_from_file(f,x,y,comp,req_comp); + fclose(f); + return result; +} + +unsigned char *stbi_load_from_file(FILE *f, int *x, int *y, int *comp, int req_comp) +{ + int i; + if (stbi_jpeg_test_file(f)) return stbi_jpeg_load_from_file(f,x,y,comp,req_comp); + if (stbi_png_test_file(f)) return stbi_png_load_from_file(f,x,y,comp,req_comp); + if (stbi_bmp_test_file(f)) return stbi_bmp_load_from_file(f,x,y,comp,req_comp); + if (stbi_gif_test_file(f)) return stbi_gif_load_from_file(f,x,y,comp,req_comp); + if (stbi_psd_test_file(f)) return stbi_psd_load_from_file(f,x,y,comp,req_comp); + if (stbi_pic_test_file(f)) return stbi_pic_load_from_file(f,x,y,comp,req_comp); + + #ifndef STBI_NO_HDR + if (stbi_hdr_test_file(f)) { + float *hdr = stbi_hdr_load_from_file(f, x,y,comp,req_comp); + return hdr_to_ldr(hdr, *x, *y, req_comp ? req_comp : *comp); + } + #endif + + for (i=0; i < max_loaders; ++i) + if (loaders[i]->test_file(f)) + return loaders[i]->load_from_file(f,x,y,comp,req_comp); + // test tga last because it's a crappy test! + if (stbi_tga_test_file(f)) + return stbi_tga_load_from_file(f,x,y,comp,req_comp); + return epuc("unknown image type", "Image not of any known type, or corrupt"); +} +#endif + +unsigned char *stbi_load_from_memory(stbi_uc const *buffer, int len, int *x, int *y, int *comp, int req_comp) +{ + int i; + if (stbi_jpeg_test_memory(buffer,len)) return stbi_jpeg_load_from_memory(buffer,len,x,y,comp,req_comp); + if (stbi_png_test_memory(buffer,len)) return stbi_png_load_from_memory(buffer,len,x,y,comp,req_comp); + if (stbi_bmp_test_memory(buffer,len)) return stbi_bmp_load_from_memory(buffer,len,x,y,comp,req_comp); + if (stbi_gif_test_memory(buffer,len)) return stbi_gif_load_from_memory(buffer,len,x,y,comp,req_comp); + if (stbi_psd_test_memory(buffer,len)) return stbi_psd_load_from_memory(buffer,len,x,y,comp,req_comp); + if (stbi_pic_test_memory(buffer,len)) return stbi_pic_load_from_memory(buffer,len,x,y,comp,req_comp); + + #ifndef STBI_NO_HDR + if (stbi_hdr_test_memory(buffer, len)) { + float *hdr = stbi_hdr_load_from_memory(buffer, len,x,y,comp,req_comp); + return hdr_to_ldr(hdr, *x, *y, req_comp ? req_comp : *comp); + } + #endif + + for (i=0; i < max_loaders; ++i) + if (loaders[i]->test_memory(buffer,len)) + return loaders[i]->load_from_memory(buffer,len,x,y,comp,req_comp); + // test tga last because it's a crappy test! + if (stbi_tga_test_memory(buffer,len)) + return stbi_tga_load_from_memory(buffer,len,x,y,comp,req_comp); + return epuc("unknown image type", "Image not of any known type, or corrupt"); +} + +#ifndef STBI_NO_HDR + +#ifndef STBI_NO_STDIO +float *stbi_loadf(char const *filename, int *x, int *y, int *comp, int req_comp) +{ + FILE *f = fopen(filename, "rb"); + float *result; + if (!f) return epf("can't fopen", "Unable to open file"); + result = stbi_loadf_from_file(f,x,y,comp,req_comp); + fclose(f); + return result; +} + +float *stbi_loadf_from_file(FILE *f, int *x, int *y, int *comp, int req_comp) +{ + unsigned char *data; + #ifndef STBI_NO_HDR + if (stbi_hdr_test_file(f)) + return stbi_hdr_load_from_file(f,x,y,comp,req_comp); + #endif + data = stbi_load_from_file(f, x, y, comp, req_comp); + if (data) + return ldr_to_hdr(data, *x, *y, req_comp ? req_comp : *comp); + return epf("unknown image type", "Image not of any known type, or corrupt"); +} +#endif + +float *stbi_loadf_from_memory(stbi_uc const *buffer, int len, int *x, int *y, int *comp, int req_comp) +{ + stbi_uc *data; + #ifndef STBI_NO_HDR + if (stbi_hdr_test_memory(buffer, len)) + return stbi_hdr_load_from_memory(buffer, len,x,y,comp,req_comp); + #endif + data = stbi_load_from_memory(buffer, len, x, y, comp, req_comp); + if (data) + return ldr_to_hdr(data, *x, *y, req_comp ? req_comp : *comp); + return epf("unknown image type", "Image not of any known type, or corrupt"); +} +#endif + +// these is-hdr-or-not is defined independent of whether STBI_NO_HDR is +// defined, for API simplicity; if STBI_NO_HDR is defined, it always +// reports false! + +int stbi_is_hdr_from_memory(stbi_uc const *buffer, int len) +{ + #ifndef STBI_NO_HDR + return stbi_hdr_test_memory(buffer, len); + #else + return 0; + #endif +} + +#ifndef STBI_NO_STDIO +extern int stbi_is_hdr (char const *filename) +{ + FILE *f = fopen(filename, "rb"); + int result=0; + if (f) { + result = stbi_is_hdr_from_file(f); + fclose(f); + } + return result; +} + +extern int stbi_is_hdr_from_file(FILE *f) +{ + #ifndef STBI_NO_HDR + return stbi_hdr_test_file(f); + #else + return 0; + #endif +} + +#endif + +#ifndef STBI_NO_HDR +static float h2l_gamma_i=1.0f/2.2f, h2l_scale_i=1.0f; +static float l2h_gamma=2.2f, l2h_scale=1.0f; + +void stbi_hdr_to_ldr_gamma(float gamma) { h2l_gamma_i = 1/gamma; } +void stbi_hdr_to_ldr_scale(float scale) { h2l_scale_i = 1/scale; } + +void stbi_ldr_to_hdr_gamma(float gamma) { l2h_gamma = gamma; } +void stbi_ldr_to_hdr_scale(float scale) { l2h_scale = scale; } +#endif + + +////////////////////////////////////////////////////////////////////////////// +// +// Common code used by all image loaders +// + +enum +{ + SCAN_load=0, + SCAN_type, + SCAN_header, +}; + +typedef struct +{ + uint32 img_x, img_y; + int img_n, img_out_n; + + #ifndef STBI_NO_STDIO + FILE *img_file; + int buflen; + uint8 buffer_start[128]; + int from_file; + #endif + uint8 *img_buffer, *img_buffer_end; +} stbi; + +#ifndef STBI_NO_STDIO +static void start_file(stbi *s, FILE *f) +{ + s->img_file = f; + s->buflen = sizeof(s->buffer_start); + s->img_buffer_end = s->buffer_start + s->buflen; + s->img_buffer = s->img_buffer_end; + s->from_file = 1; +} +#endif + +static void start_mem(stbi *s, uint8 const *buffer, int len) +{ +#ifndef STBI_NO_STDIO + s->img_file = NULL; + s->from_file = 0; +#endif + s->img_buffer = (uint8 *) buffer; + s->img_buffer_end = (uint8 *) buffer+len; +} + +#ifndef STBI_NO_STDIO +static void refill_buffer(stbi *s) +{ + int n = fread(s->buffer_start, 1, s->buflen, s->img_file); + if (n == 0) { + s->from_file = 0; + s->img_buffer = s->img_buffer_end-1; + *s->img_buffer = 0; + } else { + s->img_buffer = s->buffer_start; + s->img_buffer_end = s->buffer_start + n; + } +} +#endif + +__forceinline static int get8(stbi *s) +{ + if (s->img_buffer < s->img_buffer_end) + return *s->img_buffer++; +#ifndef STBI_NO_STDIO + if (s->from_file) { + refill_buffer(s); + return *s->img_buffer++; + } +#endif + return 0; +} + +__forceinline static int at_eof(stbi *s) +{ +#ifndef STBI_NO_STDIO + if (s->img_file) { + if (!feof(s->img_file)) return 0; + // if feof() is true, check if buffer = end + // special case: we've only got the special 0 character at the end + if (s->from_file == 0) return 1; + } +#endif + return s->img_buffer >= s->img_buffer_end; +} + +__forceinline static uint8 get8u(stbi *s) +{ + return (uint8) get8(s); +} + +static void skip(stbi *s, int n) +{ +#ifndef STBI_NO_STDIO + if (s->img_file) { + int blen = s->img_buffer_end - s->img_buffer; + if (blen < n) { + s->img_buffer = s->img_buffer_end; + fseek(s->img_file, n - blen, SEEK_CUR); + return; + } + } +#endif + s->img_buffer += n; +} + +static int getn(stbi *s, stbi_uc *buffer, int n) +{ +#ifndef STBI_NO_STDIO + if (s->img_file) { + int blen = s->img_buffer_end - s->img_buffer; + if (blen < n) { + int res; + memcpy(buffer, s->img_buffer, blen); + res = ((int) fread(buffer + blen, 1, n - blen, s->img_file) == (n-blen)); + s->img_buffer = s->img_buffer_end; + return res; + } + } +#endif + if (s->img_buffer+n <= s->img_buffer_end) { + memcpy(buffer, s->img_buffer, n); + s->img_buffer += n; + return 1; + } else + return 0; +} + +static int get16(stbi *s) +{ + int z = get8(s); + return (z << 8) + get8(s); +} + +static uint32 get32(stbi *s) +{ + uint32 z = get16(s); + return (z << 16) + get16(s); +} + +static int get16le(stbi *s) +{ + int z = get8(s); + return z + (get8(s) << 8); +} + +static uint32 get32le(stbi *s) +{ + uint32 z = get16le(s); + return z + (get16le(s) << 16); +} + +////////////////////////////////////////////////////////////////////////////// +// +// generic converter from built-in img_n to req_comp +// individual types do this automatically as much as possible (e.g. jpeg +// does all cases internally since it needs to colorspace convert anyway, +// and it never has alpha, so very few cases ). png can automatically +// interleave an alpha=255 channel, but falls back to this for other cases +// +// assume data buffer is malloced, so malloc a new one and free that one +// only failure mode is malloc failing + +static uint8 compute_y(int r, int g, int b) +{ + return (uint8) (((r*77) + (g*150) + (29*b)) >> 8); +} + +static unsigned char *convert_format(unsigned char *data, int img_n, int req_comp, uint x, uint y) +{ + int i,j; + unsigned char *good; + + if (req_comp == img_n) return data; + assert(req_comp >= 1 && req_comp <= 4); + + good = (unsigned char *) malloc(req_comp * x * y); + if (good == NULL) { + free(data); + return epuc("outofmem", "Out of memory"); + } + + for (j=0; j < (int) y; ++j) { + unsigned char *src = data + j * x * img_n ; + unsigned char *dest = good + j * x * req_comp; + + #define COMBO(a,b) ((a)*8+(b)) + #define CASE(a,b) case COMBO(a,b): for(i=x-1; i >= 0; --i, src += a, dest += b) + // convert source image with img_n components to one with req_comp components; + // avoid switch per pixel, so use switch per scanline and massive macros + switch (COMBO(img_n, req_comp)) { + CASE(1,2) dest[0]=src[0], dest[1]=255; break; + CASE(1,3) dest[0]=dest[1]=dest[2]=src[0]; break; + CASE(1,4) dest[0]=dest[1]=dest[2]=src[0], dest[3]=255; break; + CASE(2,1) dest[0]=src[0]; break; + CASE(2,3) dest[0]=dest[1]=dest[2]=src[0]; break; + CASE(2,4) dest[0]=dest[1]=dest[2]=src[0], dest[3]=src[1]; break; + CASE(3,4) dest[0]=src[0],dest[1]=src[1],dest[2]=src[2],dest[3]=255; break; + CASE(3,1) dest[0]=compute_y(src[0],src[1],src[2]); break; + CASE(3,2) dest[0]=compute_y(src[0],src[1],src[2]), dest[1] = 255; break; + CASE(4,1) dest[0]=compute_y(src[0],src[1],src[2]); break; + CASE(4,2) dest[0]=compute_y(src[0],src[1],src[2]), dest[1] = src[3]; break; + CASE(4,3) dest[0]=src[0],dest[1]=src[1],dest[2]=src[2]; break; + default: assert(0); + } + #undef CASE + } + + free(data); + return good; +} + +#ifndef STBI_NO_HDR +static float *ldr_to_hdr(stbi_uc *data, int x, int y, int comp) +{ + int i,k,n; + float *output = (float *) malloc(x * y * comp * sizeof(float)); + if (output == NULL) { free(data); return epf("outofmem", "Out of memory"); } + // compute number of non-alpha components + if (comp & 1) n = comp; else n = comp-1; + for (i=0; i < x*y; ++i) { + for (k=0; k < n; ++k) { + output[i*comp + k] = (float) pow(data[i*comp+k]/255.0f, l2h_gamma) * l2h_scale; + } + if (k < comp) output[i*comp + k] = data[i*comp+k]/255.0f; + } + free(data); + return output; +} + +#define float2int(x) ((int) (x)) +static stbi_uc *hdr_to_ldr(float *data, int x, int y, int comp) +{ + int i,k,n; + stbi_uc *output = (stbi_uc *) malloc(x * y * comp); + if (output == NULL) { free(data); return epuc("outofmem", "Out of memory"); } + // compute number of non-alpha components + if (comp & 1) n = comp; else n = comp-1; + for (i=0; i < x*y; ++i) { + for (k=0; k < n; ++k) { + float z = (float) pow(data[i*comp+k]*h2l_scale_i, h2l_gamma_i) * 255 + 0.5f; + if (z < 0) z = 0; + if (z > 255) z = 255; + output[i*comp + k] = (uint8) float2int(z); + } + if (k < comp) { + float z = data[i*comp+k] * 255 + 0.5f; + if (z < 0) z = 0; + if (z > 255) z = 255; + output[i*comp + k] = (uint8) float2int(z); + } + } + free(data); + return output; +} +#endif + +////////////////////////////////////////////////////////////////////////////// +// +// "baseline" JPEG/JFIF decoder (not actually fully baseline implementation) +// +// simple implementation +// - channel subsampling of at most 2 in each dimension +// - doesn't support delayed output of y-dimension +// - simple interface (only one output format: 8-bit interleaved RGB) +// - doesn't try to recover corrupt jpegs +// - doesn't allow partial loading, loading multiple at once +// - still fast on x86 (copying globals into locals doesn't help x86) +// - allocates lots of intermediate memory (full size of all components) +// - non-interleaved case requires this anyway +// - allows good upsampling (see next) +// high-quality +// - upsampled channels are bilinearly interpolated, even across blocks +// - quality integer IDCT derived from IJG's 'slow' +// performance +// - fast huffman; reasonable integer IDCT +// - uses a lot of intermediate memory, could cache poorly +// - load http://nothings.org/remote/anemones.jpg 3 times on 2.8Ghz P4 +// stb_jpeg: 1.34 seconds (MSVC6, default release build) +// stb_jpeg: 1.06 seconds (MSVC6, processor = Pentium Pro) +// IJL11.dll: 1.08 seconds (compiled by intel) +// IJG 1998: 0.98 seconds (MSVC6, makefile provided by IJG) +// IJG 1998: 0.95 seconds (MSVC6, makefile + proc=PPro) + +// huffman decoding acceleration +#define FAST_BITS 9 // larger handles more cases; smaller stomps less cache + +typedef struct +{ + uint8 fast[1 << FAST_BITS]; + // weirdly, repacking this into AoS is a 10% speed loss, instead of a win + uint16 code[256]; + uint8 values[256]; + uint8 size[257]; + unsigned int maxcode[18]; + int delta[17]; // old 'firstsymbol' - old 'firstcode' +} huffman; + +typedef struct +{ + #if STBI_SIMD + unsigned short dequant2[4][64]; + #endif + stbi s; + huffman huff_dc[4]; + huffman huff_ac[4]; + uint8 dequant[4][64]; + +// sizes for components, interleaved MCUs + int img_h_max, img_v_max; + int img_mcu_x, img_mcu_y; + int img_mcu_w, img_mcu_h; + +// definition of jpeg image component + struct + { + int id; + int h,v; + int tq; + int hd,ha; + int dc_pred; + + int x,y,w2,h2; + uint8 *data; + void *raw_data; + uint8 *linebuf; + } img_comp[4]; + + uint32 code_buffer; // jpeg entropy-coded buffer + int code_bits; // number of valid bits + unsigned char marker; // marker seen while filling entropy buffer + int nomore; // flag if we saw a marker so must stop + + int scan_n, order[4]; + int restart_interval, todo; +} jpeg; + +static int build_huffman(huffman *h, int *count) +{ + int i,j,k=0,code; + // build size list for each symbol (from JPEG spec) + for (i=0; i < 16; ++i) + for (j=0; j < count[i]; ++j) + h->size[k++] = (uint8) (i+1); + h->size[k] = 0; + + // compute actual symbols (from jpeg spec) + code = 0; + k = 0; + for(j=1; j <= 16; ++j) { + // compute delta to add to code to compute symbol id + h->delta[j] = k - code; + if (h->size[k] == j) { + while (h->size[k] == j) + h->code[k++] = (uint16) (code++); + if (code-1 >= (1 << j)) return e("bad code lengths","Corrupt JPEG"); + } + // compute largest code + 1 for this size, preshifted as needed later + h->maxcode[j] = code << (16-j); + code <<= 1; + } + h->maxcode[j] = 0xffffffff; + + // build non-spec acceleration table; 255 is flag for not-accelerated + memset(h->fast, 255, 1 << FAST_BITS); + for (i=0; i < k; ++i) { + int s = h->size[i]; + if (s <= FAST_BITS) { + int c = h->code[i] << (FAST_BITS-s); + int m = 1 << (FAST_BITS-s); + for (j=0; j < m; ++j) { + h->fast[c+j] = (uint8) i; + } + } + } + return 1; +} + +static void grow_buffer_unsafe(jpeg *j) +{ + do { + int b = j->nomore ? 0 : get8(&j->s); + if (b == 0xff) { + int c = get8(&j->s); + if (c != 0) { + j->marker = (unsigned char) c; + j->nomore = 1; + return; + } + } + j->code_buffer |= b << (24 - j->code_bits); + j->code_bits += 8; + } while (j->code_bits <= 24); +} + +// (1 << n) - 1 +static uint32 bmask[17]={0,1,3,7,15,31,63,127,255,511,1023,2047,4095,8191,16383,32767,65535}; + +// decode a jpeg huffman value from the bitstream +__forceinline static int decode(jpeg *j, huffman *h) +{ + unsigned int temp; + int c,k; + + if (j->code_bits < 16) grow_buffer_unsafe(j); + + // look at the top FAST_BITS and determine what symbol ID it is, + // if the code is <= FAST_BITS + c = (j->code_buffer >> (32 - FAST_BITS)) & ((1 << FAST_BITS)-1); + k = h->fast[c]; + if (k < 255) { + int s = h->size[k]; + if (s > j->code_bits) + return -1; + j->code_buffer <<= s; + j->code_bits -= s; + return h->values[k]; + } + + // naive test is to shift the code_buffer down so k bits are + // valid, then test against maxcode. To speed this up, we've + // preshifted maxcode left so that it has (16-k) 0s at the + // end; in other words, regardless of the number of bits, it + // wants to be compared against something shifted to have 16; + // that way we don't need to shift inside the loop. + temp = j->code_buffer >> 16; + for (k=FAST_BITS+1 ; ; ++k) + if (temp < h->maxcode[k]) + break; + if (k == 17) { + // error! code not found + j->code_bits -= 16; + return -1; + } + + if (k > j->code_bits) + return -1; + + // convert the huffman code to the symbol id + c = ((j->code_buffer >> (32 - k)) & bmask[k]) + h->delta[k]; + assert((((j->code_buffer) >> (32 - h->size[c])) & bmask[h->size[c]]) == h->code[c]); + + // convert the id to a symbol + j->code_bits -= k; + j->code_buffer <<= k; + return h->values[c]; +} + +// combined JPEG 'receive' and JPEG 'extend', since baseline +// always extends everything it receives. +__forceinline static int extend_receive(jpeg *j, int n) +{ + unsigned int m = 1 << (n-1); + unsigned int k; + if (j->code_bits < n) grow_buffer_unsafe(j); + + #if 1 + k = stbi_lrot(j->code_buffer, n); + j->code_buffer = k & ~bmask[n]; + k &= bmask[n]; + j->code_bits -= n; + #else + k = (j->code_buffer >> (32 - n)) & bmask[n]; + j->code_bits -= n; + j->code_buffer <<= n; + #endif + // the following test is probably a random branch that won't + // predict well. I tried to table accelerate it but failed. + // maybe it's compiling as a conditional move? + if (k < m) + return (-1 << n) + k + 1; + else + return k; +} + +// given a value that's at position X in the zigzag stream, +// where does it appear in the 8x8 matrix coded as row-major? +static uint8 dezigzag[64+15] = +{ + 0, 1, 8, 16, 9, 2, 3, 10, + 17, 24, 32, 25, 18, 11, 4, 5, + 12, 19, 26, 33, 40, 48, 41, 34, + 27, 20, 13, 6, 7, 14, 21, 28, + 35, 42, 49, 56, 57, 50, 43, 36, + 29, 22, 15, 23, 30, 37, 44, 51, + 58, 59, 52, 45, 38, 31, 39, 46, + 53, 60, 61, 54, 47, 55, 62, 63, + // let corrupt input sample past end + 63, 63, 63, 63, 63, 63, 63, 63, + 63, 63, 63, 63, 63, 63, 63 +}; + +// decode one 64-entry block-- +static int decode_block(jpeg *j, short data[64], huffman *hdc, huffman *hac, int b) +{ + int diff,dc,k; + int t = decode(j, hdc); + if (t < 0) return e("bad huffman code","Corrupt JPEG"); + + // 0 all the ac values now so we can do it 32-bits at a time + memset(data,0,64*sizeof(data[0])); + + diff = t ? extend_receive(j, t) : 0; + dc = j->img_comp[b].dc_pred + diff; + j->img_comp[b].dc_pred = dc; + data[0] = (short) dc; + + // decode AC components, see JPEG spec + k = 1; + do { + int r,s; + int rs = decode(j, hac); + if (rs < 0) return e("bad huffman code","Corrupt JPEG"); + s = rs & 15; + r = rs >> 4; + if (s == 0) { + if (rs != 0xf0) break; // end block + k += 16; + } else { + k += r; + // decode into unzigzag'd location + data[dezigzag[k++]] = (short) extend_receive(j,s); + } + } while (k < 64); + return 1; +} + +// take a -128..127 value and clamp it and convert to 0..255 +__forceinline static uint8 clamp(int x) +{ + // trick to use a single test to catch both cases + if ((unsigned int) x > 255) { + if (x < 0) return 0; + if (x > 255) return 255; + } + return (uint8) x; +} + +#define f2f(x) (int) (((x) * 4096 + 0.5)) +#define fsh(x) ((x) << 12) + +// derived from jidctint -- DCT_ISLOW +#define IDCT_1D(s0,s1,s2,s3,s4,s5,s6,s7) \ + int t0,t1,t2,t3,p1,p2,p3,p4,p5,x0,x1,x2,x3; \ + p2 = s2; \ + p3 = s6; \ + p1 = (p2+p3) * f2f(0.5411961f); \ + t2 = p1 + p3*f2f(-1.847759065f); \ + t3 = p1 + p2*f2f( 0.765366865f); \ + p2 = s0; \ + p3 = s4; \ + t0 = fsh(p2+p3); \ + t1 = fsh(p2-p3); \ + x0 = t0+t3; \ + x3 = t0-t3; \ + x1 = t1+t2; \ + x2 = t1-t2; \ + t0 = s7; \ + t1 = s5; \ + t2 = s3; \ + t3 = s1; \ + p3 = t0+t2; \ + p4 = t1+t3; \ + p1 = t0+t3; \ + p2 = t1+t2; \ + p5 = (p3+p4)*f2f( 1.175875602f); \ + t0 = t0*f2f( 0.298631336f); \ + t1 = t1*f2f( 2.053119869f); \ + t2 = t2*f2f( 3.072711026f); \ + t3 = t3*f2f( 1.501321110f); \ + p1 = p5 + p1*f2f(-0.899976223f); \ + p2 = p5 + p2*f2f(-2.562915447f); \ + p3 = p3*f2f(-1.961570560f); \ + p4 = p4*f2f(-0.390180644f); \ + t3 += p1+p4; \ + t2 += p2+p3; \ + t1 += p2+p4; \ + t0 += p1+p3; + +#if STBI_SIMD +typedef unsigned short stbi_dequantize_t; +#else +typedef uint8 stbi_dequantize_t; +#endif + +// .344 seconds on 3*anemones.jpg +static void idct_block(uint8 *out, int out_stride, short data[64], stbi_dequantize_t *dequantize) +{ + int i,val[64],*v=val; + stbi_dequantize_t *dq = dequantize; + uint8 *o; + short *d = data; + + // columns + for (i=0; i < 8; ++i,++d,++dq, ++v) { + // if all zeroes, shortcut -- this avoids dequantizing 0s and IDCTing + if (d[ 8]==0 && d[16]==0 && d[24]==0 && d[32]==0 + && d[40]==0 && d[48]==0 && d[56]==0) { + // no shortcut 0 seconds + // (1|2|3|4|5|6|7)==0 0 seconds + // all separate -0.047 seconds + // 1 && 2|3 && 4|5 && 6|7: -0.047 seconds + int dcterm = d[0] * dq[0] << 2; + v[0] = v[8] = v[16] = v[24] = v[32] = v[40] = v[48] = v[56] = dcterm; + } else { + IDCT_1D(d[ 0]*dq[ 0],d[ 8]*dq[ 8],d[16]*dq[16],d[24]*dq[24], + d[32]*dq[32],d[40]*dq[40],d[48]*dq[48],d[56]*dq[56]) + // constants scaled things up by 1<<12; let's bring them back + // down, but keep 2 extra bits of precision + x0 += 512; x1 += 512; x2 += 512; x3 += 512; + v[ 0] = (x0+t3) >> 10; + v[56] = (x0-t3) >> 10; + v[ 8] = (x1+t2) >> 10; + v[48] = (x1-t2) >> 10; + v[16] = (x2+t1) >> 10; + v[40] = (x2-t1) >> 10; + v[24] = (x3+t0) >> 10; + v[32] = (x3-t0) >> 10; + } + } + + for (i=0, v=val, o=out; i < 8; ++i,v+=8,o+=out_stride) { + // no fast case since the first 1D IDCT spread components out + IDCT_1D(v[0],v[1],v[2],v[3],v[4],v[5],v[6],v[7]) + // constants scaled things up by 1<<12, plus we had 1<<2 from first + // loop, plus horizontal and vertical each scale by sqrt(8) so together + // we've got an extra 1<<3, so 1<<17 total we need to remove. + // so we want to round that, which means adding 0.5 * 1<<17, + // aka 65536. Also, we'll end up with -128 to 127 that we want + // to encode as 0..255 by adding 128, so we'll add that before the shift + x0 += 65536 + (128<<17); + x1 += 65536 + (128<<17); + x2 += 65536 + (128<<17); + x3 += 65536 + (128<<17); + // tried computing the shifts into temps, or'ing the temps to see + // if any were out of range, but that was slower + o[0] = clamp((x0+t3) >> 17); + o[7] = clamp((x0-t3) >> 17); + o[1] = clamp((x1+t2) >> 17); + o[6] = clamp((x1-t2) >> 17); + o[2] = clamp((x2+t1) >> 17); + o[5] = clamp((x2-t1) >> 17); + o[3] = clamp((x3+t0) >> 17); + o[4] = clamp((x3-t0) >> 17); + } +} + +#ifdef STBI_SIMD +static stbi_idct_8x8 stbi_idct_installed = idct_block; + +extern void stbi_install_idct(stbi_idct_8x8 func) +{ + stbi_idct_installed = func; +} +#endif + +#define MARKER_none 0xff +// if there's a pending marker from the entropy stream, return that +// otherwise, fetch from the stream and get a marker. if there's no +// marker, return 0xff, which is never a valid marker value +static uint8 get_marker(jpeg *j) +{ + uint8 x; + if (j->marker != MARKER_none) { x = j->marker; j->marker = MARKER_none; return x; } + x = get8u(&j->s); + if (x != 0xff) return MARKER_none; + while (x == 0xff) + x = get8u(&j->s); + return x; +} + +// in each scan, we'll have scan_n components, and the order +// of the components is specified by order[] +#define RESTART(x) ((x) >= 0xd0 && (x) <= 0xd7) + +// after a restart interval, reset the entropy decoder and +// the dc prediction +static void reset(jpeg *j) +{ + j->code_bits = 0; + j->code_buffer = 0; + j->nomore = 0; + j->img_comp[0].dc_pred = j->img_comp[1].dc_pred = j->img_comp[2].dc_pred = 0; + j->marker = MARKER_none; + j->todo = j->restart_interval ? j->restart_interval : 0x7fffffff; + // no more than 1<<31 MCUs if no restart_interal? that's plenty safe, + // since we don't even allow 1<<30 pixels +} + +static int parse_entropy_coded_data(jpeg *z) +{ + reset(z); + if (z->scan_n == 1) { + int i,j; + #if STBI_SIMD + __declspec(align(16)) + #endif + short data[64]; + int n = z->order[0]; + // non-interleaved data, we just need to process one block at a time, + // in trivial scanline order + // number of blocks to do just depends on how many actual "pixels" this + // component has, independent of interleaved MCU blocking and such + int w = (z->img_comp[n].x+7) >> 3; + int h = (z->img_comp[n].y+7) >> 3; + for (j=0; j < h; ++j) { + for (i=0; i < w; ++i) { + if (!decode_block(z, data, z->huff_dc+z->img_comp[n].hd, z->huff_ac+z->img_comp[n].ha, n)) return 0; + #if STBI_SIMD + stbi_idct_installed(z->img_comp[n].data+z->img_comp[n].w2*j*8+i*8, z->img_comp[n].w2, data, z->dequant2[z->img_comp[n].tq]); + #else + idct_block(z->img_comp[n].data+z->img_comp[n].w2*j*8+i*8, z->img_comp[n].w2, data, z->dequant[z->img_comp[n].tq]); + #endif + // every data block is an MCU, so countdown the restart interval + if (--z->todo <= 0) { + if (z->code_bits < 24) grow_buffer_unsafe(z); + // if it's NOT a restart, then just bail, so we get corrupt data + // rather than no data + if (!RESTART(z->marker)) return 1; + reset(z); + } + } + } + } else { // interleaved! + int i,j,k,x,y; + short data[64]; + for (j=0; j < z->img_mcu_y; ++j) { + for (i=0; i < z->img_mcu_x; ++i) { + // scan an interleaved mcu... process scan_n components in order + for (k=0; k < z->scan_n; ++k) { + int n = z->order[k]; + // scan out an mcu's worth of this component; that's just determined + // by the basic H and V specified for the component + for (y=0; y < z->img_comp[n].v; ++y) { + for (x=0; x < z->img_comp[n].h; ++x) { + int x2 = (i*z->img_comp[n].h + x)*8; + int y2 = (j*z->img_comp[n].v + y)*8; + if (!decode_block(z, data, z->huff_dc+z->img_comp[n].hd, z->huff_ac+z->img_comp[n].ha, n)) return 0; + #if STBI_SIMD + stbi_idct_installed(z->img_comp[n].data+z->img_comp[n].w2*y2+x2, z->img_comp[n].w2, data, z->dequant2[z->img_comp[n].tq]); + #else + idct_block(z->img_comp[n].data+z->img_comp[n].w2*y2+x2, z->img_comp[n].w2, data, z->dequant[z->img_comp[n].tq]); + #endif + } + } + } + // after all interleaved components, that's an interleaved MCU, + // so now count down the restart interval + if (--z->todo <= 0) { + if (z->code_bits < 24) grow_buffer_unsafe(z); + // if it's NOT a restart, then just bail, so we get corrupt data + // rather than no data + if (!RESTART(z->marker)) return 1; + reset(z); + } + } + } + } + return 1; +} + +static int process_marker(jpeg *z, int m) +{ + int L; + switch (m) { + case MARKER_none: // no marker found + return e("expected marker","Corrupt JPEG"); + + case 0xC2: // SOF - progressive + return e("progressive jpeg","JPEG format not supported (progressive)"); + + case 0xDD: // DRI - specify restart interval + if (get16(&z->s) != 4) return e("bad DRI len","Corrupt JPEG"); + z->restart_interval = get16(&z->s); + return 1; + + case 0xDB: // DQT - define quantization table + L = get16(&z->s)-2; + while (L > 0) { + int q = get8(&z->s); + int p = q >> 4; + int t = q & 15,i; + if (p != 0) return e("bad DQT type","Corrupt JPEG"); + if (t > 3) return e("bad DQT table","Corrupt JPEG"); + for (i=0; i < 64; ++i) + z->dequant[t][dezigzag[i]] = get8u(&z->s); + #if STBI_SIMD + for (i=0; i < 64; ++i) + z->dequant2[t][i] = z->dequant[t][i]; + #endif + L -= 65; + } + return L==0; + + case 0xC4: // DHT - define huffman table + L = get16(&z->s)-2; + while (L > 0) { + uint8 *v; + int sizes[16],i,m=0; + int q = get8(&z->s); + int tc = q >> 4; + int th = q & 15; + if (tc > 1 || th > 3) return e("bad DHT header","Corrupt JPEG"); + for (i=0; i < 16; ++i) { + sizes[i] = get8(&z->s); + m += sizes[i]; + } + L -= 17; + if (tc == 0) { + if (!build_huffman(z->huff_dc+th, sizes)) return 0; + v = z->huff_dc[th].values; + } else { + if (!build_huffman(z->huff_ac+th, sizes)) return 0; + v = z->huff_ac[th].values; + } + for (i=0; i < m; ++i) + v[i] = get8u(&z->s); + L -= m; + } + return L==0; + } + // check for comment block or APP blocks + if ((m >= 0xE0 && m <= 0xEF) || m == 0xFE) { + skip(&z->s, get16(&z->s)-2); + return 1; + } + return 0; +} + +// after we see SOS +static int process_scan_header(jpeg *z) +{ + int i; + int Ls = get16(&z->s); + z->scan_n = get8(&z->s); + if (z->scan_n < 1 || z->scan_n > 4 || z->scan_n > (int) z->s.img_n) return e("bad SOS component count","Corrupt JPEG"); + if (Ls != 6+2*z->scan_n) return e("bad SOS len","Corrupt JPEG"); + for (i=0; i < z->scan_n; ++i) { + int id = get8(&z->s), which; + int q = get8(&z->s); + for (which = 0; which < z->s.img_n; ++which) + if (z->img_comp[which].id == id) + break; + if (which == z->s.img_n) return 0; + z->img_comp[which].hd = q >> 4; if (z->img_comp[which].hd > 3) return e("bad DC huff","Corrupt JPEG"); + z->img_comp[which].ha = q & 15; if (z->img_comp[which].ha > 3) return e("bad AC huff","Corrupt JPEG"); + z->order[i] = which; + } + if (get8(&z->s) != 0) return e("bad SOS","Corrupt JPEG"); + get8(&z->s); // should be 63, but might be 0 + if (get8(&z->s) != 0) return e("bad SOS","Corrupt JPEG"); + + return 1; +} + +static int process_frame_header(jpeg *z, int scan) +{ + stbi *s = &z->s; + int Lf,p,i,q, h_max=1,v_max=1,c; + Lf = get16(s); if (Lf < 11) return e("bad SOF len","Corrupt JPEG"); // JPEG + p = get8(s); if (p != 8) return e("only 8-bit","JPEG format not supported: 8-bit only"); // JPEG baseline + s->img_y = get16(s); if (s->img_y == 0) return e("no header height", "JPEG format not supported: delayed height"); // Legal, but we don't handle it--but neither does IJG + s->img_x = get16(s); if (s->img_x == 0) return e("0 width","Corrupt JPEG"); // JPEG requires + c = get8(s); + if (c != 3 && c != 1) return e("bad component count","Corrupt JPEG"); // JFIF requires + s->img_n = c; + for (i=0; i < c; ++i) { + z->img_comp[i].data = NULL; + z->img_comp[i].linebuf = NULL; + } + + if (Lf != 8+3*s->img_n) return e("bad SOF len","Corrupt JPEG"); + + for (i=0; i < s->img_n; ++i) { + z->img_comp[i].id = get8(s); + if (z->img_comp[i].id != i+1) // JFIF requires + if (z->img_comp[i].id != i) // some version of jpegtran outputs non-JFIF-compliant files! + return e("bad component ID","Corrupt JPEG"); + q = get8(s); + z->img_comp[i].h = (q >> 4); if (!z->img_comp[i].h || z->img_comp[i].h > 4) return e("bad H","Corrupt JPEG"); + z->img_comp[i].v = q & 15; if (!z->img_comp[i].v || z->img_comp[i].v > 4) return e("bad V","Corrupt JPEG"); + z->img_comp[i].tq = get8(s); if (z->img_comp[i].tq > 3) return e("bad TQ","Corrupt JPEG"); + } + + if (scan != SCAN_load) return 1; + + if ((1 << 30) / s->img_x / s->img_n < s->img_y) return e("too large", "Image too large to decode"); + + for (i=0; i < s->img_n; ++i) { + if (z->img_comp[i].h > h_max) h_max = z->img_comp[i].h; + if (z->img_comp[i].v > v_max) v_max = z->img_comp[i].v; + } + + // compute interleaved mcu info + z->img_h_max = h_max; + z->img_v_max = v_max; + z->img_mcu_w = h_max * 8; + z->img_mcu_h = v_max * 8; + z->img_mcu_x = (s->img_x + z->img_mcu_w-1) / z->img_mcu_w; + z->img_mcu_y = (s->img_y + z->img_mcu_h-1) / z->img_mcu_h; + + for (i=0; i < s->img_n; ++i) { + // number of effective pixels (e.g. for non-interleaved MCU) + z->img_comp[i].x = (s->img_x * z->img_comp[i].h + h_max-1) / h_max; + z->img_comp[i].y = (s->img_y * z->img_comp[i].v + v_max-1) / v_max; + // to simplify generation, we'll allocate enough memory to decode + // the bogus oversized data from using interleaved MCUs and their + // big blocks (e.g. a 16x16 iMCU on an image of width 33); we won't + // discard the extra data until colorspace conversion + z->img_comp[i].w2 = z->img_mcu_x * z->img_comp[i].h * 8; + z->img_comp[i].h2 = z->img_mcu_y * z->img_comp[i].v * 8; + z->img_comp[i].raw_data = malloc(z->img_comp[i].w2 * z->img_comp[i].h2+15); + if (z->img_comp[i].raw_data == NULL) { + for(--i; i >= 0; --i) { + free(z->img_comp[i].raw_data); + z->img_comp[i].data = NULL; + } + return e("outofmem", "Out of memory"); + } + // align blocks for installable-idct using mmx/sse + z->img_comp[i].data = (uint8*) (((size_t) z->img_comp[i].raw_data + 15) & ~15); + z->img_comp[i].linebuf = NULL; + } + + return 1; +} + +// use comparisons since in some cases we handle more than one case (e.g. SOF) +#define DNL(x) ((x) == 0xdc) +#define SOI(x) ((x) == 0xd8) +#define EOI(x) ((x) == 0xd9) +#define SOF(x) ((x) == 0xc0 || (x) == 0xc1) +#define SOS(x) ((x) == 0xda) + +static int decode_jpeg_header(jpeg *z, int scan) +{ + int m; + z->marker = MARKER_none; // initialize cached marker to empty + m = get_marker(z); + if (!SOI(m)) return e("no SOI","Corrupt JPEG"); + if (scan == SCAN_type) return 1; + m = get_marker(z); + while (!SOF(m)) { + if (!process_marker(z,m)) return 0; + m = get_marker(z); + while (m == MARKER_none) { + // some files have extra padding after their blocks, so ok, we'll scan + if (at_eof(&z->s)) return e("no SOF", "Corrupt JPEG"); + m = get_marker(z); + } + } + if (!process_frame_header(z, scan)) return 0; + return 1; +} + +static int decode_jpeg_image(jpeg *j) +{ + int m; + j->restart_interval = 0; + if (!decode_jpeg_header(j, SCAN_load)) return 0; + m = get_marker(j); + while (!EOI(m)) { + if (SOS(m)) { + if (!process_scan_header(j)) return 0; + if (!parse_entropy_coded_data(j)) return 0; + if (j->marker == MARKER_none ) { + // handle 0s at the end of image data from IP Kamera 9060 + while (!at_eof(&j->s)) { + int x = get8(&j->s); + if (x == 255) { + j->marker = get8u(&j->s); + break; + } else if (x != 0) { + return 0; + } + } + // if we reach eof without hitting a marker, get_marker() below will fail and we'll eventually return 0 + } + } else { + if (!process_marker(j, m)) return 0; + } + m = get_marker(j); + } + return 1; +} + +// static jfif-centered resampling (across block boundaries) + +typedef uint8 *(*resample_row_func)(uint8 *out, uint8 *in0, uint8 *in1, + int w, int hs); + +#define div4(x) ((uint8) ((x) >> 2)) + +static uint8 *resample_row_1(uint8 *out, uint8 *in_near, uint8 *in_far, int w, int hs) +{ + STBI_NOTUSED(out); + STBI_NOTUSED(in_far); + STBI_NOTUSED(w); + STBI_NOTUSED(hs); + return in_near; +} + +static uint8* resample_row_v_2(uint8 *out, uint8 *in_near, uint8 *in_far, int w, int hs) +{ + // need to generate two samples vertically for every one in input + int i; + STBI_NOTUSED(hs); + for (i=0; i < w; ++i) + out[i] = div4(3*in_near[i] + in_far[i] + 2); + return out; +} + +static uint8* resample_row_h_2(uint8 *out, uint8 *in_near, uint8 *in_far, int w, int hs) +{ + // need to generate two samples horizontally for every one in input + int i; + uint8 *input = in_near; + + if (w == 1) { + // if only one sample, can't do any interpolation + out[0] = out[1] = input[0]; + return out; + } + + out[0] = input[0]; + out[1] = div4(input[0]*3 + input[1] + 2); + for (i=1; i < w-1; ++i) { + int n = 3*input[i]+2; + out[i*2+0] = div4(n+input[i-1]); + out[i*2+1] = div4(n+input[i+1]); + } + out[i*2+0] = div4(input[w-2]*3 + input[w-1] + 2); + out[i*2+1] = input[w-1]; + + STBI_NOTUSED(in_far); + STBI_NOTUSED(hs); + + return out; +} + +#define div16(x) ((uint8) ((x) >> 4)) + +static uint8 *resample_row_hv_2(uint8 *out, uint8 *in_near, uint8 *in_far, int w, int hs) +{ + // need to generate 2x2 samples for every one in input + int i,t0,t1; + if (w == 1) { + out[0] = out[1] = div4(3*in_near[0] + in_far[0] + 2); + return out; + } + + t1 = 3*in_near[0] + in_far[0]; + out[0] = div4(t1+2); + for (i=1; i < w; ++i) { + t0 = t1; + t1 = 3*in_near[i]+in_far[i]; + out[i*2-1] = div16(3*t0 + t1 + 8); + out[i*2 ] = div16(3*t1 + t0 + 8); + } + out[w*2-1] = div4(t1+2); + + STBI_NOTUSED(hs); + + return out; +} + +static uint8 *resample_row_generic(uint8 *out, uint8 *in_near, uint8 *in_far, int w, int hs) +{ + // resample with nearest-neighbor + int i,j; + in_far = in_far; + for (i=0; i < w; ++i) + for (j=0; j < hs; ++j) + out[i*hs+j] = in_near[i]; + return out; +} + +#define float2fixed(x) ((int) ((x) * 65536 + 0.5)) + +// 0.38 seconds on 3*anemones.jpg (0.25 with processor = Pro) +// VC6 without processor=Pro is generating multiple LEAs per multiply! +static void YCbCr_to_RGB_row(uint8 *out, const uint8 *y, const uint8 *pcb, const uint8 *pcr, int count, int step) +{ + int i; + for (i=0; i < count; ++i) { + int y_fixed = (y[i] << 16) + 32768; // rounding + int r,g,b; + int cr = pcr[i] - 128; + int cb = pcb[i] - 128; + r = y_fixed + cr*float2fixed(1.40200f); + g = y_fixed - cr*float2fixed(0.71414f) - cb*float2fixed(0.34414f); + b = y_fixed + cb*float2fixed(1.77200f); + r >>= 16; + g >>= 16; + b >>= 16; + if ((unsigned) r > 255) { if (r < 0) r = 0; else r = 255; } + if ((unsigned) g > 255) { if (g < 0) g = 0; else g = 255; } + if ((unsigned) b > 255) { if (b < 0) b = 0; else b = 255; } + out[0] = (uint8)r; + out[1] = (uint8)g; + out[2] = (uint8)b; + out[3] = 255; + out += step; + } +} + +#if STBI_SIMD +static stbi_YCbCr_to_RGB_run stbi_YCbCr_installed = YCbCr_to_RGB_row; + +void stbi_install_YCbCr_to_RGB(stbi_YCbCr_to_RGB_run func) +{ + stbi_YCbCr_installed = func; +} +#endif + + +// clean up the temporary component buffers +static void cleanup_jpeg(jpeg *j) +{ + int i; + for (i=0; i < j->s.img_n; ++i) { + if (j->img_comp[i].data) { + free(j->img_comp[i].raw_data); + j->img_comp[i].data = NULL; + } + if (j->img_comp[i].linebuf) { + free(j->img_comp[i].linebuf); + j->img_comp[i].linebuf = NULL; + } + } +} + +typedef struct +{ + resample_row_func resample; + uint8 *line0,*line1; + int hs,vs; // expansion factor in each axis + int w_lores; // horizontal pixels pre-expansion + int ystep; // how far through vertical expansion we are + int ypos; // which pre-expansion row we're on +} stbi_resample; + +static uint8 *load_jpeg_image(jpeg *z, int *out_x, int *out_y, int *comp, int req_comp) +{ + int n, decode_n; + // validate req_comp + if (req_comp < 0 || req_comp > 4) return epuc("bad req_comp", "Internal error"); + z->s.img_n = 0; + + // load a jpeg image from whichever source + if (!decode_jpeg_image(z)) { cleanup_jpeg(z); return NULL; } + + // determine actual number of components to generate + n = req_comp ? req_comp : z->s.img_n; + + if (z->s.img_n == 3 && n < 3) + decode_n = 1; + else + decode_n = z->s.img_n; + + // resample and color-convert + { + int k; + uint i,j; + uint8 *output; + uint8 *coutput[4]; + + stbi_resample res_comp[4]; + + for (k=0; k < decode_n; ++k) { + stbi_resample *r = &res_comp[k]; + + // allocate line buffer big enough for upsampling off the edges + // with upsample factor of 4 + z->img_comp[k].linebuf = (uint8 *) malloc(z->s.img_x + 3); + if (!z->img_comp[k].linebuf) { cleanup_jpeg(z); return epuc("outofmem", "Out of memory"); } + + r->hs = z->img_h_max / z->img_comp[k].h; + r->vs = z->img_v_max / z->img_comp[k].v; + r->ystep = r->vs >> 1; + r->w_lores = (z->s.img_x + r->hs-1) / r->hs; + r->ypos = 0; + r->line0 = r->line1 = z->img_comp[k].data; + + if (r->hs == 1 && r->vs == 1) r->resample = resample_row_1; + else if (r->hs == 1 && r->vs == 2) r->resample = resample_row_v_2; + else if (r->hs == 2 && r->vs == 1) r->resample = resample_row_h_2; + else if (r->hs == 2 && r->vs == 2) r->resample = resample_row_hv_2; + else r->resample = resample_row_generic; + } + + // can't error after this so, this is safe + output = (uint8 *) malloc(n * z->s.img_x * z->s.img_y + 1); + if (!output) { cleanup_jpeg(z); return epuc("outofmem", "Out of memory"); } + + // now go ahead and resample + for (j=0; j < z->s.img_y; ++j) { + uint8 *out = output + n * z->s.img_x * j; + for (k=0; k < decode_n; ++k) { + stbi_resample *r = &res_comp[k]; + int y_bot = r->ystep >= (r->vs >> 1); + coutput[k] = r->resample(z->img_comp[k].linebuf, + y_bot ? r->line1 : r->line0, + y_bot ? r->line0 : r->line1, + r->w_lores, r->hs); + if (++r->ystep >= r->vs) { + r->ystep = 0; + r->line0 = r->line1; + if (++r->ypos < z->img_comp[k].y) + r->line1 += z->img_comp[k].w2; + } + } + if (n >= 3) { + uint8 *y = coutput[0]; + if (z->s.img_n == 3) { + #if STBI_SIMD + stbi_YCbCr_installed(out, y, coutput[1], coutput[2], z->s.img_x, n); + #else + YCbCr_to_RGB_row(out, y, coutput[1], coutput[2], z->s.img_x, n); + #endif + } else + for (i=0; i < z->s.img_x; ++i) { + out[0] = out[1] = out[2] = y[i]; + out[3] = 255; // not used if n==3 + out += n; + } + } else { + uint8 *y = coutput[0]; + if (n == 1) + for (i=0; i < z->s.img_x; ++i) out[i] = y[i]; + else + for (i=0; i < z->s.img_x; ++i) *out++ = y[i], *out++ = 255; + } + } + cleanup_jpeg(z); + *out_x = z->s.img_x; + *out_y = z->s.img_y; + if (comp) *comp = z->s.img_n; // report original components, not output + return output; + } +} + +#ifndef STBI_NO_STDIO +unsigned char *stbi_jpeg_load_from_file(FILE *f, int *x, int *y, int *comp, int req_comp) +{ + jpeg j; + start_file(&j.s, f); + return load_jpeg_image(&j, x,y,comp,req_comp); +} + +unsigned char *stbi_jpeg_load(char const *filename, int *x, int *y, int *comp, int req_comp) +{ + unsigned char *data; + FILE *f = fopen(filename, "rb"); + if (!f) return NULL; + data = stbi_jpeg_load_from_file(f,x,y,comp,req_comp); + fclose(f); + return data; +} +#endif + +unsigned char *stbi_jpeg_load_from_memory(stbi_uc const *buffer, int len, int *x, int *y, int *comp, int req_comp) +{ + jpeg j; + start_mem(&j.s, buffer,len); + return load_jpeg_image(&j, x,y,comp,req_comp); +} + +static int stbi_jpeg_info_raw(jpeg *j, int *x, int *y, int *comp) +{ + if (!decode_jpeg_header(j, SCAN_header)) + return 0; + if (x) *x = j->s.img_x; + if (y) *y = j->s.img_y; + if (comp) *comp = j->s.img_n; + return 1; +} + +#ifndef STBI_NO_STDIO +int stbi_jpeg_test_file(FILE *f) +{ + int n,r; + jpeg j; + n = ftell(f); + start_file(&j.s, f); + r = decode_jpeg_header(&j, SCAN_type); + fseek(f,n,SEEK_SET); + return r; +} + +int stbi_jpeg_info_from_file(FILE *f, int *x, int *y, int *comp) +{ + jpeg j; + long n = ftell(f); + int res; + start_file(&j.s, f); + res = stbi_jpeg_info_raw(&j, x, y, comp); + fseek(f, n, SEEK_SET); + return res; +} + +int stbi_jpeg_info(char const *filename, int *x, int *y, int *comp) +{ + FILE *f = fopen(filename, "rb"); + int result; + if (!f) return e("can't fopen", "Unable to open file"); + result = stbi_jpeg_info_from_file(f, x, y, comp); + fclose(f); + return result; +} +#endif + +int stbi_jpeg_test_memory(stbi_uc const *buffer, int len) +{ + jpeg j; + start_mem(&j.s, buffer,len); + return decode_jpeg_header(&j, SCAN_type); +} + +int stbi_jpeg_info_from_memory(stbi_uc const *buffer, int len, int *x, int *y, int *comp) +{ + jpeg j; + start_mem(&j.s, buffer, len); + return stbi_jpeg_info_raw(&j, x, y, comp); +} + +#ifndef STBI_NO_STDIO +extern int stbi_jpeg_info (char const *filename, int *x, int *y, int *comp); +extern int stbi_jpeg_info_from_file (FILE *f, int *x, int *y, int *comp); +#endif +extern int stbi_jpeg_info_from_memory(stbi_uc const *buffer, int len, int *x, int *y, int *comp); + +// public domain zlib decode v0.2 Sean Barrett 2006-11-18 +// simple implementation +// - all input must be provided in an upfront buffer +// - all output is written to a single output buffer (can malloc/realloc) +// performance +// - fast huffman + +// fast-way is faster to check than jpeg huffman, but slow way is slower +#define ZFAST_BITS 9 // accelerate all cases in default tables +#define ZFAST_MASK ((1 << ZFAST_BITS) - 1) + +// zlib-style huffman encoding +// (jpegs packs from left, zlib from right, so can't share code) +typedef struct +{ + uint16 fast[1 << ZFAST_BITS]; + uint16 firstcode[16]; + int maxcode[17]; + uint16 firstsymbol[16]; + uint8 size[288]; + uint16 value[288]; +} zhuffman; + +__forceinline static int bitreverse16(int n) +{ + n = ((n & 0xAAAA) >> 1) | ((n & 0x5555) << 1); + n = ((n & 0xCCCC) >> 2) | ((n & 0x3333) << 2); + n = ((n & 0xF0F0) >> 4) | ((n & 0x0F0F) << 4); + n = ((n & 0xFF00) >> 8) | ((n & 0x00FF) << 8); + return n; +} + +__forceinline static int bit_reverse(int v, int bits) +{ + assert(bits <= 16); + // to bit reverse n bits, reverse 16 and shift + // e.g. 11 bits, bit reverse and shift away 5 + return bitreverse16(v) >> (16-bits); +} + +static int zbuild_huffman(zhuffman *z, uint8 *sizelist, int num) +{ + int i,k=0; + int code, next_code[16], sizes[17]; + + // DEFLATE spec for generating codes + memset(sizes, 0, sizeof(sizes)); + memset(z->fast, 255, sizeof(z->fast)); + for (i=0; i < num; ++i) + ++sizes[sizelist[i]]; + sizes[0] = 0; + for (i=1; i < 16; ++i) + assert(sizes[i] <= (1 << i)); + code = 0; + for (i=1; i < 16; ++i) { + next_code[i] = code; + z->firstcode[i] = (uint16) code; + z->firstsymbol[i] = (uint16) k; + code = (code + sizes[i]); + if (sizes[i]) + if (code-1 >= (1 << i)) return e("bad codelengths","Corrupt JPEG"); + z->maxcode[i] = code << (16-i); // preshift for inner loop + code <<= 1; + k += sizes[i]; + } + z->maxcode[16] = 0x10000; // sentinel + for (i=0; i < num; ++i) { + int s = sizelist[i]; + if (s) { + int c = next_code[s] - z->firstcode[s] + z->firstsymbol[s]; + z->size[c] = (uint8)s; + z->value[c] = (uint16)i; + if (s <= ZFAST_BITS) { + int k = bit_reverse(next_code[s],s); + while (k < (1 << ZFAST_BITS)) { + z->fast[k] = (uint16) c; + k += (1 << s); + } + } + ++next_code[s]; + } + } + return 1; +} + +// zlib-from-memory implementation for PNG reading +// because PNG allows splitting the zlib stream arbitrarily, +// and it's annoying structurally to have PNG call ZLIB call PNG, +// we require PNG read all the IDATs and combine them into a single +// memory buffer + +typedef struct +{ + uint8 *zbuffer, *zbuffer_end; + int num_bits; + uint32 code_buffer; + + char *zout; + char *zout_start; + char *zout_end; + int z_expandable; + + zhuffman z_length, z_distance; +} zbuf; + +__forceinline static int zget8(zbuf *z) +{ + if (z->zbuffer >= z->zbuffer_end) return 0; + return *z->zbuffer++; +} + +static void fill_bits(zbuf *z) +{ + do { + assert(z->code_buffer < (1U << z->num_bits)); + z->code_buffer |= zget8(z) << z->num_bits; + z->num_bits += 8; + } while (z->num_bits <= 24); +} + +__forceinline static unsigned int zreceive(zbuf *z, int n) +{ + unsigned int k; + if (z->num_bits < n) fill_bits(z); + k = z->code_buffer & ((1 << n) - 1); + z->code_buffer >>= n; + z->num_bits -= n; + return k; +} + +__forceinline static int zhuffman_decode(zbuf *a, zhuffman *z) +{ + int b,s,k; + if (a->num_bits < 16) fill_bits(a); + b = z->fast[a->code_buffer & ZFAST_MASK]; + if (b < 0xffff) { + s = z->size[b]; + a->code_buffer >>= s; + a->num_bits -= s; + return z->value[b]; + } + + // not resolved by fast table, so compute it the slow way + // use jpeg approach, which requires MSbits at top + k = bit_reverse(a->code_buffer, 16); + for (s=ZFAST_BITS+1; ; ++s) + if (k < z->maxcode[s]) + break; + if (s == 16) return -1; // invalid code! + // code size is s, so: + b = (k >> (16-s)) - z->firstcode[s] + z->firstsymbol[s]; + assert(z->size[b] == s); + a->code_buffer >>= s; + a->num_bits -= s; + return z->value[b]; +} + +static int expand(zbuf *z, int n) // need to make room for n bytes +{ + char *q; + int cur, limit; + if (!z->z_expandable) return e("output buffer limit","Corrupt PNG"); + cur = (int) (z->zout - z->zout_start); + limit = (int) (z->zout_end - z->zout_start); + while (cur + n > limit) + limit *= 2; + q = (char *) realloc(z->zout_start, limit); + if (q == NULL) return e("outofmem", "Out of memory"); + z->zout_start = q; + z->zout = q + cur; + z->zout_end = q + limit; + return 1; +} + +static int length_base[31] = { + 3,4,5,6,7,8,9,10,11,13, + 15,17,19,23,27,31,35,43,51,59, + 67,83,99,115,131,163,195,227,258,0,0 }; + +static int length_extra[31]= +{ 0,0,0,0,0,0,0,0,1,1,1,1,2,2,2,2,3,3,3,3,4,4,4,4,5,5,5,5,0,0,0 }; + +static int dist_base[32] = { 1,2,3,4,5,7,9,13,17,25,33,49,65,97,129,193, +257,385,513,769,1025,1537,2049,3073,4097,6145,8193,12289,16385,24577,0,0}; + +static int dist_extra[32] = +{ 0,0,0,0,1,1,2,2,3,3,4,4,5,5,6,6,7,7,8,8,9,9,10,10,11,11,12,12,13,13}; + +static int parse_huffman_block(zbuf *a) +{ + for(;;) { + int z = zhuffman_decode(a, &a->z_length); + if (z < 256) { + if (z < 0) return e("bad huffman code","Corrupt PNG"); // error in huffman codes + if (a->zout >= a->zout_end) if (!expand(a, 1)) return 0; + *a->zout++ = (char) z; + } else { + uint8 *p; + int len,dist; + if (z == 256) return 1; + z -= 257; + len = length_base[z]; + if (length_extra[z]) len += zreceive(a, length_extra[z]); + z = zhuffman_decode(a, &a->z_distance); + if (z < 0) return e("bad huffman code","Corrupt PNG"); + dist = dist_base[z]; + if (dist_extra[z]) dist += zreceive(a, dist_extra[z]); + if (a->zout - a->zout_start < dist) return e("bad dist","Corrupt PNG"); + if (a->zout + len > a->zout_end) if (!expand(a, len)) return 0; + p = (uint8 *) (a->zout - dist); + while (len--) + *a->zout++ = *p++; + } + } +} + +static int compute_huffman_codes(zbuf *a) +{ + static uint8 length_dezigzag[19] = { 16,17,18,0,8,7,9,6,10,5,11,4,12,3,13,2,14,1,15 }; + zhuffman z_codelength; + uint8 lencodes[286+32+137];//padding for maximum single op + uint8 codelength_sizes[19]; + int i,n; + + int hlit = zreceive(a,5) + 257; + int hdist = zreceive(a,5) + 1; + int hclen = zreceive(a,4) + 4; + + memset(codelength_sizes, 0, sizeof(codelength_sizes)); + for (i=0; i < hclen; ++i) { + int s = zreceive(a,3); + codelength_sizes[length_dezigzag[i]] = (uint8) s; + } + if (!zbuild_huffman(&z_codelength, codelength_sizes, 19)) return 0; + + n = 0; + while (n < hlit + hdist) { + int c = zhuffman_decode(a, &z_codelength); + assert(c >= 0 && c < 19); + if (c < 16) + lencodes[n++] = (uint8) c; + else if (c == 16) { + c = zreceive(a,2)+3; + memset(lencodes+n, lencodes[n-1], c); + n += c; + } else if (c == 17) { + c = zreceive(a,3)+3; + memset(lencodes+n, 0, c); + n += c; + } else { + assert(c == 18); + c = zreceive(a,7)+11; + memset(lencodes+n, 0, c); + n += c; + } + } + if (n != hlit+hdist) return e("bad codelengths","Corrupt PNG"); + if (!zbuild_huffman(&a->z_length, lencodes, hlit)) return 0; + if (!zbuild_huffman(&a->z_distance, lencodes+hlit, hdist)) return 0; + return 1; +} + +static int parse_uncompressed_block(zbuf *a) +{ + uint8 header[4]; + int len,nlen,k; + if (a->num_bits & 7) + zreceive(a, a->num_bits & 7); // discard + // drain the bit-packed data into header + k = 0; + while (a->num_bits > 0) { + header[k++] = (uint8) (a->code_buffer & 255); // wtf this warns? + a->code_buffer >>= 8; + a->num_bits -= 8; + } + assert(a->num_bits == 0); + // now fill header the normal way + while (k < 4) + header[k++] = (uint8) zget8(a); + len = header[1] * 256 + header[0]; + nlen = header[3] * 256 + header[2]; + if (nlen != (len ^ 0xffff)) return e("zlib corrupt","Corrupt PNG"); + if (a->zbuffer + len > a->zbuffer_end) return e("read past buffer","Corrupt PNG"); + if (a->zout + len > a->zout_end) + if (!expand(a, len)) return 0; + memcpy(a->zout, a->zbuffer, len); + a->zbuffer += len; + a->zout += len; + return 1; +} + +static int parse_zlib_header(zbuf *a) +{ + int cmf = zget8(a); + int cm = cmf & 15; + /* int cinfo = cmf >> 4; */ + int flg = zget8(a); + if ((cmf*256+flg) % 31 != 0) return e("bad zlib header","Corrupt PNG"); // zlib spec + if (flg & 32) return e("no preset dict","Corrupt PNG"); // preset dictionary not allowed in png + if (cm != 8) return e("bad compression","Corrupt PNG"); // DEFLATE required for png + // window = 1 << (8 + cinfo)... but who cares, we fully buffer output + return 1; +} + +// @TODO: should statically initialize these for optimal thread safety +static uint8 default_length[288], default_distance[32]; +static void init_defaults(void) +{ + int i; // use <= to match clearly with spec + for (i=0; i <= 143; ++i) default_length[i] = 8; + for ( ; i <= 255; ++i) default_length[i] = 9; + for ( ; i <= 279; ++i) default_length[i] = 7; + for ( ; i <= 287; ++i) default_length[i] = 8; + + for (i=0; i <= 31; ++i) default_distance[i] = 5; +} + +int stbi_png_partial; // a quick hack to only allow decoding some of a PNG... I should implement real streaming support instead +static int parse_zlib(zbuf *a, int parse_header) +{ + int final, type; + if (parse_header) + if (!parse_zlib_header(a)) return 0; + a->num_bits = 0; + a->code_buffer = 0; + do { + final = zreceive(a,1); + type = zreceive(a,2); + if (type == 0) { + if (!parse_uncompressed_block(a)) return 0; + } else if (type == 3) { + return 0; + } else { + if (type == 1) { + // use fixed code lengths + if (!default_distance[31]) init_defaults(); + if (!zbuild_huffman(&a->z_length , default_length , 288)) return 0; + if (!zbuild_huffman(&a->z_distance, default_distance, 32)) return 0; + } else { + if (!compute_huffman_codes(a)) return 0; + } + if (!parse_huffman_block(a)) return 0; + } + if (stbi_png_partial && a->zout - a->zout_start > 65536) + break; + } while (!final); + return 1; +} + +static int do_zlib(zbuf *a, char *obuf, int olen, int exp, int parse_header) +{ + a->zout_start = obuf; + a->zout = obuf; + a->zout_end = obuf + olen; + a->z_expandable = exp; + + return parse_zlib(a, parse_header); +} + +char *stbi_zlib_decode_malloc_guesssize(const char *buffer, int len, int initial_size, int *outlen) +{ + zbuf a; + char *p = (char *) malloc(initial_size); + if (p == NULL) return NULL; + a.zbuffer = (uint8 *) buffer; + a.zbuffer_end = (uint8 *) buffer + len; + if (do_zlib(&a, p, initial_size, 1, 1)) { + if (outlen) *outlen = (int) (a.zout - a.zout_start); + return a.zout_start; + } else { + free(a.zout_start); + return NULL; + } +} + +char *stbi_zlib_decode_malloc(char const *buffer, int len, int *outlen) +{ + return stbi_zlib_decode_malloc_guesssize(buffer, len, 16384, outlen); +} + +char *stbi_zlib_decode_malloc_guesssize_headerflag(const char *buffer, int len, int initial_size, int *outlen, int parse_header) +{ + zbuf a; + char *p = (char *) malloc(initial_size); + if (p == NULL) return NULL; + a.zbuffer = (uint8 *) buffer; + a.zbuffer_end = (uint8 *) buffer + len; + if (do_zlib(&a, p, initial_size, 1, parse_header)) { + if (outlen) *outlen = (int) (a.zout - a.zout_start); + return a.zout_start; + } else { + free(a.zout_start); + return NULL; + } +} + +int stbi_zlib_decode_buffer(char *obuffer, int olen, char const *ibuffer, int ilen) +{ + zbuf a; + a.zbuffer = (uint8 *) ibuffer; + a.zbuffer_end = (uint8 *) ibuffer + ilen; + if (do_zlib(&a, obuffer, olen, 0, 1)) + return (int) (a.zout - a.zout_start); + else + return -1; +} + +char *stbi_zlib_decode_noheader_malloc(char const *buffer, int len, int *outlen) +{ + zbuf a; + char *p = (char *) malloc(16384); + if (p == NULL) return NULL; + a.zbuffer = (uint8 *) buffer; + a.zbuffer_end = (uint8 *) buffer+len; + if (do_zlib(&a, p, 16384, 1, 0)) { + if (outlen) *outlen = (int) (a.zout - a.zout_start); + return a.zout_start; + } else { + free(a.zout_start); + return NULL; + } +} + +int stbi_zlib_decode_noheader_buffer(char *obuffer, int olen, const char *ibuffer, int ilen) +{ + zbuf a; + a.zbuffer = (uint8 *) ibuffer; + a.zbuffer_end = (uint8 *) ibuffer + ilen; + if (do_zlib(&a, obuffer, olen, 0, 0)) + return (int) (a.zout - a.zout_start); + else + return -1; +} + +// public domain "baseline" PNG decoder v0.10 Sean Barrett 2006-11-18 +// simple implementation +// - only 8-bit samples +// - no CRC checking +// - allocates lots of intermediate memory +// - avoids problem of streaming data between subsystems +// - avoids explicit window management +// performance +// - uses stb_zlib, a PD zlib implementation with fast huffman decoding + + +typedef struct +{ + uint32 length; + uint32 type; +} chunk; + +#define PNG_TYPE(a,b,c,d) (((a) << 24) + ((b) << 16) + ((c) << 8) + (d)) + +static chunk get_chunk_header(stbi *s) +{ + chunk c; + c.length = get32(s); + c.type = get32(s); + return c; +} + +static int check_png_header(stbi *s) +{ + static uint8 png_sig[8] = { 137,80,78,71,13,10,26,10 }; + int i; + for (i=0; i < 8; ++i) + if (get8(s) != png_sig[i]) return e("bad png sig","Not a PNG"); + return 1; +} + +typedef struct +{ + stbi s; + uint8 *idata, *expanded, *out; +} png; + + +enum { + F_none=0, F_sub=1, F_up=2, F_avg=3, F_paeth=4, + F_avg_first, F_paeth_first, +}; + +static uint8 first_row_filter[5] = +{ + F_none, F_sub, F_none, F_avg_first, F_paeth_first +}; + +static int paeth(int a, int b, int c) +{ + int p = a + b - c; + int pa = abs(p-a); + int pb = abs(p-b); + int pc = abs(p-c); + if (pa <= pb && pa <= pc) return a; + if (pb <= pc) return b; + return c; +} + +// create the png data from post-deflated data +static int create_png_image_raw(png *a, uint8 *raw, uint32 raw_len, int out_n, uint32 x, uint32 y) +{ + stbi *s = &a->s; + uint32 i,j,stride = x*out_n; + int k; + int img_n = s->img_n; // copy it into a local for later + assert(out_n == s->img_n || out_n == s->img_n+1); + if (stbi_png_partial) y = 1; + a->out = (uint8 *) malloc(x * y * out_n); + if (!a->out) return e("outofmem", "Out of memory"); + if (!stbi_png_partial) { + if (s->img_x == x && s->img_y == y) { + if (raw_len != (img_n * x + 1) * y) return e("not enough pixels","Corrupt PNG"); + } else { // interlaced: + if (raw_len < (img_n * x + 1) * y) return e("not enough pixels","Corrupt PNG"); + } + } + for (j=0; j < y; ++j) { + uint8 *cur = a->out + stride*j; + uint8 *prior = cur - stride; + int filter = *raw++; + if (filter > 4) return e("invalid filter","Corrupt PNG"); + // if first row, use special filter that doesn't sample previous row + if (j == 0) filter = first_row_filter[filter]; + // handle first pixel explicitly + for (k=0; k < img_n; ++k) { + switch (filter) { + case F_none : cur[k] = raw[k]; break; + case F_sub : cur[k] = raw[k]; break; + case F_up : cur[k] = raw[k] + prior[k]; break; + case F_avg : cur[k] = raw[k] + (prior[k]>>1); break; + case F_paeth : cur[k] = (uint8) (raw[k] + paeth(0,prior[k],0)); break; + case F_avg_first : cur[k] = raw[k]; break; + case F_paeth_first: cur[k] = raw[k]; break; + } + } + if (img_n != out_n) cur[img_n] = 255; + raw += img_n; + cur += out_n; + prior += out_n; + // this is a little gross, so that we don't switch per-pixel or per-component + if (img_n == out_n) { + #define CASE(f) \ + case f: \ + for (i=x-1; i >= 1; --i, raw+=img_n,cur+=img_n,prior+=img_n) \ + for (k=0; k < img_n; ++k) + switch (filter) { + CASE(F_none) cur[k] = raw[k]; break; + CASE(F_sub) cur[k] = raw[k] + cur[k-img_n]; break; + CASE(F_up) cur[k] = raw[k] + prior[k]; break; + CASE(F_avg) cur[k] = raw[k] + ((prior[k] + cur[k-img_n])>>1); break; + CASE(F_paeth) cur[k] = (uint8) (raw[k] + paeth(cur[k-img_n],prior[k],prior[k-img_n])); break; + CASE(F_avg_first) cur[k] = raw[k] + (cur[k-img_n] >> 1); break; + CASE(F_paeth_first) cur[k] = (uint8) (raw[k] + paeth(cur[k-img_n],0,0)); break; + } + #undef CASE + } else { + assert(img_n+1 == out_n); + #define CASE(f) \ + case f: \ + for (i=x-1; i >= 1; --i, cur[img_n]=255,raw+=img_n,cur+=out_n,prior+=out_n) \ + for (k=0; k < img_n; ++k) + switch (filter) { + CASE(F_none) cur[k] = raw[k]; break; + CASE(F_sub) cur[k] = raw[k] + cur[k-out_n]; break; + CASE(F_up) cur[k] = raw[k] + prior[k]; break; + CASE(F_avg) cur[k] = raw[k] + ((prior[k] + cur[k-out_n])>>1); break; + CASE(F_paeth) cur[k] = (uint8) (raw[k] + paeth(cur[k-out_n],prior[k],prior[k-out_n])); break; + CASE(F_avg_first) cur[k] = raw[k] + (cur[k-out_n] >> 1); break; + CASE(F_paeth_first) cur[k] = (uint8) (raw[k] + paeth(cur[k-out_n],0,0)); break; + } + #undef CASE + } + } + return 1; +} + +static int create_png_image(png *a, uint8 *raw, uint32 raw_len, int out_n, int interlaced) +{ + uint8 *final; + int p; + int save; + if (!interlaced) + return create_png_image_raw(a, raw, raw_len, out_n, a->s.img_x, a->s.img_y); + save = stbi_png_partial; + stbi_png_partial = 0; + + // de-interlacing + final = (uint8 *) malloc(a->s.img_x * a->s.img_y * out_n); + for (p=0; p < 7; ++p) { + int xorig[] = { 0,4,0,2,0,1,0 }; + int yorig[] = { 0,0,4,0,2,0,1 }; + int xspc[] = { 8,8,4,4,2,2,1 }; + int yspc[] = { 8,8,8,4,4,2,2 }; + int i,j,x,y; + // pass1_x[4] = 0, pass1_x[5] = 1, pass1_x[12] = 1 + x = (a->s.img_x - xorig[p] + xspc[p]-1) / xspc[p]; + y = (a->s.img_y - yorig[p] + yspc[p]-1) / yspc[p]; + if (x && y) { + if (!create_png_image_raw(a, raw, raw_len, out_n, x, y)) { + free(final); + return 0; + } + for (j=0; j < y; ++j) + for (i=0; i < x; ++i) + memcpy(final + (j*yspc[p]+yorig[p])*a->s.img_x*out_n + (i*xspc[p]+xorig[p])*out_n, + a->out + (j*x+i)*out_n, out_n); + free(a->out); + raw += (x*out_n+1)*y; + raw_len -= (x*out_n+1)*y; + } + } + a->out = final; + + stbi_png_partial = save; + return 1; +} + +static int compute_transparency(png *z, uint8 tc[3], int out_n) +{ + stbi *s = &z->s; + uint32 i, pixel_count = s->img_x * s->img_y; + uint8 *p = z->out; + + // compute color-based transparency, assuming we've + // already got 255 as the alpha value in the output + assert(out_n == 2 || out_n == 4); + + if (out_n == 2) { + for (i=0; i < pixel_count; ++i) { + p[1] = (p[0] == tc[0] ? 0 : 255); + p += 2; + } + } else { + for (i=0; i < pixel_count; ++i) { + if (p[0] == tc[0] && p[1] == tc[1] && p[2] == tc[2]) + p[3] = 0; + p += 4; + } + } + return 1; +} + +static int expand_palette(png *a, uint8 *palette, int len, int pal_img_n) +{ + uint32 i, pixel_count = a->s.img_x * a->s.img_y; + uint8 *p, *temp_out, *orig = a->out; + + p = (uint8 *) malloc(pixel_count * pal_img_n); + if (p == NULL) return e("outofmem", "Out of memory"); + + // between here and free(out) below, exitting would leak + temp_out = p; + + if (pal_img_n == 3) { + for (i=0; i < pixel_count; ++i) { + int n = orig[i]*4; + p[0] = palette[n ]; + p[1] = palette[n+1]; + p[2] = palette[n+2]; + p += 3; + } + } else { + for (i=0; i < pixel_count; ++i) { + int n = orig[i]*4; + p[0] = palette[n ]; + p[1] = palette[n+1]; + p[2] = palette[n+2]; + p[3] = palette[n+3]; + p += 4; + } + } + free(a->out); + a->out = temp_out; + + STBI_NOTUSED(len); + + return 1; +} + +static int stbi_unpremultiply_on_load = 0; +static int stbi_de_iphone_flag = 0; + +void stbi_set_unpremultiply_on_load(int flag_true_if_should_unpremultiply) +{ + stbi_unpremultiply_on_load = flag_true_if_should_unpremultiply; +} +void stbi_convert_iphone_png_to_rgb(int flag_true_if_should_convert) +{ + stbi_de_iphone_flag = flag_true_if_should_convert; +} + +static void stbi_de_iphone(png *z) +{ + stbi *s = &z->s; + uint32 i, pixel_count = s->img_x * s->img_y; + uint8 *p = z->out; + + if (s->img_out_n == 3) { // convert bgr to rgb + for (i=0; i < pixel_count; ++i) { + uint8 t = p[0]; + p[0] = p[2]; + p[2] = t; + p += 3; + } + } else { + assert(s->img_out_n == 4); + if (stbi_unpremultiply_on_load) { + // convert bgr to rgb and unpremultiply + for (i=0; i < pixel_count; ++i) { + uint8 a = p[3]; + uint8 t = p[0]; + if (a) { + p[0] = p[2] * 255 / a; + p[1] = p[1] * 255 / a; + p[2] = t * 255 / a; + } else { + p[0] = p[2]; + p[2] = t; + } + p += 4; + } + } else { + // convert bgr to rgb + for (i=0; i < pixel_count; ++i) { + uint8 t = p[0]; + p[0] = p[2]; + p[2] = t; + p += 4; + } + } + } +} + +static int parse_png_file(png *z, int scan, int req_comp) +{ + uint8 palette[1024], pal_img_n=0; + uint8 has_trans=0, tc[3]; + uint32 ioff=0, idata_limit=0, i, pal_len=0; + int first=1,k,interlace=0, iphone=0; + stbi *s = &z->s; + + if (!check_png_header(s)) return 0; + + if (scan == SCAN_type) return 1; + + for (;;) { + chunk c = get_chunk_header(s); + switch (c.type) { + case PNG_TYPE('C','g','B','I'): + iphone = stbi_de_iphone_flag; + skip(s, c.length); + break; + case PNG_TYPE('I','H','D','R'): { + int depth,color,comp,filter; + if (!first) return e("multiple IHDR","Corrupt PNG"); + first = 0; + if (c.length != 13) return e("bad IHDR len","Corrupt PNG"); + s->img_x = get32(s); if (s->img_x > (1 << 24)) return e("too large","Very large image (corrupt?)"); + s->img_y = get32(s); if (s->img_y > (1 << 24)) return e("too large","Very large image (corrupt?)"); + depth = get8(s); if (depth != 8) return e("8bit only","PNG not supported: 8-bit only"); + color = get8(s); if (color > 6) return e("bad ctype","Corrupt PNG"); + if (color == 3) pal_img_n = 3; else if (color & 1) return e("bad ctype","Corrupt PNG"); + comp = get8(s); if (comp) return e("bad comp method","Corrupt PNG"); + filter= get8(s); if (filter) return e("bad filter method","Corrupt PNG"); + interlace = get8(s); if (interlace>1) return e("bad interlace method","Corrupt PNG"); + if (!s->img_x || !s->img_y) return e("0-pixel image","Corrupt PNG"); + if (!pal_img_n) { + s->img_n = (color & 2 ? 3 : 1) + (color & 4 ? 1 : 0); + if ((1 << 30) / s->img_x / s->img_n < s->img_y) return e("too large", "Image too large to decode"); + if (scan == SCAN_header) return 1; + } else { + // if paletted, then pal_n is our final components, and + // img_n is # components to decompress/filter. + s->img_n = 1; + if ((1 << 30) / s->img_x / 4 < s->img_y) return e("too large","Corrupt PNG"); + // if SCAN_header, have to scan to see if we have a tRNS + } + break; + } + + case PNG_TYPE('P','L','T','E'): { + if (first) return e("first not IHDR", "Corrupt PNG"); + if (c.length > 256*3) return e("invalid PLTE","Corrupt PNG"); + pal_len = c.length / 3; + if (pal_len * 3 != c.length) return e("invalid PLTE","Corrupt PNG"); + for (i=0; i < pal_len; ++i) { + palette[i*4+0] = get8u(s); + palette[i*4+1] = get8u(s); + palette[i*4+2] = get8u(s); + palette[i*4+3] = 255; + } + break; + } + + case PNG_TYPE('t','R','N','S'): { + if (first) return e("first not IHDR", "Corrupt PNG"); + if (z->idata) return e("tRNS after IDAT","Corrupt PNG"); + if (pal_img_n) { + if (scan == SCAN_header) { s->img_n = 4; return 1; } + if (pal_len == 0) return e("tRNS before PLTE","Corrupt PNG"); + if (c.length > pal_len) return e("bad tRNS len","Corrupt PNG"); + pal_img_n = 4; + for (i=0; i < c.length; ++i) + palette[i*4+3] = get8u(s); + } else { + if (!(s->img_n & 1)) return e("tRNS with alpha","Corrupt PNG"); + if (c.length != (uint32) s->img_n*2) return e("bad tRNS len","Corrupt PNG"); + has_trans = 1; + for (k=0; k < s->img_n; ++k) + tc[k] = (uint8) get16(s); // non 8-bit images will be larger + } + break; + } + + case PNG_TYPE('I','D','A','T'): { + if (first) return e("first not IHDR", "Corrupt PNG"); + if (pal_img_n && !pal_len) return e("no PLTE","Corrupt PNG"); + if (scan == SCAN_header) { s->img_n = pal_img_n; return 1; } + if (ioff + c.length > idata_limit) { + uint8 *p; + if (idata_limit == 0) idata_limit = c.length > 4096 ? c.length : 4096; + while (ioff + c.length > idata_limit) + idata_limit *= 2; + p = (uint8 *) realloc(z->idata, idata_limit); if (p == NULL) return e("outofmem", "Out of memory"); + z->idata = p; + } + if (!getn(s, z->idata+ioff,c.length)) return e("outofdata","Corrupt PNG"); + ioff += c.length; + break; + } + + case PNG_TYPE('I','E','N','D'): { + uint32 raw_len; + if (first) return e("first not IHDR", "Corrupt PNG"); + if (scan != SCAN_load) return 1; + if (z->idata == NULL) return e("no IDAT","Corrupt PNG"); + z->expanded = (uint8 *) stbi_zlib_decode_malloc_guesssize_headerflag((char *) z->idata, ioff, 16384, (int *) &raw_len, !iphone); + if (z->expanded == NULL) return 0; // zlib should set error + free(z->idata); z->idata = NULL; + if ((req_comp == s->img_n+1 && req_comp != 3 && !pal_img_n) || has_trans) + s->img_out_n = s->img_n+1; + else + s->img_out_n = s->img_n; + if (!create_png_image(z, z->expanded, raw_len, s->img_out_n, interlace)) return 0; + if (has_trans) + if (!compute_transparency(z, tc, s->img_out_n)) return 0; + if (iphone && s->img_out_n > 2) + stbi_de_iphone(z); + if (pal_img_n) { + // pal_img_n == 3 or 4 + s->img_n = pal_img_n; // record the actual colors we had + s->img_out_n = pal_img_n; + if (req_comp >= 3) s->img_out_n = req_comp; + if (!expand_palette(z, palette, pal_len, s->img_out_n)) + return 0; + } + free(z->expanded); z->expanded = NULL; + return 1; + } + + default: + // if critical, fail + if (first) return e("first not IHDR", "Corrupt PNG"); + if ((c.type & (1 << 29)) == 0) { + #ifndef STBI_NO_FAILURE_STRINGS + // not threadsafe + static char invalid_chunk[] = "XXXX chunk not known"; + invalid_chunk[0] = (uint8) (c.type >> 24); + invalid_chunk[1] = (uint8) (c.type >> 16); + invalid_chunk[2] = (uint8) (c.type >> 8); + invalid_chunk[3] = (uint8) (c.type >> 0); + #endif + return e(invalid_chunk, "PNG not supported: unknown chunk type"); + } + skip(s, c.length); + break; + } + // end of chunk, read and skip CRC + get32(s); + } +} + +static unsigned char *do_png(png *p, int *x, int *y, int *n, int req_comp) +{ + unsigned char *result=NULL; + p->expanded = NULL; + p->idata = NULL; + p->out = NULL; + if (req_comp < 0 || req_comp > 4) return epuc("bad req_comp", "Internal error"); + if (parse_png_file(p, SCAN_load, req_comp)) { + result = p->out; + p->out = NULL; + if (req_comp && req_comp != p->s.img_out_n) { + result = convert_format(result, p->s.img_out_n, req_comp, p->s.img_x, p->s.img_y); + p->s.img_out_n = req_comp; + if (result == NULL) return result; + } + *x = p->s.img_x; + *y = p->s.img_y; + if (n) *n = p->s.img_n; + } + free(p->out); p->out = NULL; + free(p->expanded); p->expanded = NULL; + free(p->idata); p->idata = NULL; + + return result; +} + +#ifndef STBI_NO_STDIO +unsigned char *stbi_png_load_from_file(FILE *f, int *x, int *y, int *comp, int req_comp) +{ + png p; + start_file(&p.s, f); + return do_png(&p, x,y,comp,req_comp); +} + +unsigned char *stbi_png_load(char const *filename, int *x, int *y, int *comp, int req_comp) +{ + unsigned char *data; + FILE *f = fopen(filename, "rb"); + if (!f) return NULL; + data = stbi_png_load_from_file(f,x,y,comp,req_comp); + fclose(f); + return data; +} +#endif + +unsigned char *stbi_png_load_from_memory(stbi_uc const *buffer, int len, int *x, int *y, int *comp, int req_comp) +{ + png p; + start_mem(&p.s, buffer,len); + return do_png(&p, x,y,comp,req_comp); +} + +#ifndef STBI_NO_STDIO +int stbi_png_test_file(FILE *f) +{ + png p; + int n,r; + n = ftell(f); + start_file(&p.s, f); + r = parse_png_file(&p, SCAN_type,STBI_default); + fseek(f,n,SEEK_SET); + return r; +} +#endif + +int stbi_png_test_memory(stbi_uc const *buffer, int len) +{ + png p; + start_mem(&p.s, buffer, len); + return parse_png_file(&p, SCAN_type,STBI_default); +} + +static int stbi_png_info_raw(png *p, int *x, int *y, int *comp) +{ + if (!parse_png_file(p, SCAN_header, 0)) + return 0; + if (x) *x = p->s.img_x; + if (y) *y = p->s.img_y; + if (comp) *comp = p->s.img_n; + return 1; +} + +#ifndef STBI_NO_STDIO +int stbi_png_info (char const *filename, int *x, int *y, int *comp) +{ + int res; + FILE *f = fopen(filename, "rb"); + if (!f) return 0; + res = stbi_png_info_from_file(f, x, y, comp); + fclose(f); + return res; +} + +int stbi_png_info_from_file(FILE *f, int *x, int *y, int *comp) +{ + png p; + int res; + long n = ftell(f); + start_file(&p.s, f); + res = stbi_png_info_raw(&p, x, y, comp); + fseek(f, n, SEEK_SET); + return res; +} +#endif // !STBI_NO_STDIO + +int stbi_png_info_from_memory(stbi_uc const *buffer, int len, int *x, int *y, int *comp) +{ + png p; + start_mem(&p.s, buffer, len); + return stbi_png_info_raw(&p, x, y, comp); +} + +// Microsoft/Windows BMP image + +static int bmp_test(stbi *s) +{ + int sz; + if (get8(s) != 'B') return 0; + if (get8(s) != 'M') return 0; + get32le(s); // discard filesize + get16le(s); // discard reserved + get16le(s); // discard reserved + get32le(s); // discard data offset + sz = get32le(s); + if (sz == 12 || sz == 40 || sz == 56 || sz == 108) return 1; + return 0; +} + +#ifndef STBI_NO_STDIO +int stbi_bmp_test_file (FILE *f) +{ + stbi s; + int r,n = ftell(f); + start_file(&s,f); + r = bmp_test(&s); + fseek(f,n,SEEK_SET); + return r; +} +#endif + +int stbi_bmp_test_memory (stbi_uc const *buffer, int len) +{ + stbi s; + start_mem(&s, buffer, len); + return bmp_test(&s); +} + +// returns 0..31 for the highest set bit +static int high_bit(unsigned int z) +{ + int n=0; + if (z == 0) return -1; + if (z >= 0x10000) n += 16, z >>= 16; + if (z >= 0x00100) n += 8, z >>= 8; + if (z >= 0x00010) n += 4, z >>= 4; + if (z >= 0x00004) n += 2, z >>= 2; + if (z >= 0x00002) n += 1, z >>= 1; + return n; +} + +static int bitcount(unsigned int a) +{ + a = (a & 0x55555555) + ((a >> 1) & 0x55555555); // max 2 + a = (a & 0x33333333) + ((a >> 2) & 0x33333333); // max 4 + a = (a + (a >> 4)) & 0x0f0f0f0f; // max 8 per 4, now 8 bits + a = (a + (a >> 8)); // max 16 per 8 bits + a = (a + (a >> 16)); // max 32 per 8 bits + return a & 0xff; +} + +static int shiftsigned(int v, int shift, int bits) +{ + int result; + int z=0; + + if (shift < 0) v <<= -shift; + else v >>= shift; + result = v; + + z = bits; + while (z < 8) { + result += v >> z; + z += bits; + } + return result; +} + +static stbi_uc *bmp_load(stbi *s, int *x, int *y, int *comp, int req_comp) +{ + uint8 *out; + unsigned int mr=0,mg=0,mb=0,ma=0, fake_a=0; + stbi_uc pal[256][4]; + int psize=0,i,j,compress=0,width; + int bpp, flip_vertically, pad, target, offset, hsz; + if (get8(s) != 'B' || get8(s) != 'M') return epuc("not BMP", "Corrupt BMP"); + get32le(s); // discard filesize + get16le(s); // discard reserved + get16le(s); // discard reserved + offset = get32le(s); + hsz = get32le(s); + if (hsz != 12 && hsz != 40 && hsz != 56 && hsz != 108) return epuc("unknown BMP", "BMP type not supported: unknown"); + if (hsz == 12) { + s->img_x = get16le(s); + s->img_y = get16le(s); + } else { + s->img_x = get32le(s); + s->img_y = get32le(s); + } + if (get16le(s) != 1) return epuc("bad BMP", "bad BMP"); + bpp = get16le(s); + if (bpp == 1) return epuc("monochrome", "BMP type not supported: 1-bit"); + flip_vertically = ((int) s->img_y) > 0; + s->img_y = abs((int) s->img_y); + if (hsz == 12) { + if (bpp < 24) + psize = (offset - 14 - 24) / 3; + } else { + compress = get32le(s); + if (compress == 1 || compress == 2) return epuc("BMP RLE", "BMP type not supported: RLE"); + get32le(s); // discard sizeof + get32le(s); // discard hres + get32le(s); // discard vres + get32le(s); // discard colorsused + get32le(s); // discard max important + if (hsz == 40 || hsz == 56) { + if (hsz == 56) { + get32le(s); + get32le(s); + get32le(s); + get32le(s); + } + if (bpp == 16 || bpp == 32) { + mr = mg = mb = 0; + if (compress == 0) { + if (bpp == 32) { + mr = 0xffu << 16; + mg = 0xffu << 8; + mb = 0xffu << 0; + ma = 0xffu << 24; + fake_a = 1; // @TODO: check for cases like alpha value is all 0 and switch it to 255 + } else { + mr = 31u << 10; + mg = 31u << 5; + mb = 31u << 0; + } + } else if (compress == 3) { + mr = get32le(s); + mg = get32le(s); + mb = get32le(s); + // not documented, but generated by photoshop and handled by mspaint + if (mr == mg && mg == mb) { + // ?!?!? + return epuc("bad BMP", "bad BMP"); + } + } else + return epuc("bad BMP", "bad BMP"); + } + } else { + assert(hsz == 108); + mr = get32le(s); + mg = get32le(s); + mb = get32le(s); + ma = get32le(s); + get32le(s); // discard color space + for (i=0; i < 12; ++i) + get32le(s); // discard color space parameters + } + if (bpp < 16) + psize = (offset - 14 - hsz) >> 2; + } + s->img_n = ma ? 4 : 3; + if (req_comp && req_comp >= 3) // we can directly decode 3 or 4 + target = req_comp; + else + target = s->img_n; // if they want monochrome, we'll post-convert + out = (stbi_uc *) malloc(target * s->img_x * s->img_y); + if (!out) return epuc("outofmem", "Out of memory"); + if (bpp < 16) { + int z=0; + if (psize == 0 || psize > 256) { free(out); return epuc("invalid", "Corrupt BMP"); } + for (i=0; i < psize; ++i) { + pal[i][2] = get8u(s); + pal[i][1] = get8u(s); + pal[i][0] = get8u(s); + if (hsz != 12) get8(s); + pal[i][3] = 255; + } + skip(s, offset - 14 - hsz - psize * (hsz == 12 ? 3 : 4)); + if (bpp == 4) width = (s->img_x + 1) >> 1; + else if (bpp == 8) width = s->img_x; + else { free(out); return epuc("bad bpp", "Corrupt BMP"); } + pad = (-width)&3; + for (j=0; j < (int) s->img_y; ++j) { + for (i=0; i < (int) s->img_x; i += 2) { + int v=get8(s),v2=0; + if (bpp == 4) { + v2 = v & 15; + v >>= 4; + } + out[z++] = pal[v][0]; + out[z++] = pal[v][1]; + out[z++] = pal[v][2]; + if (target == 4) out[z++] = 255; + if (i+1 == (int) s->img_x) break; + v = (bpp == 8) ? get8(s) : v2; + out[z++] = pal[v][0]; + out[z++] = pal[v][1]; + out[z++] = pal[v][2]; + if (target == 4) out[z++] = 255; + } + skip(s, pad); + } + } else { + int rshift=0,gshift=0,bshift=0,ashift=0,rcount=0,gcount=0,bcount=0,acount=0; + int z = 0; + int easy=0; + skip(s, offset - 14 - hsz); + if (bpp == 24) width = 3 * s->img_x; + else if (bpp == 16) width = 2*s->img_x; + else /* bpp = 32 and pad = 0 */ width=0; + pad = (-width) & 3; + if (bpp == 24) { + easy = 1; + } else if (bpp == 32) { + if (mb == 0xff && mg == 0xff00 && mr == 0xff000000 && ma == 0xff000000) + easy = 2; + } + if (!easy) { + if (!mr || !mg || !mb) return epuc("bad masks", "Corrupt BMP"); + // right shift amt to put high bit in position #7 + rshift = high_bit(mr)-7; rcount = bitcount(mr); + gshift = high_bit(mg)-7; gcount = bitcount(mr); + bshift = high_bit(mb)-7; bcount = bitcount(mr); + ashift = high_bit(ma)-7; acount = bitcount(mr); + } + for (j=0; j < (int) s->img_y; ++j) { + if (easy) { + for (i=0; i < (int) s->img_x; ++i) { + int a; + out[z+2] = get8u(s); + out[z+1] = get8u(s); + out[z+0] = get8u(s); + z += 3; + a = (easy == 2 ? get8(s) : 255); + if (target == 4) out[z++] = (uint8) a; + } + } else { + for (i=0; i < (int) s->img_x; ++i) { + uint32 v = (bpp == 16 ? get16le(s) : get32le(s)); + int a; + out[z++] = (uint8) shiftsigned(v & mr, rshift, rcount); + out[z++] = (uint8) shiftsigned(v & mg, gshift, gcount); + out[z++] = (uint8) shiftsigned(v & mb, bshift, bcount); + a = (ma ? shiftsigned(v & ma, ashift, acount) : 255); + if (target == 4) out[z++] = (uint8) a; + } + } + skip(s, pad); + } + } + if (flip_vertically) { + stbi_uc t; + for (j=0; j < (int) s->img_y>>1; ++j) { + stbi_uc *p1 = out + j *s->img_x*target; + stbi_uc *p2 = out + (s->img_y-1-j)*s->img_x*target; + for (i=0; i < (int) s->img_x*target; ++i) { + t = p1[i], p1[i] = p2[i], p2[i] = t; + } + } + } + + if (req_comp && req_comp != target) { + out = convert_format(out, target, req_comp, s->img_x, s->img_y); + if (out == NULL) return out; // convert_format frees input on failure + } + + *x = s->img_x; + *y = s->img_y; + if (comp) *comp = target; + return out; +} + +#ifndef STBI_NO_STDIO +stbi_uc *stbi_bmp_load (char const *filename, int *x, int *y, int *comp, int req_comp) +{ + stbi_uc *data; + FILE *f = fopen(filename, "rb"); + if (!f) return NULL; + data = stbi_bmp_load_from_file(f, x,y,comp,req_comp); + fclose(f); + return data; +} + +stbi_uc *stbi_bmp_load_from_file (FILE *f, int *x, int *y, int *comp, int req_comp) +{ + stbi s; + start_file(&s, f); + return bmp_load(&s, x,y,comp,req_comp); +} +#endif + +stbi_uc *stbi_bmp_load_from_memory (stbi_uc const *buffer, int len, int *x, int *y, int *comp, int req_comp) +{ + stbi s; + start_mem(&s, buffer, len); + return bmp_load(&s, x,y,comp,req_comp); +} + +// Targa Truevision - TGA +// by Jonathan Dummer + +static int tga_info(stbi *s, int *x, int *y, int *comp) +{ + int tga_w, tga_h, tga_comp; + int sz; + get8u(s); // discard Offset + sz = get8u(s); // color type + if( sz > 1 ) return 0; // only RGB or indexed allowed + sz = get8u(s); // image type + // only RGB or grey allowed, +/- RLE + if ((sz != 1) && (sz != 2) && (sz != 3) && (sz != 9) && (sz != 10) && (sz != 11)) return 0; + get16le(s); // discard palette start + get16le(s); // discard palette length + get8(s); // discard bits per palette color entry + get16le(s); // discard x origin + get16le(s); // discard y origin + tga_w = get16le(s); + if( tga_w < 1 ) return 0; // test width + tga_h = get16le(s); + if( tga_h < 1 ) return 0; // test height + sz = get8(s); // bits per pixel + // only RGB or RGBA or grey allowed + if ((sz != 8) && (sz != 16) && (sz != 24) && (sz != 32)) return 0; + tga_comp = sz; + if (x) *x = tga_w; + if (y) *y = tga_h; + if (comp) *comp = tga_comp / 8; + return 1; // seems to have passed everything +} + +int stbi_tga_info_from_memory(stbi_uc const *buffer, int len, int *x, int *y, int *comp) +{ + stbi s; + start_mem(&s, buffer, len); + return tga_info(&s, x, y, comp); +} + +static int tga_test(stbi *s) +{ + int sz; + get8u(s); // discard Offset + sz = get8u(s); // color type + if ( sz > 1 ) return 0; // only RGB or indexed allowed + sz = get8u(s); // image type + if ( (sz != 1) && (sz != 2) && (sz != 3) && (sz != 9) && (sz != 10) && (sz != 11) ) return 0; // only RGB or grey allowed, +/- RLE + get16(s); // discard palette start + get16(s); // discard palette length + get8(s); // discard bits per palette color entry + get16(s); // discard x origin + get16(s); // discard y origin + if ( get16(s) < 1 ) return 0; // test width + if ( get16(s) < 1 ) return 0; // test height + sz = get8(s); // bits per pixel + if ( (sz != 8) && (sz != 16) && (sz != 24) && (sz != 32) ) return 0; // only RGB or RGBA or grey allowed + return 1; // seems to have passed everything +} + +#ifndef STBI_NO_STDIO +int stbi_tga_info_from_file(FILE *f, int *x, int *y, int *comp) +{ + stbi s; + int r; + long n = ftell(f); + start_file(&s, f); + r = tga_info(&s, x, y, comp); + fseek(f, n, SEEK_SET); + return r; +} + +int stbi_tga_test_file (FILE *f) +{ + stbi s; + int r,n = ftell(f); + start_file(&s, f); + r = tga_test(&s); + fseek(f,n,SEEK_SET); + return r; +} +#endif + +int stbi_tga_test_memory (stbi_uc const *buffer, int len) +{ + stbi s; + start_mem(&s, buffer, len); + return tga_test(&s); +} + +static stbi_uc *tga_load(stbi *s, int *x, int *y, int *comp, int req_comp) +{ + // read in the TGA header stuff + int tga_offset = get8u(s); + int tga_indexed = get8u(s); + int tga_image_type = get8u(s); + int tga_is_RLE = 0; + int tga_palette_start = get16le(s); + int tga_palette_len = get16le(s); + int tga_palette_bits = get8u(s); + int tga_x_origin = get16le(s); + int tga_y_origin = get16le(s); + int tga_width = get16le(s); + int tga_height = get16le(s); + int tga_bits_per_pixel = get8u(s); + int tga_inverted = get8u(s); + // image data + unsigned char *tga_data; + unsigned char *tga_palette = NULL; + int i, j; + unsigned char raw_data[4]; + unsigned char trans_data[4]; + int RLE_count = 0; + int RLE_repeating = 0; + int read_next_pixel = 1; + + // do a tiny bit of precessing + if ( tga_image_type >= 8 ) + { + tga_image_type -= 8; + tga_is_RLE = 1; + } + /* int tga_alpha_bits = tga_inverted & 15; */ + tga_inverted = 1 - ((tga_inverted >> 5) & 1); + + // error check + if ( //(tga_indexed) || + (tga_width < 1) || (tga_height < 1) || + (tga_image_type < 1) || (tga_image_type > 3) || + ((tga_bits_per_pixel != 8) && (tga_bits_per_pixel != 16) && + (tga_bits_per_pixel != 24) && (tga_bits_per_pixel != 32)) + ) + { + return NULL; + } + + // If I'm paletted, then I'll use the number of bits from the palette + if ( tga_indexed ) + { + tga_bits_per_pixel = tga_palette_bits; + } + + // tga info + *x = tga_width; + *y = tga_height; + if ( (req_comp < 1) || (req_comp > 4) ) + { + // just use whatever the file was + req_comp = tga_bits_per_pixel / 8; + *comp = req_comp; + } else + { + // force a new number of components + *comp = tga_bits_per_pixel/8; + } + tga_data = (unsigned char*)malloc( tga_width * tga_height * req_comp ); + + // skip to the data's starting position (offset usually = 0) + skip(s, tga_offset ); + // do I need to load a palette? + if ( tga_indexed ) + { + // any data to skip? (offset usually = 0) + skip(s, tga_palette_start ); + // load the palette + tga_palette = (unsigned char*)malloc( tga_palette_len * tga_palette_bits / 8 ); + if (!getn(s, tga_palette, tga_palette_len * tga_palette_bits / 8 )) + return NULL; + } + // load the data + trans_data[0] = trans_data[1] = trans_data[2] = trans_data[3] = 0; + for (i=0; i < tga_width * tga_height; ++i) + { + // if I'm in RLE mode, do I need to get a RLE chunk? + if ( tga_is_RLE ) + { + if ( RLE_count == 0 ) + { + // yep, get the next byte as a RLE command + int RLE_cmd = get8u(s); + RLE_count = 1 + (RLE_cmd & 127); + RLE_repeating = RLE_cmd >> 7; + read_next_pixel = 1; + } else if ( !RLE_repeating ) + { + read_next_pixel = 1; + } + } else + { + read_next_pixel = 1; + } + // OK, if I need to read a pixel, do it now + if ( read_next_pixel ) + { + // load however much data we did have + if ( tga_indexed ) + { + // read in 1 byte, then perform the lookup + int pal_idx = get8u(s); + if ( pal_idx >= tga_palette_len ) + { + // invalid index + pal_idx = 0; + } + pal_idx *= tga_bits_per_pixel / 8; + for (j = 0; j*8 < tga_bits_per_pixel; ++j) + { + raw_data[j] = tga_palette[pal_idx+j]; + } + } else + { + // read in the data raw + for (j = 0; j*8 < tga_bits_per_pixel; ++j) + { + raw_data[j] = get8u(s); + } + } + // convert raw to the intermediate format + switch (tga_bits_per_pixel) + { + case 8: + // Luminous => RGBA + trans_data[0] = raw_data[0]; + trans_data[1] = raw_data[0]; + trans_data[2] = raw_data[0]; + trans_data[3] = 255; + break; + case 16: + // Luminous,Alpha => RGBA + trans_data[0] = raw_data[0]; + trans_data[1] = raw_data[0]; + trans_data[2] = raw_data[0]; + trans_data[3] = raw_data[1]; + break; + case 24: + // BGR => RGBA + trans_data[0] = raw_data[2]; + trans_data[1] = raw_data[1]; + trans_data[2] = raw_data[0]; + trans_data[3] = 255; + break; + case 32: + // BGRA => RGBA + trans_data[0] = raw_data[2]; + trans_data[1] = raw_data[1]; + trans_data[2] = raw_data[0]; + trans_data[3] = raw_data[3]; + break; + } + // clear the reading flag for the next pixel + read_next_pixel = 0; + } // end of reading a pixel + // convert to final format + switch (req_comp) + { + case 1: + // RGBA => Luminance + tga_data[i*req_comp+0] = compute_y(trans_data[0],trans_data[1],trans_data[2]); + break; + case 2: + // RGBA => Luminance,Alpha + tga_data[i*req_comp+0] = compute_y(trans_data[0],trans_data[1],trans_data[2]); + tga_data[i*req_comp+1] = trans_data[3]; + break; + case 3: + // RGBA => RGB + tga_data[i*req_comp+0] = trans_data[0]; + tga_data[i*req_comp+1] = trans_data[1]; + tga_data[i*req_comp+2] = trans_data[2]; + break; + case 4: + // RGBA => RGBA + tga_data[i*req_comp+0] = trans_data[0]; + tga_data[i*req_comp+1] = trans_data[1]; + tga_data[i*req_comp+2] = trans_data[2]; + tga_data[i*req_comp+3] = trans_data[3]; + break; + } + // in case we're in RLE mode, keep counting down + --RLE_count; + } + // do I need to invert the image? + if ( tga_inverted ) + { + for (j = 0; j*2 < tga_height; ++j) + { + int index1 = j * tga_width * req_comp; + int index2 = (tga_height - 1 - j) * tga_width * req_comp; + for (i = tga_width * req_comp; i > 0; --i) + { + unsigned char temp = tga_data[index1]; + tga_data[index1] = tga_data[index2]; + tga_data[index2] = temp; + ++index1; + ++index2; + } + } + } + // clear my palette, if I had one + if ( tga_palette != NULL ) + { + free( tga_palette ); + } + // the things I do to get rid of an error message, and yet keep + // Microsoft's C compilers happy... [8^( + tga_palette_start = tga_palette_len = tga_palette_bits = + tga_x_origin = tga_y_origin = 0; + // OK, done + return tga_data; +} + +#ifndef STBI_NO_STDIO +stbi_uc *stbi_tga_load (char const *filename, int *x, int *y, int *comp, int req_comp) +{ + stbi_uc *data; + FILE *f = fopen(filename, "rb"); + if (!f) return NULL; + data = stbi_tga_load_from_file(f, x,y,comp,req_comp); + fclose(f); + return data; +} + +stbi_uc *stbi_tga_load_from_file (FILE *f, int *x, int *y, int *comp, int req_comp) +{ + stbi s; + start_file(&s, f); + return tga_load(&s, x,y,comp,req_comp); +} +#endif + +stbi_uc *stbi_tga_load_from_memory (stbi_uc const *buffer, int len, int *x, int *y, int *comp, int req_comp) +{ + stbi s; + start_mem(&s, buffer, len); + return tga_load(&s, x,y,comp,req_comp); +} + + +// ************************************************************************************************* +// Photoshop PSD loader -- PD by Thatcher Ulrich, integration by Nicolas Schulz, tweaked by STB + +static int psd_test(stbi *s) +{ + if (get32(s) != 0x38425053) return 0; // "8BPS" + else return 1; +} + +#ifndef STBI_NO_STDIO +int stbi_psd_test_file(FILE *f) +{ + stbi s; + int r,n = ftell(f); + start_file(&s, f); + r = psd_test(&s); + fseek(f,n,SEEK_SET); + return r; +} +#endif + +int stbi_psd_test_memory(stbi_uc const *buffer, int len) +{ + stbi s; + start_mem(&s, buffer, len); + return psd_test(&s); +} + +static stbi_uc *psd_load(stbi *s, int *x, int *y, int *comp, int req_comp) +{ + int pixelCount; + int channelCount, compression; + int channel, i, count, len; + int w,h; + uint8 *out; + + // Check identifier + if (get32(s) != 0x38425053) // "8BPS" + return epuc("not PSD", "Corrupt PSD image"); + + // Check file type version. + if (get16(s) != 1) + return epuc("wrong version", "Unsupported version of PSD image"); + + // Skip 6 reserved bytes. + skip(s, 6 ); + + // Read the number of channels (R, G, B, A, etc). + channelCount = get16(s); + if (channelCount < 0 || channelCount > 16) + return epuc("wrong channel count", "Unsupported number of channels in PSD image"); + + // Read the rows and columns of the image. + h = get32(s); + w = get32(s); + + // Make sure the depth is 8 bits. + if (get16(s) != 8) + return epuc("unsupported bit depth", "PSD bit depth is not 8 bit"); + + // Make sure the color mode is RGB. + // Valid options are: + // 0: Bitmap + // 1: Grayscale + // 2: Indexed color + // 3: RGB color + // 4: CMYK color + // 7: Multichannel + // 8: Duotone + // 9: Lab color + if (get16(s) != 3) + return epuc("wrong color format", "PSD is not in RGB color format"); + + // Skip the Mode Data. (It's the palette for indexed color; other info for other modes.) + skip(s,get32(s) ); + + // Skip the image resources. (resolution, pen tool paths, etc) + skip(s, get32(s) ); + + // Skip the reserved data. + skip(s, get32(s) ); + + // Find out if the data is compressed. + // Known values: + // 0: no compression + // 1: RLE compressed + compression = get16(s); + if (compression > 1) + return epuc("bad compression", "PSD has an unknown compression format"); + + // Create the destination image. + out = (stbi_uc *) malloc(4 * w*h); + if (!out) return epuc("outofmem", "Out of memory"); + pixelCount = w*h; + + // Initialize the data to zero. + //memset( out, 0, pixelCount * 4 ); + + // Finally, the image data. + if (compression) { + // RLE as used by .PSD and .TIFF + // Loop until you get the number of unpacked bytes you are expecting: + // Read the next source byte into n. + // If n is between 0 and 127 inclusive, copy the next n+1 bytes literally. + // Else if n is between -127 and -1 inclusive, copy the next byte -n+1 times. + // Else if n is 128, noop. + // Endloop + + // The RLE-compressed data is preceeded by a 2-byte data count for each row in the data, + // which we're going to just skip. + skip(s, h * channelCount * 2 ); + + // Read the RLE data by channel. + for (channel = 0; channel < 4; channel++) { + uint8 *p; + + p = out+channel; + if (channel >= channelCount) { + // Fill this channel with default data. + for (i = 0; i < pixelCount; i++) *p = (channel == 3 ? 255 : 0), p += 4; + } else { + // Read the RLE data. + count = 0; + while (count < pixelCount) { + len = get8(s); + if (len == 128) { + // No-op. + } else if (len < 128) { + // Copy next len+1 bytes literally. + len++; + count += len; + while (len) { + *p = get8u(s); + p += 4; + len--; + } + } else if (len > 128) { + uint8 val; + // Next -len+1 bytes in the dest are replicated from next source byte. + // (Interpret len as a negative 8-bit int.) + len ^= 0x0FF; + len += 2; + val = get8u(s); + count += len; + while (len) { + *p = val; + p += 4; + len--; + } + } + } + } + } + + } else { + // We're at the raw image data. It's each channel in order (Red, Green, Blue, Alpha, ...) + // where each channel consists of an 8-bit value for each pixel in the image. + + // Read the data by channel. + for (channel = 0; channel < 4; channel++) { + uint8 *p; + + p = out + channel; + if (channel > channelCount) { + // Fill this channel with default data. + for (i = 0; i < pixelCount; i++) *p = channel == 3 ? 255 : 0, p += 4; + } else { + // Read the data. + for (i = 0; i < pixelCount; i++) + *p = get8u(s), p += 4; + } + } + } + + if (req_comp && req_comp != 4) { + out = convert_format(out, 4, req_comp, w, h); + if (out == NULL) return out; // convert_format frees input on failure + } + + if (comp) *comp = channelCount; + *y = h; + *x = w; + + return out; +} + +#ifndef STBI_NO_STDIO +stbi_uc *stbi_psd_load(char const *filename, int *x, int *y, int *comp, int req_comp) +{ + stbi_uc *data; + FILE *f = fopen(filename, "rb"); + if (!f) return NULL; + data = stbi_psd_load_from_file(f, x,y,comp,req_comp); + fclose(f); + return data; +} + +stbi_uc *stbi_psd_load_from_file(FILE *f, int *x, int *y, int *comp, int req_comp) +{ + stbi s; + start_file(&s, f); + return psd_load(&s, x,y,comp,req_comp); +} +#endif + +stbi_uc *stbi_psd_load_from_memory (stbi_uc const *buffer, int len, int *x, int *y, int *comp, int req_comp) +{ + stbi s; + start_mem(&s, buffer, len); + return psd_load(&s, x,y,comp,req_comp); +} + +// ************************************************************************************************* +// Softimage PIC loader +// by Tom Seddon +// +// See http://softimage.wiki.softimage.com/index.php/INFO:_PIC_file_format +// See http://ozviz.wasp.uwa.edu.au/~pbourke/dataformats/softimagepic/ + +static int pic_is4(stbi *s,const char *str) +{ + int i; + for (i=0; i<4; ++i) + if (get8(s) != (stbi_uc)str[i]) + return 0; + + return 1; +} + +static int pic_test(stbi *s) +{ + int i; + + if (!pic_is4(s,"\x53\x80\xF6\x34")) + return 0; + + for(i=0;i<84;++i) + get8(s); + + if (!pic_is4(s,"PICT")) + return 0; + + return 1; +} + +typedef struct +{ + stbi_uc size,type,channel; +} pic_packet_t; + +static stbi_uc *pic_readval(stbi *s, int channel, stbi_uc *dest) +{ + int mask=0x80, i; + + for (i=0; i<4; ++i, mask>>=1) { + if (channel & mask) { + if (at_eof(s)) return epuc("bad file","PIC file too short"); + dest[i]=get8u(s); + } + } + + return dest; +} + +static void pic_copyval(int channel,stbi_uc *dest,const stbi_uc *src) +{ + int mask=0x80,i; + + for (i=0;i<4; ++i, mask>>=1) + if (channel&mask) + dest[i]=src[i]; +} + +static stbi_uc *pic_load2(stbi *s,int width,int height,int *comp, stbi_uc *result) +{ + int act_comp=0,num_packets=0,y,chained; + pic_packet_t packets[10]; + + // this will (should...) cater for even some bizarre stuff like having data + // for the same channel in multiple packets. + do { + pic_packet_t *packet; + + if (num_packets==sizeof(packets)/sizeof(packets[0])) + return epuc("bad format","too many packets"); + + packet = &packets[num_packets++]; + + chained = get8(s); + packet->size = get8u(s); + packet->type = get8u(s); + packet->channel = get8u(s); + + act_comp |= packet->channel; + + if (at_eof(s)) return epuc("bad file","file too short (reading packets)"); + if (packet->size != 8) return epuc("bad format","packet isn't 8bpp"); + } while (chained); + + *comp = (act_comp & 0x10 ? 4 : 3); // has alpha channel? + + for(y=0; ytype) { + default: + return epuc("bad format","packet has bad compression type"); + + case 0: {//uncompressed + int x; + + for(x=0;xchannel,dest)) + return 0; + break; + } + + case 1://Pure RLE + { + int left=width, i; + + while (left>0) { + stbi_uc count,value[4]; + + count=get8u(s); + if (at_eof(s)) return epuc("bad file","file too short (pure read count)"); + + if (count > left) + count = (uint8) left; + + if (!pic_readval(s,packet->channel,value)) return 0; + + for(i=0; ichannel,dest,value); + left -= count; + } + } + break; + + case 2: {//Mixed RLE + int left=width; + while (left>0) { + int count = get8(s), i; + if (at_eof(s)) return epuc("bad file","file too short (mixed read count)"); + + if (count >= 128) { // Repeated + stbi_uc value[4]; + int i; + + if (count==128) + count = get16(s); + else + count -= 127; + if (count > left) + return epuc("bad file","scanline overrun"); + + if (!pic_readval(s,packet->channel,value)) + return 0; + + for(i=0;ichannel,dest,value); + } else { // Raw + ++count; + if (count>left) return epuc("bad file","scanline overrun"); + + for(i=0;ichannel,dest)) + return 0; + } + left-=count; + } + break; + } + } + } + } + + return result; +} + +static stbi_uc *pic_load(stbi *s,int *px,int *py,int *comp,int req_comp) +{ + stbi_uc *result; + int i, x,y; + + for (i=0; i<92; ++i) + get8(s); + + x = get16(s); + y = get16(s); + if (at_eof(s)) return epuc("bad file","file too short (pic header)"); + if ((1 << 28) / x < y) return epuc("too large", "Image too large to decode"); + + get32(s); //skip `ratio' + get16(s); //skip `fields' + get16(s); //skip `pad' + + // intermediate buffer is RGBA + result = (stbi_uc *) malloc(x*y*4); + memset(result, 0xff, x*y*4); + + if (!pic_load2(s,x,y,comp, result)) { + free(result); + result=0; + } + *px = x; + *py = y; + if (req_comp == 0) req_comp = *comp; + result=convert_format(result,4,req_comp,x,y); + + return result; +} + +int stbi_pic_test_memory(stbi_uc const *buffer, int len) +{ + stbi s; + start_mem(&s,buffer,len); + return pic_test(&s); +} + +stbi_uc *stbi_pic_load_from_memory (stbi_uc const *buffer, int len, int *x, int *y, int *comp, int req_comp) +{ + stbi s; + start_mem(&s,buffer,len); + return pic_load(&s,x,y,comp,req_comp); +} + +#ifndef STBI_NO_STDIO +int stbi_pic_test_file(FILE *f) +{ + int result; + long l = ftell(f); + stbi s; + start_file(&s,f); + result = pic_test(&s); + fseek(f,l,SEEK_SET); + return result; +} + +stbi_uc *stbi_pic_load(char const *filename,int *x, int *y, int *comp, int req_comp) +{ + stbi_uc *result; + FILE *f=fopen(filename,"rb"); + if (!f) return 0; + result = stbi_pic_load_from_file(f,x,y,comp,req_comp); + fclose(f); + return result; +} + +stbi_uc *stbi_pic_load_from_file(FILE *f,int *x, int *y, int *comp, int req_comp) +{ + stbi s; + start_file(&s,f); + return pic_load(&s,x,y,comp,req_comp); +} +#endif + +// ************************************************************************************************* +// GIF loader -- public domain by Jean-Marc Lienher -- simplified/shrunk by stb +typedef struct stbi_gif_lzw_struct { + int16 prefix; + uint8 first; + uint8 suffix; +} stbi_gif_lzw; + +typedef struct stbi_gif_struct +{ + int w,h; + stbi_uc *out; // output buffer (always 4 components) + int flags, bgindex, ratio, transparent, eflags; + uint8 pal[256][4]; + uint8 lpal[256][4]; + stbi_gif_lzw codes[4096]; + uint8 *color_table; + int parse, step; + int lflags; + int start_x, start_y; + int max_x, max_y; + int cur_x, cur_y; + int line_size; +} stbi_gif; + +static int gif_test(stbi *s) +{ + int sz; + if (get8(s) != 'G' || get8(s) != 'I' || get8(s) != 'F' || get8(s) != '8') return 0; + sz = get8(s); + if (sz != '9' && sz != '7') return 0; + if (get8(s) != 'a') return 0; + return 1; +} + +#ifndef STBI_NO_STDIO +int stbi_gif_test_file (FILE *f) +{ + stbi s; + int r,n = ftell(f); + start_file(&s,f); + r = gif_test(&s); + fseek(f,n,SEEK_SET); + return r; +} +#endif + +int stbi_gif_test_memory (stbi_uc const *buffer, int len) +{ + stbi s; + start_mem(&s, buffer, len); + return gif_test(&s); +} + +static void stbi_gif_parse_colortable(stbi *s, uint8 pal[256][4], int num_entries, int transp) +{ + int i; + for (i=0; i < num_entries; ++i) { + pal[i][2] = get8u(s); + pal[i][1] = get8u(s); + pal[i][0] = get8u(s); + pal[i][3] = transp ? 0 : 255; + } +} + +static int stbi_gif_header(stbi *s, stbi_gif *g, int *comp, int is_info) +{ + uint8 version; + if (get8(s) != 'G' || get8(s) != 'I' || get8(s) != 'F' || get8(s) != '8') + return e("not GIF", "Corrupt GIF"); + + version = get8u(s); + if (version != '7' && version != '9') return e("not GIF", "Corrupt GIF"); + if (get8(s) != 'a') return e("not GIF", "Corrupt GIF"); + + failure_reason = ""; + g->w = get16le(s); + g->h = get16le(s); + g->flags = get8(s); + g->bgindex = get8(s); + g->ratio = get8(s); + g->transparent = -1; + + if (comp != 0) *comp = 4; // can't actually tell whether it's 3 or 4 until we parse the comments + + if (is_info) return 1; + + if (g->flags & 0x80) + stbi_gif_parse_colortable(s,g->pal, 2 << (g->flags & 7), -1); + + return 1; +} + +static int stbi_gif_info_raw(stbi *s, int *x, int *y, int *comp) +{ + stbi_gif g; + if (!stbi_gif_header(s, &g, comp, 1)) return 0; + if (x) *x = g.w; + if (y) *y = g.h; + return 1; +} + +static void stbi_out_gif_code(stbi_gif *g, uint16 code) +{ + uint8 *p, *c; + + // recurse to decode the prefixes, since the linked-list is backwards, + // and working backwards through an interleaved image would be nasty + if (g->codes[code].prefix >= 0) + stbi_out_gif_code(g, g->codes[code].prefix); + + if (g->cur_y >= g->max_y) return; + + p = &g->out[g->cur_x + g->cur_y]; + c = &g->color_table[g->codes[code].suffix * 4]; + + if (c[3] >= 128) { + p[0] = c[2]; + p[1] = c[1]; + p[2] = c[0]; + p[3] = c[3]; + } + g->cur_x += 4; + + if (g->cur_x >= g->max_x) { + g->cur_x = g->start_x; + g->cur_y += g->step; + + while (g->cur_y >= g->max_y && g->parse > 0) { + g->step = (1 << g->parse) * g->line_size; + g->cur_y = g->start_y + (g->step >> 1); + --g->parse; + } + } +} + +static uint8 *stbi_process_gif_raster(stbi *s, stbi_gif *g) +{ + uint8 lzw_cs; + int32 len, code; + uint32 first; + int32 codesize, codemask, avail, oldcode, bits, valid_bits, clear; + stbi_gif_lzw *p; + + lzw_cs = get8u(s); + clear = 1 << lzw_cs; + first = 1; + codesize = lzw_cs + 1; + codemask = (1 << codesize) - 1; + bits = 0; + valid_bits = 0; + for (code = 0; code < clear; code++) { + g->codes[code].prefix = -1; + g->codes[code].first = (uint8) code; + g->codes[code].suffix = (uint8) code; + } + + // support no starting clear code + avail = clear+2; + oldcode = -1; + + len = 0; + for(;;) { + if (valid_bits < codesize) { + if (len == 0) { + len = get8(s); // start new block + if (len == 0) + return g->out; + } + --len; + bits |= (int32) get8(s) << valid_bits; + valid_bits += 8; + } else { + int32 code = bits & codemask; + bits >>= codesize; + valid_bits -= codesize; + // @OPTIMIZE: is there some way we can accelerate the non-clear path? + if (code == clear) { // clear code + codesize = lzw_cs + 1; + codemask = (1 << codesize) - 1; + avail = clear + 2; + oldcode = -1; + first = 0; + } else if (code == clear + 1) { // end of stream code + skip(s, len); + while ((len = get8(s)) > 0) + skip(s,len); + return g->out; + } else if (code <= avail) { + if (first) return epuc("no clear code", "Corrupt GIF"); + + if (oldcode >= 0) { + p = &g->codes[avail++]; + if (avail > 4096) return epuc("too many codes", "Corrupt GIF"); + p->prefix = (int16) oldcode; + p->first = g->codes[oldcode].first; + p->suffix = (code == avail) ? p->first : g->codes[code].first; + } else if (code == avail) + return epuc("illegal code in raster", "Corrupt GIF"); + + stbi_out_gif_code(g, (uint16) code); + + if ((avail & codemask) == 0 && avail <= 0x0FFF) { + codesize++; + codemask = (1 << codesize) - 1; + } + + oldcode = code; + } else { + return epuc("illegal code in raster", "Corrupt GIF"); + } + } + } +} + +static void stbi_fill_gif_background(stbi_gif *g) +{ + int i; + uint8 *c = g->pal[g->bgindex]; + // @OPTIMIZE: write a dword at a time + for (i = 0; i < g->w * g->h * 4; i += 4) { + uint8 *p = &g->out[i]; + p[0] = c[2]; + p[1] = c[1]; + p[2] = c[0]; + p[3] = c[3]; + } +} + +// this function is designed to support animated gifs, although stb_image doesn't support it +static uint8 *stbi_gif_load_next(stbi *s, stbi_gif *g, int *comp, int req_comp) +{ + int i; + uint8 *old_out = 0; + + if (g->out == 0) { + if (!stbi_gif_header(s, g, comp,0)) return 0; // failure_reason set by stbi_gif_header + g->out = (uint8 *) malloc(4 * g->w * g->h); + if (g->out == 0) return epuc("outofmem", "Out of memory"); + stbi_fill_gif_background(g); + } else { + // animated-gif-only path + if (((g->eflags & 0x1C) >> 2) == 3) { + old_out = g->out; + g->out = (uint8 *) malloc(4 * g->w * g->h); + if (g->out == 0) return epuc("outofmem", "Out of memory"); + memcpy(g->out, old_out, g->w*g->h*4); + } + } + + for (;;) { + switch (get8(s)) { + case 0x2C: /* Image Descriptor */ + { + int32 x, y, w, h; + uint8 *o; + + x = get16le(s); + y = get16le(s); + w = get16le(s); + h = get16le(s); + if (((x + w) > (g->w)) || ((y + h) > (g->h))) + return epuc("bad Image Descriptor", "Corrupt GIF"); + + g->line_size = g->w * 4; + g->start_x = x * 4; + g->start_y = y * g->line_size; + g->max_x = g->start_x + w * 4; + g->max_y = g->start_y + h * g->line_size; + g->cur_x = g->start_x; + g->cur_y = g->start_y; + + g->lflags = get8(s); + + if (g->lflags & 0x40) { + g->step = 8 * g->line_size; // first interlaced spacing + g->parse = 3; + } else { + g->step = g->line_size; + g->parse = 0; + } + + if (g->lflags & 0x80) { + stbi_gif_parse_colortable(s,g->lpal, 2 << (g->lflags & 7), g->eflags & 0x01 ? g->transparent : -1); + g->color_table = (uint8 *) g->lpal; + } else if (g->flags & 0x80) { + for (i=0; i < 256; ++i) // @OPTIMIZE: reset only the previous transparent + g->pal[i][3] = 255; + if (g->transparent >= 0 && (g->eflags & 0x01)) + g->pal[g->transparent][3] = 0; + g->color_table = (uint8 *) g->pal; + } else + return epuc("missing color table", "Corrupt GIF"); + + o = stbi_process_gif_raster(s, g); + if (o == NULL) return NULL; + + if (req_comp && req_comp != 4) + o = convert_format(o, 4, req_comp, g->w, g->h); + return o; + } + + case 0x21: // Comment Extension. + { + int len; + if (get8(s) == 0xF9) { // Graphic Control Extension. + len = get8(s); + if (len == 4) { + g->eflags = get8(s); + get16le(s); // delay + g->transparent = get8(s); + } else { + skip(s, len); + break; + } + } + while ((len = get8(s)) != 0) + skip(s, len); + break; + } + + case 0x3B: // gif stream termination code + return (uint8 *) 1; + + default: + return epuc("unknown code", "Corrupt GIF"); + } + } +} + +#ifndef STBI_NO_STDIO +stbi_uc *stbi_gif_load (char const *filename, int *x, int *y, int *comp, int req_comp) +{ + uint8 *data; + FILE *f = fopen(filename, "rb"); + if (!f) return NULL; + data = stbi_gif_load_from_file(f, x,y,comp,req_comp); + fclose(f); + return data; +} + +stbi_uc *stbi_gif_load_from_file (FILE *f, int *x, int *y, int *comp, int req_comp) +{ + uint8 *u = 0; + stbi s; + stbi_gif g={0}; + start_file(&s, f); + + u = stbi_gif_load_next(&s, &g, comp, req_comp); + if (u == (void *) 1) u = 0; // end of animated gif marker + if (u) { + *x = g.w; + *y = g.h; + } + + return u; +} +#endif + +stbi_uc *stbi_gif_load_from_memory (stbi_uc const *buffer, int len, int *x, int *y, int *comp, int req_comp) +{ + uint8 *u = 0; + stbi s; + stbi_gif g={0}; + start_mem(&s, buffer, len); + u = stbi_gif_load_next(&s, &g, comp, req_comp); + if (u == (void *) 1) u = 0; // end of animated gif marker + if (u) { + *x = g.w; + *y = g.h; + } + return u; +} + +#ifndef STBI_NO_STDIO +int stbi_gif_info (char const *filename, int *x, int *y, int *comp) +{ + int res; + FILE *f = fopen(filename, "rb"); + if (!f) return 0; + res = stbi_gif_info_from_file(f, x, y, comp); + fclose(f); + return res; +} + +int stbi_gif_info_from_file(FILE *f, int *x, int *y, int *comp) +{ + stbi s; + int res; + long n = ftell(f); + start_file(&s, f); + res = stbi_gif_info_raw(&s, x, y, comp); + fseek(f, n, SEEK_SET); + return res; +} +#endif // !STBI_NO_STDIO + +int stbi_gif_info_from_memory(stbi_uc const *buffer, int len, int *x, int *y, int *comp) +{ + stbi s; + start_mem(&s, buffer, len); + return stbi_gif_info_raw(&s, x, y, comp); +} + + + + +// ************************************************************************************************* +// Radiance RGBE HDR loader +// originally by Nicolas Schulz +#ifndef STBI_NO_HDR +static int hdr_test(stbi *s) +{ + const char *signature = "#?RADIANCE\n"; + int i; + for (i=0; signature[i]; ++i) + if (get8(s) != signature[i]) + return 0; + return 1; +} + +int stbi_hdr_test_memory(stbi_uc const *buffer, int len) +{ + stbi s; + start_mem(&s, buffer, len); + return hdr_test(&s); +} + +#ifndef STBI_NO_STDIO +int stbi_hdr_test_file(FILE *f) +{ + stbi s; + int r,n = ftell(f); + start_file(&s, f); + r = hdr_test(&s); + fseek(f,n,SEEK_SET); + return r; +} +#endif + +#define HDR_BUFLEN 1024 +static char *hdr_gettoken(stbi *z, char *buffer) +{ + int len=0; + char c = '\0'; + + c = (char) get8(z); + + while (!at_eof(z) && c != '\n') { + buffer[len++] = c; + if (len == HDR_BUFLEN-1) { + // flush to end of line + while (!at_eof(z) && get8(z) != '\n') + ; + break; + } + c = (char) get8(z); + } + + buffer[len] = 0; + return buffer; +} + +static void hdr_convert(float *output, stbi_uc *input, int req_comp) +{ + if ( input[3] != 0 ) { + float f1; + // Exponent + f1 = (float) ldexp(1.0f, input[3] - (int)(128 + 8)); + if (req_comp <= 2) + output[0] = (input[0] + input[1] + input[2]) * f1 / 3; + else { + output[0] = input[0] * f1; + output[1] = input[1] * f1; + output[2] = input[2] * f1; + } + if (req_comp == 2) output[1] = 1; + if (req_comp == 4) output[3] = 1; + } else { + switch (req_comp) { + case 4: output[3] = 1; /* fallthrough */ + case 3: output[0] = output[1] = output[2] = 0; + break; + case 2: output[1] = 1; /* fallthrough */ + case 1: output[0] = 0; + break; + } + } +} + + +static float *hdr_load(stbi *s, int *x, int *y, int *comp, int req_comp) +{ + char buffer[HDR_BUFLEN]; + char *token; + int valid = 0; + int width, height; + stbi_uc *scanline; + float *hdr_data; + int len; + unsigned char count, value; + int i, j, k, c1,c2, z; + + + // Check identifier + if (strcmp(hdr_gettoken(s,buffer), "#?RADIANCE") != 0) + return epf("not HDR", "Corrupt HDR image"); + + // Parse header + for(;;) { + token = hdr_gettoken(s,buffer); + if (token[0] == 0) break; + if (strcmp(token, "FORMAT=32-bit_rle_rgbe") == 0) valid = 1; + } + + if (!valid) return epf("unsupported format", "Unsupported HDR format"); + + // Parse width and height + // can't use sscanf() if we're not using stdio! + token = hdr_gettoken(s,buffer); + if (strncmp(token, "-Y ", 3)) return epf("unsupported data layout", "Unsupported HDR format"); + token += 3; + height = strtol(token, &token, 10); + while (*token == ' ') ++token; + if (strncmp(token, "+X ", 3)) return epf("unsupported data layout", "Unsupported HDR format"); + token += 3; + width = strtol(token, NULL, 10); + + *x = width; + *y = height; + + *comp = 3; + if (req_comp == 0) req_comp = 3; + + // Read data + hdr_data = (float *) malloc(height * width * req_comp * sizeof(float)); + + // Load image data + // image data is stored as some number of sca + if ( width < 8 || width >= 32768) { + // Read flat data + for (j=0; j < height; ++j) { + for (i=0; i < width; ++i) { + stbi_uc rgbe[4]; + main_decode_loop: + getn(s, rgbe, 4); + hdr_convert(hdr_data + j * width * req_comp + i * req_comp, rgbe, req_comp); + } + } + } else { + // Read RLE-encoded data + scanline = NULL; + + for (j = 0; j < height; ++j) { + c1 = get8(s); + c2 = get8(s); + len = get8(s); + if (c1 != 2 || c2 != 2 || (len & 0x80)) { + // not run-length encoded, so we have to actually use THIS data as a decoded + // pixel (note this can't be a valid pixel--one of RGB must be >= 128) + uint8 rgbe[4]; + rgbe[0] = (uint8) c1; + rgbe[1] = (uint8) c2; + rgbe[2] = (uint8) len; + rgbe[3] = (uint8) get8u(s); + hdr_convert(hdr_data, rgbe, req_comp); + i = 1; + j = 0; + free(scanline); + goto main_decode_loop; // yes, this makes no sense + } + len <<= 8; + len |= get8(s); + if (len != width) { free(hdr_data); free(scanline); return epf("invalid decoded scanline length", "corrupt HDR"); } + if (scanline == NULL) scanline = (stbi_uc *) malloc(width * 4); + + for (k = 0; k < 4; ++k) { + i = 0; + while (i < width) { + count = get8u(s); + if (count > 128) { + // Run + value = get8u(s); + count -= 128; + for (z = 0; z < count; ++z) + scanline[i++ * 4 + k] = value; + } else { + // Dump + for (z = 0; z < count; ++z) + scanline[i++ * 4 + k] = get8u(s); + } + } + } + for (i=0; i < width; ++i) + hdr_convert(hdr_data+(j*width + i)*req_comp, scanline + i*4, req_comp); + } + free(scanline); + } + + return hdr_data; +} + +#ifndef STBI_NO_STDIO +float *stbi_hdr_load_from_file(FILE *f, int *x, int *y, int *comp, int req_comp) +{ + stbi s; + start_file(&s,f); + return hdr_load(&s,x,y,comp,req_comp); +} +#endif + +float *stbi_hdr_load_from_memory(stbi_uc const *buffer, int len, int *x, int *y, int *comp, int req_comp) +{ + stbi s; + start_mem(&s,buffer, len); + return hdr_load(&s,x,y,comp,req_comp); +} + +#endif // STBI_NO_HDR + + +#ifndef STBI_NO_STDIO +int stbi_info(char const *filename, int *x, int *y, int *comp) +{ + FILE *f = fopen(filename, "rb"); + int result; + if (!f) return e("can't fopen", "Unable to open file"); + result = stbi_info_from_file(f, x, y, comp); + fclose(f); + return result; +} + +int stbi_info_from_file(FILE *f, int *x, int *y, int *comp) +{ + if (stbi_jpeg_info_from_file(f, x, y, comp)) + return 1; + if (stbi_png_info_from_file(f, x, y, comp)) + return 1; + if (stbi_gif_info_from_file(f, x, y, comp)) + return 1; + // @TODO: stbi_bmp_info_from_file + // @TODO: stbi_psd_info_from_file + #ifndef STBI_NO_HDR + // @TODO: stbi_hdr_info_from_file + #endif + // test tga last because it's a crappy test! + if (stbi_tga_info_from_file(f, x, y, comp)) + return 1; + return e("unknown image type", "Image not of any known type, or corrupt"); +} +#endif // !STBI_NO_STDIO + +int stbi_info_from_memory(stbi_uc const *buffer, int len, int *x, int *y, int *comp) +{ + if (stbi_jpeg_info_from_memory(buffer, len, x, y, comp)) + return 1; + if (stbi_png_info_from_memory(buffer, len, x, y, comp)) + return 1; + if (stbi_gif_info_from_memory(buffer, len, x, y, comp)) + return 1; + // @TODO: stbi_bmp_info_from_memory + // @TODO: stbi_psd_info_from_memory + #ifndef STBI_NO_HDR + // @TODO: stbi_hdr_info_from_memory + #endif + // test tga last because it's a crappy test! + if (stbi_tga_info_from_memory(buffer, len, x, y, comp)) + return 1; + return e("unknown image type", "Image not of any known type, or corrupt"); +} + +#endif // STBI_HEADER_FILE_ONLY + +/* + revision history: + 1.28 (2010-08-01) fix bug in GIF palette transparency (SpartanJ) + 1.27 (2010-08-01) + cast-to-uint8 to fix warnings + 1.26 (2010-07-24) + fix bug in file buffering for PNG reported by SpartanJ + 1.25 (2010-07-17) + refix trans_data warning (Won Chun) + 1.24 (2010-07-12) + perf improvements reading from files on platforms with lock-heavy fgetc() + minor perf improvements for jpeg + deprecated type-specific functions so we'll get feedback if they're needed + attempt to fix trans_data warning (Won Chun) + 1.23 fixed bug in iPhone support + 1.22 (2010-07-10) + removed image *writing* support + removed image *writing* support + stbi_info support from Jetro Lauha + GIF support from Jean-Marc Lienher + iPhone PNG-extensions from James Brown + warning-fixes from Nicolas Schulz and Janez Zemva (i.e. Janez (U+017D)emva) + 1.21 fix use of 'uint8' in header (reported by jon blow) + 1.20 added support for Softimage PIC, by Tom Seddon + 1.19 bug in interlaced PNG corruption check (found by ryg) + 1.18 2008-08-02 + fix a threading bug (local mutable static) + 1.17 support interlaced PNG + 1.16 major bugfix - convert_format converted one too many pixels + 1.15 initialize some fields for thread safety + 1.14 fix threadsafe conversion bug + header-file-only version (#define STBI_HEADER_FILE_ONLY before including) + 1.13 threadsafe + 1.12 const qualifiers in the API + 1.11 Support installable IDCT, colorspace conversion routines + 1.10 Fixes for 64-bit (don't use "unsigned long") + optimized upsampling by Fabian "ryg" Giesen + 1.09 Fix format-conversion for PSD code (bad global variables!) + 1.08 Thatcher Ulrich's PSD code integrated by Nicolas Schulz + 1.07 attempt to fix C++ warning/errors again + 1.06 attempt to fix C++ warning/errors again + 1.05 fix TGA loading to return correct *comp and use good luminance calc + 1.04 default float alpha is 1, not 255; use 'void *' for stbi_image_free + 1.03 bugfixes to STBI_NO_STDIO, STBI_NO_HDR + 1.02 support for (subset of) HDR files, float interface for preferred access to them + 1.01 fix bug: possible bug in handling right-side up bmps... not sure + fix bug: the stbi_bmp_load() and stbi_tga_load() functions didn't work at all + 1.00 interface to zlib that skips zlib header + 0.99 correct handling of alpha in palette + 0.98 TGA loader by lonesock; dynamically add loaders (untested) + 0.97 jpeg errors on too large a file; also catch another malloc failure + 0.96 fix detection of invalid v value - particleman@mollyrocket forum + 0.95 during header scan, seek to markers in case of padding + 0.94 STBI_NO_STDIO to disable stdio usage; rename all #defines the same + 0.93 handle jpegtran output; verbose errors + 0.92 read 4,8,16,24,32-bit BMP files of several formats + 0.91 output 24-bit Windows 3.0 BMP files + 0.90 fix a few more warnings; bump version number to approach 1.0 + 0.61 bugfixes due to Marc LeBlanc, Christopher Lloyd + 0.60 fix compiling as c++ + 0.59 fix warnings: merge Dave Moore's -Wall fixes + 0.58 fix bug: zlib uncompressed mode len/nlen was wrong endian + 0.57 fix bug: jpg last huffman symbol before marker was >9 bits but less + than 16 available + 0.56 fix bug: zlib uncompressed mode len vs. nlen + 0.55 fix bug: restart_interval not initialized to 0 + 0.54 allow NULL for 'int *comp' + 0.53 fix bug in png 3->4; speedup png decoding + 0.52 png handles req_comp=3,4 directly; minor cleanup; jpeg comments + 0.51 obey req_comp requests, 1-component jpegs return as 1-component, + on 'test' only check type, not whether we support this variant +*/ diff --git a/stb_truetype.h b/stb_truetype.h new file mode 100644 index 0000000..1428744 --- /dev/null +++ b/stb_truetype.h @@ -0,0 +1,1807 @@ +// stb_truetype.h - v0.3 - public domain - 2009 Sean Barrett / RAD Game Tools +// +// This library processes TrueType files: +// parse files +// extract glyph metrics +// extract glyph shapes +// render glyphs to one-channel bitmaps with antialiasing (box filter) +// +// Todo: +// non-MS cmaps +// crashproof on bad data +// hinting +// subpixel positioning when rendering bitmap +// cleartype-style AA +// +// ADDITIONAL CONTRIBUTORS +// +// Mikko Mononen: compound shape support, more cmap formats +// +// VERSIONS +// +// 0.3 (2009-06-24) cmap fmt=12, compound shapes (MM) +// userdata, malloc-from-userdata, non-zero fill (STB) +// 0.2 (2009-03-11) Fix unsigned/signed char warnings +// 0.1 (2009-03-09) First public release +// +// USAGE +// +// Include this file in whatever places neeed to refer to it. In ONE C/C++ +// file, write: +// #define STB_TRUETYPE_IMPLEMENTATION +// before the #include of this file. This expands out the actual +// implementation into that C/C++ file. +// +// Look at the header-file sections below for the API, but here's a quick skim: +// +// Simple 3D API (don't ship this, but it's fine for tools and quick start, +// and you can cut and paste from it to move to more advanced) +// stbtt_BakeFontBitmap() -- bake a font to a bitmap for use as texture +// stbtt_GetBakedQuad() -- compute quad to draw for a given char +// +// "Load" a font file from a memory buffer (you have to keep the buffer loaded) +// stbtt_InitFont() +// stbtt_GetFontOffsetForIndex() -- use for TTC font collections +// +// Render a unicode codepoint to a bitmap +// stbtt_GetCodepointBitmap() -- allocates and returns a bitmap +// stbtt_MakeCodepointBitmap() -- renders into bitmap you provide +// stbtt_GetCodepointBitmapBox() -- how big the bitmap must be +// +// Character advance/positioning +// stbtt_GetCodepointHMetrics() +// stbtt_GetFontVMetrics() +// +// NOTES +// +// The system uses the raw data found in the .ttf file without changing it +// and without building auxiliary data structures. This is a bit inefficient +// on little-endian systems (the data is big-endian), but assuming you're +// caching the bitmaps or glyph shapes this shouldn't be a big deal. +// +// It appears to be very hard to programmatically determine what font a +// given file is in a general way. I provide an API for this, but I don't +// recommend it. +// +// +// SOURCE STATISTICS (based on v0.3, 1800 LOC) +// +// Documentation & header file 350 LOC \___ 500 LOC documentation +// Sample code 140 LOC / +// Truetype parsing 580 LOC ---- 600 LOC TrueType +// Software rasterization 240 LOC \ . +// Curve tesselation 120 LOC \__ 500 LOC Bitmap creation +// Bitmap management 70 LOC / +// Baked bitmap interface 70 LOC / +// Font name matching & access 150 LOC ---- 150 +// C runtime library abstraction 60 LOC ---- 60 + + +////////////////////////////////////////////////////////////////////////////// +////////////////////////////////////////////////////////////////////////////// +//// +//// SAMPLE PROGRAMS +//// +// +// Incomplete text-in-3d-api example, which draws quads properly aligned to be lossless +// +#if 0 +#define STB_TRUETYPE_IMPLEMENTATION // force following include to generate implementation +#include "stb_truetype.h" + +char ttf_buffer[1<<20]; +unsigned char temp_bitmap[512*512]; + +stbtt_chardata cdata[96]; // ASCII 32..126 is 95 glyphs +GLstbtt_uint ftex; + +void my_stbtt_initfont(void) +{ + fread(ttf_buffer, 1, 1<<20, fopen("c:/windows/fonts/times.ttf", "rb")); + stbtt_BakeFontBitmap(data,0, 32.0, temp_bitmap,512,512, 32,96, cdata); // no guarantee this fits! + // can free ttf_buffer at this point + glGenTextures(1, &ftex); + glBindTexture(GL_TEXTURE_2D, ftex); + glTexImage2D(GL_TEXTURE_2D, 0, GL_ALPHA, 512,512, 0, GL_ALPHA, GL_UNSIGNED_BYTE, temp_bitmap); + // can free temp_bitmap at this point + glTexParameteri(GL_TEXTURE_2D, GL_TEXTURE_MIN_FILTER, GL_LINEAR); +} + +void my_stbtt_print(float x, float y, char *text) +{ + // assume orthographic projection with units = screen pixels, origin at top left + glBindTexture(GL_TEXTURE_2D, ftex); + glBegin(GL_QUADS); + while (*text) { + if (*text >= 32 && *text < 128) { + stbtt_aligned_quad q; + stbtt_GetBakedQuad(cdata, 512,512, *text-32, &x,&y,&q,1);//1=opengl,0=old d3d + glTexCoord2f(q.s0,q.t1); glVertex2f(q.x0,q.y0); + glTexCoord2f(q.s1,q.t1); glVertex2f(q.x1,q.y0); + glTexCoord2f(q.s1,q.t0); glVertex2f(q.x1,q.y1); + glTexCoord2f(q.s0,q.t0); glVertex2f(q.x0,q.y1); + } + ++text; + } + glEnd(); +} +#endif +// +// +////////////////////////////////////////////////////////////////////////////// +// +// Complete program (this compiles): get a single bitmap, print as ASCII art +// +#if 0 +#include +#define STB_TRUETYPE_IMPLEMENTATION // force following include to generate implementation +#include "stb_truetype.h" + +char ttf_buffer[1<<25]; + +int main(int argc, char **argv) +{ + stbtt_fontinfo font; + unsigned char *bitmap; + int w,h,i,j,c = (argc > 1 ? atoi(argv[1]) : 'a'), s = (argc > 2 ? atoi(argv[2]) : 20); + + fread(ttf_buffer, 1, 1<<25, fopen(argc > 3 ? argv[3] : "c:/windows/fonts/arialbd.ttf", "rb")); + + stbtt_InitFont(&font, ttf_buffer, stbtt_GetFontOffsetForIndex(ttf_buffer,0)); + bitmap = stbtt_GetCodepointBitmap(&font, 0,stbtt_ScaleForPixelHeight(&font, s), c, &w, &h, 0,0); + + for (j=0; j < h; ++j) { + for (i=0; i < w; ++i) + putchar(" .:ioVM@"[bitmap[j*w+i]>>5]); + putchar('\n'); + } + return 0; +} +#endif +// +// Output: +// +// .ii. +// @@@@@@. +// V@Mio@@o +// :i. V@V +// :oM@@M +// :@@@MM@M +// @@o o@M +// :@@. M@M +// @@@o@@@@ +// :M@@V:@@. +// +////////////////////////////////////////////////////////////////////////////// +// +// Complete program: print "Hello World!" banner, with bugs +// +#if 0 +int main(int arg, char **argv) +{ + unsigned char screen[20][79]; + int i,j, pos=0; + float scale; + char *text = "Heljo World!"; + + fread(buffer, 1, 1000000, fopen("c:/windows/fonts/arialbd.ttf", "rb")); + stbtt_InitFont(&font, buffer, 0); + + scale = stbtt_ScaleForPixelHeight(&font, 16); + memset(screen, 0, sizeof(screen)); + + while (*text) { + int advance,lsb,x0,y0,x1,y1, newpos, baseline=13; + stbtt_GetCodepointHMetrics(&font, *text, &advance, &lsb); + stbtt_GetCodepointBitmapBox(&font, *text, scale,scale, &x0,&y0,&x1,&y1); + newpos = pos + (int) (lsb * scale) + x0; + stbtt_MakeCodepointBitmap(&font, &screen[baseline + y0][newpos], x1-x0,y1-y0, 79, scale,scale, *text); + // note that this stomps the old data, so where character boxes overlap (e.g. 'lj') it's wrong + // because this API is really for baking character bitmaps into textures + pos += (int) (advance * scale); + ++text; + } + + for (j=0; j < 20; ++j) { + for (i=0; i < 79; ++i) + putchar(" .:ioVM@"[screen[j][i]>>5]); + putchar('\n'); + } + + return 0; +} +#endif + + +////////////////////////////////////////////////////////////////////////////// +////////////////////////////////////////////////////////////////////////////// +//// +//// INTEGRATION WITH RUNTIME LIBRARIES +//// + +#ifdef STB_TRUETYPE_IMPLEMENTATION + // #define your own (u)stbtt_int8/16/32 before including to override this + #ifndef stbtt_uint8 + typedef unsigned char stbtt_uint8; + typedef signed char stbtt_int8; + typedef unsigned short stbtt_uint16; + typedef signed short stbtt_int16; + typedef unsigned int stbtt_uint32; + typedef signed int stbtt_int32; + #endif + + typedef char stbtt__check_size32[sizeof(stbtt_int32)==4 ? 1 : -1]; + typedef char stbtt__check_size16[sizeof(stbtt_int16)==2 ? 1 : -1]; + + // #define your own STBTT_sort() to override this to avoid qsort + #ifndef STBTT_sort + #include + #define STBTT_sort(data,num_items,item_size,compare_func) qsort(data,num_items,item_size,compare_func) + #endif + + // #define your own STBTT_ifloor/STBTT_iceil() to avoid math.h + #ifndef STBTT_ifloor + #include + #define STBTT_ifloor(x) ((int) floor(x)) + #define STBTT_iceil(x) ((int) ceil(x)) + #endif + + // #define your own functions "STBTT_malloc" / "STBTT_free" to avoid malloc.h + #ifndef STBTT_malloc + #include + #define STBTT_malloc(x,u) malloc(x) + #define STBTT_free(x,u) free(x) + #endif + + #ifndef STBTT_assert + #include + #define STBTT_assert(x) assert(x) + #endif + + #ifndef STBTT_strlen + #include + #define STBTT_strlen(x) strlen(x) + #endif + + //#ifndef STBTT_memcpy + //#include + #define STBTT_memcpy memcpy + #define STBTT_memset memset + //#endif +#endif + +/////////////////////////////////////////////////////////////////////////////// +/////////////////////////////////////////////////////////////////////////////// +//// +//// INTERFACE +//// +//// + +#ifndef __STB_INCLUDE_STB_TRUETYPE_H__ +#define __STB_INCLUDE_STB_TRUETYPE_H__ + +#ifdef __cplusplus +extern "C" { +#endif + +////////////////////////////////////////////////////////////////////////////// +// +// TEXTURE BAKING API +// +// If you use this API, you only have to call two functions ever. +// + +typedef struct +{ + unsigned short x0,y0,x1,y1; // coordinates of bbox in bitmap + float xoff,yoff,xadvance; +} stbtt_bakedchar; + +extern int stbtt_BakeFontBitmap(const unsigned char *data, int offset, // font location (use offset=0 for plain .ttf) + float pixel_height, // height of font in pixels + unsigned char *pixels, int pw, int ph, // bitmap to be filled in + int first_char, int num_chars, // characters to bake + stbtt_bakedchar *chardata); // you allocate this, it's num_chars long +// if return is positive, the first unused row of the bitmap +// if return is negative, returns the negative of the number of characters that fit +// if return is 0, no characters fit and no rows were used +// This uses a very crappy packing. + +typedef struct +{ + float x0,y0,s0,t0; // top-left + float x1,y1,s1,t1; // bottom-right +} stbtt_aligned_quad; + +extern void stbtt_GetBakedQuad(stbtt_bakedchar *chardata, int pw, int ph, // same data as above + int char_index, // character to display + float *xpos, float *ypos, // pointers to current position in screen pixel space + stbtt_aligned_quad *q, // output: quad to draw + int opengl_fillrule); // true if opengl fill rule; false if DX9 or earlier +// Call GetBakedQuad with char_index = 'character - first_char', and it +// creates the quad you need to draw and advances the current position. +// It's inefficient; you might want to c&p it and optimize it. + + +////////////////////////////////////////////////////////////////////////////// +// +// FONT LOADING +// +// + +extern int stbtt_GetFontOffsetForIndex(const unsigned char *data, int index); +// Each .ttf file may have more than one font. Each has a sequential index +// number starting from 0. Call this function to get the font offset for a +// given index; it returns -1 if the index is out of range. A regular .ttf +// file will only define one font and it always be at offset 0, so it will +// return '0' for index 0, and -1 for all other indices. You can just skip +// this step if you know it's that kind of font. + + +// The following structure is defined publically so you can declare one on +// the stack or as a global or etc. +typedef struct +{ + void *userdata; + unsigned char *data; // pointer to .ttf file + int fontstart; // offset of start of font + + int numGlyphs; // number of glyphs, needed for range checking + + int loca,head,glyf,hhea,hmtx; // table locations as offset from start of .ttf + int index_map; // a cmap mapping for our chosen character encoding + int indexToLocFormat; // format needed to map from glyph index to glyph +} stbtt_fontinfo; + +extern int stbtt_InitFont(stbtt_fontinfo *info, const unsigned char *data, int offset); +// Given an offset into the file that defines a font, this function builds +// the necessary cached info for the rest of the system. You must allocate +// the stbtt_fontinfo yourself, and stbtt_InitFont will fill it out. You don't +// need to do anything special to free it, because the contents are a pure +// cache with no additional data structures. Returns 0 on failure. + + +////////////////////////////////////////////////////////////////////////////// +// +// CHARACTER TO GLYPH-INDEX CONVERSIOn + +int stbtt_FindGlyphIndex(const stbtt_fontinfo *info, int unicode_codepoint); +// If you're going to perform multiple operations on the same character +// and you want a speed-up, call this function with the character you're +// going to process, then use glyph-based functions instead of the +// codepoint-based functions. + + +////////////////////////////////////////////////////////////////////////////// +// +// CHARACTER PROPERTIES +// + +extern float stbtt_ScaleForPixelHeight(const stbtt_fontinfo *info, float pixels); +// computes a scale factor to produce a font whose "height" is 'pixels' tall. +// Height is measured as the distance from the highest ascender to the lowest +// descender; in other words, it's equivalent to calling stbtt_GetFontVMetrics +// and computing: +// scale = pixels / (ascent - descent) +// so if you prefer to measure height by the ascent only, use a similar calculation. + +extern void stbtt_GetFontVMetrics(const stbtt_fontinfo *info, int *ascent, int *descent, int *lineGap); +// ascent is the coordinate above the baseline the font extends; descent +// is the coordinate below the baseline the font extends (i.e. it is typically negative) +// lineGap is the spacing between one row's descent and the next row's ascent... +// so you should advance the vertical position by "*ascent - *descent + *lineGap" +// these are expressed in unscaled coordinates + +extern void stbtt_GetCodepointHMetrics(const stbtt_fontinfo *info, int codepoint, int *advanceWidth, int *leftSideBearing); +// leftSideBearing is the offset from the current horizontal position to the left edge of the character +// advanceWidth is the offset from the current horizontal position to the next horizontal position +// these are expressed in unscaled coordinates + +extern int stbtt_GetCodepointKernAdvance(const stbtt_fontinfo *info, int ch1, int ch2); +// an additional amount to add to the 'advance' value between ch1 and ch2 +// @TODO; for now always returns 0! + +extern int stbtt_GetCodepointBox(const stbtt_fontinfo *info, int codepoint, int *x0, int *y0, int *x1, int *y1); +// Gets the bounding box of the visible part of the glyph, in unscaled coordinates + +extern void stbtt_GetGlyphHMetrics(const stbtt_fontinfo *info, int glyph_index, int *advanceWidth, int *leftSideBearing); +extern int stbtt_GetGlyphKernAdvance(const stbtt_fontinfo *info, int glyph1, int glyph2); +extern int stbtt_GetGlyphBox(const stbtt_fontinfo *info, int glyph_index, int *x0, int *y0, int *x1, int *y1); +// as above, but takes one or more glyph indices for greater efficiency + + +////////////////////////////////////////////////////////////////////////////// +// +// GLYPH SHAPES (you probably don't need these, but they have to go before +// the bitmaps for C declaration-order reasons) +// + +#ifndef STBTT_vmove // you can predefine these to use different values (but why?) + enum { + STBTT_vmove=1, + STBTT_vline, + STBTT_vcurve + }; +#endif + +#ifndef stbtt_vertex // you can predefine this to use different values + // (we share this with other code at RAD) + #define stbtt_vertex_type short // can't use stbtt_int16 because that's not visible in the header file + typedef struct + { + stbtt_vertex_type x,y,cx,cy; + unsigned char type,padding; + } stbtt_vertex; +#endif + +extern int stbtt_GetCodepointShape(const stbtt_fontinfo *info, int unicode_codepoint, stbtt_vertex **vertices); +extern int stbtt_GetGlyphShape(const stbtt_fontinfo *info, int glyph_index, stbtt_vertex **vertices); +// returns # of vertices and fills *vertices with the pointer to them +// these are expressed in "unscaled" coordinates + +extern void stbtt_FreeShape(const stbtt_fontinfo *info, stbtt_vertex *vertices); +// frees the data allocated above + +////////////////////////////////////////////////////////////////////////////// +// +// BITMAP RENDERING +// + +extern void stbtt_FreeBitmap(unsigned char *bitmap, void *userdata); +// frees the bitmap allocated below + +extern unsigned char *stbtt_GetCodepointBitmap(const stbtt_fontinfo *info, float scale_x, float scale_y, int codepoint, int *width, int *height, int *xoff, int *yoff); +// allocates a large-enough single-channel 8bpp bitmap and renders the +// specified character/glyph at the specified scale into it, with +// antialiasing. 0 is no coverage (transparent), 255 is fully covered (opaque). +// *width & *height are filled out with the width & height of the bitmap, +// which is stored left-to-right, top-to-bottom. +// +// xoff/yoff are the offset it pixel space from the glyph origin to the top-left of the bitmap + +extern void stbtt_MakeCodepointBitmap(const stbtt_fontinfo *info, unsigned char *output, int out_w, int out_h, int out_stride, float scale_x, float scale_y, int codepoint); +// the same as above, but you pass in storage for the bitmap in the form +// of 'output', with row spacing of 'out_stride' bytes. the bitmap is +// clipped to out_w/out_h bytes. call the next function to get the +// height and width and positioning info + +extern void stbtt_GetCodepointBitmapBox(const stbtt_fontinfo *font, int codepoint, float scale_x, float scale_y, int *ix0, int *iy0, int *ix1, int *iy1); +// get the bbox of the bitmap centered around the glyph origin; so the +// bitmap width is ix1-ix0, height is iy1-iy0, and location to place +// the bitmap top left is (leftSideBearing*scale,iy0). +// (Note that the bitmap uses y-increases-down, but the shape uses +// y-increases-up, so CodepointBitmapBox and CodepointBox are inverted.) + +extern unsigned char *stbtt_GetGlyphBitmap(const stbtt_fontinfo *info, float scale_x, float scale_y, int glyph, int *width, int *height, int *xoff, int *yoff); +extern void stbtt_GetGlyphBitmapBox(const stbtt_fontinfo *font, int glyph, float scale_x, float scale_y, int *ix0, int *iy0, int *ix1, int *iy1); +extern void stbtt_MakeGlyphBitmap(const stbtt_fontinfo *info, unsigned char *output, int out_w, int out_h, int out_stride, float scale_x, float scale_y, int glyph); + +//extern void stbtt_get_true_bbox(stbtt_vertex *vertices, int num_verts, float scale_x, float scale_y, int *ix0, int *iy0, int *ix1, int *iy1); + +// @TODO: don't expose this structure +typedef struct +{ + int w,h,stride; + unsigned char *pixels; +} stbtt__bitmap; + +extern void stbtt_Rasterize(stbtt__bitmap *result, float flatness_in_pixels, stbtt_vertex *vertices, int num_verts, float scale_x, float scale_y, int x_off, int y_off, int invert, void *userdata); + +////////////////////////////////////////////////////////////////////////////// +// +// Finding the right font... +// +// You should really just solve this offline, keep your own tables +// of what font is what, and don't try to get it out of the .ttf file. +// That's because getting it out of the .ttf file is really hard, because +// the names in the file can appear in many possible encodings, in many +// possible languages, and e.g. if you need a case-insensitive comparison, +// the details of that depend on the encoding & language in a complex way +// (actually underspecified in truetype, but also gigantic). +// +// But you can use the provided functions in two possible ways: +// stbtt_FindMatchingFont() will use *case-sensitive* comparisons on +// unicode-encoded names to try to find the font you want; +// you can run this before calling stbtt_InitFont() +// +// stbtt_GetFontNameString() lets you get any of the various strings +// from the file yourself and do your own comparisons on them. +// You have to have called stbtt_InitFont() first. + + +extern int stbtt_FindMatchingFont(const unsigned char *fontdata, const char *name, int flags); +// returns the offset (not index) of the font that matches, or -1 if none +// if you use STBTT_MACSTYLE_DONTCARE, use a font name like "Arial Bold". +// if you use any other flag, use a font name like "Arial"; this checks +// the 'macStyle' header field; i don't know if fonts set this consistently +#define STBTT_MACSTYLE_DONTCARE 0 +#define STBTT_MACSTYLE_BOLD 1 +#define STBTT_MACSTYLE_ITALIC 2 +#define STBTT_MACSTYLE_UNDERSCORE 4 +#define STBTT_MACSTYLE_NONE 8 // <= not same as 0, this makes us check the bitfield is 0 + +extern int stbtt_CompareUTF8toUTF16_bigendian(const char *s1, int len1, const char *s2, int len2); +// returns 1/0 whether the first string interpreted as utf8 is identical to +// the second string interpreted as big-endian utf16... useful for strings from next func + +extern char *stbtt_GetFontNameString(const stbtt_fontinfo *font, int *length, int platformID, int encodingID, int languageID, int nameID); +// returns the string (which may be big-endian double byte, e.g. for unicode) +// and puts the length in bytes in *length. +// +// some of the values for the IDs are below; for more see the truetype spec: +// http://developer.apple.com/textfonts/TTRefMan/RM06/Chap6name.html +// http://www.microsoft.com/typography/otspec/name.htm + +enum { // platformID + STBTT_PLATFORM_ID_UNICODE =0, + STBTT_PLATFORM_ID_MAC =1, + STBTT_PLATFORM_ID_ISO =2, + STBTT_PLATFORM_ID_MICROSOFT =3 +}; + +enum { // encodingID for STBTT_PLATFORM_ID_UNICODE + STBTT_UNICODE_EID_UNICODE_1_0 =0, + STBTT_UNICODE_EID_UNICODE_1_1 =1, + STBTT_UNICODE_EID_ISO_10646 =2, + STBTT_UNICODE_EID_UNICODE_2_0_BMP=3, + STBTT_UNICODE_EID_UNICODE_2_0_FULL=4, +}; + +enum { // encodingID for STBTT_PLATFORM_ID_MICROSOFT + STBTT_MS_EID_SYMBOL =0, + STBTT_MS_EID_UNICODE_BMP =1, + STBTT_MS_EID_SHIFTJIS =2, + STBTT_MS_EID_UNICODE_FULL =10, +}; + +enum { // encodingID for STBTT_PLATFORM_ID_MAC; same as Script Manager codes + STBTT_MAC_EID_ROMAN =0, STBTT_MAC_EID_ARABIC =4, + STBTT_MAC_EID_JAPANESE =1, STBTT_MAC_EID_HEBREW =5, + STBTT_MAC_EID_CHINESE_TRAD =2, STBTT_MAC_EID_GREEK =6, + STBTT_MAC_EID_KOREAN =3, STBTT_MAC_EID_RUSSIAN =7, +}; + +enum { // languageID for STBTT_PLATFORM_ID_MICROSOFT; same as LCID... + // problematic because there are e.g. 16 english LCIDs and 16 arabic LCIDs + STBTT_MS_LANG_ENGLISH =0x0409, STBTT_MS_LANG_ITALIAN =0x0410, + STBTT_MS_LANG_CHINESE =0x0804, STBTT_MS_LANG_JAPANESE =0x0411, + STBTT_MS_LANG_DUTCH =0x0413, STBTT_MS_LANG_KOREAN =0x0412, + STBTT_MS_LANG_FRENCH =0x040c, STBTT_MS_LANG_RUSSIAN =0x0419, + STBTT_MS_LANG_GERMAN =0x0407, STBTT_MS_LANG_SPANISH =0x0409, + STBTT_MS_LANG_HEBREW =0x040d, STBTT_MS_LANG_SWEDISH =0x041D, +}; + +enum { // languageID for STBTT_PLATFORM_ID_MAC + STBTT_MAC_LANG_ENGLISH =0 , STBTT_MAC_LANG_JAPANESE =11, + STBTT_MAC_LANG_ARABIC =12, STBTT_MAC_LANG_KOREAN =23, + STBTT_MAC_LANG_DUTCH =4 , STBTT_MAC_LANG_RUSSIAN =32, + STBTT_MAC_LANG_FRENCH =1 , STBTT_MAC_LANG_SPANISH =6 , + STBTT_MAC_LANG_GERMAN =2 , STBTT_MAC_LANG_SWEDISH =5 , + STBTT_MAC_LANG_HEBREW =10, STBTT_MAC_LANG_CHINESE_SIMPLIFIED =33, + STBTT_MAC_LANG_ITALIAN =3 , STBTT_MAC_LANG_CHINESE_TRAD =19, +}; + +#ifdef __cplusplus +} +#endif + +#endif // __STB_INCLUDE_STB_TRUETYPE_H__ + +/////////////////////////////////////////////////////////////////////////////// +/////////////////////////////////////////////////////////////////////////////// +//// +//// IMPLEMENTATION +//// +//// + +#ifdef STB_TRUETYPE_IMPLEMENTATION + +////////////////////////////////////////////////////////////////////////// +// +// accessors to parse data from file +// + +// on platforms that don't allow misaligned reads, if we want to allow +// truetype fonts that aren't padded to alignment, define ALLOW_UNALIGNED_TRUETYPE + +#define ttBYTE(p) (* (stbtt_uint8 *) (p)) +#define ttCHAR(p) (* (stbtt_int8 *) (p)) +#define ttFixed(p) ttLONG(p) + +#if defined(STB_TRUETYPE_BIGENDIAN) && !defined(ALLOW_UNALIGNED_TRUETYPE) + + #define ttUSHORT(p) (* (stbtt_uint16 *) (p)) + #define ttSHORT(p) (* (stbtt_int16 *) (p)) + #define ttULONG(p) (* (stbtt_uint32 *) (p)) + #define ttLONG(p) (* (stbtt_int32 *) (p)) + +#else + + stbtt_uint16 ttUSHORT(const stbtt_uint8 *p) { return p[0]*256 + p[1]; } + stbtt_int16 ttSHORT(const stbtt_uint8 *p) { return p[0]*256 + p[1]; } + stbtt_uint32 ttULONG(const stbtt_uint8 *p) { return (p[0]<<24) + (p[1]<<16) + (p[2]<<8) + p[3]; } + stbtt_int32 ttLONG(const stbtt_uint8 *p) { return (p[0]<<24) + (p[1]<<16) + (p[2]<<8) + p[3]; } + +#endif + +#define stbtt_tag4(p,c0,c1,c2,c3) ((p)[0] == (c0) && (p)[1] == (c1) && (p)[2] == (c2) && (p)[3] == (c3)) +#define stbtt_tag(p,str) stbtt_tag4(p,str[0],str[1],str[2],str[3]) + +static int stbtt__isfont(const stbtt_uint8 *font) +{ + // check the version number + if (stbtt_tag(font, "1")) return 1; // TrueType 1 + if (stbtt_tag(font, "typ1")) return 1; // TrueType with type 1 font -- we don't support this! + if (stbtt_tag(font, "OTTO")) return 1; // OpenType with CFF + if (stbtt_tag4(font, 0,1,0,0)) return 1; // OpenType 1.0 + return 0; +} + +// @OPTIMIZE: binary search +static stbtt_uint32 stbtt__find_table(stbtt_uint8 *data, stbtt_uint32 fontstart, char *tag) +{ + stbtt_int32 num_tables = ttUSHORT(data+fontstart+4); + stbtt_uint32 tabledir = fontstart + 12; + stbtt_int32 i; + for (i=0; i < num_tables; ++i) { + stbtt_uint32 loc = tabledir + 16*i; + if (stbtt_tag(data+loc+0, tag)) + return ttULONG(data+loc+8); + } + return 0; +} + +int stbtt_GetFontOffsetForIndex(const unsigned char *font_collection, int index) +{ + // if it's just a font, there's only one valid index + if (stbtt__isfont(font_collection)) + return index == 0 ? 0 : -1; + + // check if it's a TTC + if (stbtt_tag(font_collection, "ttcf")) { + // version 1? + if (ttULONG(font_collection+4) == 0x00010000 || ttULONG(font_collection+4) == 0x00020000) { + stbtt_int32 n = ttLONG(font_collection+8); + if (index >= n) + return -1; + return ttULONG(font_collection+12+index*14); + } + } + return -1; +} + +int stbtt_InitFont(stbtt_fontinfo *info, const unsigned char *data2, int fontstart) +{ + stbtt_uint8 *data = (stbtt_uint8 *) data2; + stbtt_uint32 cmap, t; + stbtt_int32 i,numTables; + + info->data = data; + info->fontstart = fontstart; + + cmap = stbtt__find_table(data, fontstart, "cmap"); + info->loca = stbtt__find_table(data, fontstart, "loca"); + info->head = stbtt__find_table(data, fontstart, "head"); + info->glyf = stbtt__find_table(data, fontstart, "glyf"); + info->hhea = stbtt__find_table(data, fontstart, "hhea"); + info->hmtx = stbtt__find_table(data, fontstart, "hmtx"); + if (!cmap || !info->loca || !info->head || !info->glyf || !info->hhea || !info->hmtx) + return 0; + + t = stbtt__find_table(data, fontstart, "maxp"); + if (t) + info->numGlyphs = ttUSHORT(data+t+4); + else + info->numGlyphs = 0xffff; + + // find a cmap encoding table we understand *now* to avoid searching + // later. (todo: could make this installable) + // the same regardless of glyph. + numTables = ttUSHORT(data + cmap + 2); + info->index_map = 0; + for (i=0; i < numTables; ++i) { + stbtt_uint32 encoding_record = cmap + 4 + 8 * i; + // find an encoding we understand: + switch(ttUSHORT(data+encoding_record)) { + case STBTT_PLATFORM_ID_MICROSOFT: + switch (ttUSHORT(data+encoding_record+2)) { + case STBTT_MS_EID_UNICODE_BMP: + case STBTT_MS_EID_UNICODE_FULL: + // MS/Unicode + info->index_map = cmap + ttULONG(data+encoding_record+4); + break; + } + break; + } + } + if (info->index_map == 0) + return 0; + + info->indexToLocFormat = ttUSHORT(data+info->head + 50); + return 1; +} + +int stbtt_FindGlyphIndex(const stbtt_fontinfo *info, int unicode_codepoint) +{ + stbtt_uint8 *data = info->data; + stbtt_uint32 index_map = info->index_map; + + stbtt_uint16 format = ttUSHORT(data + index_map + 0); + if (format == 0) { // apple byte encoding + stbtt_int32 bytes = ttUSHORT(data + index_map + 2); + if (unicode_codepoint < bytes-6) + return ttBYTE(data + index_map + 6 + unicode_codepoint); + return 0; + } else if (format == 6) { + stbtt_uint32 first = ttUSHORT(data + index_map + 6); + stbtt_uint32 count = ttUSHORT(data + index_map + 8); + if ((stbtt_uint32) unicode_codepoint >= first && (stbtt_uint32) unicode_codepoint < first+count) + return ttUSHORT(data + index_map + 10 + (unicode_codepoint - first)*2); + return 0; + } else if (format == 2) { + STBTT_assert(0); // @TODO: high-byte mapping for japanese/chinese/korean + return 0; + } else if (format == 4) { // standard mapping for windows fonts: binary search collection of ranges + stbtt_uint16 segcount = ttUSHORT(data+index_map+6) >> 1; + stbtt_uint16 searchRange = ttUSHORT(data+index_map+8) >> 1; + stbtt_uint16 entrySelector = ttUSHORT(data+index_map+10); + stbtt_uint16 rangeShift = ttUSHORT(data+index_map+12) >> 1; + stbtt_uint16 item, offset, start, end; + + // do a binary search of the segments + stbtt_uint32 endCount = index_map + 14; + stbtt_uint32 search = endCount; + + if (unicode_codepoint > 0xffff) + return 0; + + // they lie from endCount .. endCount + segCount + // but searchRange is the nearest power of two, so... + if (unicode_codepoint >= ttUSHORT(data + search + rangeShift*2)) + search += rangeShift*2; + + // now decrement to bias correctly to find smallest + search -= 2; + while (entrySelector) { + stbtt_uint16 start, end; + searchRange >>= 1; + start = ttUSHORT(data + search + 2 + segcount*2 + 2); + end = ttUSHORT(data + search + 2); + start = ttUSHORT(data + search + searchRange*2 + segcount*2 + 2); + end = ttUSHORT(data + search + searchRange*2); + if (unicode_codepoint > end) + search += searchRange*2; + --entrySelector; + } + search += 2; + + item = (stbtt_uint16) ((search - endCount) >> 1); + + STBTT_assert(unicode_codepoint <= ttUSHORT(data + endCount + 2*item)); + start = ttUSHORT(data + index_map + 14 + segcount*2 + 2 + 2*item); + end = ttUSHORT(data + index_map + 14 + 2 + 2*item); + if (unicode_codepoint < start) + return 0; + + offset = ttUSHORT(data + index_map + 14 + segcount*6 + 2 + 2*item); + if (offset == 0) + return unicode_codepoint + ttSHORT(data + index_map + 14 + segcount*4 + 2 + 2*item); + + return ttUSHORT(data + offset + (unicode_codepoint-start)*2 + index_map + 14 + segcount*6 + 2 + 2*item); + } else if (format == 12) { + stbtt_uint16 ngroups = ttUSHORT(data+index_map+6); + stbtt_int32 low,high; + stbtt_uint16 g = 0; + low = 0; high = (stbtt_int32)ngroups; + // Binary search the right group. + while (low <= high) { + stbtt_int32 mid = low + ((high-low) >> 1); // rounds down, so low <= mid < high + stbtt_uint32 start_char = ttULONG(data+index_map+16+mid*12); + stbtt_uint32 end_char = ttULONG(data+index_map+16+mid*12+4); + if ((stbtt_uint32) unicode_codepoint < start_char) + high = mid-1; + else if ((stbtt_uint32) unicode_codepoint > end_char) + low = mid+1; + else { + stbtt_uint32 start_glyph = ttULONG(data+index_map+16+mid*12+8); + return start_glyph + unicode_codepoint-start_char; + } + } + return 0; // not found + } + // @TODO + STBTT_assert(0); + return 0; +} + +int stbtt_GetCodepointShape(const stbtt_fontinfo *info, int unicode_codepoint, stbtt_vertex **vertices) +{ + return stbtt_GetGlyphShape(info, stbtt_FindGlyphIndex(info, unicode_codepoint), vertices); +} + +static void stbtt_setvertex(stbtt_vertex *v, stbtt_uint8 type, stbtt_int16 x, stbtt_int16 y, stbtt_int16 cx, stbtt_int16 cy) +{ + v->type = type; + v->x = x; + v->y = y; + v->cx = cx; + v->cy = cy; +} + +static int stbtt__GetGlyfOffset(const stbtt_fontinfo *info, int glyph_index) +{ + int g1,g2; + + if (glyph_index >= info->numGlyphs) return -1; // glyph index out of range + if (info->indexToLocFormat >= 2) return -1; // unknown index->glyph map format + + if (info->indexToLocFormat == 0) { + g1 = info->glyf + ttUSHORT(info->data + info->loca + glyph_index * 2) * 2; + g2 = info->glyf + ttUSHORT(info->data + info->loca + glyph_index * 2 + 2) * 2; + } else { + g1 = info->glyf + ttULONG (info->data + info->loca + glyph_index * 4); + g2 = info->glyf + ttULONG (info->data + info->loca + glyph_index * 4 + 4); + } + + return g1==g2 ? -1 : g1; // if length is 0, return -1 +} + +int stbtt_GetGlyphBox(const stbtt_fontinfo *info, int glyph_index, int *x0, int *y0, int *x1, int *y1) +{ + int g = stbtt__GetGlyfOffset(info, glyph_index); + if (g < 0) return 0; + + if (x0) *x0 = ttSHORT(info->data + g + 2); + if (y0) *y0 = ttSHORT(info->data + g + 4); + if (x1) *x1 = ttSHORT(info->data + g + 6); + if (y1) *y1 = ttSHORT(info->data + g + 8); + return 1; +} + +int stbtt_GetCodepointBox(const stbtt_fontinfo *info, int codepoint, int *x0, int *y0, int *x1, int *y1) +{ + return stbtt_GetGlyphBox(info, stbtt_FindGlyphIndex(info,codepoint), x0,y0,x1,y1); +} + +int stbtt_GetGlyphShape(const stbtt_fontinfo *info, int glyph_index, stbtt_vertex **pvertices) +{ + stbtt_int16 numberOfContours; + stbtt_uint8 *endPtsOfContours; + stbtt_uint8 *data = info->data; + stbtt_vertex *vertices=0; + int num_vertices=0; + int g = stbtt__GetGlyfOffset(info, glyph_index); + + *pvertices = NULL; + + if (g < 0) return 0; + + numberOfContours = ttSHORT(data + g); + + if (numberOfContours > 0) { + stbtt_uint8 flags=0,flagcount; + stbtt_int32 ins, i,j=0,m,n, next_move, was_off=0, off; + stbtt_int16 x,y,cx,cy,sx,sy; + stbtt_uint8 *points; + endPtsOfContours = (data + g + 10); + ins = ttUSHORT(data + g + 10 + numberOfContours * 2); + points = data + g + 10 + numberOfContours * 2 + 2 + ins; + + n = 1+ttUSHORT(endPtsOfContours + numberOfContours*2-2); + + m = n + numberOfContours; // a loose bound on how many vertices we might need + vertices = (stbtt_vertex *) STBTT_malloc(m * sizeof(vertices[0]), info->userdata); + if (vertices == 0) + return 0; + + next_move = 0; + flagcount=0; + + // in first pass, we load uninterpreted data into the allocated array + // above, shifted to the end of the array so we won't overwrite it when + // we create our final data starting from the front + + off = m - n; // starting offset for uninterpreted data, regardless of how m ends up being calculated + + // first load flags + + for (i=0; i < n; ++i) { + if (flagcount == 0) { + flags = *points++; + if (flags & 8) + flagcount = *points++; + } else + --flagcount; + vertices[off+i].type = flags; + } + + // now load x coordinates + x=0; + for (i=0; i < n; ++i) { + flags = vertices[off+i].type; + if (flags & 2) { + stbtt_int16 dx = *points++; + x += (flags & 16) ? dx : -dx; // ??? + } else { + if (!(flags & 16)) { + x = x + (stbtt_int16) (points[0]*256 + points[1]); + points += 2; + } + } + vertices[off+i].x = x; + } + + // now load y coordinates + y=0; + for (i=0; i < n; ++i) { + flags = vertices[off+i].type; + if (flags & 4) { + stbtt_int16 dy = *points++; + y += (flags & 32) ? dy : -dy; // ??? + } else { + if (!(flags & 32)) { + y = y + (stbtt_int16) (points[0]*256 + points[1]); + points += 2; + } + } + vertices[off+i].y = y; + } + + // now convert them to our format + num_vertices=0; + sx = sy = cx = cy = 0; + for (i=0; i < n; ++i) { + flags = vertices[off+i].type; + x = (stbtt_int16) vertices[off+i].x; + y = (stbtt_int16) vertices[off+i].y; + if (next_move == i) { + // when we get to the end, we have to close the shape explicitly + if (i != 0) { + if (was_off) + stbtt_setvertex(&vertices[num_vertices++], STBTT_vcurve,sx,sy,cx,cy); + else + stbtt_setvertex(&vertices[num_vertices++], STBTT_vline,sx,sy,0,0); + } + + // now start the new one + stbtt_setvertex(&vertices[num_vertices++], STBTT_vmove,x,y,0,0); + next_move = 1 + ttUSHORT(endPtsOfContours+j*2); + ++j; + was_off = 0; + sx = x; + sy = y; + } else { + if (!(flags & 1)) { // if it's a curve + if (was_off) // two off-curve control points in a row means interpolate an on-curve midpoint + stbtt_setvertex(&vertices[num_vertices++], STBTT_vcurve, (cx+x)>>1, (cy+y)>>1, cx, cy); + cx = x; + cy = y; + was_off = 1; + } else { + if (was_off) + stbtt_setvertex(&vertices[num_vertices++], STBTT_vcurve, x,y, cx, cy); + else + stbtt_setvertex(&vertices[num_vertices++], STBTT_vline, x,y,0,0); + was_off = 0; + } + } + } + if (i != 0) { + if (was_off) + stbtt_setvertex(&vertices[num_vertices++], STBTT_vcurve,sx,sy,cx,cy); + else + stbtt_setvertex(&vertices[num_vertices++], STBTT_vline,sx,sy,0,0); + } + } else if (numberOfContours == -1) { + // Compound shapes. + int more = 1; + stbtt_uint8 *comp = data + g + 10; + num_vertices = 0; + vertices = 0; + while (more) { + stbtt_uint16 flags, gidx; + int comp_num_verts = 0, i; + stbtt_vertex *comp_verts = 0, *tmp = 0; + float mtx[6] = {1,0,0,1,0,0}, m, n; + + flags = ttSHORT(comp); comp+=2; + gidx = ttSHORT(comp); comp+=2; + + if (flags & 2) { // XY values + if (flags & 1) { // shorts + mtx[4] = ttSHORT(comp); comp+=2; + mtx[5] = ttSHORT(comp); comp+=2; + } else { + mtx[4] = ttCHAR(comp); comp+=1; + mtx[5] = ttCHAR(comp); comp+=1; + } + } + else { + // @TODO handle matching point + STBTT_assert(0); + } + if (flags & (1<<3)) { // WE_HAVE_A_SCALE + mtx[0] = mtx[3] = ttSHORT(comp)/16384.0f; comp+=2; + mtx[1] = mtx[2] = 0; + } else if (flags & (1<<6)) { // WE_HAVE_AN_X_AND_YSCALE + mtx[0] = ttSHORT(comp)/16384.0f; comp+=2; + mtx[1] = mtx[2] = 0; + mtx[3] = ttSHORT(comp)/16384.0f; comp+=2; + } else if (flags & (1<<7)) { // WE_HAVE_A_TWO_BY_TWO + mtx[0] = ttSHORT(comp)/16384.0f; comp+=2; + mtx[1] = ttSHORT(comp)/16384.0f; comp+=2; + mtx[2] = ttSHORT(comp)/16384.0f; comp+=2; + mtx[3] = ttSHORT(comp)/16384.0f; comp+=2; + } + + // Find transformation scales. + m = (float) sqrt(mtx[0]*mtx[0] + mtx[1]*mtx[1]); + n = (float) sqrt(mtx[2]*mtx[2] + mtx[3]*mtx[3]); + + // Get indexed glyph. + comp_num_verts = stbtt_GetGlyphShape(info, gidx, &comp_verts); + if (comp_num_verts > 0) { + // Transform vertices. + for (i = 0; i < comp_num_verts; ++i) { + stbtt_vertex* v = &comp_verts[i]; + stbtt_vertex_type x,y; + x=v->x; y=v->y; + v->x = (stbtt_vertex_type)(m * (mtx[0]*x + mtx[2]*y + mtx[4])); + v->y = (stbtt_vertex_type)(n * (mtx[1]*x + mtx[3]*y + mtx[5])); + x=v->cx; y=v->cy; + v->cx = (stbtt_vertex_type)(m * (mtx[0]*x + mtx[2]*y + mtx[4])); + v->cy = (stbtt_vertex_type)(n * (mtx[1]*x + mtx[3]*y + mtx[5])); + } + // Append vertices. + tmp = (stbtt_vertex*)STBTT_malloc((num_vertices+comp_num_verts)*sizeof(stbtt_vertex), info->userdata); + if (!tmp) { + if (vertices) STBTT_free(vertices, info->userdata); + if (comp_verts) STBTT_free(comp_verts, info->userdata); + return 0; + } + if (num_vertices > 0) memcpy(tmp, vertices, num_vertices*sizeof(stbtt_vertex)); + memcpy(tmp+num_vertices, comp_verts, comp_num_verts*sizeof(stbtt_vertex)); + if (vertices) STBTT_free(vertices, info->userdata); + vertices = tmp; + STBTT_free(comp_verts, info->userdata); + num_vertices += comp_num_verts; + } + // More components ? + more = flags & (1<<5); + } + } else if (numberOfContours < 0) { + // @TODO other compound variations? + STBTT_assert(0); + } else { + // numberOfCounters == 0, do nothing + } + + *pvertices = vertices; + return num_vertices; +} + +void stbtt_GetGlyphHMetrics(const stbtt_fontinfo *info, int glyph_index, int *advanceWidth, int *leftSideBearing) +{ + stbtt_uint16 numOfLongHorMetrics = ttUSHORT(info->data+info->hhea + 34); + if (glyph_index < numOfLongHorMetrics) { + if (advanceWidth) *advanceWidth = ttSHORT(info->data + info->hmtx + 4*glyph_index); + if (leftSideBearing) *leftSideBearing = ttSHORT(info->data + info->hmtx + 4*glyph_index + 2); + } else { + if (advanceWidth) *advanceWidth = ttSHORT(info->data + info->hmtx + 4*(numOfLongHorMetrics-1)); + if (leftSideBearing) *leftSideBearing = ttSHORT(info->data + info->hmtx + 4*numOfLongHorMetrics + 2*(glyph_index - numOfLongHorMetrics)); + } +} + +int stbtt_GetGlyphKernAdvance(const stbtt_fontinfo *info, int glyph1, int glyph2) +{ + return 0; +} + +int stbtt_GetCodepointKernAdvance(const stbtt_fontinfo *info, int ch1, int ch2) +{ + return 0; +} + +void stbtt_GetCodepointHMetrics(const stbtt_fontinfo *info, int codepoint, int *advanceWidth, int *leftSideBearing) +{ + stbtt_GetGlyphHMetrics(info, stbtt_FindGlyphIndex(info,codepoint), advanceWidth, leftSideBearing); +} + +void stbtt_GetFontVMetrics(const stbtt_fontinfo *info, int *ascent, int *descent, int *lineGap) +{ + if (ascent ) *ascent = ttSHORT(info->data+info->hhea + 4); + if (descent) *descent = ttSHORT(info->data+info->hhea + 6); + if (lineGap) *lineGap = ttSHORT(info->data+info->hhea + 8); +} + +float stbtt_ScaleForPixelHeight(const stbtt_fontinfo *info, float height) +{ + int fheight = ttSHORT(info->data + info->hhea + 4) - ttSHORT(info->data + info->hhea + 6); + return (float) height / fheight; +} + +void stbtt_FreeShape(const stbtt_fontinfo *info, stbtt_vertex *v) +{ + STBTT_free(v, info->userdata); +} + +////////////////////////////////////////////////////////////////////////////// +// +// antialiasing software rasterizer +// + +void stbtt_GetGlyphBitmapBox(const stbtt_fontinfo *font, int glyph, float scale_x, float scale_y, int *ix0, int *iy0, int *ix1, int *iy1) +{ + int x0,y0,x1,y1; + if (!stbtt_GetGlyphBox(font, glyph, &x0,&y0,&x1,&y1)) + x0=y0=x1=y1=0; // e.g. space character + // now move to integral bboxes (treating pixels as little squares, what pixels get touched)? + if (ix0) *ix0 = STBTT_ifloor(x0 * scale_x); + if (iy0) *iy0 = -STBTT_iceil (y1 * scale_y); + if (ix1) *ix1 = STBTT_iceil (x1 * scale_x); + if (iy1) *iy1 = -STBTT_ifloor(y0 * scale_y); +} + +void stbtt_GetCodepointBitmapBox(const stbtt_fontinfo *font, int codepoint, float scale_x, float scale_y, int *ix0, int *iy0, int *ix1, int *iy1) +{ + stbtt_GetGlyphBitmapBox(font, stbtt_FindGlyphIndex(font,codepoint), scale_x, scale_y, ix0,iy0,ix1,iy1); +} + +typedef struct stbtt__edge { + float x0,y0, x1,y1; + int invert; +} stbtt__edge; + +typedef struct stbtt__active_edge +{ + int x,dx; + float ey; + struct stbtt__active_edge *next; + int valid; +} stbtt__active_edge; + +#define FIXSHIFT 10 +#define FIX (1 << FIXSHIFT) +#define FIXMASK (FIX-1) + +static stbtt__active_edge *new_active(stbtt__edge *e, int off_x, float start_point, void *userdata) +{ + stbtt__active_edge *z = (stbtt__active_edge *) STBTT_malloc(sizeof(*z), userdata); // @TODO: make a pool of these!!! + float dxdy = (e->x1 - e->x0) / (e->y1 - e->y0); + STBTT_assert(e->y0 <= start_point); + if (!z) return z; + // round dx down to avoid going too far + if (dxdy < 0) + z->dx = -STBTT_ifloor(FIX * -dxdy); + else + z->dx = STBTT_ifloor(FIX * dxdy); + z->x = STBTT_ifloor(FIX * (e->x0 + dxdy * (start_point - e->y0))); + z->x -= off_x * FIX; + z->ey = e->y1; + z->next = 0; + z->valid = e->invert ? 1 : -1; + return z; +} + +// note: this routine clips fills that extend off the edges... ideally this +// wouldn't happen, but it could happen if the truetype glyph bounding boxes +// are wrong, or if the user supplies a too-small bitmap +static void stbtt__fill_active_edges(unsigned char *scanline, int len, stbtt__active_edge *e, int max_weight) +{ + // non-zero winding fill + int x0=0, w=0; + + while (e) { + if (w == 0) { + // if we're currently at zero, we need to record the edge start point + x0 = e->x; w += e->valid; + } else { + int x1 = e->x; w += e->valid; + // if we went to zero, we need to draw + if (w == 0) { + int i = x0 >> FIXSHIFT; + int j = x1 >> FIXSHIFT; + + if (i < len && j >= 0) { + if (i == j) { + // x0,x1 are the same pixel, so compute combined coverage + scanline[i] = scanline[i] + (stbtt_uint8) ((x1 - x0) * max_weight >> FIXSHIFT); + } else { + if (i >= 0) // add antialiasing for x0 + scanline[i] = scanline[i] + (stbtt_uint8) (((FIX - (x0 & FIXMASK)) * max_weight) >> FIXSHIFT); + else + i = -1; // clip + + if (j < len) // add antialiasing for x1 + scanline[j] = scanline[j] + (stbtt_uint8) (((x1 & FIXMASK) * max_weight) >> FIXSHIFT); + else + j = len; // clip + + for (++i; i < j; ++i) // fill pixels between x0 and x1 + scanline[i] = scanline[i] + (stbtt_uint8) max_weight; + } + } + } + } + + e = e->next; + } +} + +static void stbtt__rasterize_sorted_edges(stbtt__bitmap *result, stbtt__edge *e, int n, int vsubsample, int off_x, int off_y, void *userdata) +{ + stbtt__active_edge *active = NULL; + int y,j=0; + int max_weight = (255 / vsubsample); // weight per vertical scanline + int s; // vertical subsample index + unsigned char scanline_data[512], *scanline; + + if (result->w > 512) + scanline = (unsigned char *) STBTT_malloc(result->w, userdata); + else + scanline = scanline_data; + + y = off_y * vsubsample; + e[n].y0 = (off_y + result->h) * (float) vsubsample + 1; + + while (j < result->h) { + STBTT_memset(scanline, 0, result->w); + for (s=0; s < vsubsample; ++s) { + // find center of pixel for this scanline + float scan_y = y + 0.5f; + stbtt__active_edge **step = &active; + + // update all active edges; + // remove all active edges that terminate before the center of this scanline + while (*step) { + stbtt__active_edge * z = *step; + if (z->ey <= scan_y) { + *step = z->next; // delete from list + STBTT_assert(z->valid); + z->valid = 0; + STBTT_free(z, userdata); + } else { + z->x += z->dx; // advance to position for current scanline + step = &((*step)->next); // advance through list + } + } + + // resort the list if needed + for(;;) { + int changed=0; + step = &active; + while (*step && (*step)->next) { + if ((*step)->x > (*step)->next->x) { + stbtt__active_edge *t = *step; + stbtt__active_edge *q = t->next; + + t->next = q->next; + q->next = t; + *step = q; + changed = 1; + } + step = &(*step)->next; + } + if (!changed) break; + } + + // insert all edges that start before the center of this scanline -- omit ones that also end on this scanline + while (e->y0 <= scan_y) { + if (e->y1 > scan_y) { + stbtt__active_edge *z = new_active(e, off_x, scan_y, userdata); + // find insertion point + if (active == NULL) + active = z; + else if (z->x < active->x) { + // insert at front + z->next = active; + active = z; + } else { + // find thing to insert AFTER + stbtt__active_edge *p = active; + while (p->next && p->next->x < z->x) + p = p->next; + // at this point, p->next->x is NOT < z->x + z->next = p->next; + p->next = z; + } + } + ++e; + } + + // now process all active edges in XOR fashion + if (active) + stbtt__fill_active_edges(scanline, result->w, active, max_weight); + + ++y; + } + STBTT_memcpy(result->pixels + j * result->stride, scanline, result->w); + ++j; + } + + while (active) { + stbtt__active_edge *z = active; + active = active->next; + STBTT_free(z, userdata); + } + + if (scanline != scanline_data) + STBTT_free(scanline, userdata); +} + +static int stbtt__edge_compare(const void *p, const void *q) +{ + stbtt__edge *a = (stbtt__edge *) p; + stbtt__edge *b = (stbtt__edge *) q; + + if (a->y0 < b->y0) return -1; + if (a->y0 > b->y0) return 1; + return 0; +} + +typedef struct +{ + float x,y; +} stbtt__point; + +static void stbtt__rasterize(stbtt__bitmap *result, stbtt__point *pts, int *wcount, int windings, float scale_x, float scale_y, int off_x, int off_y, int invert, void *userdata) +{ + float y_scale_inv = invert ? -scale_y : scale_y; + stbtt__edge *e; + int n,i,j,k,m; + int vsubsample = result->h < 8 ? 15 : 5; + // vsubsample should divide 255 evenly; otherwise we won't reach full opacity + + // now we have to blow out the windings into explicit edge lists + n = 0; + for (i=0; i < windings; ++i) + n += wcount[i]; + + e = (stbtt__edge *) STBTT_malloc(sizeof(*e) * (n+1), userdata); // add an extra one as a sentinel + if (e == 0) return; + n = 0; + + m=0; + for (i=0; i < windings; ++i) { + stbtt__point *p = pts + m; + m += wcount[i]; + j = wcount[i]-1; + for (k=0; k < wcount[i]; j=k++) { + int a=k,b=j; + // skip the edge if horizontal + if (p[j].y == p[k].y) + continue; + // add edge from j to k to the list + e[n].invert = 0; + if (invert ? p[j].y > p[k].y : p[j].y < p[k].y) { + e[n].invert = 1; + a=j,b=k; + } + e[n].x0 = p[a].x * scale_x; + e[n].y0 = p[a].y * y_scale_inv * vsubsample; + e[n].x1 = p[b].x * scale_x; + e[n].y1 = p[b].y * y_scale_inv * vsubsample; + ++n; + } + } + + // now sort the edges by their highest point (should snap to integer, and then by x) + STBTT_sort(e, n, sizeof(e[0]), stbtt__edge_compare); + + // now, traverse the scanlines and find the intersections on each scanline, use xor winding rule + stbtt__rasterize_sorted_edges(result, e, n, vsubsample, off_x, off_y, userdata); + + STBTT_free(e, userdata); +} + +static void stbtt__add_point(stbtt__point *points, int n, float x, float y) +{ + if (!points) return; // during first pass, it's unallocated + points[n].x = x; + points[n].y = y; +} + +// tesselate until threshhold p is happy... @TODO warped to compensate for non-linear stretching +static int stbtt__tesselate_curve(stbtt__point *points, int *num_points, float x0, float y0, float x1, float y1, float x2, float y2, float objspace_flatness_squared, int n) +{ + // midpoint + float mx = (x0 + 2*x1 + x2)/4; + float my = (y0 + 2*y1 + y2)/4; + // versus directly drawn line + float dx = (x0+x2)/2 - mx; + float dy = (y0+y2)/2 - my; + if (n > 16) // 65536 segments on one curve better be enough! + return 1; + if (dx*dx+dy*dy > objspace_flatness_squared) { // half-pixel error allowed... need to be smaller if AA + stbtt__tesselate_curve(points, num_points, x0,y0, (x0+x1)/2.0f,(y0+y1)/2.0f, mx,my, objspace_flatness_squared,n+1); + stbtt__tesselate_curve(points, num_points, mx,my, (x1+x2)/2.0f,(y1+y2)/2.0f, x2,y2, objspace_flatness_squared,n+1); + } else { + stbtt__add_point(points, *num_points,x2,y2); + *num_points = *num_points+1; + } + return 1; +} + +// returns number of contours +stbtt__point *stbtt_FlattenCurves(stbtt_vertex *vertices, int num_verts, float objspace_flatness, int **contour_lengths, int *num_contours, void *userdata) +{ + stbtt__point *points=0; + int num_points=0; + + float objspace_flatness_squared = objspace_flatness * objspace_flatness; + int i,n=0,start=0, pass; + + // count how many "moves" there are to get the contour count + for (i=0; i < num_verts; ++i) + if (vertices[i].type == STBTT_vmove) + ++n; + + *num_contours = n; + if (n == 0) return 0; + + *contour_lengths = (int *) STBTT_malloc(sizeof(**contour_lengths) * n, userdata); + + if (*contour_lengths == 0) { + *num_contours = 0; + return 0; + } + + // make two passes through the points so we don't need to realloc + for (pass=0; pass < 2; ++pass) { + float x=0,y=0; + if (pass == 1) { + points = (stbtt__point *) STBTT_malloc(num_points * sizeof(points[0]), userdata); + if (points == NULL) goto error; + } + num_points = 0; + n= -1; + for (i=0; i < num_verts; ++i) { + switch (vertices[i].type) { + case STBTT_vmove: + // start the next contour + if (n >= 0) + (*contour_lengths)[n] = num_points - start; + ++n; + start = num_points; + + x = vertices[i].x, y = vertices[i].y; + stbtt__add_point(points, num_points++, x,y); + break; + case STBTT_vline: + x = vertices[i].x, y = vertices[i].y; + stbtt__add_point(points, num_points++, x, y); + break; + case STBTT_vcurve: + stbtt__tesselate_curve(points, &num_points, x,y, + vertices[i].cx, vertices[i].cy, + vertices[i].x, vertices[i].y, + objspace_flatness_squared, 0); + x = vertices[i].x, y = vertices[i].y; + break; + } + } + (*contour_lengths)[n] = num_points - start; + } + + return points; +error: + STBTT_free(points, userdata); + STBTT_free(*contour_lengths, userdata); + *contour_lengths = 0; + *num_contours = 0; + return NULL; +} + +void stbtt_Rasterize(stbtt__bitmap *result, float flatness_in_pixels, stbtt_vertex *vertices, int num_verts, float scale_x, float scale_y, int x_off, int y_off, int invert, void *userdata) +{ + float scale = scale_x > scale_y ? scale_y : scale_x; + int winding_count, *winding_lengths; + stbtt__point *windings = stbtt_FlattenCurves(vertices, num_verts, flatness_in_pixels / scale, &winding_lengths, &winding_count, userdata); + if (windings) { + stbtt__rasterize(result, windings, winding_lengths, winding_count, scale_x, scale_y, x_off, y_off, invert, userdata); + STBTT_free(winding_lengths, userdata); + STBTT_free(windings, userdata); + } +} + +void stbtt_FreeBitmap(unsigned char *bitmap, void *userdata) +{ + STBTT_free(bitmap, userdata); +} + +unsigned char *stbtt_GetGlyphBitmap(const stbtt_fontinfo *info, float scale_x, float scale_y, int glyph, int *width, int *height, int *xoff, int *yoff) +{ + int ix0,iy0,ix1,iy1; + stbtt__bitmap gbm; + stbtt_vertex *vertices; + int num_verts = stbtt_GetGlyphShape(info, glyph, &vertices); + + if (scale_x == 0) scale_x = scale_y; + if (scale_y == 0) { + if (scale_x == 0) return NULL; + scale_y = scale_x; + } + + stbtt_GetGlyphBitmapBox(info, glyph, scale_x, scale_y, &ix0,&iy0,&ix1,&iy1); + + // now we get the size + gbm.w = (ix1 - ix0); + gbm.h = (iy1 - iy0); + gbm.pixels = NULL; // in case we error + + if (width ) *width = gbm.w; + if (height) *height = gbm.h; + if (xoff ) *xoff = ix0; + if (yoff ) *yoff = iy0; + + if (gbm.w && gbm.h) { + gbm.pixels = (unsigned char *) STBTT_malloc(gbm.w * gbm.h, info->userdata); + if (gbm.pixels) { + gbm.stride = gbm.w; + + stbtt_Rasterize(&gbm, 0.35f, vertices, num_verts, scale_x, scale_y, ix0, iy0, 1, info->userdata); + } + } + STBTT_free(vertices, info->userdata); + return gbm.pixels; +} + +void stbtt_MakeGlyphBitmap(const stbtt_fontinfo *info, unsigned char *output, int out_w, int out_h, int out_stride, float scale_x, float scale_y, int glyph) +{ + int ix0,iy0; + stbtt_vertex *vertices; + int num_verts = stbtt_GetGlyphShape(info, glyph, &vertices); + stbtt__bitmap gbm; + + stbtt_GetGlyphBitmapBox(info, glyph, scale_x, scale_y, &ix0,&iy0,0,0); + gbm.pixels = output; + gbm.w = out_w; + gbm.h = out_h; + gbm.stride = out_stride; + + if (gbm.w && gbm.h) + stbtt_Rasterize(&gbm, 0.35f, vertices, num_verts, scale_x, scale_y, ix0,iy0, 1, info->userdata); + + STBTT_free(vertices, info->userdata); +} + +unsigned char *stbtt_GetCodepointBitmap(const stbtt_fontinfo *info, float scale_x, float scale_y, int codepoint, int *width, int *height, int *xoff, int *yoff) +{ + return stbtt_GetGlyphBitmap(info, scale_x, scale_y, stbtt_FindGlyphIndex(info,codepoint), width,height,xoff,yoff); +} + +void stbtt_MakeCodepointBitmap(const stbtt_fontinfo *info, unsigned char *output, int out_w, int out_h, int out_stride, float scale_x, float scale_y, int codepoint) +{ + stbtt_MakeGlyphBitmap(info, output, out_w, out_h, out_stride, scale_x, scale_y, stbtt_FindGlyphIndex(info,codepoint)); +} + +////////////////////////////////////////////////////////////////////////////// +// +// bitmap baking +// +// This is SUPER-SHITTY packing to keep source code small + +extern int stbtt_BakeFontBitmap(const unsigned char *data, int offset, // font location (use offset=0 for plain .ttf) + float pixel_height, // height of font in pixels + unsigned char *pixels, int pw, int ph, // bitmap to be filled in + int first_char, int num_chars, // characters to bake + stbtt_bakedchar *chardata) +{ + float scale; + int x,y,bottom_y, i; + stbtt_fontinfo f; + stbtt_InitFont(&f, data, offset); + STBTT_memset(pixels, 0, pw*ph); // background of 0 around pixels + x=y=1; + bottom_y = 1; + + scale = stbtt_ScaleForPixelHeight(&f, pixel_height); + + for (i=0; i < num_chars; ++i) { + int advance, lsb, x0,y0,x1,y1,gw,gh; + int g = stbtt_FindGlyphIndex(&f, first_char + i); + stbtt_GetGlyphHMetrics(&f, g, &advance, &lsb); + stbtt_GetGlyphBitmapBox(&f, g, scale,scale, &x0,&y0,&x1,&y1); + gw = x1-x0; + gh = y1-y0; + if (x + gw + 1 >= pw) + y = bottom_y, x = 1; // advance to next row + if (y + gh + 1 >= ph) // check if it fits vertically AFTER potentially moving to next row + return -i; + STBTT_assert(x+gw < pw); + STBTT_assert(y+gh < ph); + stbtt_MakeGlyphBitmap(&f, pixels+x+y*pw, gw,gh,pw, scale,scale, g); + chardata[i].x0 = (stbtt_int16) x; + chardata[i].y0 = (stbtt_int16) y; + chardata[i].x1 = (stbtt_int16) (x + gw); + chardata[i].y1 = (stbtt_int16) (y + gh); + chardata[i].xadvance = scale * advance; + chardata[i].xoff = (float) x0; + chardata[i].yoff = (float) y0; + x = x + gw + 2; + if (y+gh+2 > bottom_y) + bottom_y = y+gh+2; + } + return bottom_y; +} + +void stbtt_GetBakedQuad(stbtt_bakedchar *chardata, int pw, int ph, int char_index, float *xpos, float *ypos, stbtt_aligned_quad *q, int opengl_fillrule) +{ + float d3d_bias = opengl_fillrule ? 0 : -0.5f; + float ipw = 1.0f / pw, iph = 1.0f / ph; + stbtt_bakedchar *b = chardata + char_index; + int round_x = STBTT_ifloor((*xpos + b->xoff) + 0.5); + int round_y = STBTT_ifloor((*ypos + b->yoff) + 0.5); + + q->x0 = round_x + d3d_bias; + q->y0 = round_y + d3d_bias; + q->x1 = round_x + b->x1 - b->x0 + d3d_bias; + q->y1 = round_y + b->y1 - b->y0 + d3d_bias; + + q->s0 = b->x0 * ipw; + q->t0 = b->y0 * ipw; + q->s1 = b->x1 * iph; + q->t1 = b->y1 * iph; + + *xpos += b->xadvance; +} + +////////////////////////////////////////////////////////////////////////////// +// +// font name matching -- recommended not to use this +// + +// check if a utf8 string contains a prefix which is the utf16 string; if so return length of matching utf8 string +static stbtt_int32 stbtt__CompareUTF8toUTF16_bigendian_prefix(stbtt_uint8 *s1, stbtt_int32 len1, stbtt_uint8 *s2, stbtt_int32 len2) +{ + stbtt_int32 i=0; + + // convert utf16 to utf8 and compare the results while converting + while (len2) { + stbtt_uint16 ch = s2[0]*256 + s2[1]; + if (ch < 0x80) { + if (i >= len1) return -1; + if (s1[i++] != ch) return -1; + } else if (ch < 0x800) { + if (i+1 >= len1) return -1; + if (s1[i++] != 0xc0 + (ch >> 6)) return -1; + if (s1[i++] != 0x80 + (ch & 0x3f)) return -1; + } else if (ch >= 0xd800 && ch < 0xdc00) { + stbtt_uint32 c; + stbtt_uint16 ch2 = s2[2]*256 + s2[3]; + if (i+3 >= len1) return -1; + c = ((ch - 0xd800) << 10) + (ch2 - 0xdc00) + 0x10000; + if (s1[i++] != 0xf0 + (c >> 18)) return -1; + if (s1[i++] != 0x80 + ((c >> 12) & 0x3f)) return -1; + if (s1[i++] != 0x80 + ((c >> 6) & 0x3f)) return -1; + if (s1[i++] != 0x80 + ((c ) & 0x3f)) return -1; + s2 += 2; // plus another 2 below + len2 -= 2; + } else if (ch >= 0xdc00 && ch < 0xe000) { + return -1; + } else { + if (i+2 >= len1) return -1; + if (s1[i++] != 0xe0 + (ch >> 12)) return -1; + if (s1[i++] != 0x80 + ((ch >> 6) & 0x3f)) return -1; + if (s1[i++] != 0x80 + ((ch ) & 0x3f)) return -1; + } + s2 += 2; + len2 -= 2; + } + return i; +} + +int stbtt_CompareUTF8toUTF16_bigendian(const char *s1, int len1, const char *s2, int len2) +{ + return len1 == stbtt__CompareUTF8toUTF16_bigendian_prefix((stbtt_uint8*) s1, len1, (stbtt_uint8*) s2, len2); +} + +// returns results in whatever encoding you request... but note that 2-byte encodings +// will be BIG-ENDIAN... use stbtt_CompareUTF8toUTF16_bigendian() to compare +char *stbtt_GetFontNameString(const stbtt_fontinfo *font, int *length, int platformID, int encodingID, int languageID, int nameID) +{ + stbtt_int32 i,count,stringOffset; + stbtt_uint8 *fc = font->data; + stbtt_uint32 offset = font->fontstart; + stbtt_uint32 nm = stbtt__find_table(fc, offset, "name"); + if (!nm) return NULL; + + count = ttUSHORT(fc+nm+2); + stringOffset = nm + ttUSHORT(fc+nm+4); + for (i=0; i < count; ++i) { + stbtt_uint32 loc = nm + 6 + 12 * i; + if (platformID == ttUSHORT(fc+loc+0) && encodingID == ttUSHORT(fc+loc+2) + && languageID == ttUSHORT(fc+loc+4) && nameID == ttUSHORT(fc+loc+6)) { + *length = ttUSHORT(fc+loc+8); + return (char *) (fc+stringOffset+ttUSHORT(fc+loc+10)); + } + } + return NULL; +} + +static int stbtt__matchpair(stbtt_uint8 *fc, stbtt_uint32 nm, stbtt_uint8 *name, stbtt_int32 nlen, stbtt_int32 target_id, stbtt_int32 next_id) +{ + stbtt_int32 i; + stbtt_int32 count = ttUSHORT(fc+nm+2); + stbtt_int32 stringOffset = nm + ttUSHORT(fc+nm+4); + + for (i=0; i < count; ++i) { + stbtt_uint32 loc = nm + 6 + 12 * i; + stbtt_int32 id = ttUSHORT(fc+loc+6); + if (id == target_id) { + // find the encoding + stbtt_int32 platform = ttUSHORT(fc+loc+0), encoding = ttUSHORT(fc+loc+2), language = ttUSHORT(fc+loc+4); + + // is this a Unicode encoding? + if (platform == 0 || (platform == 3 && encoding == 1) || (platform == 3 && encoding == 10)) { + stbtt_int32 slen = ttUSHORT(fc+loc+8), off = ttUSHORT(fc+loc+10); + + // check if there's a prefix match + stbtt_int32 matchlen = stbtt__CompareUTF8toUTF16_bigendian_prefix(name, nlen, fc+stringOffset+off,slen); + if (matchlen >= 0) { + // check for target_id+1 immediately following, with same encoding & language + if (i+1 < count && ttUSHORT(fc+loc+12+6) == next_id && ttUSHORT(fc+loc+12) == platform && ttUSHORT(fc+loc+12+2) == encoding && ttUSHORT(fc+loc+12+4) == language) { + stbtt_int32 slen = ttUSHORT(fc+loc+12+8), off = ttUSHORT(fc+loc+12+10); + if (slen == 0) { + if (matchlen == nlen) + return 1; + } else if (matchlen < nlen && name[matchlen] == ' ') { + ++matchlen; + if (stbtt_CompareUTF8toUTF16_bigendian((char*) (name+matchlen), nlen-matchlen, (char*)(fc+stringOffset+off),slen)) + return 1; + } + } else { + // if nothing immediately following + if (matchlen == nlen) + return 1; + } + } + } + + // @TODO handle other encodings + } + } + return 0; +} + +static int stbtt__matches(stbtt_uint8 *fc, stbtt_uint32 offset, stbtt_uint8 *name, stbtt_int32 flags) +{ + stbtt_int32 nlen = STBTT_strlen((char *) name); + stbtt_uint32 nm,hd; + if (!stbtt__isfont(fc+offset)) return 0; + + // check italics/bold/underline flags in macStyle... + if (flags) { + hd = stbtt__find_table(fc, offset, "head"); + if ((ttUSHORT(fc+hd+44) & 7) != (flags & 7)) return 0; + } + + nm = stbtt__find_table(fc, offset, "name"); + if (!nm) return 0; + + if (flags) { + // if we checked the macStyle flags, then just check the family and ignore the subfamily + if (stbtt__matchpair(fc, nm, name, nlen, 16, -1)) return 1; + if (stbtt__matchpair(fc, nm, name, nlen, 1, -1)) return 1; + if (stbtt__matchpair(fc, nm, name, nlen, 3, -1)) return 1; + } else { + if (stbtt__matchpair(fc, nm, name, nlen, 16, 17)) return 1; + if (stbtt__matchpair(fc, nm, name, nlen, 1, 2)) return 1; + if (stbtt__matchpair(fc, nm, name, nlen, 3, -1)) return 1; + } + + return 0; +} + +int stbtt_FindMatchingFont(const unsigned char *font_collection, const char *name_utf8, stbtt_int32 flags) +{ + stbtt_int32 i; + for (i=0;;++i) { + stbtt_int32 off = stbtt_GetFontOffsetForIndex(font_collection, i); + if (off < 0) return off; + if (stbtt__matches((stbtt_uint8 *) font_collection, off, (stbtt_uint8*) name_utf8, flags)) + return off; + } +} + +#endif // STB_TRUETYPE_IMPLEMENTATION diff --git a/strlib.c b/strlib.c new file mode 100644 index 0000000..91fdfc8 --- /dev/null +++ b/strlib.c @@ -0,0 +1,43 @@ +#include "strlib.h" + +char *strcpytrim(char *d, // destination + char *s, // source + int mode, + char *delim + ) { + char *o = d; // save orig + char *e = 0; // end space ptr. + char dtab[256] = {0}; + if (!s || !d) return 0; + + if (!delim) delim = " \t\n\f"; + while (*delim) + dtab[*delim++] = 1; + + while ( (*d = *s++) != 0 ) { + if (!dtab[*d]) { // Not a match char + e = 0; // Reset end pointer + } else { + if (!e) e = d; // Found first match. + + if ( mode == STRLIB_MODE_ALL || ((mode != STRLIB_MODE_RIGHT) && (d == o)) ) + continue; + } + d++; + } + if (mode != STRLIB_MODE_LEFT && e) { // for everything but trim_left, delete trailing matches. + *e = 0; + } + return o; +} + +// perhaps these could be inlined in strlib.h +char *strtriml(char *d, char *s) { return strcpytrim(d, s, STRLIB_MODE_LEFT, 0); } +char *strtrimr(char *d, char *s) { return strcpytrim(d, s, STRLIB_MODE_RIGHT, 0); } +char *strtrim(char *d, char *s) { return strcpytrim(d, s, STRLIB_MODE_BOTH, 0); } +char *strstrlibkill(char *d, char *s) { return strcpytrim(d, s, STRLIB_MODE_ALL, 0); } + +char *triml(char *s) { return strcpytrim(s, s, STRLIB_MODE_LEFT, 0); } +char *trimr(char *s) { return strcpytrim(s, s, STRLIB_MODE_RIGHT, 0); } +char *trim(char *s) { return strcpytrim(s, s, STRLIB_MODE_BOTH, 0); } +char *strlibkill(char *s) { return strcpytrim(s, s, STRLIB_MODE_ALL, 0); } \ No newline at end of file diff --git a/strlib.h b/strlib.h new file mode 100644 index 0000000..e3ed963 --- /dev/null +++ b/strlib.h @@ -0,0 +1,24 @@ +#ifndef STRLIB_H_ +enum strtrim_mode_t { + STRLIB_MODE_ALL = 0, + STRLIB_MODE_RIGHT = 0x01, + STRLIB_MODE_LEFT = 0x02, + STRLIB_MODE_BOTH = 0x03 +}; + +char *strcpytrim(char *d, // destination + char *s, // source + int mode, + char *delim + ); + +char *strtriml(char *d, char *s); +char *strtrimr(char *d, char *s); +char *strtrim(char *d, char *s); +char *strstrlibkill(char *d, char *s); + +char *triml(char *s); +char *trimr(char *s); +char *trim(char *s); +char *strlibkill(char *s); +#endif diff --git a/sys.c b/sys.c new file mode 100644 index 0000000..d1c68d0 --- /dev/null +++ b/sys.c @@ -0,0 +1,359 @@ +#include +#include +#include +#include "sys.h" +#include "types.h" +#include "everdrive.h" +#include "errors.h" +#include "usb.h" + + +u32 asm_date; + +Options_st options; + +u32 native_tv_mode; + +#define CIC_6101 1 +#define CIC_6102 2 +#define CIC_6103 3 +#define CIC_6104 4 +#define CIC_6105 5 +#define CIC_6106 6 + +u16 strcon(u8 *str1, u8 *str2, u8 *dst, u16 max_len) { + + u16 len = 0; + max_len -= 1; + + while (*str1 != 0 && len < max_len) { + *dst++ = *str1++; + len++; + } + + while (*str2 != 0 && len < max_len) { + *dst++ = *str2++; + len++; + } + *dst++ = 0; + return len; + +} + + +void dma_read_s(void * ram_address, unsigned long pi_address, unsigned long len) { + + u32 buff[256]; + + u32 *bptr; + + u32 *rptr = (u32 *) ram_address; + + // if(len==32768) + // rptr = (u32 *) 0x803F7988; + + u16 i; + + //u16 blen = 512; + //if (len < 512)blen = len; + //*(volatile u32*) 0x1FC007FC = 0x08; + + IO_WRITE(PI_STATUS_REG, 3); + while (len) { + dma_read(buff, pi_address, 512); + while ((IO_READ(PI_STATUS_REG) & 3) != 0); + //while ((*((volatile u32*) PI_STATUS_REG) & 0x02) != 1); + data_cache_hit_invalidate(buff, 512); + bptr = buff; + for (i = 0; i < 512 && i < len; i += 4)*rptr++ = *bptr++; + len = len < 512 ? 0 : len - 512; + pi_address += 512; + } +} + +void dma_write_s(void * ram_address, unsigned long pi_address, unsigned long len) { + + + //if(len==32768) + //ram_address = (u32 *) 0x803F7988; + + + data_cache_hit_writeback(ram_address, len); + dma_write(ram_address, pi_address, len); + + + +} + + +/* +void showError(char *str, u32 code) { + + + console_printf("%s%u\n", str, code); + joyWait(); + + +} + */ +void sleep(u32 ms) { + + u32 current_ms = get_ticks_ms(); + + while (get_ticks_ms() - current_ms < ms); + +} + +void dma_read_sram(void *dest, u32 offset, u32 size) { + /* + PI_DMAWait(); + + IO_WRITE(PI_STATUS_REG, 0x03); + IO_WRITE(PI_DRAM_ADDR_REG, K1_TO_PHYS(dest)); + IO_WRITE(PI_CART_ADDR_REG, (0xA8000000 + offset)); + // data_cache_invalidate_all(); + IO_WRITE(PI_WR_LEN_REG, (size - 1)); + */ + /* 0xA8000000 + * 0xb0000000 + * 0x4000000 + * */ + dma_read_s(dest, 0xA8000000 + offset, size); + //data_cache_invalidate(dest,size); + +} + +void dma_write_sram(void* src, u32 offset, u32 size) { + /* + PI_DMAWait(); + + IO_WRITE(PI_STATUS_REG, 0x02); + IO_WRITE(PI_DRAM_ADDR_REG, K1_TO_PHYS(src)); + IO_WRITE(PI_CART_ADDR_REG, (0xA8000000 + offset)); + // data_cache_invalidate_all(); + IO_WRITE(PI_RD_LEN_REG, (size - 1)); + */ + dma_write_s(src, 0xA8000000 + offset, size); + +} + +#define DO1(buf) crc = crc_table[((int)crc ^ (*buf++)) & 0xff] ^ (crc >> 8); +#define DO2(buf) DO1(buf); DO1(buf); +#define DO4(buf) DO2(buf); DO2(buf); +#define DO8(buf) DO4(buf); DO4(buf); + +unsigned int CRC_Calculate(unsigned int crc, unsigned char* buf, unsigned int len) { + static unsigned int crc_table[256]; + static int make_crc_table = 1; + + if (make_crc_table) { + unsigned int c, n; + int k; + unsigned int poly; + const unsigned char p[] = {0, 1, 2, 4, 5, 7, 8, 10, 11, 12, 16, 22, 23, 26}; + + /* make exclusive-or pattern from polynomial (0xedb88320L) */ + poly = 0L; + for (n = 0; n < sizeof (p) / sizeof (unsigned char); n++) + poly |= 1L << (31 - p[n]); + + for (n = 0; n < 256; n++) { + c = n; + for (k = 0; k < 8; k++) + c = c & 1 ? poly ^ (c >> 1) : c >> 1; + crc_table[n] = c; + } + make_crc_table = 0; + } + + if (buf == (void*) 0) return 0L; + + crc = crc ^ 0xffffffffL; + while (len >= 8) { + DO8(buf); + len -= 8; + } + if (len) + do { + DO1(buf); + } while (--len); + + return crc ^ 0xffffffffL; +} + + + +u32 ii; +volatile u32 *pt; +void clean(); + +#define MEM32(addr) *((volatile u32 *)addr) + + +u8 str_buff[128]; + +u8 STR_intToDecString(u32 val, u8 *str) { + + int len; + + if (val < 10)len = 1; + else + if (val < 100)len = 2; + else + if (val < 1000)len = 3; + else + if (val < 10000)len = 4; + else + if (val < 100000)len = 5; + else + if (val < 1000000)len = 6; + else + if (val < 10000000)len = 7; + else + if (val < 100000000)len = 8; + else + if (val < 1000000000)len = 9; + else len = 10; + + str += len; + str[0] = 0; + if (val == 0)*--str = '0'; + while (val) { + + *--str = '0' + val % 10; + val /= 10; + } + + + return len; +} + +void STR_intToDecStringMin(u32 val, u8 *str, u8 min_size) { + + int len; + u8 i; + + if (val < 10)len = 1; + else + if (val < 100)len = 2; + else + if (val < 1000)len = 3; + else + if (val < 10000)len = 4; + else + if (val < 100000)len = 5; + else + if (val < 1000000)len = 6; + else + if (val < 10000000)len = 7; + else + if (val < 100000000)len = 8; + else + if (val < 1000000000)len = 9; + else len = 10; + + if (len < min_size) { + + i = min_size - len; + while (i--)str[i] = '0'; + len = min_size; + } + str += len; + str[0] = 0; + if (val == 0)*--str = '0'; + while (val) { + + *--str = '0' + val % 10; + val /= 10; + } +} + + +u8 streq(u8 *str1, u8 *str2) { + + u8 s1; + u8 s2; + + for (;;) { + s1 = *str1++; + s2 = *str2++; + if (s1 >= 'a' && s1 <= 'z')s1 -= 0x20; + if (s2 >= 'a' && s2 <= 'z')s2 -= 0x20; + + if (s1 != s2) return 0; + + if (*str1 == 0 && *str2 == 0)return 1; + } +} + +u8 streql(u8 *str1, u8 *str2, u8 len) { + + u8 s1; + u8 s2; + while (len--) { + + s1 = *str1++; + s2 = *str2++; + if (s1 >= 'a' && s1 <= 'z')s1 -= 0x20; + if (s2 >= 'a' && s2 <= 'z')s2 -= 0x20; + + if (s1 != s2) return 0; + } + + return 1; +} + +u16 strContain(u8 *target, u8 *str) { + + u16 targ_len = slen(target); + u16 eq_len; + + + for (eq_len = 0; eq_len < targ_len;) { + + if (*str == 0)return 0; + if (*str++ == target[eq_len]) { + eq_len++; + } else { + eq_len = 0; + } + } + + if (eq_len != targ_len)return 0; + return 1; + +} + +u8 slen(u8 *str) { + + u8 len = 0; + while (*str++)len++; + return len; +} + +u8 scopy(u8 *src, u8 *dst) { + + u8 len = 0; + while (*src != 0) { + *dst++ = *src++; + len++; + } + *dst = 0; + return len; +} + +void strhicase(u8 *str, u8 len) { + + if (len) { + while (len--) { + if (*str >= 'a' && *str <= 'z')*str -= 0x20; + str++; + } + } else { + while (*str != 0) { + if (*str >= 'a' && *str <= 'z')*str -= 0x20; + str++; + } + } + +} diff --git a/sys.h b/sys.h new file mode 100644 index 0000000..892313c --- /dev/null +++ b/sys.h @@ -0,0 +1,61 @@ +/* + * File: tools.h + * Author: KRIK + * + * Created on 16 Àïðåëü 2011 ã., 2:30 + */ + +#ifndef _SYS_H +#define _SYS_H +#include "types.h" +#include "utils.h" +#include + + +void dma_read_s(void * ram_address, unsigned long pi_address, unsigned long len); +void dma_write_s(void * ram_address, unsigned long pi_address, unsigned long len); + + +void sleep(u32 ms); +void dma_write_sram(void* src, u32 offset, u32 size); +void dma_read_sram(void *dest, u32 offset, u32 size); +u8 getSaveType(); +u8 getCicType(u8 bios_cic); + +u16 strcon(u8 *str1, u8 *str2, u8 *dst, u16 max_len); +u8 slen(u8 *str); +u8 scopy(u8 *src, u8 *dst); + +u16 strContain(u8 *target, u8 *str); + +typedef struct SP_regs_s { + u32 mem_addr; + u32 dram_addr; + u32 rd_len; + u32 wr_len; + u32 status; +} _SP_regs_s; + +#define SP_PC *((volatile u32 *)0xA4080000) +#define SP_IBIST_REG *((volatile u32 *)0xA4080004) + +static volatile struct AI_regs_s * const AI_regs = (struct AI_regs_s *) 0xa4500000; +static volatile struct MI_regs_s * const MI_regs = (struct MI_regs_s *) 0xa4300000; +static volatile struct VI_regs_s * const VI_regs = (struct VI_regs_s *) 0xa4400000; +static volatile struct PI_regs_s * const PI_regs = (struct PI_regs_s *) 0xa4600000; +static volatile struct SP_regs_s * const SP_regs = (struct SP_regs_s *) 0xA4040000; + +extern u32 native_tv_mode; + +typedef struct { + u16 sd_speed; + u16 font_size; + u16 tv_mode; + u8 wall[256]; +} Options_st; + + +extern Options_st options; +extern u32 asm_date; +#endif /* _TOOLS_H */ + diff --git a/types.h b/types.h new file mode 100644 index 0000000..625c877 --- /dev/null +++ b/types.h @@ -0,0 +1,36 @@ +/* + * File: types.h + * Author: KRIK + * + * Created on 16 Àïðåëü 2011 ã., 2:24 + */ + +#include + +#ifndef _TYPES_H +#define _TYPES_H + +#define u8 unsigned char +#define u16 unsigned short +#define u32 unsigned long +#define u64 unsigned long long + +#define vu8 volatile unsigned char +#define vu16 volatile unsigned short +#define vu32 volatile unsigned long +#define vu64 volatile unsigned long long + +#define s8 signed char +#define s16 short +#define s32 long +#define s64 long long + + +typedef volatile uint64_t sim_vu64; +typedef volatile uint64_t sim_vu64; +typedef unsigned int sim_u32; +typedef uint64_t sim_u64; + + +#endif /* _TYPES_H */ + diff --git a/upload.sh b/upload.sh new file mode 100755 index 0000000..d4fba53 --- /dev/null +++ b/upload.sh @@ -0,0 +1,15 @@ +#! /bin/bash +sudo mount /dev/sdb1 /mnt +file=/mnt/ED64/OS64.v64 +if [ -e $file ] +then + echo -e "File $file exists - mount ok" + echo -e "copy..." + sudo cp OS64.v64 /mnt/ED64/ + sudo cp ALT64.INI /mnt/ED64/ + echo -e "umounting..." + sudo umount /mnt + echo -e "done..." +else + echo -e "File $file doesnt exists - sdcard problem?" +fi diff --git a/usb.c b/usb.c new file mode 100644 index 0000000..da43d86 --- /dev/null +++ b/usb.c @@ -0,0 +1,180 @@ + + +#include "everdrive.h" +#include "sys.h" +#include "types.h" +#include +#include "rom.h" + +u8 cmdTest(); +u8 cmdFill(); +u8 cmdReadRom(); +u8 cmdWriteRom(); + +u64 usb_buff[128]; +u8 *usb_buff8; // = (u8 *) usb_buff; + + +#define PI_BSD_DOM1_LAT_REG (PI_BASE_REG+0x14) + +/* PI dom1 pulse width (R/W): [7:0] domain 1 device R/W strobe pulse width */ +#define PI_BSD_DOM1_PWD_REG (PI_BASE_REG+0x18) + +/* PI dom1 page size (R/W): [3:0] domain 1 device page size */ +#define PI_BSD_DOM1_PGS_REG (PI_BASE_REG+0x1C) /* page size */ + +/* PI dom1 release (R/W): [1:0] domain 1 device R/W release duration */ +#define PI_BSD_DOM1_RLS_REG (PI_BASE_REG+0x20) +/* PI dom2 latency (R/W): [7:0] domain 2 device latency */ +#define PI_BSD_DOM2_LAT_REG (PI_BASE_REG+0x24) /* Domain 2 latency */ + +/* PI dom2 pulse width (R/W): [7:0] domain 2 device R/W strobe pulse width */ +#define PI_BSD_DOM2_PWD_REG (PI_BASE_REG+0x28) /* pulse width */ + +/* PI dom2 page size (R/W): [3:0] domain 2 device page size */ +#define PI_BSD_DOM2_PGS_REG (PI_BASE_REG+0x2C) /* page size */ + +/* PI dom2 release (R/W): [1:0] domain 2 device R/W release duration */ +#define PI_BSD_DOM2_RLS_REG (PI_BASE_REG+0x30) /* release duration */ + +#define PHYS_TO_K1(x) ((u32)(x)|0xA0000000) /* physical to kseg1 */ +#define IO_WRITE(addr,data) (*(volatile u32*)PHYS_TO_K1(addr)=(u32)(data)) +#define PI_BASE_REG 0x04600000 + +extern u8 system_cic; + +u8 usbListener() { + + volatile u16 resp; + volatile u8 cmd; + usb_buff8 = (u8 *) usb_buff; + + + if (evd_fifoRxf())return 0; + + resp = evd_fifoRd(usb_buff, 1); + + if (resp != 0) return 1; + + if (usb_buff8[0] != 'C' || usb_buff8[1] != 'M' || usb_buff8[2] != 'D')return 2; + + cmd = usb_buff8[3]; + + + switch (cmd) { + + case 'R': + resp = cmdReadRom(); + if (resp)return 10; + break; + case 'W': + resp = cmdWriteRom(); + if (resp)return 11; + break; + case 'T': + resp = cmdTest(); + if (resp)return 12; + break; + case 'F': + resp = cmdFill(); + if (resp)return 13; + break; + case 'S': + //IO_WRITE(PI_BSD_DOM1_PGS_REG, 0x0c); + //IO_WRITE(PI_BSD_DOM1_PGS_REG, 0x80); + //evdSetESaveType(SAVE_TYPE_EEP16k); + system_cic = CIC_6102; + evd_lockRegs(); + IO_WRITE(PI_STATUS_REG, 3); + sleep(2); + pif_boot(); + break; + + } + + + return 0; +} + +u8 cmdTest() { + + u16 resp; + usb_buff8[3] = 'k'; + resp = evd_fifoWr(usb_buff, 1); + if (resp)return 1; + + return 0; + +} + +u8 cmdFill() { + + u16 resp; + u32 i; + //console_printf("fill...\n"); + + for (i = 0; i < 512; i++) { + usb_buff8[i] = 0; + } + //console_printf("buff prepared\n"); + romFill(0, 0x200000, 0); + //console_printf("fill done\n"); + + usb_buff8[3] = 'k'; + resp = evd_fifoWr(usb_buff, 1); + + if (resp)return 1; + //console_printf("resp sent ok\n"); + + return 0; +} + +u8 cmdReadRom() { + + u16 resp; + u16 ptr; + u16 len; + u32 addr; + ptr = 4; + + + addr = usb_buff8[ptr++]; + addr <<= 8; + addr |= usb_buff8[ptr++]; + addr *= 2048; + + len = usb_buff8[ptr++]; + len <<= 8; + len |= usb_buff8[ptr++]; + + + resp = evd_fifoWrFromCart(addr, len); + if (resp)return 1; + + + return 0; +} + +u8 cmdWriteRom() { + + u16 resp; + u16 ptr; + u16 len; + u32 addr; + ptr = 4; + + addr = usb_buff8[ptr++]; + addr <<= 8; + addr |= usb_buff8[ptr++]; + addr *= 2048; + + len = usb_buff8[ptr++]; + len <<= 8; + len |= usb_buff8[ptr++]; + + resp = evd_fifoRdToCart(addr, len); + if (resp)return 1; + + return 0; + +} diff --git a/usb.h b/usb.h new file mode 100644 index 0000000..3683941 --- /dev/null +++ b/usb.h @@ -0,0 +1,16 @@ +/* + * File: fifo.h + * Author: KRIK + * + * Created on 22 Àïðåëü 2011 ã., 20:46 + */ + +#ifndef _FIFO_H +#define _FIFO_H + +#include "types.h" +u8 usbListener(); + + +#endif /* _FIFO_H */ + diff --git a/utils.c b/utils.c new file mode 100644 index 0000000..a5e7ec8 --- /dev/null +++ b/utils.c @@ -0,0 +1,1640 @@ +#include +#include +#include +#include +#include +#include +#include +#include "everdrive.h" +#include "sys.h" + +#include "types.h" +#include "utils.h" +#include "sram.h" + + + + +#define STBI_HEADER_FILE_ONLY +#include "stb_image.c" + +extern short int gCheats; /* 0 = off, 1 = select, 2 = all */ +extern short int force_tv; +extern short int boot_country; + +struct gscEntry { + char *description; + char *gscodes; + u16 count; + u16 state; + u16 mask; + u16 value; +}; +typedef struct gscEntry gscEntry_t; + +extern gscEntry_t gGSCodes[]; + + + +static u8 __attribute__((aligned(16))) dmaBuf[128*1024]; +static volatile struct _PI_regs_s * const _PI_regs = (struct _PI_regs_s *)0xa4600000; + +int is_valid_rom(unsigned char *buffer) +{ + /* Test if rom is a native .z64 image with header 0x80371240. [ABCD] */ + if((buffer[0]==0x80)&&(buffer[1]==0x37)&&(buffer[2]==0x12)&&(buffer[3]==0x40)) + return 0; + /* Test if rom is a byteswapped .v64 image with header 0x37804012. [BADC] */ + else if((buffer[0]==0x37)&&(buffer[1]==0x80)&&(buffer[2]==0x40)&&(buffer[3]==0x12)) + return 1; + /* Test if rom is a wordswapped .n64 image with header 0x40123780. [DCBA] */ + else if((buffer[0]==0x40)&&(buffer[1]==0x12)&&(buffer[2]==0x37)&&(buffer[3]==0x80)) + return 2; + else + return 0; +} + +void swap_header(unsigned char* header, int loadlength) +{ + unsigned char temp; + int i; + + /* Btyeswap if .v64 image. */ + if( header[0]==0x37) + { + // *imagetype = V64IMAGE; + for (i = 0; i < loadlength; i+=2) + { + temp= header[i]; + header[i]= header[i+1]; + header[i+1]=temp; + } + } + /* Wordswap if .n64 image. */ + else if( header[0]==0x40) + { + // *imagetype = N64IMAGE; + for (i = 0; i < loadlength; i+=4) + { + temp= header[i]; + header[i]= header[i+3]; + header[i+3]=temp; + temp= header[i+1]; + header[i+1]= header[i+2]; + header[i+2]=temp; + } + } + // else + // *imagetype = Z64IMAGE; +} + + + + +u8 getCicType(u8 bios_cic) { + + u8 cic_buff[2048]; + volatile u8 cic_chip; + volatile u32 val; // = *(u32 *) 0xB0000170; + if (bios_cic) { + evd_setCfgBit(ED_CFG_SDRAM_ON, 0); + sleep(10); + val = *(u32 *) 0xB0000170; + dma_read_s(cic_buff, 0xB0000040, 1024); + cic_chip = get_cic(cic_buff); + evd_setCfgBit(ED_CFG_SDRAM_ON, 1); + sleep(10); + + } else { + val = *(u32 *) 0xB0000170; + dma_read_s(cic_buff, 0xB0000040, 1024); + cic_chip = get_cic(cic_buff); + + } + return cic_chip; +} + + + +int get_cic(unsigned char *buffer) +{ + unsigned int crc; + // figure out the CIC + crc = CRC_Calculate(0, buffer, 1000); + switch(crc) + { + case 0x303faac9: + case 0xf0da3d50: + return 1; + case 0xf3106101: + return 2; + case 0xe7cd9d51: + return 3; + case 0x7ae65c9: + return 5; + case 0x86015f8f: + return 6; + } + return 2; +} + +int get_cic_save(char *cartid, int *cic, int *save) +{ + // variables + int NUM_CARTS = 137; + int i; + + //data arrays + //char *names[] = {"gnuboy64lite", "FRAMTestRom", "SRAMTestRom", "Worms Armageddon", "Super Smash Bros.", "Banjo-Tooie", "Blast Corps", "Bomberman Hero", "Body Harvest", "Banjo-Kazooie", "Bomberman 64", "Bomberman 64: Second Attack", "Command & Conquer", "Chopper Attack", "NBA Courtside 2 featuring Kobe Bryant", "Penny Racers", "Chameleon Twist", "Cruis'n USA", "Cruis'n World", "Legend of Zelda: Majora's Mask, The", "Donkey Kong 64", "Donkey Kong 64", "Donald Duck: Goin' Quackers", "Loony Toons: Duck Dodgers", "Diddy Kong Racing", "PGA European Tour", "Star Wars Episode 1 Racer", "AeroFighters Assault", "Bass Hunter 64", "Conker's Bad Fur Day", "F-1 World Grand Prix", "Star Fox 64", "F-Zero X", "GT64 Championship Edition", "GoldenEye 007", "Glover", "Bomberman 64", "Indy Racing 2000", "Indiana Jones and the Infernal Machine", "Jet Force Gemini", "Jet Force Gemini", "Earthworm Jim 3D", "Snowboard Kids 2", "Kirby 64: The Crystal Shards", "Fighters Destiny", "Major League Baseball featuring Ken Griffey Jr.", "Killer Instinct Gold", "Ken Griffey Jr's Slugfest", "Mario Kart 64", "Mario Party", "Lode Runner 3D", "Megaman 64", "Mario Tennis", "Mario Golf", "Mission: Impossible", "Mickey's Speedway USA", "Monopoly", "Paper Mario", "Multi-Racing Championship", "Big Mountain 2000", "Mario Party 3", "Mario Party 2", "Excitebike 64", "Dr. Mario 64", "Star Wars Episode 1: Battle for Naboo", "Kobe Bryant in NBA Courtside", "Excitebike 64", "Ogre Battle 64: Person of Lordly Caliber", "Pokémon Stadium 2", "Pokémon Stadium 2", "Perfect Dark", "Pokémon Snap", "Hey you, Pikachu!", "Pokémon Snap", "Pokémon Puzzle League", "Pokémon Stadium", "Pokémon Stadium", "Pilotwings 64", "Top Gear Overdrive", "Resident Evil 2", "New Tetris, The", "Star Wars: Rogue Squadron", "Ridge Racer 64", "Star Soldier: Vanishing Earth", "AeroFighters Assault", "Starshot Space Circus", "Super Mario 64", "Starcraft 64", "Rocket: Robot on Wheels", "Space Station Silicon Valley", "Star Wars: Shadows of the Empire", "Tigger's Honey Hunt", "1080º Snowboarding", "Tom & Jerry in Fists of Furry", "Mischief Makers", "All-Star Tennis '99", "Tetrisphere", "V-Rally Edition '99", "V-Rally Edition '99", "WCW/NWO Revenge", "WWF: No Mercy", "Waialae Country Club: True Golf Classics", "Wave Race 64", "Worms Armageddon", "WWF: Wrestlemania 2000", "Cruis'n Exotica", "Yoshi's Story", "Harvest Moon 64", "Legend of Zelda: Ocarina of Time, The", "Legend of Zelda: Majora's Mask, The", "Airboarder 64", "Bakuretsu Muteki Bangaioh", "Choro-Q 64 II", "Custom Robo", "Custom Robo V2", "Densha de Go! 64", "Doraemon: Mittsu no Seireiseki", "Dezaemon 3D", "Transformers Beast Wars", "Transformers Beast Wars Metals", "64 Trump Collection", "Bass Rush", "ECW Hardcore Revolution", "40 Winks", "Aero Gauge", "Aidyn Chronicles The First Mage", "Derby Stallion 64", "Doraemon 2 - Hikari no Shinden", "Doraemon 3 - Nobi Dai No Machi SOS", "F-1 World Grand Prix II", "Fushigi no Dungeon - Furai no Shiren 2", "Heiwa Pachinko World 64", "Neon Genesis Evangelion", "Racing Simulation", "Tsumi to Batsu", "Sonic Wings Assault", "Virtual Pro Wrestling", "Virtual Pro Wrestling 2", "Wild Choppers"}; + char *cartIDs[] = {"DZ", "B6", "ZY", "ZZ", "AD", "AL", "B7", "BC", "BD", "BH", "BK", "BM", "BV", "CC", "CH", "CK", "CR", "CT", "CU", "CW", "DL", "DO", "DP", "DQ", "DU", "DY", "EA", "EP", "ER", "FH", "FU", "FW", "FX", "FZ", "GC", "GE", "GV", "HA", "IC", "IJ", "JD", "JF", "JM", "K2", "K4", "KA", "KG", "KI", "KJ", "KT", "LB", "LR", "M6", "M8", "MF", "MI", "ML", "MO", "MQ", "MR", "MU", "MV", "MW", "MX", "N6", "NA", "NB", "NX", "OB", "P2", "P3", "PD", "PF", "PG", "PH", "PN", "PO", "PS", "PW", "RC", "RE", "RI", "RS", "RZ", "S6", "SA", "SC", "SM", "SQ", "SU", "SV", "SW", "T9", "TE", "TJ", "TM", "TN", "TP", "VL", "VY", "W2", "W4", "WL", "WR", "WU", "WX", "XO", "YS", "YW", "ZL", "ZS", "AB", "BN", "CG", "CX", "CZ", "D6", "DR", "DZ", "OH", "TB", "TC", "VB", "WI", "4W", "AG", "AY", "DA", "D2", "3D", "F2", "SI", "HP", "EV", "MG", "GU", "SA", "VP", "A2", "WC"}; + + //int saveTypes[] = {5, 1, 6, 5, 5, 5, 5, 5, 5, 4, 5, 4, 5, 5, 5, 6, 4, 6, 6, 5, 5, 5, 5, 6, 5, 5, 6, 5, 5, 1, 5, 5, 5, 5, 5, 5, 4, 4, 5, 5, 5, 5, 1, 5, 4, 5, 5, 5, 4, 6, 1, 5, 5, 5, 4, 5, 5, 6, 5, 6, 5, 5, 6, 6, 1, 4, 4, 6, 4, 5, 4, 4, 4, 4, 5, 5, 1, 1, 5, 6, 5, 5, 5, 5, 4, 5, 5, 5, 4, 1, 5, 5, 5, 5, 5, 5, 1, 4, 5, 5, 5, 1, 5, 6, 1, 1, 4, 5, 5, 5, 5, 6, 1, 5, 1, 5, 5, 5, 1, 1, 5, 5, 1, 1, 6, 6, 6, 4, 5, 6, 5, 5, 5, 1, 1, 5}; + // Banjo-Tooie B7 -> set to sram 'cause crk converts ek16->sram + int saveTypes[] = {2, 1, 5, 1, 3, 1, 1, 3, 3, 3, 3, 3, 3, 5, 3, 5, 3, 3, 3, 4, 5, 4, 4, 3, 3, 3, 3, 4, 3, 3, 4, 3, 3, 1, 3, 3, 3, 3, 3, 3, 5, 5, 3, 3, 3, 3, 1, 3, 5, 3, 3, 3, 5, 4, 1, 3, 3, 3, 5, 3, 3, 4, 3, 4, 3, 3, 4, 4, 1, 5, 5, 4, 5, 3, 5, 5, 5, 5, 3, 3, 1, 1, 3, 4, 3, 3, 3, 3, 5, 3, 3, 3, 5, 1, 3, 3, 3, 3, 3, 3, 1, 5, 3, 3, 3, 1, 3, 4, 1, 1, 5, 3, 3, 3, 3, 4, 1, 3, 1, 3, 3, 3, 1, 1, 3, 3, 1, 1, 4, 4, 4, 5, 3, 4, 3, 3, 3, 1, 1, 3}; + + //bt cic to 2 pos6 was 5 + int cicTypes[] = {2, 2, 2, 2, 2, 3, 2, 2, 2, 2, 3, 2, 2, 2, 2, 2, 2, 2, 2, 6, 5, 5, 5, 2, 2, 3, 2, 2, 2, 2, 5, 2, 1, 6, 2, 2, 2, 2, 2, 2, 5, 5, 2, 2, 3, 2, 3, 2, 3, 2, 2, 2, 2, 2, 2, 2, 5, 2, 3, 2, 2, 2, 2, 3, 2, 2, 3, 3, 2, 3, 3, 5, 3, 2, 3, 2, 3, 3, 2, 2, 2, 2, 2, 2, 2, 2, 2, 2, 2, 2, 2, 2, 2, 3, 2, 2, 2, 2, 2, 2, 2, 2, 2, 2, 2, 2, 2, 6, 2, 5, 5, 2, 2, 2, 2, 2, 2, 2, 2, 2, 2, 2, 2, 2, 2, 2, 2, 2, 2, 2, 2, 2, 2, 2, 2, 2, 2, 2, 2, 2}; + + // search for cartid + for (i=0; i + IO_WRITE(PI_BSD_DOM1_LAT_REG, 0x40); + IO_WRITE(PI_BSD_DOM1_PWD_REG, 0x12); + IO_WRITE(PI_BSD_DOM1_PGS_REG, 0x07); + IO_WRITE(PI_BSD_DOM1_RLS_REG, 0x03); + + IO_WRITE(PI_BSD_DOM2_LAT_REG, 0x40); + IO_WRITE(PI_BSD_DOM2_PWD_REG, 0x12); + IO_WRITE(PI_BSD_DOM2_PGS_REG, 0x07); + IO_WRITE(PI_BSD_DOM2_RLS_REG, 0x03); +} + +/* + * Load an image from the rom filesystem, returning a pointer to the + * sprite that hold the image. + */ +sprite_t *loadImageDFS(char *fname) +{ + int size, x, y, n, fd; + u8 *tbuf; + u8 *ibuf; + sprite_t *sbuf; + + fd = dfs_open(fname); + if (fd < 0) + return 0; // couldn't open image + + size = dfs_size(fd); + tbuf = malloc(size); + if (!tbuf) + { + dfs_close(fd); + return 0; // out of memory + } + + dfs_read(tbuf, 1, size, fd); + dfs_close(fd); + + ibuf = stbi_load_from_memory(tbuf, size, &x, &y, &n, 4); + free(tbuf); + if (!ibuf) + return 0; // couldn't decode image + + sbuf = (sprite_t*)malloc(sizeof(sprite_t) + x * y * 2); + if (!sbuf) + { + stbi_image_free(ibuf); + return 0; // out of memory + } + sbuf->width = x; + sbuf->height = y; + sbuf->bitdepth = 2; + sbuf->format = 0; + sbuf->hslices = x / 32; + sbuf->vslices = y / 16; + + color_t *src = (color_t*)ibuf; + u16 *dst = (u16*)((u32)sbuf + sizeof(sprite_t)); + + for (int j=0; jdata, sbuf->width * sbuf->height * sbuf->bitdepth ); + //data_cache_writeback_invalidate( sbuf->data, sbuf->width * sbuf->height * sbuf->bitdepth ); + // data_cache_hit_writeback_invalidate(buffer,size); + + stbi_image_free(ibuf); + return sbuf; +} + + +sprite_t *loadImage32(u8 *png, int size) +{ + int x, y, n, fd; + u8 *tbuf; + u32 *ibuf; + sprite_t *sbuf; + + tbuf = malloc(size); + memcpy(tbuf,png,size); + + ibuf = stbi_load_from_memory(tbuf, size, &x, &y, &n, 4); + free(tbuf); + if (!ibuf) + return 0; // couldn't decode image + + sbuf = (sprite_t*)malloc(sizeof(sprite_t) + x * y * 4); + if (!sbuf) + { + stbi_image_free(ibuf); + return 0; // out of memory + } + sbuf->width = x; + sbuf->height = y; + sbuf->bitdepth = 4; + sbuf->format = 0; + sbuf->hslices = x / 32; + sbuf->vslices = y / 32; + + // color_t *src = (color_t*)ibuf; + u32 *dst = (u32*)((u32)sbuf + sizeof(sprite_t)); + + for (int j=0; jdata, sbuf->width * sbuf->height * sbuf->bitdepth ); + //data_cache_writeback_invalidate( sbuf->data, sbuf->width * sbuf->height * sbuf->bitdepth ); + // data_cache_hit_writeback_invalidate(buffer,size); + + stbi_image_free(ibuf); + return sbuf; +} + + +sprite_t *loadImage32DFS(char *fname) +{ + int size, x, y, n, fd; + u8 *tbuf; + u32 *ibuf; + sprite_t *sbuf; + + fd = dfs_open(fname); + if (fd < 0) + return 0; // couldn't open image + + size = dfs_size(fd); + tbuf = malloc(size); + if (!tbuf) + { + dfs_close(fd); + return 0; // out of memory + } + + dfs_read(tbuf, 1, size, fd); + dfs_close(fd); + + ibuf = stbi_load_from_memory(tbuf, size, &x, &y, &n, 4); + free(tbuf); + if (!ibuf) + return 0; // couldn't decode image + + sbuf = (sprite_t*)malloc(sizeof(sprite_t) + x * y * 4); + if (!sbuf) + { + stbi_image_free(ibuf); + return 0; // out of memory + } + sbuf->width = x; + sbuf->height = y; + sbuf->bitdepth = 4; + sbuf->format = 0; + sbuf->hslices = x / 32; + sbuf->vslices = y / 32; + + // color_t *src = (color_t*)ibuf; + u32 *dst = (u32*)((u32)sbuf + sizeof(sprite_t)); + + for (int j=0; jdata, sbuf->width * sbuf->height * sbuf->bitdepth ); + //data_cache_writeback_invalidate( sbuf->data, sbuf->width * sbuf->height * sbuf->bitdepth ); + // data_cache_hit_writeback_invalidate(buffer,size); + + stbi_image_free(ibuf); + return sbuf; +} + +/* + * Draw an image to the screen using the sprite passed. + */ +void drawImage(display_context_t dcon, sprite_t *sprite) +{ + int x, y = 0; + + rdp_sync(SYNC_PIPE); + rdp_set_default_clipping(); + rdp_enable_texture_copy(); + rdp_attach_display(dcon); + // Draw image + for (int j=0; jvslices; j++) + { + x = 0; + for (int i=0; ihslices; i++) + { + rdp_sync(SYNC_PIPE); + rdp_load_texture_stride(0, 0, MIRROR_DISABLED, sprite, j*sprite->hslices + i); + rdp_draw_sprite(0, x, y); + x += 32; + } + y += 16; + } + rdp_detach_display(); +} + + +#define CIC_6101 1 +#define CIC_6102 2 +#define CIC_6103 3 +#define CIC_6104 4 +#define CIC_6105 5 +#define CIC_6106 6 + + +void globalTest(void){ + +gCheats=1; + +} + + +void simulate_boot(u32 cic_chip, u8 gBootCic) +{ + + if (cic_chip == CIC_6104) + cic_chip = CIC_6102; + + u32 ix, sz, cart, country; + vu32 *src, *dst; + u32 info = *(vu32 *)0xB000003C; + vu64 *gGPR = (vu64 *)0xA03E0000; + //vu64 *gGPR = (vu64 *)0xA0080000; //n + vu32 *codes = (vu32 *)0xA0000180; + u64 bootAddr = 0xFFFFFFFFA4000040LL; + char *cp, *vp, *tp; + char temp[8]; + int i, type, val; + int curr_cheat = 0; + + cart = info >> 16; + country = (info >> 8) & 0xFF; + + if(boot_country!=0){ + switch(boot_country){ + case 1: country = 0x45; break; //ntsc region + case 2: country = 0x50; break; //pal region + default: break; + } + } + + // clear XBUS/Flush/Freeze + ((vu32 *)0xA4100000)[3] = 0x15; + + + sz = (gBootCic != CIC_6105) ? *(vu32 *)0xA0000318 : *(vu32 *)0xA00003F0; + if (cic_chip == CIC_6105) + *(vu32 *)0xA00003F0 = sz; + else + *(vu32 *)0xA0000318 = sz; + + // clear some OS globals for cleaner boot + *(vu32 *)0xA000030C = 0; // cold boot + memset((void *)0xA000031C, 0, 64); // clear app nmi buffer + + + + if (gCheats) + { + + u16 xv, yv, zv; + u32 xx; + vu32 *sp, *dp; + // get rom os boot segment - note, memcpy won't work for copying rom + sp = (vu32 *)0xB0001000; + dp = (vu32 *)0xA02A0000; + for (ix=0; ix<0x100000; ix++) + *dp++ = *sp++; + // default boot address with cheats + sp = (vu32 *)0xB0000008; + bootAddr = 0xFFFFFFFF00000000LL | *sp; + + // move general int handler + sp = (vu32 *)0xA0000180; + dp = (vu32 *)0xA0000120; + for (ix=0; ix<0x60; ix+=4) + *dp++ = *sp++; + + // insert new general int handler prologue + *codes++ = 0x401a6800; // mfc0 k0,c0_cause + *codes++ = 0x241b005c; // li k1,23*4 + *codes++ = 0x335a007c; // andi k0,k0,0x7c + *codes++ = 0x175b0012; // bne k0,k1,0x1d8 + *codes++ = 0x00000000; // nop + *codes++ = 0x40809000; // mtc0 zero,c0_watchlo + *codes++ = 0x401b7000; // mfc0 k1,c0_epc + *codes++ = 0x8f7a0000; // lw k0,0(k1) + *codes++ = 0x3c1b03e0; // lui k1,0x3e0 + *codes++ = 0x035bd024; // and k0,k0,k1 + *codes++ = 0x001ad142; // srl k0,k0,0x5 + *codes++ = 0x3c1ba000; // lui k1,0xa000 + *codes++ = 0x8f7b01cc; // lw k1,0x01cc(k1) + *codes++ = 0x035bd025; // or k0,k0,k1 + *codes++ = 0x3c1ba000; // lui k1,0xa000 + *codes++ = 0xaf7a01cc; // sw k0,0x01cc(k1) + *codes++ = 0x3c1b8000; // lui k1,0x8000 + *codes++ = 0xbf7001cc; // cache 0x10,0x01cc(k1) + *codes++ = 0x3c1aa000; // lui k0,0xa000 + *codes++ = 0x37400120; // ori zero,k0,0x120 + *codes++ = 0x42000018; // eret + *codes++ = 0x00000000; // nop + + // process cheats + while (gGSCodes[curr_cheat].count != 0xFFFF) + { + if (!gGSCodes[curr_cheat].state || !gGSCodes[curr_cheat].count) + { + // cheat not enabled or no codes, skip + curr_cheat++; + continue; + } + + for (i=0; i boot address = 800XXXXX, msn ignored + temp[0] = cp[2]; + temp[1] = cp[3]; + temp[2] = cp[4]; + temp[3] = cp[5]; + temp[4] = cp[6]; + temp[5] = cp[7]; + temp[6] = 0; + val = strtol(temp, (char **)NULL, 16); + bootAddr = 0xFFFFFFFF80000000LL | (val & 0xFFFFF); + break; + case 0xEE: + // deactivate expansion ram using 1st method + // EE000000 0000 + if (cic_chip == CIC_6105) + *(vu32 *)0xA00003F0 = 0x00400000; + else + *(vu32 *)0xA0000318 = 0x00400000; + break; + case 0xF0: + // write 8-bit value to ram before boot + // F0XXXXXX 00YY + temp[0] = cp[2]; + temp[1] = cp[3]; + temp[2] = cp[4]; + temp[3] = cp[5]; + temp[4] = cp[6]; + temp[5] = cp[7]; + temp[6] = 0; + val = strtol(temp, (char **)NULL, 16); + val -= (bootAddr & 0xFFFFFF); + tp = (char *)(0xFFFFFFFFA02A0000LL + val); + if (gGSCodes[curr_cheat].mask) + { + val = gGSCodes[curr_cheat].value & gGSCodes[curr_cheat].mask; + } + else + { + temp[0] = vp[2]; + temp[1] = vp[3]; + temp[2] = 0; + val = strtol(temp, (char **)NULL, 16); + } + *tp = val & 0x00FF; + break; + case 0xF1: + // write 16-bit value to ram before boot + // F1XXXXXX YYYY + temp[0] = cp[2]; + temp[1] = cp[3]; + temp[2] = cp[4]; + temp[3] = cp[5]; + temp[4] = cp[6]; + temp[5] = cp[7]; + temp[6] = 0; + val = strtol(temp, (char **)NULL, 16); + val -= (bootAddr & 0xFFFFFF); + tp = (char *)(0xFFFFFFFFA02A0000LL + val); + if (gGSCodes[curr_cheat].mask) + { + val = gGSCodes[curr_cheat].value & gGSCodes[curr_cheat].mask; + } + else + { + temp[0] = vp[0]; + temp[1] = vp[1]; + temp[2] = vp[2]; + temp[3] = vp[3]; + temp[4] = 0; + val = strtol(temp, (char **)NULL, 16); + } + *tp++ = (val >> 8) & 0x00FF; + *tp = val & 0x00FF; + break; + case 0xFF: + // set code base + // FFXXXXXX 0000 + temp[0] = cp[2]; + temp[1] = cp[3]; + temp[2] = cp[4]; + temp[3] = cp[5]; + temp[4] = cp[6]; + temp[5] = cp[7]; + temp[6] = 0; + val = strtol(temp, (char **)NULL, 16); + //codes = (vu32 *)(0xA0000000 | (val & 0xFFFFFF)); + break; + } + } + curr_cheat++; + } + + // generate jump to moved general int handler + *codes++ = 0x3c1a8000; // lui k0,0x8000 + *codes++ = 0x375a0120; // ori k0,k0,0x120 + *codes++ = 0x03400008; // jr k0 + *codes++ = 0x00000000; // nop + + // flush general int handler memory + data_cache_hit_writeback_invalidate((void *)0x80000120, 0x2E0); + inst_cache_hit_invalidate((void *)0x80000120, 0x2E0); + + // flush os boot segment + data_cache_hit_writeback_invalidate((void *)0x802A0000, 0x100000); + + // flush os boot segment memory + data_cache_hit_writeback_invalidate((void *)bootAddr, 0x100000); + inst_cache_hit_invalidate((void *)bootAddr, 0x100000); + } + + // Copy low 0x1000 bytes to DMEM + //copy bootcode to RSP DMEM starting at 0xA4000040; <- bootaddr + src = (vu32 *)0xB0000000; //i think 0xB0000040; is the right value + dst = (vu32 *)0xA4000000; //0xA4000040; + for (ix=0; ix<(0x1000>>2); ix++) + dst[ix] = src[ix]; + + + // Need to copy crap to IMEM for CIC-6105 boot. + dst = (vu32 *)0xA4001000; + + // register values due to pif boot for CiC chip and country code, and IMEM crap + gGPR[0]=0x0000000000000000LL; + gGPR[6]=0xFFFFFFFFA4001F0CLL; + gGPR[7]=0xFFFFFFFFA4001F08LL; + gGPR[8]=0x00000000000000C0LL; + gGPR[9]=0x0000000000000000LL; + gGPR[10]=0x0000000000000040LL; + gGPR[11]=bootAddr; // 0xFFFFFFFFA4000040LL; + gGPR[16]=0x0000000000000000LL; + gGPR[17]=0x0000000000000000LL; + gGPR[18]=0x0000000000000000LL; + gGPR[19]=0x0000000000000000LL; + gGPR[21]=0x0000000000000000LL; + gGPR[26]=0x0000000000000000LL; + gGPR[27]=0x0000000000000000LL; + gGPR[28]=0x0000000000000000LL; + gGPR[29]=0xFFFFFFFFA4001FF0LL; + gGPR[30]=0x0000000000000000LL; + + switch (country) + { + case 0x44: //Germany + case 0x46: //french + case 0x49: //Italian + case 0x50: //Europe + case 0x53: //Spanish + case 0x55: //Australia + case 0x58: // ???? + case 0x59: // X (PAL) + { + + if(force_tv!=0) + *(u32 *) 0x80000300 = force_tv; //pal + else + *(u32 *) 0x80000300 = 2; + + switch (cic_chip) + { + case CIC_6102: + gGPR[5]=0xFFFFFFFFC0F1D859LL; + gGPR[14]=0x000000002DE108EALL; + gGPR[24]=0x0000000000000000LL; + break; + case CIC_6103: + gGPR[5]=0xFFFFFFFFD4646273LL; + gGPR[14]=0x000000001AF99984LL; + gGPR[24]=0x0000000000000000LL; + break; + case CIC_6105: + dst[0x04>>2] = 0xBDA807FC; + gGPR[5]=0xFFFFFFFFDECAAAD1LL; + gGPR[14]=0x000000000CF85C13LL; + gGPR[24]=0x0000000000000002LL; + break; + case CIC_6106: + gGPR[5]=0xFFFFFFFFB04DC903LL; + gGPR[14]=0x000000001AF99984LL; + gGPR[24]=0x0000000000000002LL; + break; + } + + gGPR[20]=0x0000000000000000LL; + gGPR[23]=0x0000000000000006LL; + gGPR[31]=0xFFFFFFFFA4001554LL; + break; + } + case 0x37: // 7 (Beta) + case 0x41: // ???? + case 0x45: //USA + case 0x4A: //Japan + + if(force_tv!=0) + *(u32 *) 0x80000300 = force_tv; //pal + else + *(u32 *) 0x80000300 = 1; //ntsc + + default: + { + switch (cic_chip) + { + case CIC_6102: + gGPR[5]=0xFFFFFFFFC95973D5LL; + gGPR[14]=0x000000002449A366LL; + break; + case CIC_6103: + gGPR[5]=0xFFFFFFFF95315A28LL; + gGPR[14]=0x000000005BACA1DFLL; + break; + case CIC_6105: + dst[0x04>>2] = 0x8DA807FC; + gGPR[5]=0x000000005493FB9ALL; + gGPR[14]=0xFFFFFFFFC2C20384LL; + break; + case CIC_6106: + gGPR[5]=0xFFFFFFFFE067221FLL; + gGPR[14]=0x000000005CD2B70FLL; + break; + } + + gGPR[20]=0x0000000000000001LL; + gGPR[23]=0x0000000000000000LL; + gGPR[24]=0x0000000000000003LL; + gGPR[31]=0xFFFFFFFFA4001550LL; + break; + } + } + + switch (cic_chip) + { + case CIC_6101: + gGPR[22]=0x000000000000003FLL; + break; + case CIC_6102: + gGPR[1]=0x0000000000000001LL; + gGPR[2]=0x000000000EBDA536LL; + gGPR[3]=0x000000000EBDA536LL; + gGPR[4]=0x000000000000A536LL; + gGPR[12]=0xFFFFFFFFED10D0B3LL; + gGPR[13]=0x000000001402A4CCLL; + gGPR[15]=0x000000003103E121LL; + gGPR[22]=0x000000000000003FLL; + gGPR[25]=0xFFFFFFFF9DEBB54FLL; + break; + case CIC_6103: + gGPR[1]=0x0000000000000001LL; + gGPR[2]=0x0000000049A5EE96LL; + gGPR[3]=0x0000000049A5EE96LL; + gGPR[4]=0x000000000000EE96LL; + gGPR[12]=0xFFFFFFFFCE9DFBF7LL; + gGPR[13]=0xFFFFFFFFCE9DFBF7LL; + gGPR[15]=0x0000000018B63D28LL; + gGPR[22]=0x0000000000000078LL; + gGPR[25]=0xFFFFFFFF825B21C9LL; + break; + case CIC_6105: + dst[0x00>>2] = 0x3C0DBFC0; + dst[0x08>>2] = 0x25AD07C0; + dst[0x0C>>2] = 0x31080080; + dst[0x10>>2] = 0x5500FFFC; + dst[0x14>>2] = 0x3C0DBFC0; + dst[0x18>>2] = 0x8DA80024; + dst[0x1C>>2] = 0x3C0BB000; + gGPR[1]=0x0000000000000000LL; + gGPR[2]=0xFFFFFFFFF58B0FBFLL; + gGPR[3]=0xFFFFFFFFF58B0FBFLL; + gGPR[4]=0x0000000000000FBFLL; + gGPR[12]=0xFFFFFFFF9651F81ELL; + gGPR[13]=0x000000002D42AAC5LL; + gGPR[15]=0x0000000056584D60LL; + gGPR[22]=0x0000000000000091LL; + gGPR[25]=0xFFFFFFFFCDCE565FLL; + break; + case CIC_6106: + gGPR[1]=0x0000000000000000LL; + gGPR[2]=0xFFFFFFFFA95930A4LL; + gGPR[3]=0xFFFFFFFFA95930A4LL; + gGPR[4]=0x00000000000030A4LL; + gGPR[12]=0xFFFFFFFFBCB59510LL; + gGPR[13]=0xFFFFFFFFBCB59510LL; + gGPR[15]=0x000000007A3C07F4LL; + gGPR[22]=0x0000000000000085LL; + gGPR[25]=0x00000000465E3F72LL; + break; + } + + + // set HW registers + IO_WRITE(PI_STATUS_REG, 0x03); + switch (cart) + { + case 0x4258: // 'BX' - Battle Tanx + + IO_WRITE(PI_BSD_DOM1_LAT_REG, 0x80); + IO_WRITE(PI_BSD_DOM1_RLS_REG, 0x37); + IO_WRITE(PI_BSD_DOM1_PWD_REG, 0x12); + IO_WRITE(PI_BSD_DOM1_PGS_REG, 0x40); + + break; + + case 0x4237: // 'B7' - Banjo Tooie + + IO_WRITE(PI_BSD_DOM1_LAT_REG, 0x80); + IO_WRITE(PI_BSD_DOM1_RLS_REG, 0x37); + IO_WRITE(PI_BSD_DOM1_PWD_REG, 0x12); + IO_WRITE(PI_BSD_DOM1_PGS_REG, 0x40); + + break; + + + case 0x5A4C: // 'ZL' - Zelda OOT + +/* + IO_WRITE(PI_BSD_DOM2_LAT_REG, 0x00000005); + IO_WRITE(PI_BSD_DOM2_PWD_REG, 0x0000000C); + IO_WRITE(PI_BSD_DOM2_PGS_REG, 0x0000000D); + IO_WRITE(PI_BSD_DOM2_RLS_REG, 0x00000002); +*/ + +/* unstable + IO_WRITE(PI_BSD_DOM1_LAT_REG, 0x00000040); + IO_WRITE(PI_BSD_DOM1_RLS_REG, 0x00803712); + IO_WRITE(PI_BSD_DOM1_PWD_REG, 0x00008037); + IO_WRITE(PI_BSD_DOM1_PGS_REG, 0x00000803); + */ + break; + default: + break; + } + + + + +/* + + +load immediate: + +li register_destination, value +#load immediate value into destination register + + --- + + Registers in coprocessor 0 cannot be used directly by MIPS instructions. Instead, there are two + instructions that work much like load and store instructions. The mfc0 (move from coprocessor 0) instruction + loads data from a coprocessor 0 register into a CPU register. The mtc0 likewise stores data in a cp0 register. + +Note + +The mtc0 instruction, like the store instruction has the destination last. This is especially important to note, + since the syntax for cp0 registers looks the same as the syntax for CPU registers. For example, the following copies + the contents of CPU register 13 to cp0 register 12. + mtc0 $13, $12 + + + mthi 010001 MoveTo hi = $s + + + LUI -- Load upper immediate + +Description: +The immediate value is shifted left 16 bits and stored in the register. The lower 16 bits are zeroes. +Operation: +$t = (imm << 16); advance_pc (4); +Syntax: +lui $t, imm +Encoding: +0011 11-- ---t tttt iiii iiii iiii iiii + + * */ + + // now set MIPS registers - set CP0, and then GPRs, then jump thru gpr11 (which is usually 0xA400040) + if (!gCheats) + + asm(".set noat\n\t" + ".set noreorder\n\t" + "li $8,0x34000000\n\t" + "mtc0 $8,$12\n\t" + "nop\n\t" + "nop\n\t" + "li $9,0x0006E463\n\t" + "mtc0 $9,$16\n\t" + "nop\n\t" + "nop\n\t" + "li $8,0x00005000\n\t" + "mtc0 $8,$9\n\t" + "nop\n\t" + "nop\n\t" + "li $9,0x0000005C\n\t" + "mtc0 $9,$13\n\t" + "nop\n\t" + "nop\n\t" + "li $8,0x007FFFF0\n\t" + "mtc0 $8,$4\n\t" + "nop\n\t" + "nop\n\t" + "nop\n\t" + "li $9,0xFFFFFFFF\n\t" + "mtc0 $9,$14\n\t" + "nop\n\t" + "nop\n\t" + "mtc0 $9,$8\n\t" + "nop\n\t" + "nop\n\t" + "mtc0 $9,$30\n\t" + "nop\n\t" + "nop\n\t" + "lui $8,0\n\t" + "mthi $8\n\t" + "nop\n\t" + "nop\n\t" + "mtlo $8\n\t" + "nop\n\t" + "nop\n\t" + "ctc1 $8,$31\n\t" + "nop\n\t" + "lui $31,0xA03E\n\t" + //"lui $31,0xA008\n\t" + "ld $1,0x08($31)\n\t" + "ld $2,0x10($31)\n\t" + "ld $3,0x18($31)\n\t" + "ld $4,0x20($31)\n\t" + "ld $5,0x28($31)\n\t" + "ld $6,0x30($31)\n\t" + "ld $7,0x38($31)\n\t" + "ld $8,0x40($31)\n\t" + "ld $9,0x48($31)\n\t" + "ld $10,0x50($31)\n\t" + "ld $11,0x58($31)\n\t" + "ld $12,0x60($31)\n\t" + "ld $13,0x68($31)\n\t" + "ld $14,0x70($31)\n\t" + "ld $15,0x78($31)\n\t" + "ld $16,0x80($31)\n\t" + "ld $17,0x88($31)\n\t" + "ld $18,0x90($31)\n\t" + "ld $19,0x98($31)\n\t" + "ld $20,0xA0($31)\n\t" + "ld $21,0xA8($31)\n\t" + "ld $22,0xB0($31)\n\t" + "ld $23,0xB8($31)\n\t" + "ld $24,0xC0($31)\n\t" + "ld $25,0xC8($31)\n\t" + "ld $26,0xD0($31)\n\t" + "ld $27,0xD8($31)\n\t" + "ld $28,0xE0($31)\n\t" + "ld $29,0xE8($31)\n\t" + "ld $30,0xF0($31)\n\t" + "ld $31,0xF8($31)\n\t" + "jr $11\n\t" + "nop" + ::: "$8" ); + + + + + else + asm(".set noreorder\n\t" + "li $8,0x34000000\n\t" + "mtc0 $8,$12\n\t" + "nop\n\t" + "li $9,0x0006E463\n\t" + "mtc0 $9,$16\n\t" + "nop\n\t" + "li $8,0x00005000\n\t" + "mtc0 $8,$9\n\t" + "nop\n\t" + "li $9,0x0000005C\n\t" + "mtc0 $9,$13\n\t" + "nop\n\t" + "li $8,0x007FFFF0\n\t" + "mtc0 $8,$4\n\t" + "nop\n\t" + "li $9,0xFFFFFFFF\n\t" + "mtc0 $9,$14\n\t" + "nop\n\t" + "mtc0 $9,$30\n\t" + "nop\n\t" + "lui $8,0\n\t" + "mthi $8\n\t" + "nop\n\t" + "mtlo $8\n\t" + "nop\n\t" + "ctc1 $8,$31\n\t" + "nop\n\t" + "li $9,0x00000183\n\t" + "mtc0 $9,$18\n\t" + "nop\n\t" + "mtc0 $zero,$19\n\t" + "nop\n\t" + "lui $8,0xA03C\n\t" + "la $9,2f\n\t" + "la $10,9f\n\t" + ".set noat\n" + "1:\n\t" + "lw $2,($9)\n\t" + "sw $2,($8)\n\t" + "addiu $8,$8,4\n\t" + "addiu $9,$9,4\n\t" + "bne $9,$10,1b\n\t" + "nop\n\t" + "lui $8,0xA03C\n\t" + "jr $8\n\t" + "nop\n" + "2:\n\t" + "lui $9,0xB000\n\t" + "lw $9,8($9)\n\t" + "lui $8,0x2000\n\t" + "or $8,$8,$9\n\t" + "lui $9,0xA02A\n\t" + "lui $10,0xA03A\n\t" + "3:\n\t" + "lw $2,($9)\n\t" + "sw $2,($8)\n\t" + "addiu $8,$8,4\n\t" + "addiu $9,$9,4\n\t" + "bne $9,$10,3b\n\t" + "nop\n\t" + "lui $31,0xA03E\n\t" + "ld $1,0x08($31)\n\t" + "ld $2,0x10($31)\n\t" + "ld $3,0x18($31)\n\t" + "ld $4,0x20($31)\n\t" + "ld $5,0x28($31)\n\t" + "ld $6,0x30($31)\n\t" + "ld $7,0x38($31)\n\t" + "ld $8,0x40($31)\n\t" + "ld $9,0x48($31)\n\t" + "ld $10,0x50($31)\n\t" + "ld $11,0x58($31)\n\t" + "ld $12,0x60($31)\n\t" + "ld $13,0x68($31)\n\t" + "ld $14,0x70($31)\n\t" + "ld $15,0x78($31)\n\t" + "ld $16,0x80($31)\n\t" + "ld $17,0x88($31)\n\t" + "ld $18,0x90($31)\n\t" + "ld $19,0x98($31)\n\t" + "ld $20,0xA0($31)\n\t" + "ld $21,0xA8($31)\n\t" + "ld $22,0xB0($31)\n\t" + "ld $23,0xB8($31)\n\t" + "ld $24,0xC0($31)\n\t" + "ld $25,0xC8($31)\n\t" + "ld $26,0xD0($31)\n\t" + "ld $27,0xD8($31)\n\t" + "ld $28,0xE0($31)\n\t" + "ld $29,0xE8($31)\n\t" + "ld $30,0xF0($31)\n\t" + "ld $31,0xF8($31)\n\t" + "jr $11\n\t" + "nop\n" + "9:\n" + ::: "$8" ); +} diff --git a/utils.h b/utils.h new file mode 100644 index 0000000..3c3761c --- /dev/null +++ b/utils.h @@ -0,0 +1,170 @@ +// rom.h +// rom tools - header inspect +#include +#include + +/* + * + +0000h (1 byte): initial PI_BSB_DOM1_LAT_REG value (0x80) +0001h (1 byte): initial PI_BSB_DOM1_PGS_REG value (0x37) +0002h (1 byte): initial PI_BSB_DOM1_PWD_REG value (0x12) +0003h (1 byte): initial PI_BSB_DOM1_PGS_REG value (0x40) +0004h - 0007h (1 dword): ClockRate +0008h - 000Bh (1 dword): Program Counter (PC) +000Ch - 000Fh (1 dword): Release +0010h - 0013h (1 dword): CRC1 +0014h - 0017h (1 dword): CRC2 +0018h - 001Fh (2 dwords): Unknown (0x0000000000000000) +0020h - 0033h (20 bytes): Image name + Padded with 0x00 or spaces (0x20) +0034h - 0037h (1 dword): Unknown (0x00000000) +0038h - 003Bh (1 dword): Manufacturer ID + 0x0000004E = Nintendo ('N') +003Ch - 003Dh (1 word): Cartridge ID +003Eh - 003Fh (1 word): Country code + 0x4400 = Germany ('D') + 0x4500 = USA ('E') + 0x4A00 = Japan ('J') + 0x5000 = Europe ('P') + 0x5500 = Australia ('U') +0040h - 0FFFh (1008 dwords): Boot code +*/ + +#define PI_BASE_REG 0x04600000 +#define PIF_RAM_START 0x1FC007C0 + + +/* + * PI status register has 3 bits active when read from (PI_STATUS_REG - read) + * Bit 0: DMA busy - set when DMA is in progress + * Bit 1: IO busy - set when IO is in progress + * Bit 2: Error - set when CPU issues IO request while DMA is busy + */ + +#define PI_STATUS_REG (PI_BASE_REG+0x10) + +/* PI DRAM address (R/W): starting RDRAM address */ +#define PI_DRAM_ADDR_REG (PI_BASE_REG+0x00) /* DRAM address */ + +/* PI pbus (cartridge) address (R/W): starting AD16 address */ +#define PI_CART_ADDR_REG (PI_BASE_REG+0x04) + +/* PI read length (R/W): read data length */ +#define PI_RD_LEN_REG (PI_BASE_REG+0x08) + +/* PI write length (R/W): write data length */ +#define PI_WR_LEN_REG (PI_BASE_REG+0x0C) + +/* + * PI status (R): [0] DMA busy, [1] IO busy, [2], error + * (W): [0] reset controller (and abort current op), [1] clear intr + */ + +#define PI_BSD_DOM1_LAT_REG (PI_BASE_REG+0x14) + +/* PI dom1 pulse width (R/W): [7:0] domain 1 device R/W strobe pulse width */ +#define PI_BSD_DOM1_PWD_REG (PI_BASE_REG+0x18) + +/* PI dom1 page size (R/W): [3:0] domain 1 device page size */ +#define PI_BSD_DOM1_PGS_REG (PI_BASE_REG+0x1C) /* page size */ + +/* PI dom1 release (R/W): [1:0] domain 1 device R/W release duration */ +#define PI_BSD_DOM1_RLS_REG (PI_BASE_REG+0x20) +/* PI dom2 latency (R/W): [7:0] domain 2 device latency */ +#define PI_BSD_DOM2_LAT_REG (PI_BASE_REG+0x24) /* Domain 2 latency */ + +/* PI dom2 pulse width (R/W): [7:0] domain 2 device R/W strobe pulse width */ +#define PI_BSD_DOM2_PWD_REG (PI_BASE_REG+0x28) /* pulse width */ + +/* PI dom2 page size (R/W): [3:0] domain 2 device page size */ +#define PI_BSD_DOM2_PGS_REG (PI_BASE_REG+0x2C) /* page size */ + +/* PI dom2 release (R/W): [1:0] domain 2 device R/W release duration */ +#define PI_BSD_DOM2_RLS_REG (PI_BASE_REG+0x30) /* release duration */ + + +#define PI_DOMAIN1_REG PI_BSD_DOM1_LAT_REG +#define PI_DOMAIN2_REG PI_BSD_DOM2_LAT_REG + + +#define PI_STATUS_ERROR 0x04 +#define PI_STATUS_IO_BUSY 0x02 +#define PI_STATUS_DMA_BUSY 0x01 + +#define PHYS_TO_K0(x) ((u32)(x)|0x80000000) /* physical to kseg0 */ +#define K0_TO_PHYS(x) ((u32)(x)&0x1FFFFFFF) /* kseg0 to physical */ +#define PHYS_TO_K1(x) ((u32)(x)|0xA0000000) /* physical to kseg1 */ +#define K1_TO_PHYS(x) ((u32)(x)&0x1FFFFFFF) /* kseg1 to physical */ + +#define IO_READ(addr) (*(volatile u32*)PHYS_TO_K1(addr)) +#define IO_WRITE(addr,data) (*(volatile u32*)PHYS_TO_K1(addr)=(u32)(data)) + +#define SAVE_SIZE_SRAM 32768 +#define SAVE_SIZE_SRAM128 131072 +#define SAVE_SIZE_EEP4k 4096 +#define SAVE_SIZE_EEP16k 16384 +#define SAVE_SIZE_FLASH 131072 + +#define ROM_ADDR 0xb0000000 + + +#define FRAM_EXECUTE_CMD 0xD2000000 +#define FRAM_STATUS_MODE_CMD 0xE1000000 +#define FRAM_ERASE_OFFSET_CMD 0x4B000000 +#define FRAM_WRITE_OFFSET_CMD 0xA5000000 +#define FRAM_ERASE_MODE_CMD 0x78000000 +#define FRAM_WRITE_MODE_CMD 0xB4000000 +#define FRAM_READ_MODE_CMD 0xF0000000 + +#define FRAM_STATUS_REG 0xA8000000 +#define FRAM_COMMAND_REG 0xA8010000 + + +#define CIC_6101 1 +#define CIC_6102 2 +#define CIC_6103 3 +#define CIC_6104 4 +#define CIC_6105 5 +#define CIC_6106 6 + +sprite_t *loadImage32DFS(char *fname); +sprite_t *loadImageDFS(char *fname); +sprite_t *loadImage32(u8 *tbuf, int size); +void drawImage(display_context_t dcon, sprite_t *sprite); +void _sync_bus(void); +void _data_cache_invalidate_all(void); +void printText(char *msg, int x, int y, display_context_t dcon); + +// End ... +int is_valid_rom(unsigned char *buffer); +void swap_header(unsigned char* header, int loadlength); + +void restoreTiming(void); + +void simulate_boot(u32 boot_cic, u8 bios_cic); +void globalTest(void); + + +u8 getCicType(u8 bios_cic); +int get_cic(unsigned char *buffer); +int get_cic_save(char *cartid, int *cic, int *save); +//const char* saveTypeToExtension(int type); +const char* saveTypeToExtension(int type, int etype); +int saveTypeToSize(int type); +int getSaveFromCart(int stype, uint8_t *buffer); +int pushSaveToCart(int stype, uint8_t *buffer); + +int getSRAM( uint8_t *buffer,int size); +int getSRAM32( uint8_t *buffer); +int getSRAM128( uint8_t *buffer); +int getEeprom4k( uint8_t *buffer); +int getEeprom16k( uint8_t *buffer); +int getFlashRAM( uint8_t *buffer); + +int setSRAM(uint8_t *buffer,int size); +int setSRAM32( uint8_t *buffer); +int setSRAM128( uint8_t *buffer); +int setEeprom4k( uint8_t *buffer); +int setEeprom16k( uint8_t *buffer); +int setFlashRAM( uint8_t *buffer);