1/*****************************************************************************\
2 Snes9x - Portable Super Nintendo Entertainment System (TM) emulator.
3 This file is licensed under the Snes9x License.
4 For further information, consult the LICENSE file in the root directory.
5\*****************************************************************************/
6
7#include <ctype.h>
8
9#include "snes9x.h"
10#include "memmap.h"
11#include "cheats.h"
12#include "bml.h"
13
14static inline char *trim (char *string)
15{
16 int start;
17 int end;
18
19 for (start = 0; string[start] && isspace (string[start]); start++) {}
20 for (end = start; string[end] && !isspace (string[end]); end++) {}
21 string[end] = '\0';
22 return &string[start];
23}
24
25static inline uint8 S9xGetByteFree (uint32 Address)
26{
27 int block = (Address & 0xffffff) >> MEMMAP_SHIFT;
28 uint8 *GetAddress = Memory.Map[block];
29 uint8 byte;
30
31 if (GetAddress >= (uint8 *) CMemory::MAP_LAST)
32 {
33 byte = *(GetAddress + (Address & 0xffff));
34 return (byte);
35 }
36
37 switch ((pint) GetAddress)
38 {
39 case CMemory::MAP_CPU:
40 byte = S9xGetCPU(Address & 0xffff);
41 return (byte);
42
43 case CMemory::MAP_PPU:
44 if (CPU.InDMAorHDMA && (Address & 0xff00) == 0x2100)
45 return (OpenBus);
46
47 byte = S9xGetPPU(Address & 0xffff);
48 return (byte);
49
50 case CMemory::MAP_LOROM_SRAM:
51 case CMemory::MAP_SA1RAM:
52 // Address & 0x7fff : offset into bank
53 // Address & 0xff0000 : bank
54 // bank >> 1 | offset : SRAM address, unbound
55 // unbound & SRAMMask : SRAM offset
56 byte = *(Memory.SRAM + ((((Address & 0xff0000) >> 1) | (Address & 0x7fff)) & Memory.SRAMMask));
57 return (byte);
58
59 case CMemory::MAP_LOROM_SRAM_B:
60 byte = *(Multi.sramB + ((((Address & 0xff0000) >> 1) | (Address & 0x7fff)) & Multi.sramMaskB));
61 return (byte);
62
63 case CMemory::MAP_HIROM_SRAM:
64 case CMemory::MAP_RONLY_SRAM:
65 byte = *(Memory.SRAM + (((Address & 0x7fff) - 0x6000 + ((Address & 0xf0000) >> 3)) & Memory.SRAMMask));
66 return (byte);
67
68 case CMemory::MAP_BWRAM:
69 byte = *(Memory.BWRAM + ((Address & 0x7fff) - 0x6000));
70 return (byte);
71
72 case CMemory::MAP_DSP:
73 byte = S9xGetDSP(Address & 0xffff);
74 return (byte);
75
76 case CMemory::MAP_SPC7110_ROM:
77 byte = S9xGetSPC7110Byte(Address);
78 return (byte);
79
80 case CMemory::MAP_SPC7110_DRAM:
81 byte = S9xGetSPC7110(0x4800);
82 return (byte);
83
84 case CMemory::MAP_C4:
85 byte = S9xGetC4(Address & 0xffff);
86 return (byte);
87
88 case CMemory::MAP_OBC_RAM:
89 byte = S9xGetOBC1(Address & 0xffff);
90 return (byte);
91
92 case CMemory::MAP_SETA_DSP:
93 byte = S9xGetSetaDSP(Address);
94 return (byte);
95
96 case CMemory::MAP_SETA_RISC:
97 byte = S9xGetST018(Address);
98 return (byte);
99
100 case CMemory::MAP_BSX:
101 byte = S9xGetBSX(Address);
102 return (byte);
103
104 case CMemory::MAP_NONE:
105 default:
106 byte = OpenBus;
107 return (byte);
108 }
109}
110
111static inline void S9xSetByteFree (uint8 Byte, uint32 Address)
112{
113 int block = (Address & 0xffffff) >> MEMMAP_SHIFT;
114 uint8 *SetAddress = Memory.Map[block];
115
116 if (SetAddress >= (uint8 *) CMemory::MAP_LAST)
117 {
118 *(SetAddress + (Address & 0xffff)) = Byte;
119 return;
120 }
121
122 switch ((pint) SetAddress)
123 {
124 case CMemory::MAP_CPU:
125 S9xSetCPU(Byte, Address & 0xffff);
126 return;
127
128 case CMemory::MAP_PPU:
129 if (CPU.InDMAorHDMA && (Address & 0xff00) == 0x2100)
130 return;
131
132 S9xSetPPU(Byte, Address & 0xffff);
133 return;
134
135 case CMemory::MAP_LOROM_SRAM:
136 if (Memory.SRAMMask)
137 {
138 *(Memory.SRAM + ((((Address & 0xff0000) >> 1) | (Address & 0x7fff)) & Memory.SRAMMask)) = Byte;
139 CPU.SRAMModified = TRUE;
140 }
141
142 return;
143
144 case CMemory::MAP_LOROM_SRAM_B:
145 if (Multi.sramMaskB)
146 {
147 *(Multi.sramB + ((((Address & 0xff0000) >> 1) | (Address & 0x7fff)) & Multi.sramMaskB)) = Byte;
148 CPU.SRAMModified = TRUE;
149 }
150
151 return;
152
153 case CMemory::MAP_HIROM_SRAM:
154 if (Memory.SRAMMask)
155 {
156 *(Memory.SRAM + (((Address & 0x7fff) - 0x6000 + ((Address & 0xf0000) >> 3)) & Memory.SRAMMask)) = Byte;
157 CPU.SRAMModified = TRUE;
158 }
159 return;
160
161 case CMemory::MAP_BWRAM:
162 *(Memory.BWRAM + ((Address & 0x7fff) - 0x6000)) = Byte;
163 CPU.SRAMModified = TRUE;
164 return;
165
166 case CMemory::MAP_SA1RAM:
167 *(Memory.SRAM + (Address & 0xffff)) = Byte;
168 return;
169
170 case CMemory::MAP_DSP:
171 S9xSetDSP(Byte, Address & 0xffff);
172 return;
173
174 case CMemory::MAP_C4:
175 S9xSetC4(Byte, Address & 0xffff);
176 return;
177
178 case CMemory::MAP_OBC_RAM:
179 S9xSetOBC1(Byte, Address & 0xffff);
180 return;
181
182 case CMemory::MAP_SETA_DSP:
183 S9xSetSetaDSP(Byte, Address);
184 return;
185
186 case CMemory::MAP_SETA_RISC:
187 S9xSetST018(Byte, Address);
188 return;
189
190 case CMemory::MAP_BSX:
191 S9xSetBSX(Byte, Address);
192 return;
193
194 case CMemory::MAP_NONE:
195 default:
196 return;
197 }
198}
199
200void S9xInitWatchedAddress (void)
201{
202 for (unsigned int i = 0; i < sizeof(watches) / sizeof(watches[0]); i++)
203 watches[i].on = false;
204}
205
206void S9xInitCheatData (void)
207{
208 Cheat.RAM = Memory.RAM;
209 Cheat.SRAM = Memory.SRAM;
210 Cheat.FillRAM = Memory.FillRAM;
211}
212
213
214void S9xUpdateCheatInMemory (SCheat *c)
215{
216 uint8 byte;
217
218 if (!c->enabled)
219 return;
220
221 byte = S9xGetByteFree (c->address);
222
223 if (byte != c->byte)
224 {
225 /* The game wrote a different byte to the address, update saved_byte */
226 c->saved_byte = byte;
227
228 if (c->conditional)
229 {
230 if (c->saved_byte != c->cond_byte && c->cond_true)
231 {
232 /* Condition is now false, let the byte stand */
233 c->cond_true = false;
234 }
235 else if (c->saved_byte == c->cond_byte && !c->cond_true)
236 {
237 c->cond_true = true;
238 S9xSetByteFree (c->byte, c->address);
239 }
240 }
241 else
242 S9xSetByteFree (c->byte, c->address);
243 }
244 else if (c->conditional)
245 {
246 if (byte == c->cond_byte)
247 {
248 c->cond_true = true;
249 c->saved_byte = byte;
250 S9xSetByteFree (c->byte, c->address);
251 }
252 }
253}
254
255void S9xDisableCheat (SCheat *c)
256{
257 if (!c->enabled)
258 return;
259
260 if (!Cheat.enabled)
261 {
262 c->enabled = false;
263 return;
264 }
265
266 /* Make sure we restore the up-to-date written byte */
267 S9xUpdateCheatInMemory (c);
268 c->enabled = false;
269
270 if (c->conditional && !c->cond_true)
271 return;
272
273 S9xSetByteFree (c->saved_byte, c->address);
274 c->cond_true = false;
275}
276
277void S9xDeleteCheatGroup (uint32 g)
278{
279 unsigned int i;
280
281 if (g >= Cheat.g.size ())
282 return;
283
284 for (i = 0; i < Cheat.g[g].c.size (); i++)
285 {
286 S9xDisableCheat (&Cheat.g[g].c[i]);
287 }
288
289 delete[] Cheat.g[g].name;
290
291 Cheat.g.erase (Cheat.g.begin () + g);
292}
293
294void S9xDeleteCheats (void)
295{
296 unsigned int i;
297
298 for (i = 0; i < Cheat.g.size (); i++)
299 {
300 S9xDisableCheatGroup (i);
301
302 delete[] Cheat.g[i].name;
303 }
304
305 Cheat.g.clear ();
306}
307
308void S9xEnableCheat (SCheat *c)
309{
310 uint8 byte;
311
312 if (c->enabled)
313 return;
314
315 c->enabled = true;
316
317 if (!Cheat.enabled)
318 return;
319
320 byte = S9xGetByteFree(c->address);
321
322 if (c->conditional)
323 {
324 if (byte != c->cond_byte)
325 return;
326
327 c->cond_true = true;
328 }
329
330 c->saved_byte = byte;
331 S9xSetByteFree (c->byte, c->address);
332}
333
334void S9xEnableCheatGroup (uint32 num)
335{
336 unsigned int i;
337
338 for (i = 0; i < Cheat.g[num].c.size (); i++)
339 {
340 S9xEnableCheat (&Cheat.g[num].c[i]);
341 }
342
343 Cheat.g[num].enabled = true;
344}
345
346void S9xDisableCheatGroup (uint32 num)
347{
348 unsigned int i;
349
350 for (i = 0; i < Cheat.g[num].c.size (); i++)
351 {
352 S9xDisableCheat (&Cheat.g[num].c[i]);
353 }
354
355 Cheat.g[num].enabled = false;
356}
357
358SCheat S9xTextToCheat (char *text)
359{
360 SCheat c;
361 unsigned int byte = 0;
362 unsigned int cond_byte = 0;
363
364 c.enabled = false;
365 c.conditional = false;
366
367 if (!S9xGameGenieToRaw (text, c.address, c.byte))
368 {
369 byte = c.byte;
370 }
371
372 else if (!S9xProActionReplayToRaw (text, c.address, c.byte))
373 {
374 byte = c.byte;
375 }
376
377 else if (sscanf (text, "%x = %x ? %x", &c.address, &cond_byte, &byte) == 3)
378 {
379 c.conditional = true;
380 }
381
382 else if (sscanf (text, "%x = %x", &c.address, &byte) == 2)
383 {
384 }
385
386 else if (sscanf (text, "%x / %x / %x", &c.address, &cond_byte, &byte) == 3)
387 {
388 c.conditional = true;
389 }
390
391 else if (sscanf (text, "%x / %x", &c.address, &byte) == 2)
392 {
393 }
394
395 else
396 {
397 c.address = 0;
398 byte = 0;
399 }
400
401 c.byte = byte;
402 c.cond_byte = cond_byte;
403
404 return c;
405}
406
407SCheatGroup S9xCreateCheatGroup (const char *name, const char *cheat)
408{
409 SCheatGroup g;
410 char *code_string = strdup (cheat);
411 char *code_ptr = code_string;
412 int len;
413
414 g.name = strdup (name);
415 g.enabled = false;
416
417 for (len = strcspn (code_ptr, "+"); len; len = strcspn (code_ptr, "+"))
418 {
419 char *code = code_ptr;
420 code_ptr += len + (code_ptr[len] == '\0' ? 0 : 1);
421 code[len] = '\0';
422 code = trim (code);
423
424 SCheat c = S9xTextToCheat (code);
425 if (c.address)
426 g.c.push_back (c);
427 }
428
429 delete[] code_string;
430
431 return g;
432}
433
434int S9xAddCheatGroup (const char *name, const char *cheat)
435{
436 SCheatGroup g = S9xCreateCheatGroup (name, cheat);
437 if (g.c.size () == 0)
438 return -1;
439
440 Cheat.g.push_back (g);
441
442 return Cheat.g.size () - 1;
443}
444
445int S9xModifyCheatGroup (uint32 num, const char *name, const char *cheat)
446{
447 if (num >= Cheat.g.size())
448 return -1;
449
450 S9xDisableCheatGroup (num);
451 delete[] Cheat.g[num].name;
452
453 Cheat.g[num] = S9xCreateCheatGroup (name, cheat);
454
455 return num;
456}
457
458char *S9xCheatToText (SCheat *c)
459{
460 int size = 10; /* 6 address, 1 =, 2 byte, 1 NUL */
461 char *text;
462
463 if (c->conditional)
464 size += 3; /* additional 2 byte, 1 ? */
465
466 text = new char[size];
467
468 if (c->conditional)
469 snprintf (text, size, "%06x=%02x?%02x", c->address, c->cond_byte, c->byte);
470 else
471 snprintf (text, size, "%06x=%02x", c->address, c->byte);
472
473 return text;
474}
475
476char *S9xCheatGroupToText (SCheatGroup *g)
477{
478 std::string text = "";
479 unsigned int i;
480
481 if (g->c.size () == 0)
482 return NULL;
483
484 for (i = 0; i < g->c.size (); i++)
485 {
486 char *tmp = S9xCheatToText (&g->c[i]);
487 if (i != 0)
488 text += " + ";
489 text += tmp;
490 delete[] tmp;
491 }
492
493 return strdup (text.c_str ());
494}
495
496char *S9xCheatValidate (const char *code_string)
497{
498 SCheatGroup g = S9xCreateCheatGroup ("temp", code_string);
499
500 delete[] g.name;
501
502 if (g.c.size() > 0)
503 {
504 return S9xCheatGroupToText (&g);
505 }
506
507 return NULL;
508}
509
510char *S9xCheatGroupToText (uint32 num)
511{
512 if (num >= Cheat.g.size ())
513 return NULL;
514
515 return S9xCheatGroupToText (&Cheat.g[num]);
516}
517
518void S9xUpdateCheatsInMemory (void)
519{
520 unsigned int i;
521 unsigned int j;
522
523 if (!Cheat.enabled)
524 return;
525
526 for (i = 0; i < Cheat.g.size (); i++)
527 {
528 for (j = 0; j < Cheat.g[i].c.size (); j++)
529 {
530 S9xUpdateCheatInMemory (&Cheat.g[i].c[j]);
531 }
532 }
533}
534
535static int S9xCheatIsDuplicate (const char *name, const char *code)
536{
537 unsigned int i;
538
539 for (i = 0; i < Cheat.g.size(); i++)
540 {
541 if (!strcmp (name, Cheat.g[i].name))
542 {
543 char *code_string = S9xCheatGroupToText (i);
544 char *validated = S9xCheatValidate (code);
545
546 if (validated && !strcmp (code_string, validated))
547 {
548 free (code_string);
549 free (validated);
550 return TRUE;
551 }
552
553 free (code_string);
554 free (validated);
555 }
556 }
557
558 return FALSE;
559}
560
561static void S9xLoadCheatsFromBMLNode (bml_node *n)
562{
563 unsigned int i;
564
565 for (i = 0; i < n->child.size (); i++)
566 {
567 if (!strcasecmp (n->child[i].name.c_str(), "cheat"))
568 {
569 const char *desc = NULL;
570 const char *code = NULL;
571 bool8 enabled = false;
572
573 bml_node *c = &n->child[i];
574 bml_node *tmp = NULL;
575
576 tmp = c->find_subnode("name");
577 if (!tmp)
578 desc = (char *) "";
579 else
580 desc = tmp->data.c_str();
581
582 tmp = c->find_subnode("code");
583 if (tmp)
584 code = tmp->data.c_str();
585
586 if (c->find_subnode("enable"))
587 enabled = true;
588
589 if (code && !S9xCheatIsDuplicate (desc, code))
590 {
591 int index = S9xAddCheatGroup (desc, code);
592
593 if (enabled)
594 S9xEnableCheatGroup (index);
595 }
596 }
597 }
598
599 return;
600}
601
602static bool8 S9xLoadCheatFileClassic (const char *filename)
603{
604 FILE *fs;
605 uint8 data[28];
606
607 fs = fopen(filename, "rb");
608 if (!fs)
609 return (FALSE);
610
611 while (fread ((void *) data, 1, 28, fs) == 28)
612 {
613 SCheat c;
614 char name[21];
615 char cheat[10];
616 c.enabled = (data[0] & 4) == 0;
617 c.byte = data[1];
618 c.address = data[2] | (data[3] << 8) | (data[4] << 16);
619 memcpy (name, &data[8], 20);
620 name[20] = 0;
621
622 snprintf (cheat, 10, "%x=%x", c.address, c.byte);
623 S9xAddCheatGroup (name, cheat);
624
625 if (c.enabled)
626 S9xEnableCheatGroup (Cheat.g.size () - 1);
627 }
628
629 fclose(fs);
630
631 return (TRUE);
632}
633
634bool8 S9xLoadCheatFile (const char *filename)
635{
636 bml_node bml;
637 if (!bml.parse_file(filename))
638 {
639 return S9xLoadCheatFileClassic (filename);
640 }
641
642 bml_node *n = bml.find_subnode("cheat");
643 if (n)
644 {
645 S9xLoadCheatsFromBMLNode (&bml);
646 }
647
648 if (!n)
649 {
650 return S9xLoadCheatFileClassic (filename);
651 }
652
653 return (TRUE);
654}
655
656bool8 S9xSaveCheatFile (const char *filename)
657{
658 unsigned int i;
659 FILE *file = NULL;
660
661 if (Cheat.g.size () == 0)
662 {
663 remove (filename);
664 return TRUE;
665 }
666
667 file = fopen (filename, "w");
668
669 if (!file)
670 return FALSE;
671
672 for (i = 0; i < Cheat.g.size (); i++)
673 {
674 char *txt = S9xCheatGroupToText (i);
675
676 fprintf (file,
677 "cheat\n"
678 " name: %s\n"
679 " code: %s\n"
680 "%s\n",
681 Cheat.g[i].name ? Cheat.g[i].name : "",
682 txt,
683 Cheat.g[i].enabled ? " enable\n" : ""
684 );
685
686 delete[] txt;
687 }
688
689 fclose (file);
690
691 return TRUE;
692}
693
694void S9xCheatsDisable (void)
695{
696 unsigned int i;
697
698 if (!Cheat.enabled)
699 return;
700
701 for (i = 0; i < Cheat.g.size (); i++)
702 {
703 if (Cheat.g[i].enabled)
704 {
705 S9xDisableCheatGroup (i);
706 Cheat.g[i].enabled = TRUE;
707 }
708 }
709
710 Cheat.enabled = FALSE;
711}
712
713void S9xCheatsEnable (void)
714{
715 unsigned int i;
716
717 if (Cheat.enabled)
718 return;
719
720 Cheat.enabled = TRUE;
721
722 for (i = 0; i < Cheat.g.size (); i++)
723 {
724 if (Cheat.g[i].enabled)
725 {
726 Cheat.g[i].enabled = FALSE;
727 S9xEnableCheatGroup (i);
728 }
729 }
730}
731
732int S9xImportCheatsFromDatabase (const char *filename)
733{
734 char sha256_txt[65];
735 char hextable[] = "0123456789abcdef";
736 unsigned int i;
737
738 bml_node bml;
739 if (!bml.parse_file(filename))
740 return -1; // No file
741
742 for (i = 0; i < 32; i++)
743 {
744 sha256_txt[i * 2] = hextable[Memory.ROMSHA256[i] >> 4];
745 sha256_txt[i * 2 + 1] = hextable[Memory.ROMSHA256[i] & 0xf];
746 }
747 sha256_txt[64] = '\0';
748
749 for (i = 0; i < bml.child.size (); i++)
750 {
751 if (!strcasecmp (bml.child[i].name.c_str(), "cartridge"))
752 {
753 bml_node *n;
754
755 if ((n = bml.child[i].find_subnode ("sha256")))
756 {
757 if (!strcasecmp (n->data.c_str(), sha256_txt))
758 {
759 S9xLoadCheatsFromBMLNode (&bml.child[i]);
760 return 0;
761 }
762 }
763 }
764 }
765
766 return -2; /* No codes */
767}
768