Added missing launcher
[mupen64plus-pandora.git] / source / front-end / src / cheat.c
CommitLineData
5288f542 1/* * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * *
2 * Mupen64plus - cheat.c *
3 * Mupen64Plus homepage: http://code.google.com/p/mupen64plus/ *
4 * Copyright (C) 2009-2010 Richard Goedeken *
5 * Copyright (C) 2010 Rhett Osborne (spinout) *
6 * *
7 * This program is free software; you can redistribute it and/or modify *
8 * it under the terms of the GNU General Public License as published by *
9 * the Free Software Foundation; either version 2 of the License, or *
10 * (at your option) any later version. *
11 * *
12 * This program is distributed in the hope that it will be useful, *
13 * but WITHOUT ANY WARRANTY; without even the implied warranty of *
14 * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the *
15 * GNU General Public License for more details. *
16 * *
17 * You should have received a copy of the GNU General Public License *
18 * along with this program; if not, write to the *
19 * Free Software Foundation, Inc., *
20 * 51 Franklin Street, Fifth Floor, Boston, MA 02110-1301, USA. *
21 * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * */
22
23#include <stdlib.h>
24#include <string.h>
25#include <stdio.h>
26
27#include "m64p_types.h"
28
29#include "main.h"
30#include "cheat.h"
31#include "core_interface.h"
32
33/* local definitions */
34#define CHEAT_FILE "mupencheat.txt"
35
36typedef struct {
37 int address;
38 int *variables;
39 char **variable_names;
40 int var_to_use;
41 int var_count;
42} cheat_code;
43
44typedef struct _sCheatInfo {
45 int Number;
46 int Count;
47 int VariableLine;
48 const char *Name;
49 const char *Description;
50 cheat_code *Codes;
51 struct _sCheatInfo *Next;
52 } sCheatInfo;
53
54/* local variables */
55static m64p_rom_header l_RomHeader;
56static char *l_IniText = NULL;
57static char *l_CheatGameName = NULL;
58static sCheatInfo *l_CheatList = NULL;
59static int l_CheatCodesFound = 0;
60static int l_RomFound = 0;
61
62/*********************************************************************************************************
63 * Static (Local) functions
64 */
65
66static int isSpace(char ch)
67{
68 return (ch == ' ' || ch == '\t' || ch == '\r' || ch == '\n');
69}
70
71/* Find cheat code */
72static sCheatInfo *CheatFindCode(int Number)
73{
74 sCheatInfo *pCur = l_CheatList;
75 while (pCur != NULL)
76 {
77 if (pCur->Number == Number) break;
78 pCur = pCur->Next;
79 }
80 return pCur;
81}
82
83
84/* Activate a code */
85static void CheatActivate(sCheatInfo *pCheat)
86{
87 int i;
88
89 /* Get a m64p_cheat_code object */
90 m64p_cheat_code * code = (m64p_cheat_code*) calloc(pCheat->Count, sizeof(m64p_cheat_code));
91 if (code == NULL)
92 {
93 DebugMessage(M64MSG_WARNING, "could not allocate memory for code '%s'", pCheat->Name);
94 return;
95 }
96 /* Fill in members */
97 for (i = 0; i < pCheat->Count; i++)
98 {
99 code[i].address = pCheat->Codes[i].address;
100 code[i].value = pCheat->Codes[i].variables[pCheat->Codes[i].var_to_use];
101 }
102 /* Enable cheat */
103 if (CoreAddCheat(pCheat->Name, code, pCheat->Count) != M64ERR_SUCCESS)
104 {
105 DebugMessage(M64MSG_WARNING, "CoreAddCheat() failed for cheat code %i (%s)", pCheat->Number, pCheat->Name);
106 free(code);
107 return;
108 }
109
110 free(code);
111 DebugMessage(M64MSG_STATUS, "activated cheat code %i: %s", pCheat->Number, pCheat->Name);
112}
113
114static void CheatFreeAll(void)
115{
116 if (l_IniText != NULL)
117 free(l_IniText);
118 l_IniText = NULL;
119
120 sCheatInfo *pCur = l_CheatList;
121 while (pCur != NULL)
122 {
123 sCheatInfo *pNext = pCur->Next;
124 if (pCur->Codes != NULL)
125 {
126 int i;
127 for (i=0; i < pCur->Count; i++)
128 {
129 if (pCur->Codes[i].variables != NULL)
130 free(pCur->Codes[i].variables);
131 if (pCur->Codes[i].variable_names != NULL)
132 free(pCur->Codes[i].variable_names);
133 }
134 free(pCur->Codes);
135 }
136 free(pCur);
137 pCur = pNext;
138 }
139
140 l_CheatList = NULL;
141}
142
143/* Append new code */
144static sCheatInfo * NewCode(char *CheatName, int CheatNum)
145{
146 /* allocate memory for a new sCheatInfo struct */
147 sCheatInfo *pNew = (sCheatInfo *) malloc(sizeof(sCheatInfo));
148 if (pNew == NULL) return NULL;
149
150 /* fill in the data members */
151 pNew->Number = CheatNum;
152 pNew->Count = 0;
153 pNew->VariableLine = -1;
154 pNew->Name = CheatName;
155 pNew->Description = NULL;
156 pNew->Codes = NULL;
157 pNew->Next = NULL;
158
159 l_CheatCodesFound++;
160
161 /* stick it at the end of the list */
162 if (l_CheatList == NULL)
163 {
164 l_CheatList = pNew;
165 return pNew;
166 }
167 sCheatInfo *pLast = l_CheatList;
168 while (pLast->Next != NULL) pLast = pLast->Next;
169 pLast->Next = pNew;
170 return pNew;
171}
172
173static void CheatAddVariables(cheat_code * Code, char *varlist)
174{
175 /* needs to be more verbose? */
176 Code->variables = NULL;
177 Code->variable_names = NULL;
178 Code->var_count = 0;
179 while (*varlist != 0)
180 {
181 if ((Code->variables = (int*) realloc(Code->variables, sizeof(int) * (Code->var_count + 1))) == NULL)
182 return;
183 if ((Code->variable_names = (char**) realloc(Code->variable_names, sizeof(char*) * (Code->var_count + 1))) == NULL)
184 return;
185 if (sscanf(varlist, "%04X", &Code->variables[Code->var_count]) != 1)
186 Code->variables[Code->var_count] = 0;
187 if (strchr(varlist, '"') == NULL)
188 return;
189 Code->variable_names[Code->var_count] = strchr(varlist, '"') + 1;
190 if ((varlist = strchr(Code->variable_names[Code->var_count], '"')) == NULL)
191 return;
192 *varlist++ = 0;
193 if (*varlist == ',')
194 varlist++;
195 Code->var_count++;
196 }
197}
198
199/*********************************************************************************************************
200* global functions
201*/
202
203static void ReadCheats(char *RomSection)
204{
205 sCheatInfo *curr_code = NULL;
206 const char *romdbpath = ConfigGetSharedDataFilepath(CHEAT_FILE);
207 if (romdbpath == NULL)
208 {
209 DebugMessage(M64MSG_WARNING, "cheat code database file '%s' not found.", CHEAT_FILE);
210 return;
211 }
212
213 /* read the INI file into a new buffer */
214 FILE *fPtr = NULL;
215 fPtr = fopen(romdbpath, "rb");
216 if (fPtr == NULL)
217 {
218 DebugMessage(M64MSG_WARNING, "Couldn't open cheat code database file '%s'.", romdbpath);
219 return;
220 }
221 fseek(fPtr, 0L, SEEK_END);
222 long IniLength = ftell(fPtr);
223 fseek(fPtr, 0L, SEEK_SET);
224 l_IniText = (char *) malloc(IniLength + 1);
225 if (l_IniText == NULL)
226 {
227 DebugMessage(M64MSG_WARNING, "Couldn't allocate %li bytes of memory to read cheat file.", IniLength);
228 fclose(fPtr);
229 return;
230 }
231 if (fread(l_IniText, 1, IniLength, fPtr) != IniLength)
232 {
233 DebugMessage(M64MSG_WARNING, "Couldn't read %li bytes from cheat file.", IniLength);
234 free(l_IniText);
235 l_IniText = NULL;
236 fclose(fPtr);
237 return;
238 }
239 fclose(fPtr);
240 l_IniText[IniLength] = 0; /* null-terminate the text data */
241
242 /* parse lines from cheat database */
243 char *curline = NULL;
244 char *nextline = l_IniText;
245
246 while(nextline != NULL && *nextline != 0)
247 {
248 curline = nextline;
249 /* get pointer to next line and NULL-terminate the current line */
250 nextline = strchr(curline, '\n');
251 if (nextline != NULL)
252 {
253 *nextline = 0;
254 nextline++;
255 }
256
257 /* remove leading and trailing white space */
258 while(isSpace(*curline)) curline++;
259 char *endptr = curline + strlen(curline) - 1;
260 while(isSpace(*endptr)) *endptr-- = 0;
261
262 /* ignore line if comment or empty */
263 if (*curline == '#' || strncmp(curline, "//", 2) == 0 || *curline == 0)
264 continue;
265
266 /* handle beginning of new rom section */
267 if (strncmp(curline, "crc ", 4) == 0)
268 {
269 /* if we have already found cheats for the given ROM file, then exit upon encountering a new ROM section */
270 if (l_RomFound && (l_CheatGameName != NULL || l_CheatList != NULL))
271 return;
272 /* else see if this Rom Section matches */
273 if (strcmp(curline+4, RomSection) == 0)
274 l_RomFound = 1;
275 continue;
276 }
277
278 /* if we haven't found the specified ROM section, then continue looking */
279 if (!l_RomFound)
280 continue;
281
282 /* Game name */
283 if (strncmp(curline, "gn ", 3) == 0)
284 {
285 l_CheatGameName = curline+3;
286 continue;
287 }
288
289 /* code name */
290 if (strncmp(curline, "cn ", 3) == 0)
291 {
292 curr_code = NewCode(curline + 3, l_CheatCodesFound);
293 if (curr_code == NULL)
294 DebugMessage(M64MSG_WARNING, "error getting new code (%s)", curline+3);
295 continue;
296 }
297
298 /* if curr_code is NULL, don't do these checks */
299 if (curr_code == NULL)
300 continue;
301
302 /* code description */
303 if (strncmp(curline, "cd ", 3) == 0)
304 {
305 curr_code->Description = curline+3;
306 continue;
307 }
308
309 /* code line */
310 int address;
311 if (sscanf(curline, "%8X %*s", &address) == 1)
312 {
313 curr_code->Codes = (cheat_code*) realloc(curr_code->Codes, sizeof(cheat_code) * (curr_code->Count + 1));
314 if (strncmp(curline+9, "????", 4) == 0)
315 {
316 curr_code->Codes[curr_code->Count].var_count = 0;
317 CheatAddVariables(&curr_code->Codes[curr_code->Count], curline+14);
318 curr_code->VariableLine = curr_code->Count;
319 }
320 else
321 {
322 int var;
323 curr_code->Codes[curr_code->Count].var_count = 1;
324 curr_code->Codes[curr_code->Count].variables = (int*) malloc(sizeof(int));
325 if(curr_code->Codes[curr_code->Count].variables == NULL)
326 {
327 DebugMessage(M64MSG_WARNING, "couldn't allocate memory; ignoring line: '%s'", curline);
328 continue;
329 }
330 if (sscanf(curline+9, "%04X", &var) != 1)
331 var = 0;
332 curr_code->Codes[curr_code->Count].variables[0] = var;
333 curr_code->Codes[curr_code->Count].variable_names = NULL;
334 }
335 curr_code->Codes[curr_code->Count].var_to_use = 0;
336 curr_code->Codes[curr_code->Count].address = address;
337 curr_code->Count++;
338 continue;
339 }
340
341 /* otherwise we don't know what this line is */
342 DebugMessage(M64MSG_WARNING, "unrecognized line in cheat file: '%s'", curline);
343 }
344
345}
346
347void CheatStart(eCheatMode CheatMode, char *CheatNumList)
348{
349 /* if cheat codes are disabled, then we don't have to do anything */
350 if (CheatMode == CHEAT_DISABLE || (CheatMode == CHEAT_LIST && strlen(CheatNumList) == 0))
351 {
352 DebugMessage(M64MSG_STATUS, "Cheat codes disabled.");
353 return;
354 }
355
356 /* get the ROM header for the currently loaded ROM image from the core */
357 if ((*CoreDoCommand)(M64CMD_ROM_GET_HEADER, sizeof(l_RomHeader), &l_RomHeader) != M64ERR_SUCCESS)
358 {
359 DebugMessage(M64MSG_WARNING, "couldn't get ROM header information from core library");
360 return;
361 }
362
363 /* generate section name from ROM's CRC and country code */
364 char RomSection[24];
365 sprintf(RomSection, "%08X-%08X-C:%X", sl(l_RomHeader.CRC1), sl(l_RomHeader.CRC2), l_RomHeader.Country_code & 0xff);
366
367 /* parse through the cheat INI file and load up any cheat codes found for this ROM */
368 ReadCheats(RomSection);
369 if (!l_RomFound || l_CheatCodesFound == 0)
370 {
371 DebugMessage(M64MSG_WARNING, "no cheat codes found for ROM image '%.20s'", l_RomHeader.Name);
372 CheatFreeAll();
373 return;
374 }
375
376 /* handle the list command */
377 if (CheatMode == CHEAT_SHOW_LIST)
378 {
379 DebugMessage(M64MSG_INFO, "%i cheat code(s) found for ROM '%s'", l_CheatCodesFound, l_CheatGameName);
380 sCheatInfo *pCur = l_CheatList;
381 while (pCur != NULL)
382 {
383 if (pCur->Description == NULL)
384 DebugMessage(M64MSG_INFO, " %i: %s", pCur->Number, pCur->Name);
385 else
386 DebugMessage(M64MSG_INFO, " %i: %s (%s)", pCur->Number, pCur->Name, pCur->Description);
387 if(pCur->VariableLine != -1)
388 {
389 int i;
390 for (i = 0; i < pCur->Codes[pCur->VariableLine].var_count; i++)
391 DebugMessage(M64MSG_INFO, " %i: %s", i, pCur->Codes[pCur->VariableLine].variable_names[i]);
392 }
393 pCur = pCur->Next;
394 }
395 CheatFreeAll();
396 return;
397 }
398
399 /* handle all cheats enabled mode */
400 if (CheatMode == CHEAT_ALL)
401 {
402 sCheatInfo *pCur = l_CheatList;
403 while (pCur != NULL)
404 {
405 CheatActivate(pCur);
406 pCur = pCur->Next;
407 }
408 CheatFreeAll();
409 return;
410 }
411
412 /* handle list of cheats enabled mode */
413 if (CheatMode == CHEAT_LIST)
414 {
415 int option, number;
416 char *cheat_next;
417 sCheatInfo *pCheat;
418 while(CheatNumList != NULL && *CheatNumList)
419 {
420 if ((cheat_next = strchr(CheatNumList, ',')) != NULL)
421 {
422 *cheat_next = 0;
423 cheat_next ++;
424 }
425
426 if (strchr(CheatNumList, '-') != NULL)
427 {
428 sscanf(CheatNumList, "%i-%i", &number, &option); /* option */
429 }
430 else
431 {
432 option=0;
433 sscanf(CheatNumList, "%i", &number);
434 }
435
436 pCheat = CheatFindCode(number);
437 if (pCheat == NULL)
438 DebugMessage(M64MSG_WARNING, "invalid cheat code number %i", number);
439 else
440 {
441 if (pCheat->VariableLine != -1 && pCheat->Count > pCheat->VariableLine && option < pCheat->Codes[pCheat->VariableLine].var_count)
442 pCheat->Codes[pCheat->VariableLine].var_to_use = option;
443 CheatActivate(pCheat);
444 }
445
446 CheatNumList = cheat_next;
447 }
448 CheatFreeAll();
449
450 return;
451 }
452
453 /* otherwise the mode is invalid */
454 DebugMessage(M64MSG_WARNING, "internal error; invalid CheatMode in CheatStart()");
455
456 return;
457}
458