Genesis-Plus-GX/source/gx/gui/cheats.c
EkeEke 28775cc3aa [Core/SCD]
----------
* added Mega CD / Sega CD hardware emulation (incl. Sub 68K, CDD, CDC, PCM, GFX
rotation/scaling, etc)
* added .ISO & .BIN CD image file support
* added 512K backup cartridge RAM support
* added savestate support for CD games

NOTES:
~~~~~~
* to play CD games, original BIOS ROM files are required in /genplus/bios/
directory: unzip & rename them to bios_CD_U.bin, bios_CD_E.bin, bios_CD_J.bin
* CD audio tracks (CD-DA) are not supported (yet)

[Core/CPU]
----------
* modified 68k core for Mega CD / Sega CD support

[Core/VDP]
---------------
* improved DMA accuracy
* added support for 8-bit VRAM writes with undocumented code value (verified on
real hardware by Nemesis)

[Gamecube/Wii]
---------------
* modified Master System & Game Gear "BIOS" support (files should be named
bios_U.sms, bios_J.sms, bios_E.sms & bios.gg and copied to /genplus/bios
directory).
* replaced "Hard Reset" button by a Soft Reset for systems having a Reset button
(Mega Drive / Genesis & Master System)
* State & SRAM files are now only compressed when saving to Gamecube Memory
Cards
* various fixes & cleanup.

[Core/SCD]
----------
* added Mega CD / Sega CD hardware emulation (incl. Sub 68K, CDD, CDC, PCM, GFX
rotation/scaling, etc)
* added .ISO & .BIN CD image file support
* added 512K backup cartridge RAM support
* added savestate support for CD games

NOTES:
~~~~~~
* to play CD games, original BIOS ROM files are required in /genplus/bios/
directory: unzip & rename them to bios_CD_U.bin, bios_CD_E.bin, bios_CD_J.bin
* CD audio tracks (CD-DA) are not supported (yet)

[Core/CPU]
----------
* modified 68k core for Mega CD / Sega CD support

[Core/VDP]
---------------
* improved DMA accuracy
* added support for 8-bit VRAM writes with undocumented code value (verified on
real hardware by Nemesis)

[Gamecube/Wii]
---------------
* modified Master System & Game Gear "BIOS" support (files should be named
bios_U.sms, bios_J.sms, bios_E.sms & bios.gg and copied to /genplus/bios
directory).
* replaced "Hard Reset" button by a Soft Reset for systems having a Reset button
(Mega Drive / Genesis & Master System)
* State & SRAM files are now only compressed when saving to Gamecube Memory
Cards
* various fixes & cleanup.
2012-07-05 21:45:37 +02:00

1492 lines
44 KiB
C

/*
* cheats.c
*
* Cheats menu
*
* Copyright Eke-Eke (2010-2012)
*
* Redistribution and use of this code or any derivative works are permitted
* provided that the following conditions are met:
*
* - Redistributions may not be sold, nor may they be used in a commercial
* product or activity.
*
* - Redistributions that are modified from the original source must include the
* complete source code, including the source code for all components used by a
* binary built from the modified sources. However, as a special exception, the
* source code distributed need not include anything that is normally distributed
* (in either source or binary form) with the major components (compiler, kernel,
* and so on) of the operating system on which the executable runs, unless that
* component itself accompanies the executable.
*
* - Redistributions must reproduce the above copyright notice, this list of
* conditions and the following disclaimer in the documentation and/or other
* materials provided with the distribution.
*
* THIS SOFTWARE IS PROVIDED BY THE COPYRIGHT HOLDERS AND CONTRIBUTORS "AS IS"
* AND ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE
* IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE
* ARE DISCLAIMED. IN NO EVENT SHALL THE COPYRIGHT OWNER OR CONTRIBUTORS BE
* LIABLE FOR ANY DIRECT, INDIRECT, INCIDENTAL, SPECIAL, EXEMPLARY, OR
* CONSEQUENTIAL DAMAGES (INCLUDING, BUT NOT LIMITED TO, PROCUREMENT OF
* SUBSTITUTE GOODS OR SERVICES; LOSS OF USE, DATA, OR PROFITS; OR BUSINESS
* INTERRUPTION) HOWEVER CAUSED AND ON ANY THEORY OF LIABILITY, WHETHER IN
* CONTRACT, STRICT LIABILITY, OR TORT (INCLUDING NEGLIGENCE OR OTHERWISE)
* ARISING IN ANY WAY OUT OF THE USE OF THIS SOFTWARE, EVEN IF ADVISED OF THE
* POSSIBILITY OF SUCH DAMAGE.
*
****************************************************************************************/
#include "shared.h"
#include "cheats.h"
#include "font.h"
#include "gui.h"
#define BG_COLOR_1 {0x49,0x49,0x49,0xff}
#define BG_COLOR_2 {0x66,0x66,0x66,0xff}
#define MAX_CHEATS (150)
#define MAX_DESC_LENGTH (63)
#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[MAX_DESC_LENGTH];
u8 enable;
u16 data;
u16 old;
u32 address;
u8 *prev;
} CHEATENTRY;
static int string_offset = 0;
static int selection = 0;
static int offset = 0;
static int type = 0;
static int maxcheats = 0;
static int maxROMcheats = 0;
static int maxRAMcheats = 0;
static CHEATENTRY cheatlist[MAX_CHEATS];
static u8 cheatIndexes[MAX_CHEATS];
static void cheatmenu_cb(void);
/*****************************************************************************/
/* GUI Buttons data */
/*****************************************************************************/
static butn_data arrow_up_data =
{
{NULL,NULL},
{Button_up_png,Button_up_over_png}
};
static butn_data arrow_down_data =
{
{NULL,NULL},
{Button_down_png,Button_down_over_png}
};
static butn_data button_digit_data =
{
{NULL,NULL},
{Button_digit_png,Button_digit_over_png}
};
/*****************************************************************************/
/* GUI Arrows button */
/*****************************************************************************/
static gui_butn arrow_up = {&arrow_up_data,BUTTON_VISIBLE|BUTTON_ACTIVE|BUTTON_OVER_SFX,{0,0,0,0},14,76,360,32};
static gui_butn arrow_down = {&arrow_down_data,BUTTON_VISIBLE|BUTTON_ACTIVE|BUTTON_OVER_SFX,{0,0,0,0},14,368,360,32};
/*****************************************************************************/
/* 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_layer_png,IMAGE_VISIBLE|IMAGE_REPEAT,0,0,640,480,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","Backspace" ,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,{1,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},
{&arrow_up,&arrow_down},
cheatmenu_cb
};
static char ggvalidchars[] = "ABCDEFGHJKLMNPRSTVWXYZ0123456789";
static char arvalidchars[] = "0123456789ABCDEF";
static u32 decode_cheat(char *string, int index)
{
char *p;
int i,n;
u32 len = 0;
u32 address = 0;
u16 data = 0;
u8 ref = 0;
/* 16-bit Game Genie code (ABCD-EFGH) */
if ((strlen(string) >= 9) && (string[4] == '-'))
{
/* 16-bit system only */
if ((system_hw & SYSTEM_PBC) != SYSTEM_MD)
{
return 0;
}
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;
}
}
/* code length */
len = 9;
}
/* 8-bit Game Genie code (DDA-AAA-XXX) */
else if ((strlen(string) >= 11) && (string[3] == '-') && (string[7] == '-'))
{
/* 8-bit system only */
if ((system_hw & SYSTEM_PBC) == SYSTEM_MD)
{
return 0;
}
/* decode 8-bit data */
for (i=0; i<2; i++)
{
p = strchr (arvalidchars, *string++);
if (p == NULL) return 0;
n = (p - arvalidchars) & 0xF;
data |= (n << ((1 - i) * 4));
}
/* decode 16-bit address (low 12-bits) */
for (i=0; i<3; i++)
{
if (i==1) string++; /* skip separator */
p = strchr (arvalidchars, *string++);
if (p == NULL) return 0;
n = (p - arvalidchars) & 0xF;
address |= (n << ((2 - i) * 4));
}
/* decode 16-bit address (high 4-bits) */
p = strchr (arvalidchars, *string++);
if (p == NULL) return 0;
n = (p - arvalidchars) & 0xF;
n ^= 0xF; /* bits inversion */
address |= (n << 12);
/* RAM address are also supported */
if (address >= 0xC000)
{
/* convert to 24-bit Work RAM address */
address = 0xFF0000 | (address & 0x1FFF);
}
/* decode reference 8-bit data */
for (i=0; i<2; i++)
{
string++; /* skip separator and 2nd digit */
p = strchr (arvalidchars, *string++);
if (p == NULL) return 0;
n = (p - arvalidchars) & 0xF;
ref |= (n << ((1 - i) * 4));
}
ref = (ref >> 2) | ((ref & 0x03) << 6); /* 2-bit right rotation */
ref ^= 0xBA; /* XOR */
/* update old data value */
cheatlist[index].old = ref;
/* code length */
len = 11;
}
/* Action Replay code */
else if (string[6] == ':')
{
if ((system_hw & SYSTEM_PBC) == SYSTEM_MD)
{
/* 16-bit code (AAAAAA:DDDD) */
if (strlen(string) < 11) return 0;
/* decode 24-bit address */
for (i=0; i<6; i++)
{
p = strchr (arvalidchars, *string++);
if (p == NULL) return 0;
n = (p - arvalidchars) & 0xF;
address |= (n << ((5 - i) * 4));
}
/* decode 16-bit data */
string++;
for (i=0; i<4; i++)
{
p = strchr (arvalidchars, *string++);
if (p == NULL) return 0;
n = (p - arvalidchars) & 0xF;
data |= (n << ((3 - i) * 4));
}
/* code length */
len = 11;
}
else
{
/* 8-bit code (xxAAAA:DD) */
if (strlen(string) < 9) return 0;
/* decode 16-bit address */
string+=2;
for (i=0; i<4; i++)
{
p = strchr (arvalidchars, *string++);
if (p == NULL) return 0;
n = (p - arvalidchars) & 0xF;
address |= (n << ((3 - i) * 4));
}
/* ROM addresses are not supported */
if (address < 0xC000) return 0;
/* convert to 24-bit Work RAM address */
address = 0xFF0000 | (address & 0x1FFF);
/* decode 8-bit data */
string++;
for (i=0; i<2; i++)
{
p = strchr (arvalidchars, *string++);
if (p == NULL) return 0;
n = (p - arvalidchars) & 0xF;
data |= (n << ((1 - i) * 4));
}
/* code length */
len = 9;
}
}
/* Valid code found ? */
if (len)
{
/* update cheat address & data values */
cheatlist[index].address = address;
cheatlist[index].data = data;
}
/* return code length (0 = invalid) */
return len;
}
static void apply_cheats(void)
{
u8 *ptr;
/* clear ROM&RAM patches counter */
maxROMcheats = maxRAMcheats = 0;
int i;
for (i = 0; i < maxcheats; i++)
{
if (cheatlist[i].enable)
{
if (cheatlist[i].address < cart.romsize)
{
if ((system_hw & SYSTEM_PBC) == SYSTEM_MD)
{
/* patch ROM data */
cheatlist[i].old = *(u16 *)(cart.rom + (cheatlist[i].address & 0xFFFFFE));
*(u16 *)(cart.rom + (cheatlist[i].address & 0xFFFFFE)) = cheatlist[i].data;
}
else
{
/* add ROM patch */
maxROMcheats++;
cheatIndexes[MAX_CHEATS - maxROMcheats] = i;
/* get current banked ROM address */
ptr = &z80_readmap[(cheatlist[i].address) >> 10][cheatlist[i].address & 0x03FF];
/* check if reference matches original ROM data */
if (((u8)cheatlist[i].old) == *ptr)
{
/* patch data */
*ptr = cheatlist[i].data;
/* save patched ROM address */
cheatlist[i].prev = ptr;
}
else
{
/* no patched ROM address yet */
cheatlist[i].prev = NULL;
}
}
}
else if (cheatlist[i].address >= 0xFF0000)
{
/* add RAM patch */
cheatIndexes[maxRAMcheats++] = i;
}
}
}
}
static void clear_cheats(void)
{
int i = maxcheats;
/* disable cheats in reversed order in case the same address is used by multiple patches */
while (i > 0)
{
if (cheatlist[i-1].enable)
{
if (cheatlist[i-1].address < cart.romsize)
{
if ((system_hw & SYSTEM_PBC) == SYSTEM_MD)
{
/* restore original ROM data */
*(u16 *)(cart.rom + (cheatlist[i-1].address & 0xFFFFFE)) = cheatlist[i-1].old;
}
else
{
/* check if previous banked ROM address has been patched */
if (cheatlist[i-1].prev != NULL)
{
/* restore original data */
*cheatlist[i-1].prev = cheatlist[i-1].old;
/* no more patched ROM address */
cheatlist[i-1].prev = NULL;
}
}
}
}
i--;
}
}
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[MAX_DESC_LENGTH];
/* 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; ((offset + i) < maxcheats) && (i < 10); i++)
{
if (i == selection)
{
/* selection bar */
gxDrawTexture(bar_over.texture,16,yoffset+1,356,24,255);
/* cheat description need to be specifically handled */
if (type)
{
/* check if text is being edited */
if (menu_cheats.bg_images[6].state & IMAGE_VISIBLE)
{
/* adjust offset so that last characters are visible */
string_offset += FONT_writeCenter(cheatlist[offset + i].text+string_offset,16,40,366,yoffset+21,(GXColor)WHITE);
}
else
{
/* scroll text (speed = 1/10 frame) */
if ((string_offset/10) >= strlen(cheatlist[offset + i].text))
{
string_offset = 0;
}
if (string_offset)
{
sprintf(temp,"%s ",cheatlist[offset + i].text+string_offset/10);
strncat(temp, cheatlist[offset + i].text, string_offset/10);
}
else
{
strcpy(temp, cheatlist[offset + 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[offset + i].code,18,40,366,yoffset+22,(GXColor)WHITE);
}
}
else
{
if (type)
{
FONT_writeCenter(cheatlist[offset + i].text,16,40,366,yoffset+21,(GXColor)WHITE);
}
else
{
FONT_writeCenter(cheatlist[offset + i].code,18,40,366,yoffset+22,(GXColor)WHITE);
}
}
/* draw cheat enable mark */
if (cheatlist[offset + i].enable)
{
gxDrawTexture(star.texture,20,yoffset+5,16,16,255);
}
yoffset += 26;
}
/* New Entry */
if (i < 10)
{
if (i == 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[offset + 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);
gxTextureClose(&star.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 ((offset + selection) < 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[offset + selection].enable ? "Disable\nCheat":"Enable\nCheat",16,404,436,640,(GXColor)WHITE);
#else
key_delete.texture = gxTextureOpenPNG(Key_L_gcn_png,0);
key_enable.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[offset + 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 digit_cnt = 0;
int max = 0;
char temp[256];
char *str = NULL;
gui_menu *m = &menu_cheats;
/* clear existing ROM patches */
clear_cheats();
/* reset scrolling */
string_offset = 0;
/* 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)
{
/* update arrows buttons */
if (offset > 0) m->arrows[0]->state |= BUTTON_VISIBLE;
else m->arrows[0]->state &= ~BUTTON_VISIBLE;
if ((offset + 10) < (maxcheats + 1)) m->arrows[1]->state |= BUTTON_VISIBLE;
else m->arrows[1]->state &= ~BUTTON_VISIBLE;
/* draw menu */
GUI_DrawMenu(m);
/* restore cheats offset */
if (!(menu_cheats.bg_images[6].state & IMAGE_VISIBLE))
{
m->offset = offset;
m->max_items = maxcheats + 1;
m->max_buttons = 10;
}
/* update menu */
update = GUI_UpdateMenu(m);
/* update selected cheat */
if ((m->selected < 10) && (selection != m->selected))
{
selection = m->selected;
string_offset = 0;
}
/* save offset then restore default */
if (!(m->bg_images[6].state & IMAGE_VISIBLE))
{
offset = m->offset;
m->offset = 0;
m->max_items = m->max_buttons = 30;
}
/* 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 + offset) != maxcheats))
{
/* cheat description */
str = cheatlist[offset + selection].text;
strcpy(temp, str);
max = MAX_DESC_LENGTH - 2;
digit_cnt = strlen(str);
if (digit_cnt <= max)
{
str[digit_cnt] = '*';
str[digit_cnt+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[offset + selection].code;
strcpy(temp, str);
if ((offset + selection) == maxcheats)
{
/* initialize code */
max = 0;
digit_cnt = 0;
str[0] = '*';
str[1] = 0;
}
else
{
/* code type */
if (str[6] == ':')
{
/* Action Replay code */
if ((system_hw & SYSTEM_PBC) == SYSTEM_MD)
{
/* 16-bit code */
max = 10;
}
else
{
/* 8-bit code */
max = 8;
}
}
else if (str[4] == '-')
{
/* 16-bit Game Genie code */
max = 8;
}
else
{
/* 8-bit Game Genie code */
max = 10;
}
/* set cursor to end of code */
digit_cnt = 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;
/* disable arrow buttons */
m->arrows[0]->state &= ~BUTTON_ACTIVE;
m->arrows[1]->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 (digit_cnt > 0)
{
/* delete last character */
str[digit_cnt--] = 0;
/* code separator is being deleted */
if ((str[digit_cnt] == ':') || (str[digit_cnt] == '-'))
{
/* reset detected code type (except 8-bit Game Genie code using 2 separators) */
if (((system_hw & SYSTEM_PBC) == SYSTEM_MD) || (digit_cnt != 7))
{
max = 0;
}
}
/* edit mark */
str[digit_cnt] = '*';
/* update scroll value if necessary */
if (string_offset > 0) string_offset--;
}
break;
}
case 27:
{
if (type && ((offset + selection) != maxcheats))
{
/* SPACE character */
if (digit_cnt <= max)
{
str[digit_cnt++] = ' ';
str[digit_cnt] = 0;
if (digit_cnt <= max)
{
str[digit_cnt] = '*';
str[digit_cnt+1] = 0;
}
}
}
else
{
/* Separator character */
if ((system_hw & SYSTEM_PBC) == SYSTEM_MD)
{
/* 16-bit codes */
if (digit_cnt == 4)
{
/* Game Genie code */
max = 8;
str[4] = '-';
}
else if ((digit_cnt == 6) && (max != 8))
{
/* Action Replay code */
max = 10;
str[6] = ':';
}
else
{
break;
}
}
else
{
/* 8-bit codes */
if (digit_cnt == 3)
{
/* Game Genie code */
max = 10;
str[3] = '-';
}
else if ((digit_cnt == 7) && (max == 10))
{
/* Game Genie code (last part) */
str[7] = '-';
}
else if ((digit_cnt == 6) && (max != 10))
{
/* Action Replay code */
max = 8;
str[6] = ':';
}
else
{
break;
}
}
digit_cnt++;
str[digit_cnt] = '*';
str[digit_cnt+1] = 0;
}
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 && ((offset + selection) != maxcheats))
{
str[digit_cnt] = 0;
update = -1;
}
else if (max && (digit_cnt > max))
{
if (decode_cheat(cheatlist[offset + selection].code, offset + selection))
{
/* new cheat ? */
if ((offset + selection) == maxcheats)
{
/* increase cheat count */
maxcheats++;
/* enable cheat by default */
cheatlist[offset + selection].enable = 1;
/* no description by default */
strcpy(cheatlist[offset + 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) && (digit_cnt == 6)) break;
/* force 8-bit Game Genie code last separator */
if (((system_hw & SYSTEM_PBC) != SYSTEM_MD) && (max == 10) && (digit_cnt == 7)) break;
/* add character */
if ((digit_cnt <= max) || (max == 0))
{
str[digit_cnt++] = m->items[m->selected].text[0];
str[digit_cnt] = 0;
if ((digit_cnt <= max) || (max == 0))
{
str[digit_cnt] = '*';
str[digit_cnt+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 ((offset + selection) < maxcheats)
{
/* Special inputs */
if (m_input.keys & PAD_TRIGGER_L)
{
/* sort cheat list */
for (i = offset + 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 - offset) < 10)
{
m->buttons[maxcheats - offset].state &= ~BUTTON_ACTIVE;
m->buttons[maxcheats - offset - 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[offset + 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;
/* update left buttons */
for (i=0; i<10; i++)
{
if ((offset + i) < maxcheats)
{
m->buttons[i].state |= BUTTON_ACTIVE;
m->buttons[i].shift[1] = 1;
}
else if ((offset + i) == maxcheats)
{
m->buttons[i].state |= BUTTON_ACTIVE;
m->buttons[i].shift[1] = 0;
}
else
{
m->buttons[i].state &= ~BUTTON_ACTIVE;
m->buttons[i].shift[1] = 0;
}
}
/* enable arrow buttons */
m->arrows[0]->state |= BUTTON_ACTIVE;
m->arrows[1]->state |= BUTTON_ACTIVE;
/* 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; i<maxcheats; i++)
{
fprintf(f, "%s\t%s\n", cheatlist[i].code, cheatlist[i].text);
fprintf(f, "%s\n", cheatlist[i].enable ? "ON" : "OFF");
}
fclose(f);
}
}
/* unlock 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;
/* 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;
char temp[256];
/* reset cheat count */
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 < MAX_CHEATS) && (cnt < MAX_CHEATS))
{
/* 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 */
len = decode_cheat(temp, maxcheats);
if (len)
{
/* 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], MAX_DESC_LENGTH - 1);
cheatlist[maxcheats].text[MAX_DESC_LENGTH - 1] = 0;
/* increment cheat count */
maxcheats++;
}
else if (!strcmp(temp,"ON") && config.autocheat)
{
/* enable flag */
cheatlist[cnt++].enable = 1;
}
else if (!strcmp(temp,"OFF") && config.autocheat)
{
/* 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<10; cnt++)
{
if (cnt < maxcheats)
{
menu_cheats.buttons[cnt].state |= BUTTON_ACTIVE;
menu_cheats.buttons[cnt].shift[1] = 1;
}
else if (cnt == maxcheats)
{
menu_cheats.buttons[cnt].state |= BUTTON_ACTIVE;
menu_cheats.buttons[cnt].shift[1] = 0;
}
else
{
menu_cheats.buttons[cnt].shift[1] = 0;
menu_cheats.buttons[cnt].state &= ~BUTTON_ACTIVE;
}
}
/* reset menu */
selection = offset = 0;
}
/****************************************************************************
* RAMCheatUpdate
*
* Apply RAM patches (this should be called once per frame)
*
****************************************************************************/
void RAMCheatUpdate(void)
{
int index, cnt = maxRAMcheats;
while (cnt)
{
/* get cheat index */
index = cheatIndexes[--cnt];
/* apply RAM patch */
if (cheatlist[index].data & 0xFF00)
{
/* word patch */
*(u16 *)(work_ram + (cheatlist[index].address & 0xFFFE)) = cheatlist[index].data;
}
else
{
/* byte patch */
work_ram[cheatlist[index].address & 0xFFFF] = cheatlist[index].data;
}
}
}
/****************************************************************************
* ROMCheatUpdate
*
* Apply ROM patches (this should be called each time banking is changed)
*
****************************************************************************/
void ROMCheatUpdate(void)
{
int index, cnt = maxROMcheats;
u8 *ptr;
while (cnt)
{
/* get cheat index */
index = cheatIndexes[MAX_CHEATS - cnt];
/* check if previous banked ROM address was patched */
if (cheatlist[index].prev != NULL)
{
/* restore original data */
*cheatlist[index].prev = cheatlist[index].old;
/* no more patched ROM address */
cheatlist[index].prev = NULL;
}
/* get current banked ROM address */
ptr = &z80_readmap[(cheatlist[index].address) >> 10][cheatlist[index].address & 0x03FF];
/* check if reference matches original ROM data */
if (((u8)cheatlist[index].old) == *ptr)
{
/* patch data */
*ptr = cheatlist[index].data;
/* save patched ROM address */
cheatlist[index].prev = ptr;
}
/* next ROM patch */
cnt--;
}
}