Added missing launcher
[mupen64plus-pandora.git] / source / front-end / src / cheat.c
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
36 typedef struct {
37    int    address;
38    int   *variables;
39    char **variable_names;
40    int    var_to_use;
41    int    var_count;
42 } cheat_code;
43
44 typedef 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 */
55 static m64p_rom_header    l_RomHeader;
56 static char              *l_IniText = NULL;
57 static char              *l_CheatGameName = NULL;
58 static sCheatInfo        *l_CheatList = NULL;
59 static int                l_CheatCodesFound = 0;
60 static int                l_RomFound = 0;
61
62 /*********************************************************************************************************
63  *  Static (Local) functions
64  */
65
66 static int isSpace(char ch)
67 {
68     return (ch == ' ' || ch == '\t' || ch == '\r' || ch == '\n');
69 }
70
71 /* Find cheat code */
72 static 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 */
85 static 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
114 static 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 */
144 static 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
173 static 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
203 static 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
347 void 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