From b67ef287e73ed2c8e92a1d8fa53aac7d58998d37 Mon Sep 17 00:00:00 2001 From: notaz Date: Mon, 26 Feb 2007 23:46:45 +0000 Subject: [PATCH] patch/gg support, 1.201 release git-svn-id: file:///home/notaz/opt/svn/PicoDrive@58 be3aeb3a-fb24-0410-a615-afba39da0efa --- Pico/Memory.c | 2 +- Pico/Patch.c | 337 ++++++++++++++++++++++++++++++++++++++++ Pico/Patch.h | 31 ++++ platform/gp2x/Makefile | 3 +- platform/gp2x/emu.c | 22 ++- platform/gp2x/menu.c | 64 +++++++- platform/gp2x/version.h | 2 +- platform/linux/Makefile | 3 +- 8 files changed, 456 insertions(+), 8 deletions(-) create mode 100644 Pico/Patch.c create mode 100644 Pico/Patch.h diff --git a/Pico/Memory.c b/Pico/Memory.c index c9c9a020..3157a633 100644 --- a/Pico/Memory.c +++ b/Pico/Memory.c @@ -401,7 +401,7 @@ static void CPU_CALL PicoWrite8(u32 a,u8 d) OtherWrite8(a,d,8); } -static void CPU_CALL PicoWrite16(u32 a,u16 d) +void CPU_CALL PicoWrite16(u32 a,u16 d) { #ifdef __debug_io dprintf("w16: %06x, %04x", a&0xffffff, d); diff --git a/Pico/Patch.c b/Pico/Patch.c new file mode 100644 index 00000000..1b063ccb --- /dev/null +++ b/Pico/Patch.c @@ -0,0 +1,337 @@ +/* Decode a Game Genie code into an M68000 address/data pair. + * The Game Genie code is made of the characters + * ABCDEFGHJKLMNPRSTVWXYZ0123456789 (notice the missing I, O, Q and U). + * Where A = 00000, B = 00001, C = 00010, ... , on to 9 = 11111. + * + * These come out to a very scrambled bit pattern like this: + * (SCRA-MBLE is just an example) + * + * S C R A - M B L E + * 01111 00010 01110 00000 01011 00001 01010 00100 + * ijklm nopIJ KLMNO PABCD EFGHd efgha bcQRS TUVWX + * + * Our goal is to rearrange that to this: + * + * 0000 0101 1001 1100 0100 0100 : 1011 0000 0111 1000 + * ABCD EFGH IJKL MNOP QRST UVWX : abcd efgh ijkl mnop + * + * which in Hexadecimal is 059C44:B078. Simple, huh? ;) + * + * So, then, we dutifully change memory location 059C44 to B078! + * (of course, that's handled by a different source file :) + */ + +//#include +//#include +#include + +#include "PicoInt.h" +#include "Patch.h" + +struct patch +{ + unsigned int addr; + unsigned short data; +}; + +struct patch_inst *PicoPatches = NULL; +int PicoPatchCount = 0; + +static char genie_chars[] = "AaBbCcDdEeFfGgHhJjKkLlMmNnPpRrSsTtVvWwXxYyZz0O1I2233445566778899"; + +/* genie_decode + * This function converts a Game Genie code to an address:data pair. + * The code is given as an 8-character string, like "BJX0SA1C". It need not + * be null terminated, since only the first 8 characters are taken. It is + * assumed that the code is already made of valid characters, i.e. there are no + * Q's, U's, or symbols. If such a character is + * encountered, the function will return with a warning on stderr. + * + * The resulting address:data pair is returned in the struct patch pointed to + * by result. If an error results, both the address and data will be set to -1. + */ + +static void genie_decode(const char* code, struct patch* result) +{ + int i = 0, n; + char* x; + + for(; i < 8; ++i) + { + /* If strchr returns NULL, we were given a bad character */ + if(!(x = strchr(genie_chars, code[i]))) + { + result->addr = -1; result->data = -1; + return; + } + n = (x - genie_chars) >> 1; + /* Now, based on which character this is, fit it into the result */ + switch(i) + { + case 0: + /* ____ ____ ____ ____ ____ ____ : ____ ____ ABCD E___ */ + result->data |= n << 3; + break; + case 1: + /* ____ ____ DE__ ____ ____ ____ : ____ ____ ____ _ABC */ + result->data |= n >> 2; + result->addr |= (n & 3) << 14; + break; + case 2: + /* ____ ____ __AB CDE_ ____ ____ : ____ ____ ____ ____ */ + result->addr |= n << 9; + break; + case 3: + /* BCDE ____ ____ ___A ____ ____ : ____ ____ ____ ____ */ + result->addr |= (n & 0xF) << 20 | (n >> 4) << 8; + break; + case 4: + /* ____ ABCD ____ ____ ____ ____ : ___E ____ ____ ____ */ + result->data |= (n & 1) << 12; + result->addr |= (n >> 1) << 16; + break; + case 5: + /* ____ ____ ____ ____ ____ ____ : E___ ABCD ____ ____ */ + result->data |= (n & 1) << 15 | (n >> 1) << 8; + break; + case 6: + /* ____ ____ ____ ____ CDE_ ____ : _AB_ ____ ____ ____ */ + result->data |= (n >> 3) << 13; + result->addr |= (n & 7) << 5; + break; + case 7: + /* ____ ____ ____ ____ ___A BCDE : ____ ____ ____ ____ */ + result->addr |= n; + break; + } + /* Go around again */ + } + return; +} + +/* "Decode" an address/data pair into a structure. This is for "012345:ABCD" + * type codes. You're more likely to find Genie codes circulating around, but + * there's a chance you could come on to one of these. Which is nice, since + * they're MUCH easier to implement ;) Once again, the input should be depunc- + * tuated already. */ + +static char hex_chars[] = "00112233445566778899AaBbCcDdEeFf"; + +static void hex_decode(const char *code, struct patch *result) +{ + char *x; + int i; + /* 6 digits for address */ + for(i = 0; i < 6; ++i) + { + if(!(x = strchr(hex_chars, code[i]))) + { + result->addr = result->data = -1; + return; + } + result->addr = (result->addr << 4) | ((x - hex_chars) >> 1); + } + /* 4 digits for data */ + for(i = 6; i < 10; ++i) + { + if(!(x = strchr(hex_chars, code[i]))) + { + result->addr = result->data = -1; + return; + } + result->data = (result->data << 4) | ((x - hex_chars) >> 1); + } +} + +/* THIS is the function you call from the MegaDrive or whatever. This figures + * out whether it's a genie or hex code, depunctuates it, and calls the proper + * decoder. */ +static void decode(const char* code, struct patch* result) +{ + int len = strlen(code), i, j; + char code_to_pass[16], *x; + const char *ad, *da; + int adl, dal; + + /* Initialize the result */ + result->addr = result->data = 0; + + /* Just assume 8 char long string to be Game Genie code */ + if (len == 8) + { + genie_decode(code, result); + return; + } + + /* If it's 9 chars long and the 5th is a hyphen, we have a Game Genie + * code. */ + if(len == 9 && code[4] == '-') + { + /* Remove the hyphen and pass to genie_decode */ + code_to_pass[0] = code[0]; + code_to_pass[1] = code[1]; + code_to_pass[2] = code[2]; + code_to_pass[3] = code[3]; + code_to_pass[4] = code[5]; + code_to_pass[5] = code[6]; + code_to_pass[6] = code[7]; + code_to_pass[7] = code[8]; + code_to_pass[8] = '\0'; + genie_decode(code_to_pass, result); + return; + } + + /* Otherwise, we assume it's a hex code. + * Find the colon so we know where address ends and data starts. If there's + * no colon, then we haven't a code at all! */ + if(!(x = strchr(code, ':'))) goto bad_code; + ad = code; da = x + 1; adl = x - code; dal = len - adl - 1; + + /* If a section is empty or too long, toss it */ + if(adl == 0 || adl > 6 || dal == 0 || dal > 4) goto bad_code; + + /* Pad the address with zeros, then fill it with the value */ + for(i = 0; i < (6 - adl); ++i) code_to_pass[i] = '0'; + for(j = 0; i < 6; ++i, ++j) code_to_pass[i] = ad[j]; + + /* Do the same for data */ + for(i = 6; i < (10 - dal); ++i) code_to_pass[i] = '0'; + for(j = 0; i < 10; ++i, ++j) code_to_pass[i] = da[j]; + + code_to_pass[10] = '\0'; + + /* Decode and goodbye */ + hex_decode(code_to_pass, result); + return; + +bad_code: + + /* AGH! Invalid code! */ + result->data = result->addr = -1; + return; +} + + + +unsigned short PicoRead16(unsigned int a); +void PicoWrite16(unsigned int a, unsigned short d); + + +void PicoPatchUnload(void) +{ + if (PicoPatches != NULL) + { + free(PicoPatches); + PicoPatches = NULL; + } + PicoPatchCount = 0; +} + +int PicoPatchLoad(const char *fname) +{ + FILE *f; + char buff[256]; + struct patch pt; + int array_len = 0; + + PicoPatchUnload(); + + f = fopen(fname, "r"); + if (f == NULL) + { + return -1; + } + + while (fgets(buff, sizeof(buff), f)) + { + int llen, clen; + + llen = strlen(buff); + for (clen = 0; clen < llen; clen++) + if (isspace(buff[clen])) break; + buff[clen] = 0; + + if (clen > 11 || clen < 8) + continue; + + decode(buff, &pt); + if (pt.addr == (unsigned int)-1 || pt.data == (unsigned short)-1) + continue; + + /* code was good, add it */ + if (array_len < PicoPatchCount + 1) + { + void *ptr; + array_len *= 2; + array_len++; + ptr = realloc(PicoPatches, array_len * sizeof(PicoPatches[0])); + if (ptr == NULL) break; + PicoPatches = ptr; + } + strcpy(PicoPatches[PicoPatchCount].code, buff); + /* strip */ + for (clen++; clen < llen; clen++) + if (!isspace(buff[clen])) break; + for (llen--; llen > 0; llen--) + if (!isspace(buff[llen])) break; + buff[llen+1] = 0; + strncpy(PicoPatches[PicoPatchCount].name, buff + clen, 51); + PicoPatches[PicoPatchCount].name[51] = 0; + PicoPatches[PicoPatchCount].active = 0; + PicoPatches[PicoPatchCount].addr = pt.addr; + PicoPatches[PicoPatchCount].data = pt.data; + PicoPatches[PicoPatchCount].data_old = 0; + PicoPatchCount++; + // fprintf(stderr, "loaded patch #%i: %06x:%04x \"%s\"\n", PicoPatchCount-1, pt.addr, pt.data, + // PicoPatches[PicoPatchCount-1].name); + } + fclose(f); + + return 0; +} + +/* to be called when the Rom is loaded and byteswapped */ +void PicoPatchPrepare(void) +{ + int i; + + for (i = 0; i < PicoPatchCount; i++) + { + PicoPatches[i].addr &= ~1; + PicoPatches[i].data_old = PicoRead16(PicoPatches[i].addr); + if (strstr(PicoPatches[i].name, "AUTO")) + PicoPatches[i].active = 1; + } +} + +void PicoPatchApply(void) +{ + int i, u; + unsigned int addr; + + for (i = 0; i < PicoPatchCount; i++) + { + addr = PicoPatches[i].addr; + if (addr < Pico.romsize) + { + if (PicoPatches[i].active) + *(unsigned short *)(Pico.rom + addr) = PicoPatches[i].data; + else { + // if current addr is not patched by older patch, write back original val + for (u = 0; u < i; u++) + if (PicoPatches[u].addr == addr) break; + if (u == i) + *(unsigned short *)(Pico.rom + addr) = PicoPatches[i].data_old; + } + // fprintf(stderr, "patched %i: %06x:%04x\n", PicoPatches[i].active, addr, + // *(unsigned short *)(Pico.rom + addr)); + } + else + { + /* RAM or some other weird patch */ + if (PicoPatches[i].active) + PicoWrite16(addr, PicoPatches[i].data); + } + } +} + diff --git a/Pico/Patch.h b/Pico/Patch.h new file mode 100644 index 00000000..6e9420f8 --- /dev/null +++ b/Pico/Patch.h @@ -0,0 +1,31 @@ +#ifndef _GENIE_DECODE_H__ +#define _GENIE_DECODE_H__ + +#ifdef __cplusplus +extern "C" { +#endif + +struct patch_inst +{ + char code[12]; + char name[52]; + unsigned int active; + unsigned int addr; + unsigned short data; + unsigned short data_old; +}; + +extern struct patch_inst *PicoPatches; +extern int PicoPatchCount; + +int PicoPatchLoad(const char *fname); +void PicoPatchUnload(void); +void PicoPatchPrepare(void); +void PicoPatchApply(void); + + +#ifdef __cplusplus +} // extern "C" +#endif + +#endif // _GENIE_DECODE_H__ diff --git a/platform/gp2x/Makefile b/platform/gp2x/Makefile index c5d339f5..a3c69613 100644 --- a/platform/gp2x/Makefile +++ b/platform/gp2x/Makefile @@ -38,7 +38,8 @@ OBJS += main.o menu.o fonts.o gp2x.o usbjoy.o emu.o squidgehack.o asmutils.o cpu OBJS += 940ctl.o # Pico OBJS += ../../Pico/Area.o ../../Pico/Cart.o ../../Pico/Utils.o ../../Pico/Memory.o ../../Pico/Misc.o \ - ../../Pico/Pico.o ../../Pico/Sek.o ../../Pico/VideoPort.o ../../Pico/Draw2.o ../../Pico/Draw.o + ../../Pico/Pico.o ../../Pico/Sek.o ../../Pico/VideoPort.o ../../Pico/Draw2.o ../../Pico/Draw.o \ + ../../Pico/Patch.o # Pico - CD OBJS += ../../Pico/cd/Pico.o ../../Pico/cd/Memory.o ../../Pico/cd/Sek.o ../../Pico/cd/LC89510.o \ ../../Pico/cd/cd_sys.o ../../Pico/cd/cd_file.o ../../Pico/cd/gfx_cd.o \ diff --git a/platform/gp2x/emu.c b/platform/gp2x/emu.c index d9738055..c8c4f8a2 100644 --- a/platform/gp2x/emu.c +++ b/platform/gp2x/emu.c @@ -21,8 +21,9 @@ #include "asmutils.h" #include "cpuctrl.h" -#include "Pico/PicoInt.h" -#include "zlib/zlib.h" +#include +#include +#include #ifdef BENCHMARK @@ -229,6 +230,8 @@ int emu_ReloadRom(void) return 0; } + PicoPatchUnload(); + // check for movie file if(movie_data) { free(movie_data); @@ -269,6 +272,16 @@ int emu_ReloadRom(void) } get_ext(romFileName, ext); } + else if (!strcmp(ext, ".pat")) { + int dummy; + PicoPatchLoad(romFileName); + dummy = try_rfn_cut() || try_rfn_cut(); + if (!dummy) { + sprintf(menuErrorMsg, "Could't find a ROM to patch."); + return 0; + } + get_ext(romFileName, ext); + } // check for MegaCD image cd_state = cd_check(ext, &used_rom_name); @@ -351,6 +364,11 @@ int emu_ReloadRom(void) strncpy(currentConfig.lastRomFile, romFileName, sizeof(currentConfig.lastRomFile)-1); currentConfig.lastRomFile[sizeof(currentConfig.lastRomFile)-1] = 0; + if (PicoPatches) { + PicoPatchPrepare(); + PicoPatchApply(); + } + // additional movie stuff if(movie_data) { if(movie_data[0x14] == '6') diff --git a/platform/gp2x/menu.c b/platform/gp2x/menu.c index 236b6939..032616aa 100644 --- a/platform/gp2x/menu.c +++ b/platform/gp2x/menu.c @@ -17,8 +17,9 @@ #include "usbjoy.h" #include "version.h" -#include "Pico/PicoInt.h" -#include "zlib/zlib.h" +#include +#include +#include #ifndef _DIRENT_HAVE_D_TYPE #error "need d_type for file browser @@ -391,6 +392,54 @@ static char *romsel_loop(char *curr_path) return ret; } +// ------------ patch/gg menu ------------ + +static void draw_patchlist(int sel) +{ + int start, i, pos; + + start = 12 - sel; + + gp2x_pd_clone_buffer2(); + + for (i = 0; i < PicoPatchCount; i++) { + pos = start + i; + if (pos < 0) continue; + if (pos > 23) break; + gp2x_smalltext8_lim(14, pos*10, PicoPatches[i].active ? "ON " : "OFF", 3); + gp2x_smalltext8_lim(14+6*4, pos*10, PicoPatches[i].name, 53-5); + } + pos = start + i; + if (pos < 24) gp2x_smalltext8_lim(14, pos*10, "done", 4); + + gp2x_text_out8(5, 120, ">"); + gp2x_video_flip2(); +} + + +void patches_menu_loop(void) +{ + int menu_sel = 0; + unsigned long inp = 0; + + for(;;) + { + draw_patchlist(menu_sel); + inp = wait_for_input(GP2X_UP|GP2X_DOWN|GP2X_LEFT|GP2X_RIGHT|GP2X_L|GP2X_R|GP2X_B|GP2X_X); + if(inp & GP2X_UP ) { menu_sel--; if (menu_sel < 0) menu_sel = PicoPatchCount; } + if(inp & GP2X_DOWN) { menu_sel++; if (menu_sel > PicoPatchCount) menu_sel = 0; } + if(inp &(GP2X_LEFT|GP2X_L)) { menu_sel-=10; if (menu_sel < 0) menu_sel = 0; } + if(inp &(GP2X_RIGHT|GP2X_R)) { menu_sel+=10; if (menu_sel > PicoPatchCount) menu_sel = PicoPatchCount; } + if(inp & GP2X_B) { // action + if (menu_sel < PicoPatchCount) + PicoPatches[menu_sel].active = !PicoPatches[menu_sel].active; + else return; + } + if(inp & GP2X_X) return; + } + +} + // ------------ savestate loader ------------ static void menu_prepare_bg(void); @@ -1106,6 +1155,8 @@ static void draw_menu_root(int menu_sel) gp2x_text_out8(tl_x, (y+=10), "Configure controls"); gp2x_text_out8(tl_x, (y+=10), "Credits"); gp2x_text_out8(tl_x, (y+=10), "Exit"); + if (PicoPatches) + gp2x_text_out8(tl_x, (y+=10), "Patches / GameGenie"); // draw cursor gp2x_text_out8(tl_x - 16, tl_y + menu_sel*10, ">"); @@ -1133,6 +1184,7 @@ static void menu_loop_root(void) } if (rom_data) menu_sel = menu_sel_min = 0; + if (PicoPatches) menu_sel_max = 9; for(;;) { @@ -1199,6 +1251,14 @@ static void menu_loop_root(void) case 8: // exit engineState = PGS_Quit; return; + case 9: // patches/gg + if (rom_data && PicoPatches) { + patches_menu_loop(); + PicoPatchApply(); + strcpy(menuErrorMsg, "Patches applied"); + continue; + } + break; } } menuErrorMsg[0] = 0; // clear error msg diff --git a/platform/gp2x/version.h b/platform/gp2x/version.h index eeb782ce..db5382df 100644 --- a/platform/gp2x/version.h +++ b/platform/gp2x/version.h @@ -1,2 +1,2 @@ -#define VERSION "1.20" +#define VERSION "1.201" diff --git a/platform/linux/Makefile b/platform/linux/Makefile index 732a830d..fc5ff062 100644 --- a/platform/linux/Makefile +++ b/platform/linux/Makefile @@ -27,7 +27,8 @@ LDFLAGS += `pkg-config --libs gthread-2.0` OBJS += ../gp2x/main.o ../gp2x/menu.o ../gp2x/fonts.o ../gp2x/emu.o ../gp2x/usbjoy.o blit.o gp2x.o 940ctl_ym2612.o # Pico OBJS += ../../Pico/Area.o ../../Pico/Cart.o ../../Pico/Utils.o ../../Pico/Memory.o ../../Pico/Misc.o \ - ../../Pico/Pico.o ../../Pico/Sek.o ../../Pico/VideoPort.o ../../Pico/Draw2.o ../../Pico/Draw.o + ../../Pico/Pico.o ../../Pico/Sek.o ../../Pico/VideoPort.o ../../Pico/Draw2.o ../../Pico/Draw.o \ + ../../Pico/Patch.o # Pico - CD OBJS += ../../Pico/cd/Pico.o ../../Pico/cd/Memory.o ../../Pico/cd/Sek.o ../../Pico/cd/LC89510.o \ ../../Pico/cd/cd_sys.o ../../Pico/cd/cd_file.o ../../Pico/cd/gfx_cd.o \ -- 2.39.5