1 // VisualBoyAdvance - Nintendo Gameboy/GameboyAdvance (TM) emulator.
2 // Copyright (C) 1999-2003 Forgotten
3 // Copyright (C) 2004 Forgotten and the VBA development team
4
5 // This program is free software; you can redistribute it and/or modify
6 // it under the terms of the GNU General Public License as published by
7 // the Free Software Foundation; either version 2, or(at your option)
8 // any later version.
9 //
10 // This program is distributed in the hope that it will be useful,
11 // but WITHOUT ANY WARRANTY; without even the implied warranty of
12 // MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
13 // GNU General Public License for more details.
14 //
15 // You should have received a copy of the GNU General Public License
16 // along with this program; if not, write to the Free Software Foundation,
17 // Inc., 59 Temple Place - Suite 330, Boston, MA 02111-1307, USA.
18
19 #include <string.h>
20 #include <stdio.h>
21 #include <stdlib.h>
22 #include <ctype.h>
23
24 #include "../System.h"
25 #include "../NLS.h"
26 #include "../Util.h"
27
28 #include "gbCheats.h"
29 #include "gbGlobals.h"
30
31 gbCheat gbCheatList[100];
32 int gbCheatNumber = 0;
33 bool gbCheatMap[0x10000];
34
35 extern bool cheatsEnabled;
36
37 #define GBCHEAT_IS_HEX(a) ( ((a)>='A' && (a) <='F') || ((a) >='0' && (a) <= '9'))
38 #define GBCHEAT_HEX_VALUE(a) ( (a) >= 'A' ? (a) - 'A' + 10 : (a) - '0')
39
gbCheatUpdateMap()40 void gbCheatUpdateMap()
41 {
42 memset(gbCheatMap, 0, 0x10000);
43
44 for(int i = 0; i < gbCheatNumber; i++) {
45 if(gbCheatList[i].enabled)
46 gbCheatMap[gbCheatList[i].address] = true;
47 }
48 }
49
gbCheatsSaveGame(gzFile gzFile)50 void gbCheatsSaveGame(gzFile gzFile)
51 {
52 utilWriteInt(gzFile, gbCheatNumber);
53 if(gbCheatNumber)
54 utilGzWrite(gzFile, &gbCheatList[0], sizeof(gbCheat)*gbCheatNumber);
55 }
56
gbCheatsReadGame(gzFile gzFile,int version)57 void gbCheatsReadGame(gzFile gzFile, int version)
58 {
59 if(version <= 8) {
60 int gbGgOn = utilReadInt(gzFile);
61
62 if(gbGgOn) {
63 int n = utilReadInt(gzFile);
64 gbXxCheat tmpCheat;
65 for(int i = 0; i < n; i++) {
66 utilGzRead(gzFile,&tmpCheat, sizeof(gbXxCheat));
67 gbAddGgCheat(tmpCheat.cheatCode, tmpCheat.cheatDesc);
68 }
69 }
70
71 int gbGsOn = utilReadInt(gzFile);
72
73 if(gbGsOn) {
74 int n = utilReadInt(gzFile);
75 gbXxCheat tmpCheat;
76 for(int i = 0; i < n; i++) {
77 utilGzRead(gzFile,&tmpCheat, sizeof(gbXxCheat));
78 gbAddGsCheat(tmpCheat.cheatCode, tmpCheat.cheatDesc);
79 }
80 }
81 } else {
82 gbCheatNumber = utilReadInt(gzFile);
83
84 if(gbCheatNumber) {
85 utilGzRead(gzFile, &gbCheatList[0], sizeof(gbCheat)*gbCheatNumber);
86 }
87 }
88
89 gbCheatUpdateMap();
90 }
91
gbCheatsSaveCheatList(const char * file)92 void gbCheatsSaveCheatList(const char *file)
93 {
94 if(gbCheatNumber == 0)
95 return;
96 FILE *f = fopen(file, "wb");
97 if(f == NULL)
98 return;
99 int version = 1;
100 fwrite(&version, 1, sizeof(version), f);
101 int type = 1;
102 fwrite(&type, 1, sizeof(type), f);
103 fwrite(&gbCheatNumber, 1, sizeof(gbCheatNumber), f);
104 fwrite(gbCheatList, 1, sizeof(gbCheatList), f);
105 fclose(f);
106 }
107
gbCheatsLoadCheatList(const char * file)108 bool gbCheatsLoadCheatList(const char *file)
109 {
110 gbCheatNumber = 0;
111
112 gbCheatUpdateMap();
113
114 int count = 0;
115
116 FILE *f = fopen(file, "rb");
117
118 if(f == NULL)
119 return false;
120
121 int version = 0;
122
123 if(fread(&version, 1, sizeof(version), f) != sizeof(version)) {
124 fclose(f);
125 return false;
126 }
127
128 if(version != 1) {
129 systemMessage(MSG_UNSUPPORTED_CHEAT_LIST_VERSION,
130 N_("Unsupported cheat list version %d"), version);
131 fclose(f);
132 return false;
133 }
134
135 int type = 0;
136 if(fread(&type, 1, sizeof(type), f) != sizeof(type)) {
137 fclose(f);
138 return false;
139 }
140
141 if(type != 1) {
142 systemMessage(MSG_UNSUPPORTED_CHEAT_LIST_TYPE,
143 N_("Unsupported cheat list type %d"), type);
144 fclose(f);
145 return false;
146 }
147
148 if(fread(&count, 1, sizeof(count), f) != sizeof(count)) {
149 fclose(f);
150 return false;
151 }
152
153 if(fread(gbCheatList, 1, sizeof(gbCheatList), f) != sizeof(gbCheatList)) {
154 fclose(f);
155 return false;
156 }
157
158 gbCheatNumber = count;
159 gbCheatUpdateMap();
160
161 return true;
162 }
163
gbVerifyGsCode(const char * code)164 bool gbVerifyGsCode(const char *code)
165 {
166 int len = strlen(code);
167
168 if(len == 0)
169 return true;
170
171 if(len != 8)
172 return false;
173
174 for(int i = 0; i < 8; i++)
175 if(!GBCHEAT_IS_HEX(code[i]))
176 return false;
177
178 int address = GBCHEAT_HEX_VALUE(code[6]) << 12 |
179 GBCHEAT_HEX_VALUE(code[7]) << 8 |
180 GBCHEAT_HEX_VALUE(code[4]) << 4 |
181 GBCHEAT_HEX_VALUE(code[5]);
182
183 if(address < 0xa000 ||
184 address > 0xdfff)
185 return false;
186
187 return true;
188 }
189
gbAddGsCheat(const char * code,const char * desc)190 void gbAddGsCheat(const char *code, const char *desc)
191 {
192 if(gbCheatNumber > 99) {
193 systemMessage(MSG_MAXIMUM_NUMBER_OF_CHEATS,
194 N_("Maximum number of cheats reached."));
195 return;
196 }
197
198 if(!gbVerifyGsCode(code)) {
199 systemMessage(MSG_INVALID_GAMESHARK_CODE,
200 N_("Invalid GameShark code: %s"), code);
201 return;
202 }
203
204 int i = gbCheatNumber;
205
206 strcpy(gbCheatList[i].cheatCode, code);
207 strcpy(gbCheatList[i].cheatDesc, desc);
208
209 gbCheatList[i].code = GBCHEAT_HEX_VALUE(code[0]) << 4 |
210 GBCHEAT_HEX_VALUE(code[1]);
211
212 gbCheatList[i].value = GBCHEAT_HEX_VALUE(code[2]) << 4 |
213 GBCHEAT_HEX_VALUE(code[3]);
214
215 gbCheatList[i].address = GBCHEAT_HEX_VALUE(code[6]) << 12 |
216 GBCHEAT_HEX_VALUE(code[7]) << 8 |
217 GBCHEAT_HEX_VALUE(code[4]) << 4 |
218 GBCHEAT_HEX_VALUE(code[5]);
219
220 gbCheatList[i].compare = 0;
221
222 gbCheatList[i].enabled = true;
223
224 gbCheatMap[gbCheatList[i].address] = true;
225
226 gbCheatNumber++;
227 }
228
gbVerifyGgCode(const char * code)229 bool gbVerifyGgCode(const char *code)
230 {
231 int len = strlen(code);
232
233 if(len != 11 &&
234 len != 7 &&
235 len != 6 &&
236 len != 0)
237 return false;
238
239 if(len == 0)
240 return true;
241
242 if(!GBCHEAT_IS_HEX(code[0]))
243 return false;
244 if(!GBCHEAT_IS_HEX(code[1]))
245 return false;
246 if(!GBCHEAT_IS_HEX(code[2]))
247 return false;
248 if(code[3] != '-')
249 return false;
250 if(!GBCHEAT_IS_HEX(code[4]))
251 return false;
252 if(!GBCHEAT_IS_HEX(code[5]))
253 return false;
254 if(!GBCHEAT_IS_HEX(code[6]))
255 return false;
256 if(code[7] != 0) {
257 if(code[7] != '-')
258 return false;
259 if(code[8] != 0) {
260 if(!GBCHEAT_IS_HEX(code[8]))
261 return false;
262 if(!GBCHEAT_IS_HEX(code[9]))
263 return false;
264 if(!GBCHEAT_IS_HEX(code[10]))
265 return false;
266 }
267 }
268
269 // int replace = (GBCHEAT_HEX_VALUE(code[0]) << 4) +
270 // GBCHEAT_HEX_VALUE(code[1]);
271
272 int address = (GBCHEAT_HEX_VALUE(code[2]) << 8) +
273 (GBCHEAT_HEX_VALUE(code[4]) << 4) +
274 (GBCHEAT_HEX_VALUE(code[5])) +
275 ((GBCHEAT_HEX_VALUE(code[6]) ^ 0x0f) << 12);
276
277 if(address >= 0x8000 && address <= 0x9fff)
278 return false;
279
280 if(address >= 0xc000)
281 return false;
282
283 if(code[7] == 0 || code[8] == '0')
284 return true;
285
286 int compare = (GBCHEAT_HEX_VALUE(code[8]) << 4) +
287 (GBCHEAT_HEX_VALUE(code[10]));
288 compare = compare ^ 0xff;
289 compare = (compare >> 2) | ( (compare << 6) & 0xc0);
290 compare ^= 0x45;
291
292 int cloak = (GBCHEAT_HEX_VALUE(code[8])) ^ (GBCHEAT_HEX_VALUE(code[9]));
293
294 if(cloak >=1 && cloak <= 7)
295 return false;
296
297 return true;
298 }
299
gbAddGgCheat(const char * code,const char * desc)300 void gbAddGgCheat(const char *code, const char *desc)
301 {
302 if(gbCheatNumber > 99) {
303 systemMessage(MSG_MAXIMUM_NUMBER_OF_CHEATS,
304 N_("Maximum number of cheats reached."));
305 return;
306 }
307
308 if(!gbVerifyGgCode(code)) {
309 systemMessage(MSG_INVALID_GAMEGENIE_CODE,
310 N_("Invalid GameGenie code: %s"), code);
311 return;
312 }
313
314 int i = gbCheatNumber;
315
316 int len = strlen(code);
317
318 strcpy(gbCheatList[i].cheatCode, code);
319 strcpy(gbCheatList[i].cheatDesc, desc);
320
321 gbCheatList[i].code = 1;
322 gbCheatList[i].value = (GBCHEAT_HEX_VALUE(code[0]) << 4) +
323 GBCHEAT_HEX_VALUE(code[1]);
324
325 gbCheatList[i].address = (GBCHEAT_HEX_VALUE(code[2]) << 8) +
326 (GBCHEAT_HEX_VALUE(code[4]) << 4) +
327 (GBCHEAT_HEX_VALUE(code[5])) +
328 ((GBCHEAT_HEX_VALUE(code[6]) ^ 0x0f) << 12);
329
330 gbCheatList[i].compare = 0;
331
332 if(len != 7 && len != 8) {
333
334 int compare = (GBCHEAT_HEX_VALUE(code[8]) << 4) +
335 (GBCHEAT_HEX_VALUE(code[10]));
336 compare = compare ^ 0xff;
337 compare = (compare >> 2) | ( (compare << 6) & 0xc0);
338 compare ^= 0x45;
339
340 gbCheatList[i].compare = compare;
341 gbCheatList[i].code = 0;
342 }
343
344 gbCheatList[i].enabled = true;
345
346 gbCheatMap[gbCheatList[i].address] = true;
347
348 gbCheatNumber++;
349 }
350
gbCheatRemove(int i)351 void gbCheatRemove(int i)
352 {
353 if(i < 0 || i >= gbCheatNumber) {
354 systemMessage(MSG_INVALID_CHEAT_TO_REMOVE,
355 N_("Invalid cheat to remove %d"), i);
356 return;
357 }
358
359 if((i+1) < gbCheatNumber) {
360 memcpy(&gbCheatList[i], &gbCheatList[i+1], sizeof(gbCheat)*
361 (gbCheatNumber-i-1));
362 }
363
364 gbCheatNumber--;
365
366 gbCheatUpdateMap();
367 }
368
gbCheatRemoveAll()369 void gbCheatRemoveAll()
370 {
371 gbCheatNumber = 0;
372 gbCheatUpdateMap();
373 }
374
gbCheatEnable(int i)375 void gbCheatEnable(int i)
376 {
377 if(i >=0 && i < gbCheatNumber) {
378 if(!gbCheatList[i].enabled) {
379 gbCheatList[i].enabled = true;
380 gbCheatUpdateMap();
381 }
382 }
383 }
384
gbCheatDisable(int i)385 void gbCheatDisable(int i)
386 {
387 if(i >=0 && i < gbCheatNumber) {
388 if(gbCheatList[i].enabled) {
389 gbCheatList[i].enabled = false;
390 gbCheatUpdateMap();
391 }
392 }
393 }
394
gbCheatReadGSCodeFile(const char * fileName)395 bool gbCheatReadGSCodeFile(const char *fileName)
396 {
397 FILE *file = fopen(fileName, "rb");
398
399 if(!file) {
400 systemMessage(MSG_CANNOT_OPEN_FILE, N_("Cannot open file %s"), fileName);
401 return false;
402 }
403
404 fseek(file, 0x18, SEEK_SET);
405 int count = 0;
406 fread(&count, 1, 2, file);
407 int dummy = 0;
408 gbCheatRemoveAll();
409 char desc[13];
410 char code[9];
411 int i;
412 for(i = 0; i < count; i++) {
413 fread(&dummy, 1, 2, file);
414 fread(desc, 1, 12, file);
415 desc[12] = 0;
416 fread(code, 1, 8, file);
417 code[8] = 0;
418 gbAddGsCheat(code, desc);
419 }
420
421 for(i = 0; i < gbCheatNumber; i++)
422 gbCheatDisable(i);
423
424 fclose(file);
425 return true;
426 }
427
gbCheatRead(u16 address)428 u8 gbCheatRead(u16 address)
429 {
430 if(!cheatsEnabled)
431 return gbMemoryMap[address>>12][address & 0xFFF];
432
433 for(int i = 0; i < gbCheatNumber; i++) {
434 if(gbCheatList[i].enabled && gbCheatList[i].address == address) {
435 switch(gbCheatList[i].code) {
436 case 0x100: // GameGenie support
437 if(gbMemoryMap[address>>12][address&0xFFF] == gbCheatList[i].compare)
438 return gbCheatList[i].value;
439 break;
440 case 0x00:
441 case 0x01:
442 case 0x80:
443 return gbCheatList[i].value;
444 case 0x90:
445 case 0x91:
446 case 0x92:
447 case 0x93:
448 case 0x94:
449 case 0x95:
450 case 0x96:
451 case 0x97:
452 if(address >= 0xd000 && address < 0xe000) {
453 if(((gbMemoryMap[0x0d] - gbWram)/0x1000) ==
454 (gbCheatList[i].code - 0x90))
455 return gbCheatList[i].value;
456 } else
457 return gbCheatList[i].value;
458 }
459 }
460 }
461 return gbMemoryMap[address>>12][address&0xFFF];
462 }
463