fceux/cheat.c

856 lines
16 KiB
C

/* FCE Ultra - NES/Famicom Emulator
*
* Copyright notice for this file:
* Copyright (C) 2002 Xodnizel
*
* 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 <stdlib.h>
#include <string.h>
#include <stdio.h>
#include <ctype.h>
#include "types.h"
#include "x6502.h"
#include "cheat.h"
#include "fceu.h"
#include "general.h"
#include "cart.h"
#include "memory.h"
static uint8 *CheatRPtrs[64];
void FCEU_CheatResetRAM(void)
{
int x;
for(x=0;x<64;x++)
CheatRPtrs[x]=0;
}
void FCEU_CheatAddRAM(int s, uint32 A, uint8 *p)
{
uint32 AB=A>>10;
int x;
for(x=s-1;x>=0;x--)
CheatRPtrs[AB+x]=p-A;
}
struct CHEATF {
struct CHEATF *next;
char *name;
uint16 addr;
uint8 val;
int compare; /* -1 for no compare. */
int type; /* 0 for replace, 1 for substitute(GG). */
int status;
};
typedef struct {
uint16 addr;
uint8 val;
int compare;
readfunc PrevRead;
} CHEATF_SUBFAST;
static CHEATF_SUBFAST SubCheats[256];
static int numsubcheats=0;
struct CHEATF *cheats=0,*cheatsl=0;
#define CHEATC_NONE 0x8000
#define CHEATC_EXCLUDED 0x4000
#define CHEATC_NOSHOW 0xC000
static uint16 *CheatComp=0;
static int savecheats;
static DECLFR(SubCheatsRead)
{
CHEATF_SUBFAST *s=SubCheats;
int x=numsubcheats;
do
{
if(s->addr==A)
{
if(s->compare>=0)
{
uint8 pv=s->PrevRead(A);
if(pv==s->compare)
return(s->val);
else return(pv);
}
else return(s->val);
}
s++;
} while(--x);
return(0); /* We should never get here. */
}
void RebuildSubCheats(void)
{
int x;
struct CHEATF *c=cheats;
for(x=0;x<numsubcheats;x++)
SetReadHandler(SubCheats[x].addr,SubCheats[x].addr,SubCheats[x].PrevRead);
numsubcheats=0;
while(c)
{
if(c->type==1 && c->status)
{
if(GetReadHandler(c->addr)==SubCheatsRead)
{
/* Prevent a catastrophe by this check. */
//FCEU_DispMessage("oops");
}
else
{
SubCheats[numsubcheats].PrevRead=GetReadHandler(c->addr);
SubCheats[numsubcheats].addr=c->addr;
SubCheats[numsubcheats].val=c->val;
SubCheats[numsubcheats].compare=c->compare;
SetReadHandler(c->addr,c->addr,SubCheatsRead);
numsubcheats++;
}
}
c=c->next;
}
}
void FCEU_PowerCheats()
{
numsubcheats=0; /* Quick hack to prevent setting of ancient read addresses. */
RebuildSubCheats();
}
static int AddCheatEntry(char *name, uint32 addr, uint8 val, int compare, int status, int type);
static void CheatMemErr(void)
{
FCEUD_PrintError("Error allocating memory for cheat data.");
}
/* This function doesn't allocate any memory for "name" */
static int AddCheatEntry(char *name, uint32 addr, uint8 val, int compare, int status, int type)
{
struct CHEATF *temp;
if(!(temp=(struct CHEATF *)malloc(sizeof(struct CHEATF))))
{
CheatMemErr();
return(0);
}
temp->name=name;
temp->addr=addr;
temp->val=val;
temp->status=status;
temp->compare=compare;
temp->type=type;
temp->next=0;
if(cheats)
{
cheatsl->next=temp;
cheatsl=temp;
}
else
cheats=cheatsl=temp;
return(1);
}
void FCEU_LoadGameCheats(FILE *override)
{
FILE *fp;
unsigned int addr;
unsigned int val;
unsigned int status;
unsigned int type;
unsigned int compare;
int x;
char linebuf[2048];
char *namebuf;
int tc=0;
char *fn;
numsubcheats=savecheats=0;
if(override)
fp = override;
else
{
fn=FCEU_MakeFName(FCEUMKF_CHEAT,0,0);
fp=FCEUD_UTF8fopen(fn,"rb");
free(fn);
if(!fp) return;
}
while(fgets(linebuf,2048,fp)>0)
{
char *tbuf=linebuf;
int doc=0;
addr=val=compare=status=type=0;
if(tbuf[0]=='S')
{
tbuf++;
type=1;
}
else type=0;
if(tbuf[0]=='C')
{
tbuf++;
doc=1;
}
if(tbuf[0]==':')
{
tbuf++;
status=0;
}
else status=1;
if(doc)
{
char *neo=&tbuf[4+2+2+1+1+1];
if(sscanf(tbuf,"%04x%*[:]%02x%*[:]%02x",&addr,&val,&compare)!=3)
continue;
namebuf=(char *)malloc(strlen(neo)+1);
strcpy(namebuf,neo);
}
else
{
char *neo=&tbuf[4+2+1+1];
if(sscanf(tbuf,"%04x%*[:]%02x",&addr,&val)!=2)
continue;
namebuf=(char *)malloc(strlen(neo)+1);
strcpy(namebuf,neo);
}
for(x=0;x<(int)strlen(namebuf);x++)
{
if(namebuf[x]==10 || namebuf[x]==13)
{
namebuf[x]=0;
break;
}
else if(namebuf[x]<0x20) namebuf[x]=' ';
}
AddCheatEntry(namebuf,addr,val,doc?compare:-1,status,type);
tc++;
}
RebuildSubCheats();
if(!override)
fclose(fp);
}
void FCEU_FlushGameCheats(FILE *override, int nosave)
{
if(CheatComp)
{
free(CheatComp);
CheatComp=0;
}
if((!savecheats || nosave) && !override) /* Always save cheats if we're being overridden. */
{
if(cheats)
{
struct CHEATF *next=cheats;
for(;;)
{
struct CHEATF *last=next;
next=next->next;
free(last->name);
free(last);
if(!next) break;
}
cheats=cheatsl=0;
}
}
else
{
char *fn = 0;
if(!override)
fn = FCEU_MakeFName(FCEUMKF_CHEAT,0,0);
if(cheats)
{
struct CHEATF *next=cheats;
FILE *fp;
if(override)
fp = override;
else
fp=FCEUD_UTF8fopen(fn,"wb");
if(fp)
{
for(;;)
{
struct CHEATF *t;
if(next->type)
fputc('S',fp);
if(next->compare>=0)
fputc('C',fp);
if(!next->status)
fputc(':',fp);
if(next->compare>=0)
fprintf(fp,"%04x:%02x:%02x:%s\n",next->addr,next->val,next->compare,next->name);
else
fprintf(fp,"%04x:%02x:%s\n",next->addr,next->val,next->name);
free(next->name);
t=next;
next=next->next;
free(t);
if(!next) break;
}
if(!override)
fclose(fp);
}
else
FCEUD_PrintError("Error saving cheats.");
cheats=cheatsl=0;
}
else if(!override)
remove(fn);
if(!override)
free(fn);
}
RebuildSubCheats(); /* Remove memory handlers. */
}
int FCEUI_AddCheat(const char *name, uint32 addr, uint8 val, int compare, int type)
{
char *t;
if(!(t=(char *)malloc(strlen(name)+1)))
{
CheatMemErr();
return(0);
}
strcpy(t,name);
if(!AddCheatEntry(t,addr,val,compare,1,type))
{
free(t);
return(0);
}
savecheats=1;
RebuildSubCheats();
return(1);
}
int FCEUI_DelCheat(uint32 which)
{
struct CHEATF *prev;
struct CHEATF *cur;
uint32 x=0;
for(prev=0,cur=cheats;;)
{
if(x==which) // Remove this cheat.
{
if(prev) // Update pointer to this cheat.
{
if(cur->next) // More cheats.
prev->next=cur->next;
else // No more.
{
prev->next=0;
cheatsl=prev; // Set the previous cheat as the last cheat.
}
}
else // This is the first cheat.
{
if(cur->next) // More cheats
cheats=cur->next;
else
cheats=cheatsl=0; // No (more) cheats.
}
free(cur->name); // Now that all references to this cheat are removed,
free(cur); // free the memory.
break;
} // *END REMOVE THIS CHEAT*
if(!cur->next) // No more cheats to go through(this shouldn't ever happen...)
return(0);
prev=cur;
cur=prev->next;
x++;
}
savecheats=1;
RebuildSubCheats();
return(1);
}
void FCEU_ApplyPeriodicCheats(void)
{
struct CHEATF *cur=cheats;
if(!cur) return;
for(;;)
{
if(cur->status && !(cur->type))
if(CheatRPtrs[cur->addr>>10])
CheatRPtrs[cur->addr>>10][cur->addr]=cur->val;
if(cur->next)
cur=cur->next;
else
break;
}
}
void FCEUI_ListCheats(int (*callb)(char *name, uint32 a, uint8 v, int compare, int s, int type, void *data), void *data)
{
struct CHEATF *next=cheats;
while(next)
{
if(!callb(next->name,next->addr,next->val,next->compare,next->status,next->type,data)) break;
next=next->next;
}
}
int FCEUI_GetCheat(uint32 which, char **name, uint32 *a, uint8 *v, int *compare, int *s, int *type)
{
struct CHEATF *next=cheats;
uint32 x=0;
while(next)
{
if(x==which)
{
if(name)
*name=next->name;
if(a)
*a=next->addr;
if(v)
*v=next->val;
if(s)
*s=next->status;
if(compare)
*compare=next->compare;
if(type)
*type=next->type;
return(1);
}
next=next->next;
x++;
}
return(0);
}
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 FCEUI_DecodeGG(const char *str, uint16 *a, uint8 *v, int *c)
{
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;
*c=-1;
return(1);
}
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;
return(1);
}
return(0);
}
int FCEUI_DecodePAR(const char *str, uint16 *a, uint8 *v, int *c, int *type)
{
int boo[4];
if(strlen(str)!=8) return(0);
sscanf(str,"%02x%02x%02x%02x",boo,boo+1,boo+2,boo+3);
*c=-1;
if(1)
{
*a=(boo[3]<<8)|(boo[2]+0x7F);
*v=0;
}
else
{
*v=boo[3];
*a=boo[2]|(boo[1]<<8);
}
/* Zero-page addressing modes don't go through the normal read/write handlers in FCEU, so
we must do the old hacky method of RAM cheats.
*/
if(*a<0x0100)
*type=0;
else
*type=1;
return(1);
}
/* name can be NULL if the name isn't going to be changed. */
/* same goes for a, v, and s(except the values of each one must be <0) */
int FCEUI_SetCheat(uint32 which, const char *name, int32 a, int32 v, int compare,int s, int type)
{
struct CHEATF *next=cheats;
uint32 x=0;
while(next)
{
if(x==which)
{
if(name)
{
char *t;
if((t=(char *)realloc(next->name,strlen(name+1))))
{
next->name=t;
strcpy(next->name,name);
}
else
return(0);
}
if(a>=0)
next->addr=a;
if(v>=0)
next->val=v;
if(s>=0)
next->status=s;
next->compare=compare;
next->type=type;
savecheats=1;
RebuildSubCheats();
return(1);
}
next=next->next;
x++;
}
return(0);
}
/* Convenience function. */
int FCEUI_ToggleCheat(uint32 which)
{
struct CHEATF *next=cheats;
uint32 x=0;
while(next)
{
if(x==which)
{
next->status=!next->status;
savecheats=1;
RebuildSubCheats();
return(next->status);
}
next=next->next;
x++;
}
return(-1);
}
static int InitCheatComp(void)
{
uint32 x;
CheatComp=(uint16*)malloc(65536*sizeof(uint16));
if(!CheatComp)
{
CheatMemErr();
return(0);
}
for(x=0;x<65536;x++)
CheatComp[x]=CHEATC_NONE;
return(1);
}
void FCEUI_CheatSearchSetCurrentAsOriginal(void)
{
uint32 x;
for(x=0x000;x<0x10000;x++)
if(!(CheatComp[x]&CHEATC_NOSHOW))
{
if(CheatRPtrs[x>>10])
CheatComp[x]=CheatRPtrs[x>>10][x];
else
CheatComp[x]|=CHEATC_NONE;
}
}
void FCEUI_CheatSearchShowExcluded(void)
{
uint32 x;
for(x=0x000;x<0x10000;x++)
CheatComp[x]&=~CHEATC_EXCLUDED;
}
int32 FCEUI_CheatSearchGetCount(void)
{
uint32 x,c=0;
if(CheatComp)
{
for(x=0x0000;x<0x10000;x++)
if(!(CheatComp[x]&CHEATC_NOSHOW) && CheatRPtrs[x>>10])
c++;
}
return c;
}
/* This function will give the initial value of the search and the current value at a location. */
void FCEUI_CheatSearchGet(int (*callb)(uint32 a, uint8 last, uint8 current, void *data),void *data)
{
uint32 x;
if(!CheatComp)
{
if(!InitCheatComp())
CheatMemErr();
return;
}
for(x=0;x<0x10000;x++)
if(!(CheatComp[x]&CHEATC_NOSHOW) && CheatRPtrs[x>>10])
if(!callb(x,CheatComp[x],CheatRPtrs[x>>10][x],data))
break;
}
void FCEUI_CheatSearchGetRange(uint32 first, uint32 last, int (*callb)(uint32 a, uint8 last, uint8 current))
{
uint32 x;
uint32 in=0;
if(!CheatComp)
{
if(!InitCheatComp())
CheatMemErr();
return;
}
for(x=0;x<0x10000;x++)
if(!(CheatComp[x]&CHEATC_NOSHOW) && CheatRPtrs[x>>10])
{
if(in>=first)
if(!callb(x,CheatComp[x],CheatRPtrs[x>>10][x]))
break;
in++;
if(in>last) return;
}
}
void FCEUI_CheatSearchBegin(void)
{
uint32 x;
if(!CheatComp)
{
if(!InitCheatComp())
{
CheatMemErr();
return;
}
}
for(x=0;x<0x10000;x++)
{
if(CheatRPtrs[x>>10])
CheatComp[x]=CheatRPtrs[x>>10][x];
else
CheatComp[x]=CHEATC_NONE;
}
}
static int INLINE CAbs(int x)
{
if(x<0)
return(0-x);
return x;
}
void FCEUI_CheatSearchEnd(int type, uint8 v1, uint8 v2)
{
uint32 x;
if(!CheatComp)
{
if(!InitCheatComp())
{
CheatMemErr();
return;
}
}
if(!type) // Change to a specific value.
{
for(x=0;x<0x10000;x++)
if(!(CheatComp[x]&CHEATC_NOSHOW))
{
if(CheatComp[x]==v1 && CheatRPtrs[x>>10][x]==v2)
{
}
else
CheatComp[x]|=CHEATC_EXCLUDED;
}
}
else if(type==1) // Search for relative change(between values).
{
for(x=0;x<0x10000;x++)
if(!(CheatComp[x]&CHEATC_NOSHOW))
{
if(CheatComp[x]==v1 && CAbs(CheatComp[x]-CheatRPtrs[x>>10][x])==v2)
{
}
else
CheatComp[x]|=CHEATC_EXCLUDED;
}
}
else if(type==2) // Purely relative change.
{
for(x=0x000;x<0x10000;x++)
if(!(CheatComp[x]&CHEATC_NOSHOW))
{
if(CAbs(CheatComp[x]-CheatRPtrs[x>>10][x])==v2)
{
}
else
CheatComp[x]|=CHEATC_EXCLUDED;
}
}
else if(type==3) // Any change.
{
for(x=0;x<0x10000;x++)
if(!(CheatComp[x]&CHEATC_NOSHOW))
{
if(CheatComp[x]!=CheatRPtrs[x>>10][x])
{
}
else
CheatComp[x]|=CHEATC_EXCLUDED;
}
}
else if(type==4) // Value decreased.
{
for(x=0;x<0x10000;x++)
if(!(CheatComp[x]&CHEATC_NOSHOW))
{
if(!(CheatRPtrs[x>>10][x]<CheatComp[x]))
CheatComp[x]|=CHEATC_EXCLUDED;
}
}
else if(type==5) // Value increased.
{
for(x=0;x<0x10000;x++)
if(!(CheatComp[x]&CHEATC_NOSHOW))
{
if(!(CheatRPtrs[x>>10][x]>CheatComp[x]))
CheatComp[x]|=CHEATC_EXCLUDED;
}
}
if(type>4)
FCEUI_CheatSearchSetCurrentAsOriginal();
}