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 | |
14 | static 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 | |
25 | static 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 | |
111 | static 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 | |
200 | void S9xInitWatchedAddress (void) |
201 | { |
202 | for (unsigned int i = 0; i < sizeof(watches) / sizeof(watches[0]); i++) |
203 | watches[i].on = false; |
204 | } |
205 | |
206 | void S9xInitCheatData (void) |
207 | { |
208 | Cheat.RAM = Memory.RAM; |
209 | Cheat.SRAM = Memory.SRAM; |
210 | Cheat.FillRAM = Memory.FillRAM; |
211 | } |
212 | |
213 | |
214 | void 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 | |
255 | void 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 | |
277 | void 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 | |
294 | void 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 | |
308 | void 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 | |
334 | void 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 | |
346 | void 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 | |
358 | SCheat 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 | |
407 | SCheatGroup 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 | |
434 | int 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 | |
445 | int 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 | |
458 | char *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 | |
476 | char *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 | |
496 | char *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 | |
510 | char *S9xCheatGroupToText (uint32 num) |
511 | { |
512 | if (num >= Cheat.g.size ()) |
513 | return NULL; |
514 | |
515 | return S9xCheatGroupToText (&Cheat.g[num]); |
516 | } |
517 | |
518 | void 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 | |
535 | static 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 | |
561 | static 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 | |
602 | static 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 | |
634 | bool8 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 | |
656 | bool8 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 | |
694 | void 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 | |
713 | void 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 | |
732 | int 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 | |