/* * cheats.c * * Cheats menu * * Softdev (2006) * Eke-Eke (2010) * * 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 "shared.h" #include "cheats.h" #include "font.h" #include "gui.h" #ifdef HW_RVL #include #endif #define BG_COLOR_1 {0x49,0x49,0x49,0xff} #define BG_COLOR_2 {0x66,0x66,0x66,0xff} #ifdef HW_RVL extern const u8 Key_Minus_wii_png[]; extern const u8 Key_Plus_wii_png[]; #else extern const u8 Key_L_gcn_png[]; extern const u8 Key_R_gcn_png[]; #endif extern const u8 Key_DPAD_png[]; typedef struct { char code[12]; char text[36]; u16 enable; u16 data; u16 old; u32 address; } CHEATENTRY; static u32 decode_cheat(char *string, u32 *address, u32 *data); static void apply_cheats(void); static void clear_cheats(void); static void cheatmenu_cb(void); static void switch_chars(void); static int selection = 0; static int maxcheats = 0; static int string_offset = 0; static int type = 0; static int patchRAM =0; static CHEATENTRY cheatlist[10]; /*****************************************************************************/ /* GUI Buttons data */ /*****************************************************************************/ static butn_data button_digit_data = { {NULL,NULL}, {Button_digit_png,Button_digit_over_png} }; /*****************************************************************************/ /* GUI helpers */ /*****************************************************************************/ static gui_item action_cancel = { NULL,Key_B_png,"","Exit",10,422,28,28 }; static gui_item action_select = { NULL,Key_A_png,"","Edit Entry",602,422,28,28 }; /*****************************************************************************/ /* GUI Background images */ /*****************************************************************************/ static gui_image bg_cheats[7] = { {NULL,Bg_main_png,IMAGE_VISIBLE,374,140,284,288,255}, {NULL,Bg_overlay_png,IMAGE_VISIBLE|IMAGE_REPEAT,0,0,640,480,255}, {NULL,Banner_top_png,IMAGE_VISIBLE|IMAGE_SLIDE_TOP,0,0,640,108,255}, {NULL,Banner_bottom_png,IMAGE_VISIBLE|IMAGE_SLIDE_BOTTOM,0,380,640,100,255}, {NULL,Main_logo_png,IMAGE_VISIBLE|IMAGE_SLIDE_TOP,466,40,152,44,255}, {NULL,Frame_s1_png,IMAGE_VISIBLE,8,70,372,336,152}, {NULL,Frame_s1_png,IMAGE_SLIDE_RIGHT,411,109,372,296,76}, }; /*****************************************************************************/ /* Menu Items description */ /*****************************************************************************/ static gui_item items_cheats[30] = { {NULL,NULL,"","Edit Entry",0,0,0,0}, {NULL,NULL,"","Edit Entry",0,0,0,0}, {NULL,NULL,"","Edit Entry",0,0,0,0}, {NULL,NULL,"","Edit Entry",0,0,0,0}, {NULL,NULL,"","Edit Entry",0,0,0,0}, {NULL,NULL,"","Edit Entry",0,0,0,0}, {NULL,NULL,"","Edit Entry",0,0,0,0}, {NULL,NULL,"","Edit Entry",0,0,0,0}, {NULL,NULL,"","Edit Entry",0,0,0,0}, {NULL,NULL,"","Edit Entry",0,0,0,0}, {NULL,NULL,"0","Add Character" ,440,136,40,40}, {NULL,NULL,"1","Add Character" ,486,136,40,40}, {NULL,NULL,"2","Add Character" ,532,136,40,40}, {NULL,NULL,"3","Add Character" ,578,136,40,40}, {NULL,NULL,"4","Add Character" ,440,182,40,40}, {NULL,NULL,"5","Add Character" ,486,182,40,40}, {NULL,NULL,"6","Add Character" ,532,182,40,40}, {NULL,NULL,"7","Add Character" ,578,182,40,40}, {NULL,NULL,"8","Add Character" ,440,228,40,40}, {NULL,NULL,"9","Add Character" ,486,228,40,40}, {NULL,NULL,"A","Add Character" ,532,228,40,40}, {NULL,NULL,"B","Add Character" ,578,228,40,40}, {NULL,NULL,"C","Add Character" ,440,274,40,40}, {NULL,NULL,"D","Add Character" ,486,274,40,40}, {NULL,NULL,"E","Add Character" ,532,274,40,40}, {NULL,NULL,"F","Add Character" ,578,274,40,40}, {NULL,NULL,"del","Delete Entry" ,440,338,40,40}, {NULL,NULL,":","Add Separator" ,486,338,40,40}, {NULL,NULL,"+","Next Characters",532,338,40,40}, {NULL,NULL,"ok","Save Entry" ,578,338,40,40} }; /*****************************************************************************/ /* Menu Buttons description */ /*****************************************************************************/ static gui_butn buttons_cheats[30] = { {NULL, BUTTON_VISIBLE|BUTTON_ACTIVE|BUTTON_SELECT_SFX|BUTTON_OVER_SFX,{0,0,0,0},15,108,358,26}, {NULL, BUTTON_VISIBLE|BUTTON_SELECT_SFX|BUTTON_OVER_SFX,{1,0,0,0},15,134,358,26}, {NULL, BUTTON_VISIBLE|BUTTON_SELECT_SFX|BUTTON_OVER_SFX,{1,0,0,0},15,160,358,26}, {NULL, BUTTON_VISIBLE|BUTTON_SELECT_SFX|BUTTON_OVER_SFX,{1,0,0,0},15,186,358,26}, {NULL, BUTTON_VISIBLE|BUTTON_SELECT_SFX|BUTTON_OVER_SFX,{1,0,0,0},15,212,358,26}, {NULL, BUTTON_VISIBLE|BUTTON_SELECT_SFX|BUTTON_OVER_SFX,{1,0,0,0},15,238,358,26}, {NULL, BUTTON_VISIBLE|BUTTON_SELECT_SFX|BUTTON_OVER_SFX,{1,0,0,0},15,264,358,26}, {NULL, BUTTON_VISIBLE|BUTTON_SELECT_SFX|BUTTON_OVER_SFX,{1,0,0,0},15,290,358,26}, {NULL, BUTTON_VISIBLE|BUTTON_SELECT_SFX|BUTTON_OVER_SFX,{1,0,0,0},15,316,358,26}, {NULL, BUTTON_VISIBLE|BUTTON_SELECT_SFX|BUTTON_OVER_SFX,{1,0,0,0},15,342,358,26}, {&button_digit_data ,BUTTON_ACTIVE|BUTTON_SLIDE_RIGHT|BUTTON_OVER_SFX,{0,4,0,1},440,136,40,40}, {&button_digit_data ,BUTTON_ACTIVE|BUTTON_SLIDE_RIGHT|BUTTON_OVER_SFX,{0,4,1,1},486,136,40,40}, {&button_digit_data ,BUTTON_ACTIVE|BUTTON_SLIDE_RIGHT|BUTTON_OVER_SFX,{0,4,1,1},532,136,40,40}, {&button_digit_data ,BUTTON_ACTIVE|BUTTON_SLIDE_RIGHT|BUTTON_OVER_SFX,{0,4,1,0},578,136,40,40}, {&button_digit_data ,BUTTON_ACTIVE|BUTTON_SLIDE_RIGHT|BUTTON_OVER_SFX,{4,4,0,1},440,182,40,40}, {&button_digit_data ,BUTTON_ACTIVE|BUTTON_SLIDE_RIGHT|BUTTON_OVER_SFX,{4,4,1,1},486,182,40,40}, {&button_digit_data ,BUTTON_ACTIVE|BUTTON_SLIDE_RIGHT|BUTTON_OVER_SFX,{4,4,1,1},532,182,40,40}, {&button_digit_data ,BUTTON_ACTIVE|BUTTON_SLIDE_RIGHT|BUTTON_OVER_SFX,{4,4,1,0},578,182,40,40}, {&button_digit_data ,BUTTON_ACTIVE|BUTTON_SLIDE_RIGHT|BUTTON_OVER_SFX,{4,4,0,1},440,228,40,40}, {&button_digit_data ,BUTTON_ACTIVE|BUTTON_SLIDE_RIGHT|BUTTON_OVER_SFX,{4,4,1,1},486,228,40,40}, {&button_digit_data ,BUTTON_ACTIVE|BUTTON_SLIDE_RIGHT|BUTTON_OVER_SFX,{4,4,1,1},532,228,40,40}, {&button_digit_data ,BUTTON_ACTIVE|BUTTON_SLIDE_RIGHT|BUTTON_OVER_SFX,{4,4,1,0},578,228,40,40}, {&button_digit_data ,BUTTON_ACTIVE|BUTTON_SLIDE_RIGHT|BUTTON_OVER_SFX,{4,4,0,1},440,274,40,40}, {&button_digit_data ,BUTTON_ACTIVE|BUTTON_SLIDE_RIGHT|BUTTON_OVER_SFX,{4,4,1,1},486,274,40,40}, {&button_digit_data ,BUTTON_ACTIVE|BUTTON_SLIDE_RIGHT|BUTTON_OVER_SFX,{4,4,1,1},532,274,40,40}, {&button_digit_data ,BUTTON_ACTIVE|BUTTON_SLIDE_RIGHT|BUTTON_OVER_SFX,{4,4,1,0},578,274,40,40}, {&button_digit_data ,BUTTON_ACTIVE|BUTTON_SLIDE_RIGHT|BUTTON_OVER_SFX,{4,0,0,1},440,338,40,40}, {&button_digit_data ,BUTTON_ACTIVE|BUTTON_SLIDE_RIGHT|BUTTON_OVER_SFX,{4,0,1,1},486,338,40,40}, {&button_digit_data ,BUTTON_ACTIVE|BUTTON_SLIDE_RIGHT|BUTTON_OVER_SFX,{4,0,1,1},532,338,40,40}, {&button_digit_data ,BUTTON_ACTIVE|BUTTON_SLIDE_RIGHT|BUTTON_OVER_SFX,{4,0,1,0},578,338,40,40} }; /*****************************************************************************/ /* Menu description */ /*****************************************************************************/ static gui_menu menu_cheats = { "Cheats Manager", 0,0, 30,30,7,0, items_cheats, buttons_cheats, bg_cheats, {&action_cancel, &action_select}, {NULL,NULL}, cheatmenu_cb }; static char ggvalidchars[] = "ABCDEFGHJKLMNPRSTVWXYZ0123456789"; static char arvalidchars[] = "0123456789ABCDEF"; static u32 decode_cheat(char *string, u32 *address, u32 *data) { char *p; int i,n; /* reset address & data values */ *address = 0; *data = 0; /* Game Genie type code (XXXX-YYYY) */ if ((strlen(string) >= 9) && (string[4] == '-')) { for (i = 0; i < 8; i++) { if (i == 4) string++; p = strchr (ggvalidchars, *string++); if (p == NULL) return 0; n = p - ggvalidchars; switch (i) { case 0: *data |= n << 3; break; case 1: *data |= n >> 2; *address |= (n & 3) << 14; break; case 2: *address |= n << 9; break; case 3: *address |= (n & 0xF) << 20 | (n >> 4) << 8; break; case 4: *data |= (n & 1) << 12; *address |= (n >> 1) << 16; break; case 5: *data |= (n & 1) << 15 | (n >> 1) << 8; break; case 6: *data |= (n >> 3) << 13; *address |= (n & 7) << 5; break; case 7: *address |= n; break; } } /* return code length */ return 9; } /* Action Replay type code (AAAAAA:DDDD) */ else if ((strlen(string) >= 11) && (string[6] == ':')) { /* decode address */ for (i=0; i<6; i++) { p = strchr (arvalidchars, *string++); if (p == NULL) return 0; n = (p - arvalidchars) & 15; *address |= (n << ((5 - i) * 4)); } /* decode data */ string++; for (i=0; i<4; i++) { p = strchr (arvalidchars, *string++); if (p == NULL) return 0; n = p - arvalidchars; *data |= (n & 15) << ((3 - i) * 4); } /* return code length */ return 11; } return 0; } static void apply_cheats(void) { /* clear RAM patch flag */ patchRAM = 0; int i; for (i = 0; i < maxcheats; i++) { if (cheatlist[i].enable) { if (cheatlist[i].address < cart.romsize) { /* patch ROM data */ cheatlist[i].old = *(u16 *)(cart.rom + (cheatlist[i].address & 0xFFFFFE)); *(u16 *)(cart.rom + (cheatlist[i].address & 0xFFFFFE)) = cheatlist[i].data; } else if (cheatlist[i].address >= 0xFF0000) { /* patch RAM data */ patchRAM = 1; } } } } static void clear_cheats(void) { int i; for (i = 0; i < maxcheats; i++) { /* restore original ROM data */ if (cheatlist[i].enable && (cheatlist[i].address < cart.romsize)) { *(u16 *)(cart.rom + (cheatlist[i].address & 0xFFFFFE)) = cheatlist[i].old; } } } static void switch_chars(void) { int i; gui_menu *m = &menu_cheats; if (m->items[10].text[0] == '0') { m->items[10].text[0] = 'G'; m->items[11].text[0] = 'H'; m->items[12].text[0] = 'J'; m->items[13].text[0] = 'K'; m->items[14].text[0] = 'L'; m->items[15].text[0] = 'M'; m->items[16].text[0] = 'N'; m->items[17].text[0] = 'P'; m->items[18].text[0] = 'R'; m->items[19].text[0] = 'S'; m->items[20].text[0] = 'T'; m->items[21].text[0] = 'V'; m->items[22].text[0] = 'W'; m->items[23].text[0] = 'X'; m->items[24].text[0] = 'Y'; m->items[25].text[0] = 'Z'; } else if (m->items[10].text[0] == 'G') { m->items[10].text[0] = '0'; m->items[11].text[0] = '1'; m->items[12].text[0] = '2'; m->items[13].text[0] = '3'; m->items[14].text[0] = '4'; m->items[15].text[0] = '5'; m->items[16].text[0] = '6'; m->items[17].text[0] = '7'; m->items[18].text[0] = '8'; m->items[19].text[0] = '9'; m->items[20].text[0] = 'A'; m->items[21].text[0] = 'B'; m->items[22].text[0] = 'C'; m->items[23].text[0] = 'D'; m->items[24].text[0] = 'E'; m->items[25].text[0] = 'F'; } else if (m->items[10].text[0] == 'A') { m->items[10].text[0] = 'Q'; m->items[11].text[0] = 'R'; m->items[12].text[0] = 'S'; m->items[13].text[0] = 'T'; m->items[14].text[0] = 'U'; m->items[15].text[0] = 'V'; m->items[16].text[0] = 'W'; m->items[17].text[0] = 'X'; m->items[18].text[0] = 'Y'; m->items[19].text[0] = 'Z'; m->items[20].text[0] = '0'; m->items[21].text[0] = '1'; m->items[22].text[0] = '2'; m->items[23].text[0] = '3'; m->items[24].text[0] = '4'; m->items[25].text[0] = '5'; } else if (m->items[10].text[0] == 'Q') { m->items[10].text[0] = '6'; m->items[11].text[0] = '7'; m->items[12].text[0] = '8'; m->items[13].text[0] = '9'; /* hide unused buttons */ for (i=14; i<26; i++) { m->buttons[i].state &= ~BUTTON_VISIBLE; } m->buttons[10].shift[1] = 16; m->buttons[11].shift[1] = 16; m->buttons[12].shift[1] = 16; m->buttons[13].shift[1] = 16; m->buttons[26].shift[0] = 16; m->buttons[27].shift[0] = 16; m->buttons[28].shift[0] = 16; m->buttons[29].shift[0] = 16; } else if (m->items[10].text[0] == '6') { m->items[10].text[0] = 'A'; m->items[11].text[0] = 'B'; m->items[12].text[0] = 'C'; m->items[13].text[0] = 'D'; m->items[14].text[0] = 'E'; m->items[15].text[0] = 'F'; m->items[16].text[0] = 'G'; m->items[17].text[0] = 'H'; m->items[18].text[0] = 'I'; m->items[19].text[0] = 'J'; m->items[20].text[0] = 'K'; m->items[21].text[0] = 'L'; m->items[22].text[0] = 'M'; m->items[23].text[0] = 'N'; m->items[24].text[0] = 'O'; m->items[25].text[0] = 'P'; /* show previously unused buttons */ for (i=14; i<26; i++) { m->buttons[i].state |= BUTTON_VISIBLE; } m->buttons[10].shift[1] = 4; m->buttons[11].shift[1] = 4; m->buttons[12].shift[1] = 4; m->buttons[13].shift[1] = 4; m->buttons[26].shift[0] = 4; m->buttons[27].shift[0] = 4; m->buttons[28].shift[0] = 4; m->buttons[29].shift[0] = 4; } } static void cheatmenu_cb(void) { int i; int yoffset = 108; gui_image bar_over; gui_image star; char temp[36]; /* Initialize textures */ bar_over.texture = gxTextureOpenPNG(Overlay_bar_png,0); star.texture = gxTextureOpenPNG(Star_full_png,0); /* Draw browser array */ gxDrawRectangle(15, 108, 358, 26, 127, (GXColor)BG_COLOR_1); gxDrawRectangle(15, 134, 358, 26, 127, (GXColor)BG_COLOR_2); gxDrawRectangle(15, 160, 358, 26, 127, (GXColor)BG_COLOR_1); gxDrawRectangle(15, 186, 358, 26, 127, (GXColor)BG_COLOR_2); gxDrawRectangle(15, 212, 358, 26, 127, (GXColor)BG_COLOR_1); gxDrawRectangle(15, 238, 358, 26, 127, (GXColor)BG_COLOR_2); gxDrawRectangle(15, 264, 358, 26, 127, (GXColor)BG_COLOR_1); gxDrawRectangle(15, 290, 358, 26, 127, (GXColor)BG_COLOR_2); gxDrawRectangle(15, 316, 358, 26, 127, (GXColor)BG_COLOR_1); gxDrawRectangle(15, 342, 358, 26, 127, (GXColor)BG_COLOR_2); /* Draw Cheat list */ for (i=0; i= strlen(cheatlist[i].text)) { string_offset = 0; } if (string_offset) { sprintf(temp,"%s ",cheatlist[i].text+string_offset/10); strncat(temp, cheatlist[i].text, string_offset/10); } else { strcpy(temp, cheatlist[i].text); } if (FONT_writeCenter(temp,16,40,366,yoffset+21,(GXColor)WHITE)) { /* scroll text if string does not fit */ string_offset ++; } } } else { FONT_writeCenter(cheatlist[i].code,18,40,366,yoffset+22,(GXColor)WHITE); } } else { if (type) { FONT_writeCenter(cheatlist[i].text,16,40,366,yoffset+21,(GXColor)WHITE); } else { FONT_writeCenter(cheatlist[i].code,18,40,366,yoffset+22,(GXColor)WHITE); } } /* draw cheat enable mark */ if (cheatlist[i].enable) { gxDrawTexture(star.texture,20,yoffset+5,16,16,255); } yoffset += 26; } /* New Entry */ if (maxcheats < 10) { if (maxcheats == selection) { /* selection bar */ gxDrawTexture(bar_over.texture,16,yoffset+1,356,24,255); /* check if new code is being edited */ if (menu_cheats.bg_images[6].state & IMAGE_VISIBLE) { FONT_writeCenter(cheatlist[selection].code,18,40,366,yoffset+22,(GXColor)WHITE); } else { FONT_writeCenter("New Code",18,40,366,yoffset+22,(GXColor)WHITE); } } else { FONT_writeCenter("New Code",18,40,366,yoffset+22,(GXColor)WHITE); } } gxTextureClose(&bar_over.texture); /* Extra helpers */ if (maxcheats && !(menu_cheats.bg_images[6].state & IMAGE_VISIBLE)) { /* switch between cheat code & description preview */ gui_image key_switch; key_switch.texture = gxTextureOpenPNG(Key_DPAD_png,0); #ifdef HW_RVL gxDrawTexture(key_switch.texture,268,424,24,24,255); FONT_write(type ? "View\nCode":"View\nText",16,300,436,640,(GXColor)WHITE); #else gxDrawTexture(key_switch.texture,272,424,24,24,255); FONT_write(type ? "View\nCode":"View\nText",16,304,436,640,(GXColor)WHITE); #endif gxTextureClose(&key_switch.texture); /* delete & enable cheats */ if (menu_cheats.selected < maxcheats) { gui_image key_enable; gui_image key_delete; #ifdef HW_RVL key_delete.texture = gxTextureOpenPNG(Key_Minus_wii_png,0); key_enable.texture = gxTextureOpenPNG(Key_Plus_wii_png,0); gxDrawTexture(key_delete.texture,152,424,24,24,255); gxDrawTexture(key_enable.texture,372,424,24,24,255); FONT_write("Delete\nCheat",16,184,436,640,(GXColor)WHITE); FONT_write(cheatlist[selection].enable ? "Disable\nCheat":"Enable\nCheat",16,404,436,640,(GXColor)WHITE); #else key_enable.texture = gxTextureOpenPNG(Key_L_gcn_png,0); key_delete.texture = gxTextureOpenPNG(Key_R_gcn_png,0); gxDrawTexture(key_delete.texture,136,426,44,20,255); gxDrawTexture(key_enable.texture,368,426,44,20,255); FONT_write("Delete\nCheat",16,188,436,640,(GXColor)WHITE); FONT_write(cheatlist[selection].enable ? "Disable\nCheat":"Enable\nCheat",16,420,436,640,(GXColor)WHITE); #endif gxTextureClose(&key_enable.texture); gxTextureClose(&key_delete.texture); } } } /**************************************************************************** * CheatMenu * * Manage cheats for the currently loaded game * ****************************************************************************/ void CheatMenu(void) { int i, update = 0; int offset = 0; int max = 0; char temp[256]; char *str = NULL; u32 address, data; gui_menu *m = &menu_cheats; /* clear existing ROM patches */ clear_cheats(); /* background type */ if (config.bg_type > 0) { bg_cheats[0].state &= ~IMAGE_REPEAT; bg_cheats[0].data = (config.bg_type > 1) ? Bg_main_png : Bg_main_2_png; bg_cheats[0].x = 374; bg_cheats[0].y = 140; bg_cheats[0].w = 284; bg_cheats[0].h = 288; } else { bg_cheats[0].state |= IMAGE_REPEAT; bg_cheats[0].data = Bg_layer_png; bg_cheats[0].x = 0; bg_cheats[0].y = 0; bg_cheats[0].w = 640; bg_cheats[0].h = 480; } /* background overlay */ if (config.bg_overlay) { bg_cheats[1].state |= IMAGE_VISIBLE; } else { bg_cheats[1].state &= ~IMAGE_VISIBLE; } /* selected item */ m->selected = selection; /* slide-in menu */ GUI_InitMenu(m); GUI_DrawMenuFX(m,30,0); m->cb = cheatmenu_cb; /* lock background elements */ m->bg_images[2].state &= ~IMAGE_SLIDE_TOP; m->bg_images[3].state &= ~IMAGE_SLIDE_BOTTOM; m->bg_images[4].state &= ~IMAGE_SLIDE_TOP; while (update != -1) { /* draw menu */ GUI_DrawMenu(m); /* update menu */ update = GUI_UpdateMenu(m); /* update selection */ if ((m->selected < 10) && (selection != m->selected)) { selection = m->selected; string_offset = 0; } /* handle pressed buttons */ if (update > 0) { switch (m->selected) { case 0: case 1: case 2: case 3: case 4: case 5: case 6: case 7: case 8: case 9: /* Edit cheat */ { if (type && (selection != maxcheats)) { /* cheat description */ str = cheatlist[selection].text; strcpy(temp, str); max = 34; offset = strlen(str); if (offset <= max) { str[offset] = '*'; str[offset+1] = 0; } /* init specific characters */ m->items[10].text[0] = '6'; m->items[27].text[0] = ' '; strcpy(m->items[27].comment,"Add White Space"); switch_chars(); } else { /* cheat code */ str = cheatlist[selection].code; strcpy(temp, str); if (selection == maxcheats) { /* initialize code */ max = 0; offset = 0; str[0] = '*'; str[1] = 0; } else { /* code type */ if (str[4] == '-') { /* GG code */ max = 8; } else { /* AR code */ max = 10; } offset = max + 1; } /* init specific characters */ m->items[10].text[0] = 'G'; m->items[27].text[0] = ':'; strcpy(m->items[27].comment,"Add Code Separator"); switch_chars(); } /* show digit buttons */ for (i=10; i<30; i++) m->buttons[i].state |= BUTTON_VISIBLE; /* show right window */ m->bg_images[6].state |= IMAGE_VISIBLE; /* disable left buttons */ for (i=0; i<10; i++) m->buttons[i].state &= ~BUTTON_ACTIVE; /* slide in right window */ GUI_DrawMenuFX(m,20,0); /* update helper */ strcpy(action_cancel.comment,"Cancel"); /* select first digit */ m->selected = 10; /* reset scrolling */ string_offset = 0; break; } case 26: /* Backspace */ { if (offset > 0) { /* delete last character */ str[offset--] = 0; /* code separator is being deleted */ if ((str[offset] == ':') || (str[offset] == '-')) { /* reset detected code type */ max = 0; } /* edit mark */ str[offset] = '*'; /* update scroll value if necessary */ if (string_offset > 0) string_offset--; } break; } case 27: { if (type && (selection != maxcheats)) { /* SPACE character */ if (offset <= max) { str[offset++] = ' '; str[offset] = 0; if (offset <= max) { str[offset] = '*'; str[offset+1] = 0; } } } else { /* Separator character (only if code type has not yet been determined) */ if (max == 0) { if (offset == 4) { /* GG code */ max = 8; str[4] = '-'; str[5] = '*'; str[6] = 0; offset++; } else if (offset == 6) { /* AR code */ max = 10; str[6] = ':'; str[7] = '*'; str[8] = 0; offset++; } } } break; } case 28: /* Next character set */ { GUI_DrawMenuFX(m,40,1); switch_chars(); GUI_DrawMenuFX(m,40,0); break; } case 29: /* Validate entry */ { /* check if entry is valid */ if (type && (selection != maxcheats)) { str[offset] = 0; update = -1; } else if (max && (offset > max)) { address = data = 0; if (decode_cheat(cheatlist[selection].code, &address, &data)) { /* update cheat address & data values */ cheatlist[selection].address = address; cheatlist[selection].data = data; /* new cheat ? */ if (selection == maxcheats) { /* increase cheat count */ maxcheats++; /* enable cheat by default */ cheatlist[selection].enable = 1; /* no description by default */ strcpy(cheatlist[selection].text,"No Description"); } /* return to cheat selection */ update = -1; } else { GUI_WaitPrompt("Error", "Invalid Cheat Code"); } } break; } default: /* Add Character */ { /* force code separator if none has been set yet */ if ((max == 0) && (offset == 6)) break; /* add character */ if ((offset <= max) || (max == 0)) { str[offset++] = m->items[m->selected].text[0]; str[offset] = 0; if ((offset <= max) || (max == 0)) { str[offset] = '*'; str[offset+1] = 0; } if (string_offset > 0) string_offset ++; } break; } } } else if (update < 0) { if (m->bg_images[6].state & IMAGE_VISIBLE) { /* Restore old entry */ strcpy(str, temp); } } else { if (maxcheats && !(m->bg_images[6].state & IMAGE_VISIBLE)) { if ((m_input.keys & PAD_BUTTON_LEFT) || (m_input.keys & PAD_BUTTON_RIGHT)) { /* Switch between cheat code & description */ type ^= 1; /* reset scrolling */ string_offset = 0; } if (selection < maxcheats) { /* Special inputs */ if (m_input.keys & PAD_TRIGGER_L) { /* sort cheat list */ for (i = selection + 1; i < maxcheats; i++) { strcpy(cheatlist[i-1].text,cheatlist[i].text); strcpy(cheatlist[i-1].code,cheatlist[i].code); cheatlist[i-1].address = cheatlist[i].address; cheatlist[i-1].data = cheatlist[i].data; cheatlist[i-1].enable = cheatlist[i].enable; } /* clear last cheat */ cheatlist[maxcheats-1].text[0] = 0; cheatlist[maxcheats-1].code[0] = 0; cheatlist[maxcheats-1].address = 0; cheatlist[maxcheats-1].data = 0; cheatlist[maxcheats-1].enable = 0; /* disable last button */ if (maxcheats < 10) { m->buttons[maxcheats].state &= ~BUTTON_ACTIVE; m->buttons[maxcheats - 1].shift[1] = 0; } /* decrease cheat count */ maxcheats--; /* reset scrolling */ string_offset = 0; } else if (m_input.keys & PAD_TRIGGER_R) { /* cheat ON/OFF */ cheatlist[selection].enable ^= 1; } } } } if (update < 0) { if (m->bg_images[6].state & IMAGE_VISIBLE) { /* slide out right window */ GUI_DrawMenuFX(m,20,1); /* hide digit buttons */ for (i=10; i<30; i++) m->buttons[i].state &= ~BUTTON_VISIBLE; /* hide right window */ m->bg_images[6].state &= ~IMAGE_VISIBLE; /* enable left buttons */ for (i=0; ibuttons[i].state |= BUTTON_ACTIVE; m->buttons[i].shift[1] = 1; } if (maxcheats < 10) { m->buttons[maxcheats].state |= BUTTON_ACTIVE; m->buttons[maxcheats].shift[1] = 0; } else { m->buttons[9].shift[1] = 0; } /* restore helper */ strcpy(action_cancel.comment,"Back"); /* select current cheat */ m->selected = selection; /* stay in menu */ update = 0; } } } /* apply ROM patches */ apply_cheats(); /* save cheats to file */ sprintf(temp, "%s/cheats/%s.pat", DEFAULT_PATH, rom_filename); if (maxcheats) { /* open file */ FILE *f = fopen(temp, "w"); /* write cheats */ if (f) { for (i=0; ibg_images[2].state |= IMAGE_SLIDE_TOP; m->bg_images[3].state |= IMAGE_SLIDE_BOTTOM; m->bg_images[4].state |= IMAGE_SLIDE_TOP; /* leave menu */ m->cb = NULL; GUI_DeleteMenu(m); GUI_DrawMenuFX(m,30,1); } /**************************************************************************** * CheatLoad * * Load cheats from associated .pat file, called when loading a new game * ROM patches are automatically applied. * RAM patches are applied once per frame. * ****************************************************************************/ void CheatLoad(void) { int len; int cnt = 0; u32 address, data; char temp[256]; /* reset cheat count */ selection = maxcheats = 0; /* make cheat filename */ sprintf(temp, "%s/cheats/%s.pat", DEFAULT_PATH, rom_filename); /* open file */ FILE *f = fopen(temp, "r"); if (f) { /* clear string */ memset(temp, 0, 256); /* read cheats from file (one line per cheat) */ while (fgets(temp, 256, f) && (maxcheats < 10) && (cnt < 10)) { /* remove CR & EOL chars */ if ((temp[strlen(temp) - 2] == 0x0d) || (temp[strlen(temp) - 2] == 0x0a)) temp[strlen(temp) - 2] = 0; else temp[strlen(temp) - 1] = 0; /* check cheat validty */ address = data = 0; len = decode_cheat(temp, &address, &data); if (len) { /* update cheat address & data values */ cheatlist[maxcheats].address = address; cheatlist[maxcheats].data = data; /* copy cheat code */ strncpy(cheatlist[maxcheats].code, temp, len); cheatlist[maxcheats].code[len] = 0; len++; /* jump TAB and SPACE characters */ while ((temp[len] == 0x20) || (temp[len] == 0x09)) len++; /* copy cheat description */ strncpy(cheatlist[maxcheats].text, &temp[len], 35); cheatlist[maxcheats].text[35] = 0; /* increment cheat count */ maxcheats++; } else if (!strncmp(temp,"ON",2)) { /* enable flag */ cheatlist[cnt++].enable = 1; } else if (!strncmp(temp,"OFF",3)) { /* disable flag */ cheatlist[cnt++].enable = 0; } } /* by default, disable cheats that were not flagged */ while (cnt < maxcheats) cheatlist[cnt++].enable = 0; /* close file */ fclose(f); } /* apply ROM patches */ apply_cheats(); /* adjust menu buttons */ for (cnt=0; cnt= 0xFF0000)) { if (cheatlist[i].data & 0xFF00) { /* word patch */ *(u16 *)(work_ram + (cheatlist[i].address & 0xFFFE)) = cheatlist[i].data; } else { /* byte patch */ work_ram[cheatlist[i].address & 0xFFFF] = cheatlist[i].data; } } } } }