mirror of
https://github.com/simple64/simple64.git
synced 2026-11-05 00:53:32 +01:00
328 lines
11 KiB
C++
328 lines
11 KiB
C++
/* * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * *
|
|
* Mupen64plus - cheat.c *
|
|
* Mupen64Plus homepage: http://code.google.com/p/mupen64plus/ *
|
|
* Copyright (C) 2009-2010 Richard Goedeken *
|
|
* Copyright (C) 2010 Rhett Osborne (spinout) *
|
|
* *
|
|
* 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 <stdio.h>
|
|
#include <stdlib.h>
|
|
#include <string.h>
|
|
|
|
#include "cheat.h"
|
|
#include "core_interface.h"
|
|
#include "common.h"
|
|
|
|
/* local definitions */
|
|
#define CHEAT_FILE "mupencheat.txt"
|
|
|
|
/* local variables */
|
|
static char *l_IniText = NULL;
|
|
char *l_CheatGameName = NULL;
|
|
sCheatInfo *l_CheatList = NULL;
|
|
int l_CheatCodesFound = 0;
|
|
int l_RomFound = 0;
|
|
|
|
/*********************************************************************************************************
|
|
* Static (Local) functions
|
|
*/
|
|
|
|
static int isSpace(char ch)
|
|
{
|
|
return (ch == ' ' || ch == '\t' || ch == '\r' || ch == '\n');
|
|
}
|
|
|
|
/* Find cheat code */
|
|
sCheatInfo *CheatFindCode(int Number)
|
|
{
|
|
sCheatInfo *pCur = l_CheatList;
|
|
while (pCur != NULL)
|
|
{
|
|
if (pCur->Number == Number) break;
|
|
pCur = pCur->Next;
|
|
}
|
|
return pCur;
|
|
}
|
|
|
|
|
|
/* Activate a code */
|
|
void CheatActivate(sCheatInfo *pCheat)
|
|
{
|
|
int i;
|
|
|
|
/* Get a m64p_cheat_code object */
|
|
m64p_cheat_code * code = (m64p_cheat_code*) calloc(pCheat->Count, sizeof(m64p_cheat_code));
|
|
if (code == NULL)
|
|
{
|
|
DebugMessage(M64MSG_WARNING, "could not allocate memory for code '%s'", pCheat->Name);
|
|
return;
|
|
}
|
|
/* Fill in members */
|
|
for (i = 0; i < pCheat->Count; i++)
|
|
{
|
|
code[i].address = pCheat->Codes[i].address;
|
|
code[i].value = pCheat->Codes[i].variables[pCheat->Codes[i].var_to_use];
|
|
}
|
|
/* Enable cheat */
|
|
if (CoreAddCheat(pCheat->Name, code, pCheat->Count) != M64ERR_SUCCESS)
|
|
{
|
|
DebugMessage(M64MSG_WARNING, "CoreAddCheat() failed for cheat code %i (%s)", pCheat->Number, pCheat->Name);
|
|
free(code);
|
|
return;
|
|
}
|
|
|
|
free(code);
|
|
DebugMessage(M64MSG_STATUS, "activated cheat code %i: %s", pCheat->Number, pCheat->Name);
|
|
}
|
|
|
|
void CheatFreeAll(void)
|
|
{
|
|
if (l_IniText != NULL)
|
|
free(l_IniText);
|
|
l_IniText = NULL;
|
|
|
|
sCheatInfo *pCur = l_CheatList;
|
|
while (pCur != NULL)
|
|
{
|
|
sCheatInfo *pNext = pCur->Next;
|
|
if (pCur->Codes != NULL)
|
|
{
|
|
int i;
|
|
for (i=0; i < pCur->Count; i++)
|
|
{
|
|
if (pCur->Codes[i].variables != NULL)
|
|
free(pCur->Codes[i].variables);
|
|
if (pCur->Codes[i].variable_names != NULL)
|
|
free(pCur->Codes[i].variable_names);
|
|
}
|
|
free(pCur->Codes);
|
|
}
|
|
free(pCur);
|
|
pCur = pNext;
|
|
}
|
|
|
|
l_CheatList = NULL;
|
|
l_RomFound = 0;
|
|
l_CheatCodesFound = 0;
|
|
}
|
|
|
|
/* Append new code */
|
|
static sCheatInfo * NewCode(char *CheatName, int CheatNum)
|
|
{
|
|
/* allocate memory for a new sCheatInfo struct */
|
|
sCheatInfo *pNew = (sCheatInfo *) malloc(sizeof(sCheatInfo));
|
|
if (pNew == NULL) return NULL;
|
|
|
|
/* fill in the data members */
|
|
pNew->Number = CheatNum;
|
|
pNew->Count = 0;
|
|
pNew->VariableLine = -1;
|
|
pNew->Name = CheatName;
|
|
pNew->Description = NULL;
|
|
pNew->Codes = NULL;
|
|
pNew->Next = NULL;
|
|
pNew->active = false;
|
|
|
|
l_CheatCodesFound++;
|
|
|
|
/* stick it at the end of the list */
|
|
if (l_CheatList == NULL)
|
|
{
|
|
l_CheatList = pNew;
|
|
return pNew;
|
|
}
|
|
sCheatInfo *pLast = l_CheatList;
|
|
while (pLast->Next != NULL) pLast = pLast->Next;
|
|
pLast->Next = pNew;
|
|
return pNew;
|
|
}
|
|
|
|
static void CheatAddVariables(cheat_code * Code, char *varlist)
|
|
{
|
|
/* needs to be more verbose? */
|
|
Code->variables = NULL;
|
|
Code->variable_names = NULL;
|
|
Code->var_count = 0;
|
|
while (*varlist != 0)
|
|
{
|
|
if ((Code->variables = (int*) realloc(Code->variables, sizeof(int) * (Code->var_count + 1))) == NULL)
|
|
return;
|
|
if ((Code->variable_names = (char**) realloc(Code->variable_names, sizeof(char*) * (Code->var_count + 1))) == NULL)
|
|
return;
|
|
if (sscanf(varlist, "%04X", &Code->variables[Code->var_count]) != 1)
|
|
Code->variables[Code->var_count] = 0;
|
|
if (strchr(varlist, '"') == NULL)
|
|
return;
|
|
Code->variable_names[Code->var_count] = strchr(varlist, '"') + 1;
|
|
if ((varlist = strchr(Code->variable_names[Code->var_count], '"')) == NULL)
|
|
return;
|
|
*varlist++ = 0;
|
|
if (*varlist == ',')
|
|
varlist++;
|
|
Code->var_count++;
|
|
}
|
|
}
|
|
|
|
/*********************************************************************************************************
|
|
* global functions
|
|
*/
|
|
|
|
void ReadCheats(char *RomSection)
|
|
{
|
|
sCheatInfo *curr_code = NULL;
|
|
const char *romdbpath = ConfigGetSharedDataFilepath(CHEAT_FILE);
|
|
if (romdbpath == NULL)
|
|
{
|
|
DebugMessage(M64MSG_WARNING, "cheat code database file '%s' not found.", CHEAT_FILE);
|
|
return;
|
|
}
|
|
|
|
/* read the INI file into a new buffer */
|
|
FILE *fPtr = NULL;
|
|
fPtr = fopen(romdbpath, "rb");
|
|
if (fPtr == NULL)
|
|
{
|
|
DebugMessage(M64MSG_WARNING, "Couldn't open cheat code database file '%s'.", romdbpath);
|
|
return;
|
|
}
|
|
fseek(fPtr, 0L, SEEK_END);
|
|
size_t IniLength = (size_t)ftell(fPtr);
|
|
fseek(fPtr, 0L, SEEK_SET);
|
|
l_IniText = (char *) malloc(IniLength + 1);
|
|
if (l_IniText == NULL)
|
|
{
|
|
DebugMessage(M64MSG_WARNING, "Couldn't allocate %li bytes of memory to read cheat file.", IniLength);
|
|
fclose(fPtr);
|
|
return;
|
|
}
|
|
if (fread(l_IniText, 1, IniLength, fPtr) != IniLength)
|
|
{
|
|
DebugMessage(M64MSG_WARNING, "Couldn't read %li bytes from cheat file.", IniLength);
|
|
free(l_IniText);
|
|
l_IniText = NULL;
|
|
fclose(fPtr);
|
|
return;
|
|
}
|
|
fclose(fPtr);
|
|
l_IniText[IniLength] = 0; /* null-terminate the text data */
|
|
|
|
/* parse lines from cheat database */
|
|
char *curline = NULL;
|
|
char *nextline = l_IniText;
|
|
|
|
while(nextline != NULL && *nextline != 0)
|
|
{
|
|
curline = nextline;
|
|
/* get pointer to next line and NULL-terminate the current line */
|
|
nextline = strchr(curline, '\n');
|
|
if (nextline != NULL)
|
|
{
|
|
*nextline = 0;
|
|
nextline++;
|
|
}
|
|
|
|
/* remove leading and trailing white space */
|
|
while(isSpace(*curline)) curline++;
|
|
char *endptr = curline + strlen(curline) - 1;
|
|
while(isSpace(*endptr)) *endptr-- = 0;
|
|
|
|
/* ignore line if comment or empty */
|
|
if (*curline == '#' || strncmp(curline, "//", 2) == 0 || *curline == 0)
|
|
continue;
|
|
|
|
/* handle beginning of new rom section */
|
|
if (strncmp(curline, "crc ", 4) == 0)
|
|
{
|
|
/* if we have already found cheats for the given ROM file, then exit upon encountering a new ROM section */
|
|
if (l_RomFound && (l_CheatGameName != NULL || l_CheatList != NULL))
|
|
return;
|
|
/* else see if this Rom Section matches */
|
|
if (strcmp(curline+4, RomSection) == 0)
|
|
l_RomFound = 1;
|
|
continue;
|
|
}
|
|
|
|
/* if we haven't found the specified ROM section, then continue looking */
|
|
if (!l_RomFound)
|
|
continue;
|
|
|
|
/* Game name */
|
|
if (strncmp(curline, "gn ", 3) == 0)
|
|
{
|
|
l_CheatGameName = curline+3;
|
|
continue;
|
|
}
|
|
|
|
/* code name */
|
|
if (strncmp(curline, "cn ", 3) == 0)
|
|
{
|
|
curr_code = NewCode(curline + 3, l_CheatCodesFound);
|
|
if (curr_code == NULL)
|
|
DebugMessage(M64MSG_WARNING, "error getting new code (%s)", curline+3);
|
|
continue;
|
|
}
|
|
|
|
/* if curr_code is NULL, don't do these checks */
|
|
if (curr_code == NULL)
|
|
continue;
|
|
|
|
/* code description */
|
|
if (strncmp(curline, "cd ", 3) == 0)
|
|
{
|
|
curr_code->Description = curline+3;
|
|
continue;
|
|
}
|
|
|
|
/* code line */
|
|
int address;
|
|
if (sscanf(curline, "%8X %*s", &address) == 1)
|
|
{
|
|
curr_code->Codes = (cheat_code*) realloc(curr_code->Codes, sizeof(cheat_code) * (curr_code->Count + 1));
|
|
if (strncmp(curline+9, "????", 4) == 0)
|
|
{
|
|
curr_code->Codes[curr_code->Count].var_count = 0;
|
|
CheatAddVariables(&curr_code->Codes[curr_code->Count], curline+14);
|
|
curr_code->VariableLine = curr_code->Count;
|
|
}
|
|
else
|
|
{
|
|
int var;
|
|
curr_code->Codes[curr_code->Count].var_count = 1;
|
|
curr_code->Codes[curr_code->Count].variables = (int*) malloc(sizeof(int));
|
|
if(curr_code->Codes[curr_code->Count].variables == NULL)
|
|
{
|
|
DebugMessage(M64MSG_WARNING, "couldn't allocate memory; ignoring line: '%s'", curline);
|
|
continue;
|
|
}
|
|
if (sscanf(curline+9, "%04X", &var) != 1)
|
|
var = 0;
|
|
curr_code->Codes[curr_code->Count].variables[0] = var;
|
|
curr_code->Codes[curr_code->Count].variable_names = NULL;
|
|
}
|
|
curr_code->Codes[curr_code->Count].var_to_use = 0;
|
|
curr_code->Codes[curr_code->Count].address = address;
|
|
curr_code->Count++;
|
|
continue;
|
|
}
|
|
|
|
/* otherwise we don't know what this line is */
|
|
DebugMessage(M64MSG_WARNING, "unrecognized line in cheat file: '%s'", curline);
|
|
}
|
|
|
|
}
|