Files
ci-libretro-fceumm/src/cheat.c
negativeExponent 565f60c0f2 Fix some warnings
2020-10-25 08:31:41 +08:00

634 lines
13 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., 51 Franklin Street, Fifth Floor, Boston, MA 02110-1301 USA
*/
#include <stdlib.h>
#include <string.h>
#include <stdio.h>
#include <ctype.h>
#include "fceu-types.h"
#include "x6502.h"
#include "cheat.h"
#include "fceu.h"
#include "general.h"
#include "cart.h"
#include "fceu-memory.h"
static uint8 *CheatRPtrs[64];
uint8 *MMapPtrs[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;
MMapPtrs[AB + x] = p + 1024 * x;
}
}
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)
{
numsubcheats = savecheats = 0;
RebuildSubCheats();
}
void FCEU_FlushGameCheats(FILE *override, int nosave) {
if (CheatComp)
{
free(CheatComp);
CheatComp = 0;
}
RebuildSubCheats(); /* Remove memory handlers. */
}
void FCEU_ResetCheats(void)
{
if (CheatComp) {
free(CheatComp);
CheatComp = 0;
}
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;
}
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;
/* 2020-08-31 - negativeExponent
* Why is the top code set as default on non-debug runtime when
* bottom code is what works for PAR?
*/
/* if (1) {
*a = (boo[3] << 8) | (boo[2] + 0x7F);
*v = 0;
} else {
*v = boo[3];
*a = boo[2] | (boo[1] << 8);
} */
*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;
if (compare >= 0)
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 INLINE int 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();
}