mirror of
https://github.com/dborth/vbagx.git
synced 2024-11-01 16:35:11 +01:00
1837 lines
49 KiB
C++
1837 lines
49 KiB
C++
// VisualBoyAdvance - Nintendo Gameboy/GameboyAdvance (TM) emulator.
|
|
// Copyright (C) 1999-2003 Forgotten
|
|
// Copyright (C) 2004 Forgotten and the VBA development team
|
|
|
|
// 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, 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 "sdfileio.h"
|
|
#include <string.h>
|
|
#include <string.h>
|
|
#include <stdio.h>
|
|
#include <ctype.h>
|
|
|
|
#include "GBA.h"
|
|
#include "GBAinline.h"
|
|
#include "Cheats.h"
|
|
#include "Globals.h"
|
|
#include "NLS.h"
|
|
#include "Util.h"
|
|
|
|
/**
|
|
* Gameshark code types:
|
|
*
|
|
* NNNNNNNN 001DC0DE - ID code for the game (game 4 character name) from ROM
|
|
* DEADFACE XXXXXXXX - changes decryption seeds
|
|
* 0AAAAAAA 000000YY - 8-bit constant write
|
|
* 1AAAAAAA 0000YYYY - 16-bit constant write
|
|
* 2AAAAAAA YYYYYYYY - 32-bit constant write
|
|
* 3AAAAAAA YYYYYYYY - ??
|
|
* 6AAAAAAA 0000YYYY - 16-bit ROM Patch (address >> 1)
|
|
* 6AAAAAAA 1000YYYY - 16-bit ROM Patch ? (address >> 1)
|
|
* 6AAAAAAA 2000YYYY - 16-bit ROM Patch ? (address >> 1)
|
|
* 8A1AAAAA 000000YY - 8-bit button write
|
|
* 8A2AAAAA 0000YYYY - 16-bit button write
|
|
* 8A3AAAAA YYYYYYYY - 32-bit button write
|
|
* 80F00000 0000YYYY - button slow motion
|
|
* DAAAAAAA 0000YYYY - if address contains 16-bit value enable next code
|
|
* FAAAAAAA 0000YYYY - Master code function
|
|
*
|
|
* CodeBreaker codes types:
|
|
*
|
|
* 0000AAAA 000Y - Game CRC (Y are flags: 8 - CRC, 2 - DI)
|
|
* 1AAAAAAA YYYY - Master Code function (store address at ((YYYY << 0x16)
|
|
* + 0x08000100))
|
|
* 2AAAAAAA YYYY - 16-bit or
|
|
* 3AAAAAAA YYYY - 8-bit constant write
|
|
* 4AAAAAAA YYYY - Slide code
|
|
* XXXXCCCC IIII (C is count and I is address increment, X is value incr.)
|
|
* 5AAAAAAA CCCC - Super code (Write bytes to address, CCCC is count)
|
|
* BBBBBBBB BBBB
|
|
* 6AAAAAAA YYYY - 16-bit and
|
|
* 7AAAAAAA YYYY - if address contains 16-bit value enable next code
|
|
* 8AAAAAAA YYYY - 16-bit constant write
|
|
* 9AAAAAAA YYYY - change decryption (when first code only?)
|
|
* AAAAAAAA YYYY - if address does not contain 16-bit value enable next code
|
|
* BAAAAAAA YYYY - if 16-bit < YYYY
|
|
* CAAAAAAA YYYY - if 16-bit > YYYY
|
|
* D0000020 YYYY - if button keys equal value enable next code
|
|
* EAAAAAAA YYYY - increase value stored in address
|
|
*/
|
|
#define UNKNOWN_CODE -1
|
|
#define INT_8_BIT_WRITE 0
|
|
#define INT_16_BIT_WRITE 1
|
|
#define INT_32_BIT_WRITE 2
|
|
#define GSA_16_BIT_ROM_PATCH 3
|
|
#define GSA_8_BIT_GS_WRITE 4
|
|
#define GSA_16_BIT_GS_WRITE 5
|
|
#define GSA_32_BIT_GS_WRITE 6
|
|
#define CBA_IF_KEYS_PRESSED 7
|
|
#define CBA_IF_TRUE 8
|
|
#define CBA_SLIDE_CODE 9
|
|
#define CBA_IF_FALSE 10
|
|
#define CBA_AND 11
|
|
#define GSA_8_BIT_GS_WRITE2 12
|
|
#define GSA_16_BIT_GS_WRITE2 13
|
|
#define GSA_32_BIT_GS_WRITE2 14
|
|
#define GSA_16_BIT_ROM_PATCH2 15
|
|
#define GSA_8_BIT_SLIDE 16
|
|
#define GSA_16_BIT_SLIDE 17
|
|
#define GSA_32_BIT_SLIDE 18
|
|
#define GSA_8_BIT_IF_TRUE 19
|
|
#define GSA_32_BIT_IF_TRUE 20
|
|
#define GSA_8_BIT_IF_FALSE 21
|
|
#define GSA_32_BIT_IF_FALSE 22
|
|
#define GSA_8_BIT_FILL 23
|
|
#define GSA_16_BIT_FILL 24
|
|
#define GSA_8_BIT_IF_TRUE2 25
|
|
#define GSA_16_BIT_IF_TRUE2 26
|
|
#define GSA_32_BIT_IF_TRUE2 27
|
|
#define GSA_8_BIT_IF_FALSE2 28
|
|
#define GSA_16_BIT_IF_FALSE2 29
|
|
#define GSA_32_BIT_IF_FALSE2 30
|
|
#define GSA_SLOWDOWN 31
|
|
#define CBA_ADD 32
|
|
#define CBA_OR 33
|
|
#define CBA_LT 34
|
|
#define CBA_GT 35
|
|
#define CBA_SUPER 36
|
|
|
|
CheatsData cheatsList[100];
|
|
int cheatsNumber = 0;
|
|
|
|
u8 cheatsCBASeedBuffer[0x30];
|
|
u32 cheatsCBASeed[4];
|
|
u32 cheatsCBATemporaryValue = 0;
|
|
u16 cheatsCBATable[256];
|
|
bool cheatsCBATableGenerated = false;
|
|
|
|
u8 cheatsCBACurrentSeed[12] = {
|
|
0x00, 0x00, 0x00, 0x00,
|
|
0x00, 0x00, 0x00, 0x00,
|
|
0x00, 0x00, 0x00, 0x00
|
|
};
|
|
|
|
#define CHEAT_IS_HEX(a) ( ((a)>='A' && (a) <='F') || ((a) >='0' && (a) <= '9'))
|
|
|
|
#define CHEAT_PATCH_ROM_16BIT(a,v) \
|
|
WRITE16LE(((u16 *)&rom[(a) & 0x1ffffff]), v);
|
|
|
|
static bool isMultilineWithData(int i)
|
|
{
|
|
// we consider it a multiline code if it has more than one line of data
|
|
// otherwise, it can still be considered a single code
|
|
if(i < cheatsNumber && i >= 0)
|
|
switch(cheatsList[i].size)
|
|
{
|
|
case INT_8_BIT_WRITE:
|
|
case INT_16_BIT_WRITE:
|
|
case INT_32_BIT_WRITE:
|
|
case GSA_16_BIT_ROM_PATCH:
|
|
case GSA_8_BIT_GS_WRITE:
|
|
case GSA_16_BIT_GS_WRITE:
|
|
case GSA_32_BIT_GS_WRITE:
|
|
case CBA_AND:
|
|
case CBA_IF_KEYS_PRESSED:
|
|
case CBA_IF_TRUE:
|
|
case CBA_IF_FALSE:
|
|
case GSA_8_BIT_IF_TRUE:
|
|
case GSA_32_BIT_IF_TRUE:
|
|
case GSA_8_BIT_IF_FALSE:
|
|
case GSA_32_BIT_IF_FALSE:
|
|
case GSA_8_BIT_FILL:
|
|
case GSA_16_BIT_FILL:
|
|
case GSA_8_BIT_IF_TRUE2:
|
|
case GSA_16_BIT_IF_TRUE2:
|
|
case GSA_32_BIT_IF_TRUE2:
|
|
case GSA_8_BIT_IF_FALSE2:
|
|
case GSA_16_BIT_IF_FALSE2:
|
|
case GSA_32_BIT_IF_FALSE2:
|
|
case GSA_SLOWDOWN:
|
|
case CBA_ADD:
|
|
case CBA_OR:
|
|
return false;
|
|
// the codes below have two lines of data
|
|
case CBA_SLIDE_CODE:
|
|
case GSA_8_BIT_GS_WRITE2:
|
|
case GSA_16_BIT_GS_WRITE2:
|
|
case GSA_32_BIT_GS_WRITE2:
|
|
case GSA_16_BIT_ROM_PATCH2:
|
|
case GSA_8_BIT_SLIDE:
|
|
case GSA_16_BIT_SLIDE:
|
|
case GSA_32_BIT_SLIDE:
|
|
case CBA_LT:
|
|
case CBA_GT:
|
|
case CBA_SUPER:
|
|
return true;
|
|
}
|
|
return false;
|
|
}
|
|
|
|
static int getCodeLength(int num)
|
|
{
|
|
if(num >= cheatsNumber || num < 0)
|
|
return 1;
|
|
|
|
// this is for all the codes that are true multiline
|
|
switch(cheatsList[num].size)
|
|
{
|
|
case INT_8_BIT_WRITE:
|
|
case INT_16_BIT_WRITE:
|
|
case INT_32_BIT_WRITE:
|
|
case GSA_16_BIT_ROM_PATCH:
|
|
case GSA_8_BIT_GS_WRITE:
|
|
case GSA_16_BIT_GS_WRITE:
|
|
case GSA_32_BIT_GS_WRITE:
|
|
case CBA_AND:
|
|
case GSA_8_BIT_FILL:
|
|
case GSA_16_BIT_FILL:
|
|
case GSA_SLOWDOWN:
|
|
case CBA_ADD:
|
|
case CBA_OR:
|
|
return 1;
|
|
case CBA_IF_KEYS_PRESSED:
|
|
case CBA_IF_TRUE:
|
|
case CBA_IF_FALSE:
|
|
case CBA_SLIDE_CODE:
|
|
case GSA_8_BIT_GS_WRITE2:
|
|
case GSA_16_BIT_GS_WRITE2:
|
|
case GSA_32_BIT_GS_WRITE2:
|
|
case GSA_16_BIT_ROM_PATCH2:
|
|
case GSA_8_BIT_SLIDE:
|
|
case GSA_16_BIT_SLIDE:
|
|
case GSA_32_BIT_SLIDE:
|
|
case GSA_8_BIT_IF_TRUE:
|
|
case GSA_32_BIT_IF_TRUE:
|
|
case GSA_8_BIT_IF_FALSE:
|
|
case GSA_32_BIT_IF_FALSE:
|
|
case CBA_LT:
|
|
case CBA_GT:
|
|
return 2;
|
|
case GSA_8_BIT_IF_TRUE2:
|
|
case GSA_16_BIT_IF_TRUE2:
|
|
case GSA_32_BIT_IF_TRUE2:
|
|
case GSA_8_BIT_IF_FALSE2:
|
|
case GSA_16_BIT_IF_FALSE2:
|
|
case GSA_32_BIT_IF_FALSE2:
|
|
return 3;
|
|
case CBA_SUPER:
|
|
return (cheatsList[num].value+5)/6;
|
|
}
|
|
return 1;
|
|
}
|
|
|
|
int cheatsCheckKeys(u32 keys, u32 extended)
|
|
{
|
|
int ticks = 0;
|
|
for(int i = 0; i < cheatsNumber; i++)
|
|
{
|
|
if(!cheatsList[i].enabled)
|
|
{
|
|
// make sure we skip other lines in this code
|
|
i += getCodeLength(i)-1;
|
|
continue;
|
|
}
|
|
switch(cheatsList[i].size)
|
|
{
|
|
case INT_8_BIT_WRITE:
|
|
CPUWriteByte(cheatsList[i].address, cheatsList[i].value);
|
|
break;
|
|
case INT_16_BIT_WRITE:
|
|
CPUWriteHalfWord(cheatsList[i].address, cheatsList[i].value);
|
|
break;
|
|
case INT_32_BIT_WRITE:
|
|
CPUWriteMemory(cheatsList[i].address, cheatsList[i].value);
|
|
break;
|
|
case GSA_16_BIT_ROM_PATCH:
|
|
if((cheatsList[i].status & 1) == 0)
|
|
{
|
|
if(CPUReadHalfWord(cheatsList[i].address) != cheatsList[i].value)
|
|
{
|
|
cheatsList[i].oldValue = CPUReadHalfWord(cheatsList[i].address);
|
|
cheatsList[i].status |= 1;
|
|
CHEAT_PATCH_ROM_16BIT(cheatsList[i].address, cheatsList[i].value);
|
|
}
|
|
}
|
|
break;
|
|
case GSA_8_BIT_GS_WRITE:
|
|
if(extended & 4)
|
|
{
|
|
CPUWriteByte(cheatsList[i].address, cheatsList[i].value);
|
|
}
|
|
break;
|
|
case GSA_16_BIT_GS_WRITE:
|
|
if(extended & 4)
|
|
{
|
|
CPUWriteHalfWord(cheatsList[i].address, cheatsList[i].value);
|
|
}
|
|
break;
|
|
case GSA_32_BIT_GS_WRITE:
|
|
if(extended & 4)
|
|
{
|
|
CPUWriteMemory(cheatsList[i].address, cheatsList[i].value);
|
|
}
|
|
break;
|
|
case CBA_IF_KEYS_PRESSED:
|
|
{
|
|
u16 value = cheatsList[i].value;
|
|
u32 addr = cheatsList[i].address;
|
|
if((addr & 0x30) == 0x20)
|
|
{
|
|
if((keys & value) != value)
|
|
{
|
|
i++;
|
|
}
|
|
}
|
|
else if((addr & 0x30) == 0x10)
|
|
{
|
|
if((keys & value) == value)
|
|
{
|
|
i++;
|
|
}
|
|
}
|
|
}
|
|
break;
|
|
case CBA_IF_TRUE:
|
|
if(CPUReadHalfWord(cheatsList[i].address) != cheatsList[i].value)
|
|
{
|
|
i++;
|
|
}
|
|
break;
|
|
case CBA_SLIDE_CODE:
|
|
{
|
|
u32 address = cheatsList[i].address;
|
|
u16 value = cheatsList[i].value;
|
|
i++;
|
|
if(i < cheatsNumber)
|
|
{
|
|
int count = (cheatsList[i].address & 0xFFFF);
|
|
u16 vinc = (cheatsList[i].address >> 16) & 0xFFFF;
|
|
int inc = cheatsList[i].value;
|
|
|
|
for(int x = 0; x < count; x++)
|
|
{
|
|
CPUWriteHalfWord(address, value);
|
|
address += inc;
|
|
value += vinc;
|
|
}
|
|
}
|
|
}
|
|
break;
|
|
case CBA_IF_FALSE:
|
|
if(CPUReadHalfWord(cheatsList[i].address) == cheatsList[i].value)
|
|
{
|
|
i++;
|
|
}
|
|
break;
|
|
case CBA_AND:
|
|
CPUWriteHalfWord(cheatsList[i].address,
|
|
CPUReadHalfWord(cheatsList[i].address) &
|
|
cheatsList[i].value);
|
|
break;
|
|
case GSA_8_BIT_GS_WRITE2:
|
|
i++;
|
|
if(i < cheatsNumber)
|
|
{
|
|
if(extended & 4)
|
|
{
|
|
CPUWriteByte(cheatsList[i-1].value, cheatsList[i].address);
|
|
}
|
|
}
|
|
break;
|
|
case GSA_16_BIT_GS_WRITE2:
|
|
i++;
|
|
if(i < cheatsNumber)
|
|
{
|
|
if(extended & 4)
|
|
{
|
|
CPUWriteHalfWord(cheatsList[i-1].value, cheatsList[i].address);
|
|
}
|
|
}
|
|
break;
|
|
case GSA_32_BIT_GS_WRITE2:
|
|
i++;
|
|
if(i < cheatsNumber)
|
|
{
|
|
if(extended & 4)
|
|
{
|
|
CPUWriteMemory(cheatsList[i-1].value, cheatsList[i].address);
|
|
}
|
|
}
|
|
break;
|
|
case GSA_16_BIT_ROM_PATCH2:
|
|
i++;
|
|
if(i < cheatsNumber)
|
|
{
|
|
if((cheatsList[i-1].status & 1) == 0)
|
|
{
|
|
u32 addr = ((cheatsList[i-1].value & 0x00FFFFFF) << 1) + 0x8000000;
|
|
if(CPUReadHalfWord(addr) != (cheatsList[i].address & 0xFFFF))
|
|
{
|
|
cheatsList[i-1].oldValue = CPUReadHalfWord(addr);
|
|
cheatsList[i-1].status |= 1;
|
|
CHEAT_PATCH_ROM_16BIT(addr,cheatsList[i].address & 0xFFFF);
|
|
}
|
|
}
|
|
}
|
|
break;
|
|
case GSA_8_BIT_SLIDE:
|
|
i++;
|
|
if(i < cheatsNumber)
|
|
{
|
|
u32 addr = cheatsList[i-1].value;
|
|
u8 value = cheatsList[i].address;
|
|
int vinc = (cheatsList[i].value >> 24) & 255;
|
|
int count = (cheatsList[i].value >> 16) & 255;
|
|
int ainc = (cheatsList[i].value & 0xffff);
|
|
while(count > 0)
|
|
{
|
|
CPUWriteByte(addr, value);
|
|
value += vinc;
|
|
addr += ainc;
|
|
count--;
|
|
}
|
|
}
|
|
break;
|
|
case GSA_16_BIT_SLIDE:
|
|
i++;
|
|
if(i < cheatsNumber)
|
|
{
|
|
u32 addr = cheatsList[i-1].value;
|
|
u16 value = cheatsList[i].address;
|
|
int vinc = (cheatsList[i].value >> 24) & 255;
|
|
int count = (cheatsList[i].value >> 16) & 255;
|
|
int ainc = (cheatsList[i].value & 0xffff)*2;
|
|
while(count > 0)
|
|
{
|
|
CPUWriteHalfWord(addr, value);
|
|
value += vinc;
|
|
addr += ainc;
|
|
count--;
|
|
}
|
|
}
|
|
break;
|
|
case GSA_32_BIT_SLIDE:
|
|
i++;
|
|
if(i < cheatsNumber)
|
|
{
|
|
u32 addr = cheatsList[i-1].value;
|
|
u32 value = cheatsList[i].address;
|
|
int vinc = (cheatsList[i].value >> 24) & 255;
|
|
int count = (cheatsList[i].value >> 16) & 255;
|
|
int ainc = (cheatsList[i].value & 0xffff)*4;
|
|
while(count > 0)
|
|
{
|
|
CPUWriteMemory(addr, value);
|
|
value += vinc;
|
|
addr += ainc;
|
|
count--;
|
|
}
|
|
}
|
|
break;
|
|
case GSA_8_BIT_IF_TRUE:
|
|
if(CPUReadByte(cheatsList[i].address) != cheatsList[i].value)
|
|
{
|
|
i++;
|
|
}
|
|
break;
|
|
case GSA_32_BIT_IF_TRUE:
|
|
if(CPUReadMemory(cheatsList[i].address) != cheatsList[i].value)
|
|
{
|
|
i++;
|
|
}
|
|
break;
|
|
case GSA_8_BIT_IF_FALSE:
|
|
if(CPUReadByte(cheatsList[i].address) == cheatsList[i].value)
|
|
{
|
|
i++;
|
|
}
|
|
break;
|
|
case GSA_32_BIT_IF_FALSE:
|
|
if(CPUReadMemory(cheatsList[i].address) == cheatsList[i].value)
|
|
{
|
|
i++;
|
|
}
|
|
break;
|
|
case GSA_8_BIT_FILL:
|
|
{
|
|
u32 addr = cheatsList[i].address;
|
|
u8 v = cheatsList[i].value & 0xff;
|
|
u32 end = addr + (cheatsList[i].value >> 8);
|
|
do
|
|
{
|
|
CPUWriteByte(addr, v);
|
|
addr++;
|
|
}
|
|
while (addr <= end);
|
|
}
|
|
break;
|
|
case GSA_16_BIT_FILL:
|
|
{
|
|
u32 addr = cheatsList[i].address;
|
|
u16 v = cheatsList[i].value & 0xffff;
|
|
u32 end = addr + ((cheatsList[i].value >> 16) << 1);
|
|
do
|
|
{
|
|
CPUWriteHalfWord(addr, v);
|
|
addr+=2;
|
|
}
|
|
while (addr <= end);
|
|
}
|
|
break;
|
|
case GSA_8_BIT_IF_TRUE2:
|
|
if(CPUReadByte(cheatsList[i].address) != cheatsList[i].value)
|
|
{
|
|
i+=2;
|
|
}
|
|
break;
|
|
case GSA_16_BIT_IF_TRUE2:
|
|
if(CPUReadHalfWord(cheatsList[i].address) != cheatsList[i].value)
|
|
{
|
|
i+=2;
|
|
}
|
|
break;
|
|
case GSA_32_BIT_IF_TRUE2:
|
|
if(CPUReadMemory(cheatsList[i].address) != cheatsList[i].value)
|
|
{
|
|
i+=2;
|
|
}
|
|
break;
|
|
case GSA_8_BIT_IF_FALSE2:
|
|
if(CPUReadByte(cheatsList[i].address) == cheatsList[i].value)
|
|
{
|
|
i+=2;
|
|
}
|
|
break;
|
|
case GSA_16_BIT_IF_FALSE2:
|
|
if(CPUReadHalfWord(cheatsList[i].address) == cheatsList[i].value)
|
|
{
|
|
i+=2;
|
|
}
|
|
break;
|
|
case GSA_32_BIT_IF_FALSE2:
|
|
if(CPUReadMemory(cheatsList[i].address) == cheatsList[i].value)
|
|
{
|
|
i+=2;
|
|
}
|
|
break;
|
|
case GSA_SLOWDOWN:
|
|
// check if button was pressed and released, if so toggle our state
|
|
if((cheatsList[i].status & 4) && !(extended & 4))
|
|
cheatsList[i].status ^= 1;
|
|
if(extended & 4)
|
|
cheatsList[i].status |= 4;
|
|
else
|
|
cheatsList[i].status &= ~4;
|
|
|
|
if(cheatsList[i].status & 1)
|
|
ticks += 2*256*((cheatsList[i].value >> 8) & 255);
|
|
break;
|
|
case CBA_ADD:
|
|
CPUWriteHalfWord(cheatsList[i].address,
|
|
CPUReadHalfWord(cheatsList[i].address) +
|
|
(u16)cheatsList[i].value);
|
|
break;
|
|
case CBA_OR:
|
|
CPUWriteHalfWord(cheatsList[i].address,
|
|
CPUReadHalfWord(cheatsList[i].address) |
|
|
cheatsList[i].value);
|
|
break;
|
|
case CBA_LT:
|
|
if(CPUReadHalfWord(cheatsList[i].address) >= cheatsList[i].value)
|
|
i++;
|
|
break;
|
|
case CBA_GT:
|
|
if(CPUReadHalfWord(cheatsList[i].address) <= cheatsList[i].value)
|
|
i++;
|
|
break;
|
|
case CBA_SUPER:
|
|
{
|
|
int count = 2*cheatsList[i].value;
|
|
u32 address = cheatsList[i].address;
|
|
for(int x = 0; x < count; x++)
|
|
{
|
|
u8 b;
|
|
int res = x % 6;
|
|
if(res < 4)
|
|
b = (cheatsList[i].address >> (24-8*res)) & 0xFF;
|
|
else
|
|
b = (cheatsList[i].value >> (8 - 8*(res-4))) & 0x0FF;
|
|
CPUWriteByte(address, b);
|
|
address++;
|
|
if(x && !res)
|
|
i++;
|
|
}
|
|
if(count % 6)
|
|
i++;
|
|
}
|
|
break;
|
|
}
|
|
}
|
|
return ticks;
|
|
}
|
|
|
|
void cheatsAdd(const char *codeStr,
|
|
const char *desc,
|
|
u32 address,
|
|
u32 value,
|
|
int code,
|
|
int size)
|
|
{
|
|
if(cheatsNumber < 100)
|
|
{
|
|
int x = cheatsNumber;
|
|
cheatsList[x].code = code;
|
|
cheatsList[x].size = size;
|
|
cheatsList[x].address = address;
|
|
cheatsList[x].value = value;
|
|
strcpy(cheatsList[x].codestring, codeStr);
|
|
strcpy(cheatsList[x].desc, desc);
|
|
cheatsList[x].enabled = true;
|
|
cheatsList[x].status = 0;
|
|
|
|
// we only store the old value for this simple codes. ROM patching
|
|
// is taken care when it actually patches the ROM
|
|
switch(cheatsList[x].size)
|
|
{
|
|
case INT_8_BIT_WRITE:
|
|
cheatsList[x].oldValue = CPUReadByte(address);
|
|
break;
|
|
case INT_16_BIT_WRITE:
|
|
cheatsList[x].oldValue = CPUReadHalfWord(address);
|
|
break;
|
|
case INT_32_BIT_WRITE:
|
|
cheatsList[x].oldValue = CPUReadMemory(address);
|
|
break;
|
|
}
|
|
cheatsNumber++;
|
|
}
|
|
}
|
|
|
|
void cheatsDelete(int number, bool restore)
|
|
{
|
|
if(number < cheatsNumber && number >= 0)
|
|
{
|
|
int x = number;
|
|
|
|
if(restore)
|
|
{
|
|
switch(cheatsList[x].size)
|
|
{
|
|
case INT_8_BIT_WRITE:
|
|
CPUWriteByte(cheatsList[x].address, (u8)cheatsList[x].oldValue);
|
|
break;
|
|
case INT_16_BIT_WRITE:
|
|
CPUWriteHalfWord(cheatsList[x].address, (u16)cheatsList[x].oldValue);
|
|
break;
|
|
case INT_32_BIT_WRITE:
|
|
CPUWriteMemory(cheatsList[x].address, cheatsList[x].oldValue);
|
|
break;
|
|
case GSA_16_BIT_ROM_PATCH:
|
|
if(cheatsList[x].status & 1)
|
|
{
|
|
cheatsList[x].status &= ~1;
|
|
CHEAT_PATCH_ROM_16BIT(cheatsList[x].address,
|
|
cheatsList[x].oldValue);
|
|
}
|
|
break;
|
|
case GSA_16_BIT_ROM_PATCH2:
|
|
if(cheatsList[x].status & 1)
|
|
{
|
|
cheatsList[x].status &= ~1;
|
|
CHEAT_PATCH_ROM_16BIT(((cheatsList[x].value & 0x00FFFFFF) << 1)+
|
|
0x8000000,
|
|
cheatsList[x].oldValue);
|
|
}
|
|
break;
|
|
}
|
|
}
|
|
if((x+1) < cheatsNumber)
|
|
{
|
|
memcpy(&cheatsList[x], &cheatsList[x+1], sizeof(CheatsData)*
|
|
(cheatsNumber-x-1));
|
|
}
|
|
cheatsNumber--;
|
|
}
|
|
}
|
|
|
|
void cheatsDeleteAll(bool restore)
|
|
{
|
|
for(int i = cheatsNumber-1; i >= 0; i--)
|
|
{
|
|
cheatsDelete(i, restore);
|
|
}
|
|
}
|
|
|
|
void cheatsEnable(int i)
|
|
{
|
|
if(i >= 0 && i < cheatsNumber)
|
|
{
|
|
cheatsList[i].enabled = true;
|
|
}
|
|
}
|
|
|
|
void cheatsDisable(int i)
|
|
{
|
|
if(i >= 0 && i < cheatsNumber)
|
|
{
|
|
switch(cheatsList[i].size)
|
|
{
|
|
case GSA_16_BIT_ROM_PATCH:
|
|
if(cheatsList[i].status & 1)
|
|
{
|
|
cheatsList[i].status &= ~1;
|
|
CHEAT_PATCH_ROM_16BIT(cheatsList[i].address,
|
|
cheatsList[i].oldValue);
|
|
}
|
|
break;
|
|
case GSA_16_BIT_ROM_PATCH2:
|
|
if(cheatsList[i].status & 1)
|
|
{
|
|
cheatsList[i].status &= ~1;
|
|
CHEAT_PATCH_ROM_16BIT(((cheatsList[i].value & 0x00FFFFFF) << 1)+
|
|
0x8000000,
|
|
cheatsList[i].oldValue);
|
|
}
|
|
break;
|
|
}
|
|
cheatsList[i].enabled = false;
|
|
}
|
|
}
|
|
|
|
bool cheatsVerifyCheatCode(const char *code, const char *desc)
|
|
{
|
|
int len = strlen(code);
|
|
if(len != 11 && len != 13 && len != 17)
|
|
{
|
|
systemMessage(MSG_INVALID_CHEAT_CODE, N_("Invalid cheat code '%s'"), code);
|
|
return false;
|
|
}
|
|
|
|
if(code[8] != ':')
|
|
{
|
|
systemMessage(MSG_INVALID_CHEAT_CODE, N_("Invalid cheat code '%s'"), code);
|
|
return false;
|
|
}
|
|
|
|
int i;
|
|
for(i = 0; i < 8; i++)
|
|
{
|
|
if(!CHEAT_IS_HEX(code[i]))
|
|
{
|
|
// wrong cheat
|
|
systemMessage(MSG_INVALID_CHEAT_CODE,
|
|
N_("Invalid cheat code '%s'"), code);
|
|
return false;
|
|
}
|
|
}
|
|
for(i = 9; i < len; i++)
|
|
{
|
|
if(!CHEAT_IS_HEX(code[i]))
|
|
{
|
|
// wrong cheat
|
|
systemMessage(MSG_INVALID_CHEAT_CODE,
|
|
N_("Invalid cheat code '%s'"), code);
|
|
return false;
|
|
}
|
|
}
|
|
|
|
u32 address = 0;
|
|
u32 value = 0;
|
|
|
|
char buffer[10];
|
|
strncpy(buffer, code, 8);
|
|
buffer[8] = 0;
|
|
sscanf(buffer, "%x", &address);
|
|
|
|
switch(address >> 24)
|
|
{
|
|
case 2:
|
|
case 3:
|
|
break;
|
|
default:
|
|
systemMessage(MSG_INVALID_CHEAT_CODE_ADDRESS,
|
|
N_("Invalid cheat code address: %08x"),
|
|
address);
|
|
return false;
|
|
}
|
|
|
|
strncpy(buffer, &code[9], 8);
|
|
sscanf(buffer, "%x", &value);
|
|
int type = 0;
|
|
if(len == 13)
|
|
type = 1;
|
|
if(len == 17)
|
|
type = 2;
|
|
cheatsAdd(code, desc, address, value, type, type);
|
|
return true;
|
|
}
|
|
|
|
void cheatsAddCheatCode(const char *code, const char *desc)
|
|
{
|
|
cheatsVerifyCheatCode(code, desc);
|
|
}
|
|
|
|
void cheatsDecryptGSACode(u32& address, u32& value, bool v3)
|
|
{
|
|
u32 rollingseed = 0xC6EF3720;
|
|
u32 seeds_v1[] = { 0x09F4FBBD, 0x9681884A, 0x352027E9, 0xF3DEE5A7 };
|
|
u32 seeds_v3[] = { 0x7AA9648F, 0x7FAE6994, 0xC0EFAAD5, 0x42712C57 };
|
|
u32 *seeds = v3 ? seeds_v3 : seeds_v1;
|
|
|
|
int bitsleft = 32;
|
|
while (bitsleft > 0)
|
|
{
|
|
value -= ((((address << 4) + seeds[2]) ^ (address + rollingseed)) ^
|
|
((address >> 5) + seeds[3]));
|
|
address -= ((((value << 4) + seeds[0]) ^ (value + rollingseed)) ^
|
|
((value >> 5) + seeds[1]));
|
|
rollingseed -= 0x9E3779B9;
|
|
bitsleft--;
|
|
}
|
|
}
|
|
|
|
void cheatsAddGSACode(const char *code, const char *desc, bool v3)
|
|
{
|
|
if(strlen(code) != 16)
|
|
{
|
|
// wrong cheat
|
|
systemMessage(MSG_INVALID_GSA_CODE,
|
|
N_("Invalid GSA code. Format is XXXXXXXXYYYYYYYY"));
|
|
return;
|
|
}
|
|
|
|
int i;
|
|
for(i = 0; i < 16; i++)
|
|
{
|
|
if(!CHEAT_IS_HEX(code[i]))
|
|
{
|
|
// wrong cheat
|
|
systemMessage(MSG_INVALID_GSA_CODE,
|
|
N_("Invalid GSA code. Format is XXXXXXXXYYYYYYYY"));
|
|
return;
|
|
}
|
|
}
|
|
|
|
char buffer[10];
|
|
strncpy(buffer, code, 8);
|
|
buffer[8] = 0;
|
|
u32 address;
|
|
sscanf(buffer, "%x", &address);
|
|
strncpy(buffer, &code[8], 8);
|
|
buffer[8] = 0;
|
|
u32 value;
|
|
sscanf(buffer, "%x", &value);
|
|
|
|
cheatsDecryptGSACode(address, value, v3);
|
|
|
|
if(value == 0x1DC0DE)
|
|
{
|
|
u32 gamecode = READ32LE(((u32 *)&rom[0xac]));
|
|
if(gamecode != address)
|
|
{
|
|
char buffer[5];
|
|
*((u32 *)buffer) = address;
|
|
buffer[4] = 0;
|
|
char buffer2[5];
|
|
*((u32 *)buffer2) = READ32LE(((u32 *)&rom[0xac]));
|
|
buffer2[4] = 0;
|
|
systemMessage(MSG_GBA_CODE_WARNING, N_("Warning: cheats are for game %s. Current game is %s.\nCodes may not work correctly."),
|
|
buffer, buffer2);
|
|
}
|
|
cheatsAdd(code, desc, address & 0x0FFFFFFF, value, v3 ? 257 : 256,
|
|
UNKNOWN_CODE);
|
|
return;
|
|
}
|
|
if(isMultilineWithData(cheatsNumber-1))
|
|
{
|
|
cheatsAdd(code, desc, address, value, v3 ? 257 : 256, UNKNOWN_CODE);
|
|
return;
|
|
}
|
|
if(v3)
|
|
{
|
|
int type = (address >> 25) & 127;
|
|
u32 addr = (address & 0x00F00000) << 4 | (address & 0x0003FFFF);
|
|
switch(type)
|
|
{
|
|
case 0x00:
|
|
if(address == 0)
|
|
{
|
|
type = (value >> 25) & 127;
|
|
addr = (value & 0x00F00000) << 4 | (value & 0x0003FFFF);
|
|
switch(type)
|
|
{
|
|
case 0x04:
|
|
cheatsAdd(code, desc, 0, value & 0x00FFFFFF, 257, GSA_SLOWDOWN);
|
|
break;
|
|
case 0x08:
|
|
cheatsAdd(code, desc, 0, addr, 257, GSA_8_BIT_GS_WRITE2);
|
|
break;
|
|
case 0x09:
|
|
cheatsAdd(code, desc, 0, addr, 257, GSA_16_BIT_GS_WRITE2);
|
|
break;
|
|
case 0x0a:
|
|
cheatsAdd(code, desc, 0, addr, 257, GSA_32_BIT_GS_WRITE2);
|
|
break;
|
|
case 0x0c:
|
|
case 0x0d:
|
|
case 0x0e:
|
|
case 0x0f:
|
|
cheatsAdd(code, desc, 0, value & 0x00FFFFFF, 257, GSA_16_BIT_ROM_PATCH2);
|
|
break;
|
|
case 0x40:
|
|
cheatsAdd(code, desc, 0, addr, 257, GSA_8_BIT_SLIDE);
|
|
break;
|
|
case 0x41:
|
|
cheatsAdd(code, desc, 0, addr, 257, GSA_16_BIT_SLIDE);
|
|
break;
|
|
case 0x42:
|
|
cheatsAdd(code, desc, 0, addr, 257, GSA_32_BIT_SLIDE);
|
|
break;
|
|
default:
|
|
cheatsAdd(code, desc, address, value, 257, UNKNOWN_CODE);
|
|
break;
|
|
}
|
|
}
|
|
else
|
|
cheatsAdd(code, desc, addr, value, 257, GSA_8_BIT_FILL);
|
|
break;
|
|
case 0x01:
|
|
cheatsAdd(code, desc, addr, value, 257, GSA_16_BIT_FILL);
|
|
break;
|
|
case 0x02:
|
|
cheatsAdd(code, desc, addr, value, 257, INT_32_BIT_WRITE);
|
|
break;
|
|
case 0x04:
|
|
cheatsAdd(code, desc, addr, value, 257, GSA_8_BIT_IF_TRUE);
|
|
break;
|
|
case 0x05:
|
|
cheatsAdd(code, desc, addr, value, 257, CBA_IF_TRUE);
|
|
break;
|
|
case 0x06:
|
|
cheatsAdd(code, desc, addr, value, 257, GSA_32_BIT_IF_TRUE);
|
|
break;
|
|
case 0x08:
|
|
cheatsAdd(code, desc, addr, value, 257, GSA_8_BIT_IF_FALSE);
|
|
break;
|
|
case 0x09:
|
|
cheatsAdd(code, desc, addr, value, 257, CBA_IF_FALSE);
|
|
break;
|
|
case 0x0a:
|
|
cheatsAdd(code, desc, addr, value, 257, GSA_32_BIT_IF_FALSE);
|
|
break;
|
|
case 0x24:
|
|
cheatsAdd(code, desc, addr, value, 257, GSA_8_BIT_IF_TRUE2);
|
|
break;
|
|
case 0x25:
|
|
cheatsAdd(code, desc, addr, value, 257, GSA_16_BIT_IF_TRUE2);
|
|
break;
|
|
case 0x26:
|
|
cheatsAdd(code, desc, addr, value, 257, GSA_32_BIT_IF_TRUE2);
|
|
break;
|
|
case 0x28:
|
|
cheatsAdd(code, desc, addr, value, 257, GSA_8_BIT_IF_FALSE2);
|
|
break;
|
|
case 0x29:
|
|
cheatsAdd(code, desc, addr, value, 257, GSA_16_BIT_IF_FALSE2);
|
|
break;
|
|
case 0x2a:
|
|
cheatsAdd(code, desc, addr, value, 257, GSA_32_BIT_IF_FALSE2);
|
|
break;
|
|
default:
|
|
cheatsAdd(code, desc, address, value, 257, UNKNOWN_CODE);
|
|
break;
|
|
}
|
|
}
|
|
else
|
|
{
|
|
int type = (address >> 28) & 15;
|
|
switch(type)
|
|
{
|
|
case 0:
|
|
case 1:
|
|
case 2:
|
|
cheatsAdd(code, desc, address & 0x0FFFFFFF, value, 256, type);
|
|
break;
|
|
case 6:
|
|
address <<= 1;
|
|
type = (address >> 28) & 15;
|
|
if(type == 0x0c)
|
|
{
|
|
cheatsAdd(code, desc, address & 0x0FFFFFFF, value, 256,
|
|
GSA_16_BIT_ROM_PATCH);
|
|
break;
|
|
}
|
|
// unsupported code
|
|
cheatsAdd(code, desc, address, value, 256,
|
|
UNKNOWN_CODE);
|
|
break;
|
|
case 8:
|
|
switch((address >> 20) & 15)
|
|
{
|
|
case 1:
|
|
cheatsAdd(code, desc, address & 0x0F0FFFFF, value, 256,
|
|
GSA_8_BIT_GS_WRITE);
|
|
break;
|
|
case 2:
|
|
cheatsAdd(code, desc, address & 0x0F0FFFFF, value, 256,
|
|
GSA_16_BIT_GS_WRITE);
|
|
break;
|
|
case 3:
|
|
cheatsAdd(code, desc, address & 0x0F0FFFFF, value, 256,
|
|
GSA_32_BIT_GS_WRITE);
|
|
case 15:
|
|
cheatsAdd(code, desc, 0, value & 0xFF00, 256, GSA_SLOWDOWN);
|
|
break;
|
|
default:
|
|
// unsupported code
|
|
cheatsAdd(code, desc, address, value, 256,
|
|
UNKNOWN_CODE);
|
|
break;
|
|
}
|
|
break;
|
|
case 0x0d:
|
|
if(address != 0xDEADFACE)
|
|
{
|
|
cheatsAdd(code, desc, address & 0x0FFFFFFF, value, 256,
|
|
CBA_IF_TRUE);
|
|
}
|
|
else
|
|
cheatsAdd(code, desc, address, value, 256,
|
|
UNKNOWN_CODE);
|
|
break;
|
|
default:
|
|
// unsupported code
|
|
cheatsAdd(code, desc, address, value, 256,
|
|
UNKNOWN_CODE);
|
|
break;
|
|
}
|
|
}
|
|
}
|
|
|
|
bool cheatsImportGSACodeFile(const char *name, int game, bool v3)
|
|
{
|
|
FILE* f = gen_fopen(name, "rb");
|
|
if(!f)
|
|
return false;
|
|
|
|
int games = 0;
|
|
int len = 0;
|
|
gen_fseek(f, 0x1e, SEEK_CUR);
|
|
gen_fread(&games, 1, 4, f);
|
|
bool found = false;
|
|
int g = 0;
|
|
while(games > 0)
|
|
{
|
|
if(g == game)
|
|
{
|
|
found = true;
|
|
break;
|
|
}
|
|
gen_fread(&len, 1, 4, f);
|
|
gen_fseek(f,len,SEEK_CUR);
|
|
int codes = 0;
|
|
gen_fread(&codes, 1, 4, f);
|
|
while(codes > 0)
|
|
{
|
|
gen_fread(&len, 1, 4, f);
|
|
gen_fseek(f, len, SEEK_CUR);
|
|
gen_fseek(f, 8, SEEK_CUR);
|
|
gen_fread(&len, 1, 4, f);
|
|
gen_fseek(f, len*12, SEEK_CUR);
|
|
codes--;
|
|
}
|
|
games--;
|
|
g++;
|
|
}
|
|
if(found)
|
|
{
|
|
char desc[256];
|
|
char code[17];
|
|
gen_fread(&len, 1, 4, f);
|
|
gen_fseek(f, len, SEEK_CUR);
|
|
int codes = 0;
|
|
gen_fread(&codes, 1, 4, f);
|
|
while(codes > 0)
|
|
{
|
|
gen_fread(&len, 1, 4, f);
|
|
gen_fread(desc, 1, len, f);
|
|
desc[len] =0;
|
|
desc[31] = 0;
|
|
gen_fread(&len, 1, 4, f);
|
|
gen_fseek(f, len, SEEK_CUR);
|
|
gen_fseek(f, 4, SEEK_CUR);
|
|
gen_fread(&len, 1, 4, f);
|
|
while(len)
|
|
{
|
|
gen_fseek(f, 4, SEEK_CUR);
|
|
gen_fread(code, 1, 8, f);
|
|
gen_fseek(f, 4, SEEK_CUR);
|
|
gen_fread(&code[8], 1, 8, f);
|
|
code[16] = 0;
|
|
cheatsAddGSACode(code, desc, v3);
|
|
len -= 2;
|
|
}
|
|
codes--;
|
|
}
|
|
}
|
|
gen_fclose(f);
|
|
return false;
|
|
}
|
|
|
|
void cheatsCBAReverseArray(u8 *array, u8 *dest)
|
|
{
|
|
dest[0] = array[3];
|
|
dest[1] = array[2];
|
|
dest[2] = array[1];
|
|
dest[3] = array[0];
|
|
dest[4] = array[5];
|
|
dest[5] = array[4];
|
|
}
|
|
|
|
void chatsCBAScramble(u8 *array, int count, u8 b)
|
|
{
|
|
u8 *x = array + (count >> 3);
|
|
u8 *y = array + (b >> 3);
|
|
u32 z = *x & (1 << (count & 7));
|
|
u32 x0 = (*x & (~(1 << (count & 7))));
|
|
if (z != 0)
|
|
z = 1;
|
|
if ((*y & (1 << (b & 7))) != 0)
|
|
x0 |= (1 << (count & 7));
|
|
*x = x0;
|
|
u32 temp = *y & (~(1 << (b & 7)));
|
|
if (z != 0)
|
|
temp |= (1 << (b & 7));
|
|
*y = temp;
|
|
}
|
|
|
|
u32 cheatsCBAGetValue(u8 *array)
|
|
{
|
|
return array[0] | array[1]<<8 | array[2] << 16 | array[3]<<24;
|
|
}
|
|
|
|
u16 cheatsCBAGetData(u8 *array)
|
|
{
|
|
return array[4] | array[5]<<8;
|
|
}
|
|
|
|
void cheatsCBAArrayToValue(u8 *array, u8 *dest)
|
|
{
|
|
dest[0] = array[3];
|
|
dest[1] = array[2];
|
|
dest[2] = array[1];
|
|
dest[3] = array[0];
|
|
dest[4] = array[5];
|
|
dest[5] = array[4];
|
|
}
|
|
|
|
void cheatsCBAParseSeedCode(u32 address, u32 value, u32 *array)
|
|
{
|
|
array[0] = 1;
|
|
array[1] = value & 0xFF;
|
|
array[2] = (address >> 0x10) & 0xFF;
|
|
array[3] = (value >> 8) & 0xFF;
|
|
array[4] = (address >> 0x18) & 0x0F;
|
|
array[5] = address & 0xFFFF;
|
|
array[6] = address;
|
|
array[7] = value;
|
|
}
|
|
|
|
u32 cheatsCBAEncWorker()
|
|
{
|
|
u32 x = (cheatsCBATemporaryValue * 0x41c64e6d) + 0x3039;
|
|
u32 y = (x * 0x41c64e6d) + 0x3039;
|
|
u32 z = x >> 0x10;
|
|
x = ((y >> 0x10) & 0x7fff) << 0x0f;
|
|
z = (z << 0x1e) | x;
|
|
x = (y * 0x41c64e6d) + 0x3039;
|
|
cheatsCBATemporaryValue = x;
|
|
return z | ((x >> 0x10) & 0x7fff);
|
|
}
|
|
|
|
#define ROR(v, s) \
|
|
(((v) >> (s)) | (((v) & ((1 << (s))-1)) << (32 - (s))))
|
|
|
|
u32 cheatsCBACalcIndex(u32 x, u32 y)
|
|
{
|
|
if(y != 0)
|
|
{
|
|
if(y == 1)
|
|
x = 0;
|
|
else if(x == y)
|
|
x = 0;
|
|
if(y < 1)
|
|
return x;
|
|
else if(x < y)
|
|
return x;
|
|
u32 x0 = 1;
|
|
|
|
while(y < 0x10000000)
|
|
{
|
|
if(y < x)
|
|
{
|
|
y = y << 4;
|
|
x0 = x0 << 4;
|
|
}
|
|
else break;
|
|
}
|
|
|
|
while(y < 0x80000000)
|
|
{
|
|
if(y < x)
|
|
{
|
|
y = y << 1;
|
|
x0 = x0 << 1;
|
|
}
|
|
else break;
|
|
}
|
|
|
|
loop:
|
|
u32 z = 0;
|
|
if(x >= y)
|
|
x -= y;
|
|
if(x >= (y >> 1))
|
|
{
|
|
x -= (y >> 1);
|
|
z |= ROR(x0, 1);
|
|
}
|
|
if(x >= (y >> 2))
|
|
{
|
|
x -= (y >> 2);
|
|
z |= ROR(x0, 2);
|
|
}
|
|
if(x >= (y >> 3))
|
|
{
|
|
x -= (y >> 3);
|
|
z |= ROR(x0, 3);
|
|
}
|
|
|
|
u32 temp = x0;
|
|
|
|
if(x != 0)
|
|
{
|
|
x0 = x0 >> 4;
|
|
if(x0 != 0)
|
|
{
|
|
y = y >> 4;
|
|
goto loop;
|
|
}
|
|
}
|
|
|
|
z = z & 0xe0000000;
|
|
|
|
if(z != 0)
|
|
{
|
|
if((temp & 7) == 0)
|
|
return x;
|
|
}
|
|
else
|
|
return x;
|
|
|
|
if((z & ROR(temp, 3)) != 0)
|
|
x += y >> 3;
|
|
if((z & ROR(temp, 2)) != 0)
|
|
x += y >> 2;
|
|
if((z & ROR(temp, 1)) != 0)
|
|
x += y >> 1;
|
|
return x;
|
|
}
|
|
else
|
|
{}
|
|
// should not happen in the current code
|
|
return 0;
|
|
}
|
|
|
|
void cheatsCBAUpdateSeedBuffer(u32 a, u8 *buffer, int count)
|
|
{
|
|
int i;
|
|
for(i = 0; i < count; i++)
|
|
buffer[i] = i;
|
|
for(i = 0; (u32)i < a; i++)
|
|
{
|
|
u32 a = cheatsCBACalcIndex(cheatsCBAEncWorker(), count);
|
|
u32 b = cheatsCBACalcIndex(cheatsCBAEncWorker(), count);
|
|
u32 t = buffer[a];
|
|
buffer[a] = buffer[b];
|
|
buffer[b] = t;
|
|
}
|
|
}
|
|
|
|
void cheatsCBAChangeEncryption(u32 *seed)
|
|
{
|
|
int i;
|
|
|
|
cheatsCBATemporaryValue = (seed[1] ^ 0x1111);
|
|
cheatsCBAUpdateSeedBuffer(0x50, cheatsCBASeedBuffer, 0x30);
|
|
cheatsCBATemporaryValue = 0x4efad1c3;
|
|
|
|
for(i = 0; (u32)i < seed[4]; i++)
|
|
{
|
|
cheatsCBATemporaryValue = cheatsCBAEncWorker();
|
|
}
|
|
cheatsCBASeed[2] = cheatsCBAEncWorker();
|
|
cheatsCBASeed[3] = cheatsCBAEncWorker();
|
|
|
|
cheatsCBATemporaryValue = seed[3] ^ 0xf254;
|
|
|
|
for(i = 0; (u32)i < seed[3]; i++)
|
|
{
|
|
cheatsCBATemporaryValue = cheatsCBAEncWorker();
|
|
}
|
|
|
|
cheatsCBASeed[0] = cheatsCBAEncWorker();
|
|
cheatsCBASeed[1] = cheatsCBAEncWorker();
|
|
|
|
*((u32 *)&cheatsCBACurrentSeed[0]) = seed[6];
|
|
*((u32 *)&cheatsCBACurrentSeed[4]) = seed[7];
|
|
*((u32 *)&cheatsCBACurrentSeed[8]) = 0;
|
|
}
|
|
|
|
u16 cheatsCBAGenValue(u32 x, u32 y, u32 z)
|
|
{
|
|
y <<= 0x10;
|
|
z <<= 0x10;
|
|
x <<= 0x18;
|
|
u32 x0 = (int)y >> 0x10;
|
|
z = (int)z >> 0x10;
|
|
x = (int)x >> 0x10;
|
|
for(int i = 0; i < 8; i++)
|
|
{
|
|
u32 temp = z ^ x;
|
|
if ((int)temp >= 0)
|
|
{
|
|
temp = z << 0x11;
|
|
}
|
|
else
|
|
{
|
|
temp = z << 0x01;
|
|
temp ^= x0;
|
|
temp = temp << 0x10;
|
|
}
|
|
z = (int)temp >> 0x10;
|
|
temp = x << 0x11;
|
|
x = (int)temp >> 0x10;
|
|
}
|
|
return z & 0xffff;
|
|
}
|
|
|
|
void cheatsCBAGenTable()
|
|
{
|
|
for (int i = 0; i < 0x100; i++)
|
|
{
|
|
cheatsCBATable[i] = cheatsCBAGenValue(i, 0x1021, 0);
|
|
}
|
|
cheatsCBATableGenerated = true;
|
|
}
|
|
|
|
u16 cheatsCBACalcCRC(u8 *rom, int count)
|
|
{
|
|
u32 crc = 0xffffffff;
|
|
|
|
if (count & 3)
|
|
{
|
|
// 0x08000EAE
|
|
}
|
|
else
|
|
{
|
|
count = (count >> 2) - 1;
|
|
if(count != -1)
|
|
{
|
|
while(count != -1)
|
|
{
|
|
crc = (((crc << 0x08) ^ cheatsCBATable[(((u32)crc << 0x10) >> 0x18)
|
|
^ *rom++]) << 0x10) >> 0x10;
|
|
crc = (((crc << 0x08) ^ cheatsCBATable[(((u32)crc << 0x10) >> 0x18)
|
|
^ *rom++]) << 0x10) >> 0x10;
|
|
crc = (((crc << 0x08) ^ cheatsCBATable[(((u32)crc << 0x10) >> 0x18)
|
|
^ *rom++]) << 0x10) >> 0x10;
|
|
crc = (((crc << 0x08) ^ cheatsCBATable[(((u32)crc << 0x10) >> 0x18)
|
|
^ *rom++]) << 0x10) >> 0x10;
|
|
count--;
|
|
}
|
|
}
|
|
}
|
|
return crc & 0xffff;
|
|
}
|
|
|
|
void cheatsCBADecrypt(u8 *decrypt)
|
|
{
|
|
u8 buffer[8] = { 0, 0, 0, 0, 0, 0, 0, 0 };
|
|
u8 *array = &buffer[1];
|
|
|
|
cheatsCBAReverseArray(decrypt, array);
|
|
|
|
for(int count = 0x2f; count >= 0; count--)
|
|
{
|
|
chatsCBAScramble(array, count, cheatsCBASeedBuffer[count]);
|
|
}
|
|
cheatsCBAArrayToValue(array, decrypt);
|
|
*((u32 *)decrypt) = cheatsCBAGetValue(decrypt) ^
|
|
cheatsCBASeed[0];
|
|
*((u16 *)(decrypt+4)) = (cheatsCBAGetData(decrypt) ^
|
|
cheatsCBASeed[1]) & 0xffff;
|
|
|
|
cheatsCBAReverseArray(decrypt, array);
|
|
|
|
u32 cs = cheatsCBAGetValue(cheatsCBACurrentSeed);
|
|
for(int i = 0; i <= 4; i++)
|
|
{
|
|
array[i] = ((cs >> 8) ^ array[i+1]) ^ array[i] ;
|
|
}
|
|
|
|
array[5] = (cs >> 8) ^ array[5];
|
|
|
|
for(int j = 5; j >=0; j--)
|
|
{
|
|
array[j] = (cs ^ array[j-1]) ^ array[j];
|
|
}
|
|
|
|
cheatsCBAArrayToValue(array, decrypt);
|
|
|
|
*((u32 *)decrypt) = cheatsCBAGetValue(decrypt)
|
|
^ cheatsCBASeed[2];
|
|
*((u16 *)(decrypt+4)) = (cheatsCBAGetData(decrypt)
|
|
^ cheatsCBASeed[3]) & 0xffff;
|
|
}
|
|
|
|
int cheatsCBAGetCount()
|
|
{
|
|
int count = 0;
|
|
for(int i = 0; i < cheatsNumber; i++)
|
|
{
|
|
if(cheatsList[i].code == 512)
|
|
count++;
|
|
}
|
|
return count;
|
|
}
|
|
|
|
bool cheatsCBAShouldDecrypt()
|
|
{
|
|
for(int i = 0; i < cheatsNumber; i++)
|
|
{
|
|
if(cheatsList[i].code == 512)
|
|
{
|
|
return (cheatsList[i].codestring[0] == '9');
|
|
}
|
|
}
|
|
return false;
|
|
}
|
|
|
|
void cheatsAddCBACode(const char *code, const char *desc)
|
|
{
|
|
if(strlen(code) != 13)
|
|
{
|
|
// wrong cheat
|
|
systemMessage(MSG_INVALID_CBA_CODE,
|
|
N_("Invalid CBA code. Format is XXXXXXXX YYYY."));
|
|
return;
|
|
}
|
|
|
|
int i;
|
|
for(i = 0; i < 8; i++)
|
|
{
|
|
if(!CHEAT_IS_HEX(code[i]))
|
|
{
|
|
// wrong cheat
|
|
systemMessage(MSG_INVALID_CBA_CODE,
|
|
N_("Invalid CBA code. Format is XXXXXXXX YYYY."));
|
|
return;
|
|
}
|
|
}
|
|
|
|
if(code[8] != ' ')
|
|
{
|
|
systemMessage(MSG_INVALID_CBA_CODE,
|
|
N_("Invalid CBA code. Format is XXXXXXXX YYYY."));
|
|
return;
|
|
}
|
|
|
|
for(i = 9; i < 13; i++)
|
|
{
|
|
if(!CHEAT_IS_HEX(code[i]))
|
|
{
|
|
// wrong cheat
|
|
systemMessage(MSG_INVALID_CBA_CODE,
|
|
N_("Invalid CBA code. Format is XXXXXXXX YYYY."));
|
|
return;
|
|
}
|
|
}
|
|
|
|
char buffer[10];
|
|
strncpy(buffer, code, 8);
|
|
buffer[8] = 0;
|
|
u32 address;
|
|
sscanf(buffer, "%x", &address);
|
|
strncpy(buffer, &code[9], 4);
|
|
buffer[4] = 0;
|
|
u32 value;
|
|
sscanf(buffer, "%x", &value);
|
|
|
|
u8 array[8] = {
|
|
address & 255,
|
|
(address >> 8) & 255,
|
|
(address >> 16) & 255,
|
|
(address >> 24) & 255,
|
|
(value & 255),
|
|
(value >> 8) & 255,
|
|
0,
|
|
0
|
|
};
|
|
|
|
if(cheatsCBAGetCount() == 0 &&
|
|
(address >> 28) == 9)
|
|
{
|
|
u32 seed[8];
|
|
cheatsCBAParseSeedCode(address, value, seed);
|
|
cheatsCBAChangeEncryption(seed);
|
|
cheatsAdd(code, desc, address & 0x0FFFFFFF, value, 512, UNKNOWN_CODE);
|
|
}
|
|
else
|
|
{
|
|
if(cheatsCBAShouldDecrypt())
|
|
cheatsCBADecrypt(array);
|
|
|
|
address = READ32LE(((u32 *)array));
|
|
value = READ16LE(((u16 *)&array[4]));
|
|
|
|
int type = (address >> 28) & 15;
|
|
|
|
if(isMultilineWithData(cheatsNumber-1))
|
|
{
|
|
cheatsAdd(code, desc, address, value, 512, UNKNOWN_CODE);
|
|
return;
|
|
}
|
|
|
|
switch(type)
|
|
{
|
|
case 0x00:
|
|
{
|
|
if(!cheatsCBATableGenerated)
|
|
cheatsCBAGenTable();
|
|
u32 crc = cheatsCBACalcCRC(rom, 0x10000);
|
|
if(crc != address)
|
|
{
|
|
systemMessage(MSG_CBA_CODE_WARNING,
|
|
N_("Warning: Codes seem to be for a different game.\nCodes may not work correctly."));
|
|
}
|
|
cheatsAdd(code, desc, address & 0x0FFFFFFF, value, 512,
|
|
UNKNOWN_CODE);
|
|
}
|
|
break;
|
|
case 0x02:
|
|
cheatsAdd(code, desc, address & 0x0FFFFFFF, value, 512,
|
|
CBA_OR);
|
|
break;
|
|
case 0x03:
|
|
cheatsAdd(code, desc, address & 0x0FFFFFFF, value, 512,
|
|
INT_8_BIT_WRITE);
|
|
break;
|
|
case 0x04:
|
|
cheatsAdd(code, desc, address & 0x0FFFFFFF, value, 512,
|
|
CBA_SLIDE_CODE);
|
|
break;
|
|
case 0x05:
|
|
cheatsAdd(code, desc, address & 0x0FFFFFFF, value, 512,
|
|
CBA_SUPER);
|
|
break;
|
|
case 0x06:
|
|
cheatsAdd(code, desc, address & 0x0FFFFFFF, value, 512,
|
|
CBA_AND);
|
|
break;
|
|
case 0x07:
|
|
cheatsAdd(code, desc, address & 0x0FFFFFFF, value, 512,
|
|
CBA_IF_TRUE);
|
|
break;
|
|
case 0x08:
|
|
cheatsAdd(code, desc, address & 0x0FFFFFFF, value, 512,
|
|
INT_16_BIT_WRITE);
|
|
break;
|
|
case 0x0a:
|
|
cheatsAdd(code, desc, address & 0x0FFFFFFF, value, 512,
|
|
CBA_IF_FALSE);
|
|
break;
|
|
case 0x0b:
|
|
cheatsAdd(code, desc, address & 0x0FFFFFFF, value, 512,
|
|
CBA_LT);
|
|
break;
|
|
case 0x0c:
|
|
cheatsAdd(code, desc, address & 0x0FFFFFFF, value, 512,
|
|
CBA_GT);
|
|
break;
|
|
case 0x0d:
|
|
cheatsAdd(code, desc, address & 0x0FFFFFFF, value, 512,
|
|
CBA_IF_KEYS_PRESSED);
|
|
break;
|
|
case 0x0e:
|
|
cheatsAdd(code, desc, address & 0x0FFFFFFF, value, 512,
|
|
CBA_ADD);
|
|
break;
|
|
default:
|
|
// unsupported code
|
|
cheatsAdd(code, desc, address & 0xFFFFFFFF, value, 512,
|
|
UNKNOWN_CODE);
|
|
break;
|
|
}
|
|
}
|
|
}
|
|
|
|
void cheatsSaveGame(gzFile file)
|
|
{
|
|
utilWriteInt(file, cheatsNumber);
|
|
|
|
utilGzWrite(file, cheatsList, sizeof(cheatsList));
|
|
}
|
|
|
|
void cheatsReadGame(gzFile file)
|
|
{
|
|
cheatsNumber = 0;
|
|
|
|
cheatsNumber = utilReadInt(file);
|
|
|
|
utilGzRead(file, cheatsList, sizeof(cheatsList));
|
|
|
|
bool firstCodeBreaker = true;
|
|
|
|
for(int i = 0; i < cheatsNumber; i++)
|
|
{
|
|
cheatsList[i].status = 0;
|
|
if(!cheatsList[i].codestring[0])
|
|
{
|
|
switch(cheatsList[i].size)
|
|
{
|
|
case 0:
|
|
sprintf(cheatsList[i].codestring, "%08x:%02x", cheatsList[i].address,
|
|
cheatsList[i].value);
|
|
break;
|
|
case 1:
|
|
sprintf(cheatsList[i].codestring, "%08x:%04x", cheatsList[i].address,
|
|
cheatsList[i].value);
|
|
break;
|
|
case 2:
|
|
sprintf(cheatsList[i].codestring, "%08x:%08x", cheatsList[i].address,
|
|
cheatsList[i].value);
|
|
break;
|
|
}
|
|
}
|
|
|
|
if(cheatsList[i].enabled)
|
|
{
|
|
cheatsEnable(i);
|
|
}
|
|
|
|
if(cheatsList[i].code == 512 && firstCodeBreaker)
|
|
{
|
|
firstCodeBreaker = false;
|
|
char buffer[10];
|
|
strncpy(buffer, cheatsList[i].codestring, 8);
|
|
buffer[8] = 0;
|
|
u32 address;
|
|
sscanf(buffer, "%x", &address);
|
|
if((address >> 28) == 9)
|
|
{
|
|
strncpy(buffer, &cheatsList[i].codestring[9], 4);
|
|
buffer[4] = 0;
|
|
u32 value;
|
|
sscanf(buffer, "%x", &value);
|
|
|
|
u32 seed[8];
|
|
cheatsCBAParseSeedCode(address, value, seed);
|
|
cheatsCBAChangeEncryption(seed);
|
|
}
|
|
}
|
|
}
|
|
}
|
|
|
|
void cheatsSaveCheatList(const char *file)
|
|
{
|
|
if(cheatsNumber == 0)
|
|
return;
|
|
FILE* f = gen_fopen(file, "wb");
|
|
if(f == NULL)
|
|
return;
|
|
int version = 1;
|
|
gen_fwrite(&version, 1, sizeof(version), f);
|
|
int type = 0;
|
|
gen_fwrite(&type, 1, sizeof(type), f);
|
|
gen_fwrite(&cheatsNumber, 1, sizeof(cheatsNumber), f);
|
|
gen_fwrite(cheatsList, 1, sizeof(cheatsList), f);
|
|
gen_fclose(f);
|
|
}
|
|
|
|
bool cheatsLoadCheatList(const char *file)
|
|
{
|
|
cheatsNumber = 0;
|
|
|
|
int count = 0;
|
|
|
|
FILE* f = gen_fopen(file, "rb");
|
|
|
|
if(f == NULL)
|
|
return false;
|
|
|
|
int version = 0;
|
|
|
|
if(gen_fread(&version, 1, sizeof(version), f) != sizeof(version))
|
|
{
|
|
gen_fclose(f);
|
|
return false;
|
|
}
|
|
|
|
if(version != 1)
|
|
{
|
|
systemMessage(MSG_UNSUPPORTED_CHEAT_LIST_VERSION,
|
|
N_("Unsupported cheat list version %d"), version);
|
|
gen_fclose(f);
|
|
return false;
|
|
}
|
|
|
|
int type = 0;
|
|
if(gen_fread(&type, 1, sizeof(type), f) != sizeof(type))
|
|
{
|
|
gen_fclose(f);
|
|
return false;
|
|
}
|
|
|
|
if(type != 0)
|
|
{
|
|
systemMessage(MSG_UNSUPPORTED_CHEAT_LIST_TYPE,
|
|
N_("Unsupported cheat list type %d"), type);
|
|
gen_fclose(f);
|
|
return false;
|
|
}
|
|
|
|
if(gen_fread(&count, 1, sizeof(count), f) != sizeof(count))
|
|
{
|
|
gen_fclose(f);
|
|
return false;
|
|
}
|
|
|
|
if(gen_fread(cheatsList, 1, sizeof(cheatsList), f) != sizeof(cheatsList))
|
|
{
|
|
gen_fclose(f);
|
|
return false;
|
|
}
|
|
|
|
bool firstCodeBreaker = true;
|
|
|
|
for(int i = 0; i < count; i++)
|
|
{
|
|
cheatsList[i].status = 0; // remove old status as it is not used
|
|
if(!cheatsList[i].codestring[0])
|
|
{
|
|
switch(cheatsList[i].size)
|
|
{
|
|
case 0:
|
|
sprintf(cheatsList[i].codestring, "%08x:%02x", cheatsList[i].address,
|
|
cheatsList[i].value);
|
|
break;
|
|
case 1:
|
|
sprintf(cheatsList[i].codestring, "%08x:%04x", cheatsList[i].address,
|
|
cheatsList[i].value);
|
|
break;
|
|
case 2:
|
|
sprintf(cheatsList[i].codestring, "%08x:%08x", cheatsList[i].address,
|
|
cheatsList[i].value);
|
|
break;
|
|
}
|
|
}
|
|
|
|
if(cheatsList[i].code == 512 && firstCodeBreaker)
|
|
{
|
|
firstCodeBreaker = false;
|
|
char buffer[10];
|
|
strncpy(buffer, cheatsList[i].codestring, 8);
|
|
buffer[8] = 0;
|
|
u32 address;
|
|
sscanf(buffer, "%x", &address);
|
|
if((address >> 28) == 9)
|
|
{
|
|
strncpy(buffer, &cheatsList[i].codestring[9], 4);
|
|
buffer[4] = 0;
|
|
u32 value;
|
|
sscanf(buffer, "%x", &value);
|
|
|
|
u32 seed[8];
|
|
cheatsCBAParseSeedCode(address, value, seed);
|
|
cheatsCBAChangeEncryption(seed);
|
|
}
|
|
}
|
|
}
|
|
cheatsNumber = count;
|
|
gen_fclose(f);
|
|
return true;
|
|
}
|
|
|
|
extern int *extCpuLoopTicks;
|
|
extern int *extClockTicks;
|
|
extern int *extTicks;
|
|
extern int cpuSavedTicks;
|
|
|
|
extern void debuggerBreakOnWrite(u32 *, u32, u32, int);
|
|
|
|
#define CPU_BREAK_LOOP \
|
|
cpuSavedTicks = cpuSavedTicks - *extCpuLoopTicks;\
|
|
*extCpuLoopTicks = *extClockTicks;\
|
|
*extTicks = *extClockTicks;
|
|
|
|
void cheatsWriteMemory(u32 *address, u32 value, u32 mask)
|
|
{
|
|
#ifdef BKPT_SUPPORT
|
|
#ifdef SDL
|
|
if(cheatsNumber == 0)
|
|
{
|
|
debuggerBreakOnWrite(address, *address, value, 2);
|
|
CPU_BREAK_LOOP;
|
|
*address = value;
|
|
return;
|
|
}
|
|
#endif
|
|
#endif
|
|
}
|
|
|
|
void cheatsWriteHalfWord(u16 *address, u16 value, u16 mask)
|
|
{
|
|
#ifdef BKPT_SUPPORT
|
|
#ifdef SDL
|
|
if(cheatsNumber == 0)
|
|
{
|
|
debuggerBreakOnWrite((u32 *)address, *address, value, 1);
|
|
CPU_BREAK_LOOP;
|
|
*address = value;
|
|
return;
|
|
}
|
|
#endif
|
|
#endif
|
|
}
|
|
|
|
#if defined BKPT_SUPPORT && defined SDL
|
|
void cheatsWriteByte(u8 *address, u8 value)
|
|
#else
|
|
void cheatsWriteByte(u8 *, u8)
|
|
#endif
|
|
{
|
|
#ifdef BKPT_SUPPORT
|
|
#ifdef SDL
|
|
if(cheatsNumber == 0)
|
|
{
|
|
debuggerBreakOnWrite((u32 *)address, *address, value, 0);
|
|
CPU_BREAK_LOOP;
|
|
*address = value;
|
|
return;
|
|
}
|
|
#endif
|
|
#endif
|
|
}
|