beetle-psx-libretro/mednafen/mempatcher.cpp

655 lines
13 KiB
C++

/* Mednafen - Multi-system Emulator
*
* 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 <string.h>
#include <ctype.h>
#include <errno.h>
#include <vector>
#ifdef _WIN32
#include <compat/msvc.h>
#endif
#include <boolean.h>
#include "mednafen.h"
#include "general.h"
#include "md5.h"
#include "mempatcher.h"
#include <libretro.h>
#include "psx/psx.h"
extern retro_log_printf_t log_cb;
static uint8 **RAMPtrs = NULL;
static uint32 PageSize;
static uint32 NumPages;
typedef MemoryPatch CHEATF;
#if 0
typedef struct __CHEATF
{
char *name;
char *conditions;
uint32 addr;
uint64 val;
uint64 compare;
unsigned int length;
bool bigendian;
unsigned int icount; // Instance count
char type; /* 'R' for replace, 'S' for substitute(GG), 'C' for substitute with compare */
int status;
} CHEATF;
#endif
static std::vector<CHEATF> cheats;
static int savecheats;
static uint32 resultsbytelen = 1;
static bool resultsbigendian = 0;
static bool CheatsActive = true;
bool SubCheatsOn = 0;
std::vector<SUBCHEAT> SubCheats[8];
MemoryPatch::MemoryPatch() : addr(0), val(0), compare(0),
mltpl_count(1), mltpl_addr_inc(0), mltpl_val_inc(0), copy_src_addr(0), copy_src_addr_inc(0),
length(0), bigendian(false), status(false), icount(0), type(0)
{
}
MemoryPatch::~MemoryPatch()
{
}
static void RebuildSubCheats(void)
{
std::vector<CHEATF>::iterator chit;
SubCheatsOn = 0;
for(int x = 0; x < 8; x++)
SubCheats[x].clear();
if(!CheatsActive) return;
for(chit = cheats.begin(); chit != cheats.end(); chit++)
{
if(chit->status && chit->type != 'R')
{
for(unsigned int x = 0; x < chit->length; x++)
{
SUBCHEAT tmpsub;
unsigned int shiftie;
if(chit->bigendian)
shiftie = (chit->length - 1 - x) * 8;
else
shiftie = x * 8;
tmpsub.addr = chit->addr + x;
tmpsub.value = (chit->val >> shiftie) & 0xFF;
if(chit->type == 'C')
tmpsub.compare = (chit->compare >> shiftie) & 0xFF;
else
tmpsub.compare = -1;
SubCheats[(chit->addr + x) & 0x7].push_back(tmpsub);
SubCheatsOn = 1;
}
}
}
}
bool MDFNMP_Init(uint32 ps, uint32 numpages)
{
PageSize = ps;
NumPages = numpages;
RAMPtrs = (uint8 **)calloc(numpages, sizeof(uint8 *));
CheatsActive = MDFN_GetSettingB("cheats");
return(1);
}
void MDFNMP_Kill(void)
{
if(RAMPtrs)
{
free(RAMPtrs);
RAMPtrs = NULL;
}
}
void MDFNMP_AddRAM(uint32 size, uint32 A, uint8 *RAM)
{
uint32 AB = A / PageSize;
size /= PageSize;
for(unsigned int x = 0; x < size; x++)
{
RAMPtrs[AB + x] = RAM;
if(RAM) // Don't increment the RAM pointer if we're passed a NULL pointer
RAM += PageSize;
}
}
void MDFNMP_RegSearchable(uint32 addr, uint32 size)
{
MDFNMP_AddRAM(size, addr, NULL);
}
void MDFNMP_InstallReadPatches(void)
{
unsigned x;
std::vector<SUBCHEAT>::iterator chit;
if(!CheatsActive) return;
for(x = 0; x < 8; x++)
for(chit = SubCheats[x].begin(); chit != SubCheats[x].end(); chit++)
{
#if 0
if(MDFNGameInfo->InstallReadPatch)
MDFNGameInfo->InstallReadPatch(chit->addr);
#endif
}
}
void MDFNMP_RemoveReadPatches(void)
{
#if 0
if(MDFNGameInfo->RemoveReadPatches)
MDFNGameInfo->RemoveReadPatches();
#endif
}
/* This function doesn't allocate any memory for "name" */
static int AddCheatEntry(char *name, char *conditions, uint32 addr, uint64 val, uint64 compare, int status, char type, unsigned int length, bool bigendian)
{
CHEATF temp = CHEATF();
temp.name=name;
temp.conditions = conditions;
temp.addr=addr;
temp.val=val;
temp.status=status;
temp.compare=compare;
temp.length = length;
temp.bigendian = bigendian;
temp.type=type;
cheats.push_back(temp);
return(1);
}
void MDFN_LoadGameCheats(void *override_ptr)
{
RebuildSubCheats();
}
void MDFN_FlushGameCheats(int nosave)
{
cheats.clear();
RebuildSubCheats();
}
void MDFNI_AddCheat(const MemoryPatch& patch)
{
cheats.push_back(patch);
savecheats = true;
MDFNMP_RemoveReadPatches();
RebuildSubCheats();
MDFNMP_InstallReadPatches();
}
int MDFNI_DelCheat(uint32 which)
{
cheats.erase(cheats.begin() + which);
savecheats=1;
MDFNMP_RemoveReadPatches();
RebuildSubCheats();
MDFNMP_InstallReadPatches();
return(1);
}
/*
Condition format(ws = white space):
<variable size><ws><endian><ws><address><ws><operation><ws><value>
[,second condition...etc.]
Value should be unsigned integer, hex(with a 0x prefix) or
base-10.
Operations:
>=
<=
>
<
==
!=
& // Result of AND between two values is nonzero
!& // Result of AND between two values is zero
^ // same, XOR
!^
| // same, OR
!|
Full example:
2 L 0xADDE == 0xDEAD, 1 L 0xC000 == 0xA0
*/
static bool TestConditions(const char *string)
{
char address[64];
char operation[64];
char value[64];
char endian;
unsigned int bytelen;
bool passed = 1;
//printf("TR: %s\n", string);
while(sscanf(string, "%u %c %63s %63s %63s", &bytelen, &endian, address, operation, value) == 5 && passed)
{
uint32 v_address;
uint64 v_value;
uint64 value_at_address;
if(address[0] == '0' && address[1] == 'x')
v_address = strtoul(address + 2, NULL, 16);
else
v_address = strtoul(address, NULL, 10);
if(value[0] == '0' && value[1] == 'x')
v_value = strtoull(value + 2, NULL, 16);
else
v_value = strtoull(value, NULL, 0);
value_at_address = 0;
for(unsigned int x = 0; x < bytelen; x++)
{
unsigned int shiftie;
if(endian == 'B')
shiftie = (bytelen - 1 - x) * 8;
else
shiftie = x * 8;
value_at_address |= PSX_MemPeek8(v_address + x) << shiftie;
}
//printf("A: %08x, V: %08llx, VA: %08llx, OP: %s\n", v_address, v_value, value_at_address, operation);
if(!strcmp(operation, ">="))
{
if(!(value_at_address >= v_value))
passed = 0;
}
else if(!strcmp(operation, "<="))
{
if(!(value_at_address <= v_value))
passed = 0;
}
else if(!strcmp(operation, ">"))
{
if(!(value_at_address > v_value))
passed = 0;
}
else if(!strcmp(operation, "<"))
{
if(!(value_at_address < v_value))
passed = 0;
}
else if(!strcmp(operation, "=="))
{
if(!(value_at_address == v_value))
passed = 0;
}
else if(!strcmp(operation, "!="))
{
if(!(value_at_address != v_value))
passed = 0;
}
else if(!strcmp(operation, "&"))
{
if(!(value_at_address & v_value))
passed = 0;
}
else if(!strcmp(operation, "!&"))
{
if(value_at_address & v_value)
passed = 0;
}
else if(!strcmp(operation, "^"))
{
if(!(value_at_address ^ v_value))
passed = 0;
}
else if(!strcmp(operation, "!^"))
{
if(value_at_address ^ v_value)
passed = 0;
}
else if(!strcmp(operation, "|"))
{
if(!(value_at_address | v_value))
passed = 0;
}
else if(!strcmp(operation, "!|"))
{
if(value_at_address | v_value)
passed = 0;
}
else
puts("Invalid operation");
string = strchr(string, ',');
if(string == NULL)
break;
else
string++;
//printf("Foo: %s\n", string);
}
return(passed);
}
void MDFNMP_ApplyPeriodicCheats(void)
{
if(!CheatsActive)
return;
//TestConditions("2 L 0x1F00F5 == 0xDEAD");
//if(TestConditions("1 L 0x1F0058 > 0")) //, 1 L 0xC000 == 0x01"));
for(std::vector<CHEATF>::iterator chit = cheats.begin(); chit != cheats.end(); chit++)
{
if(chit->status && (chit->type == 'R' || chit->type == 'A' || chit->type == 'T'))
{
if(chit->conditions.size() == 0 || TestConditions(chit->conditions.c_str()))
{
uint32 mltpl_count = chit->mltpl_count;
uint32 mltpl_addr = chit->addr;
uint64 mltpl_val = chit->val;
uint32 copy_src_addr = chit->copy_src_addr;
while(mltpl_count--)
{
uint8 carry = 0;
for(unsigned int x = 0; x < chit->length; x++)
{
const uint32 tmpaddr = chit->bigendian ? (mltpl_addr + chit->length - 1 - x) : (mltpl_addr + x);
const uint8 tmpval = mltpl_val >> (x * 8);
if(chit->type == 'A')
{
const unsigned t = PSX_MemPeek8(tmpaddr) + tmpval + carry;
carry = t >> 8;
PSX_MemPoke8(tmpaddr, t);
}
else if(chit->type == 'T')
{
const uint8 cv = PSX_MemPeek8(chit->bigendian ? (copy_src_addr + chit->length - 1 - x) : (copy_src_addr + x));
PSX_MemPoke8(tmpaddr, cv);
}
else
PSX_MemPoke8(tmpaddr, tmpval);
}
mltpl_addr += chit->mltpl_addr_inc;
mltpl_val += chit->mltpl_val_inc;
copy_src_addr += chit->copy_src_addr_inc;
}
} // end if(chit->conditions.size() == 0 || TestConditions(chit->conditions.c_str()))
}
}
}
void MDFNI_ListCheats(int (*callb)(const MemoryPatch& patch, void *data), void *data)
{
std::vector<CHEATF>::iterator chit;
for(chit = cheats.begin(); chit != cheats.end(); chit++)
{
if(!callb(*chit, data))
break;
}
}
MemoryPatch MDFNI_GetCheat(uint32 which)
{
return cheats[which];
}
static uint8 CharToNibble(char thechar)
{
const char lut[16] = {'0', '1', '2', '3', '4', '5', '6', '7', '8', '9', 'A', 'B', 'C', 'D', 'E', 'F' };
thechar = toupper(thechar);
for(int x = 0; x < 16; x++)
if(lut[x] == thechar)
return(x);
return(0xFF);
}
bool MDFNI_DecodeGBGG(const char *instr, uint32 *a, uint8 *v, uint8 *c, char *type)
{
char str[10];
int len;
for(int x = 0; x < 9; x++)
{
while(*instr && CharToNibble(*instr) == 255)
instr++;
if(!(str[x] = *instr)) break;
instr++;
}
str[9] = 0;
len = strlen(str);
if(len != 9 && len != 6)
return(0);
uint32 tmp_address;
uint8 tmp_value;
uint8 tmp_compare = 0;
tmp_address = (CharToNibble(str[5]) << 12) | (CharToNibble(str[2]) << 8) | (CharToNibble(str[3]) << 4) | (CharToNibble(str[4]) << 0);
tmp_address ^= 0xF000;
tmp_value = (CharToNibble(str[0]) << 4) | (CharToNibble(str[1]) << 0);
if(len == 9)
{
tmp_compare = (CharToNibble(str[6]) << 4) | (CharToNibble(str[8]) << 0);
tmp_compare = (tmp_compare >> 2) | ((tmp_compare << 6) & 0xC0);
tmp_compare ^= 0xBA;
}
*a = tmp_address;
*v = tmp_value;
if(len == 9)
{
*c = tmp_compare;
*type = 'C';
}
else
{
*c = 0;
*type = 'S';
}
return(1);
}
static int GGtobin(char c)
{
static char lets[16]={'A','P','Z','L','G','I','T','Y','E','O','X','U','K','S','V','N'};
int x;
for(x=0;x<16;x++)
if(lets[x] == toupper(c)) return(x);
return(0);
}
/* Returns 1 on success, 0 on failure. Sets *a,*v,*c. */
int MDFNI_DecodeGG(const char *str, uint32 *a, uint8 *v, uint8 *c, char *type)
{
uint16 A;
uint8 V,C;
uint8 t;
int s;
A=0x8000;
V=0;
C=0;
s=strlen(str);
if(s!=6 && s!=8) return(0);
t=GGtobin(*str++);
V|=(t&0x07);
V|=(t&0x08)<<4;
t=GGtobin(*str++);
V|=(t&0x07)<<4;
A|=(t&0x08)<<4;
t=GGtobin(*str++);
A|=(t&0x07)<<4;
//if(t&0x08) return(0); /* 8-character code?! */
t=GGtobin(*str++);
A|=(t&0x07)<<12;
A|=(t&0x08);
t=GGtobin(*str++);
A|=(t&0x07);
A|=(t&0x08)<<8;
if(s==6)
{
t=GGtobin(*str++);
A|=(t&0x07)<<8;
V|=(t&0x08);
*a=A;
*v=V;
*type = 'S';
*c = 0;
}
else
{
t=GGtobin(*str++);
A|=(t&0x07)<<8;
C|=(t&0x08);
t=GGtobin(*str++);
C|=(t&0x07);
C|=(t&0x08)<<4;
t=GGtobin(*str++);
C|=(t&0x07)<<4;
V|=(t&0x08);
*a=A;
*v=V;
*c=C;
*type = 'C';
}
return(1);
}
int MDFNI_DecodePAR(const char *str, uint32 *a, uint8 *v, uint8 *c, char *type)
{
int boo[4];
if(strlen(str)!=8) return(0);
sscanf(str,"%02x%02x%02x%02x",boo,boo+1,boo+2,boo+3);
*c = 0;
if(1)
{
*a=(boo[3]<<8)|(boo[2]+0x7F);
*v=0;
}
else
{
*v=boo[3];
*a=boo[2]|(boo[1]<<8);
}
*type = 'S';
return(1);
}
void MDFNI_SetCheat(uint32 which, const MemoryPatch& patch)
{
cheats[which] = patch;
savecheats = true;
MDFNMP_RemoveReadPatches();
RebuildSubCheats();
MDFNMP_InstallReadPatches();
}
/* Convenience function. */
int MDFNI_ToggleCheat(uint32 which)
{
cheats[which].status = !cheats[which].status;
savecheats = 1;
RebuildSubCheats();
return(cheats[which].status);
}
static void SettingChanged(const char *name)
{
MDFNMP_RemoveReadPatches();
CheatsActive = MDFN_GetSettingB("cheats");
RebuildSubCheats();
MDFNMP_InstallReadPatches();
}
MDFNSetting MDFNMP_Settings[] =
{
{ "cheats", MDFNSF_NOFLAGS, "Enable cheats.", NULL, MDFNST_BOOL, "1", NULL, NULL, NULL, SettingChanged },
{ NULL}
};