1 /* Mednafen - Multi-system Emulator
2 *
3 * This program is free software; you can redistribute it and/or modify
4 * it under the terms of the GNU General Public License as published by
5 * the Free Software Foundation; either version 2 of the License, or
6 * (at your option) any later version.
7 *
8 * This program is distributed in the hope that it will be useful,
9 * but WITHOUT ANY WARRANTY; without even the implied warranty of
10 * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
11 * GNU General Public License for more details.
12 *
13 * You should have received a copy of the GNU General Public License
14 * along with this program; if not, write to the Free Software
15 * Foundation, Inc., 59 Temple Place, Suite 330, Boston, MA 02111-1307 USA
16 */
17
18 #include <string.h>
19 #include <ctype.h>
20 #include <errno.h>
21 #include <vector>
22
23 #ifdef _WIN32
24 #include <compat/msvc.h>
25 #endif
26
27 #include <boolean.h>
28 #include <libretro.h>
29
30 #include "general.h"
31 #include "hash/md5.h"
32 #include "mempatcher.h"
33 #include "settings.h"
34
35 extern retro_log_printf_t log_cb;
36
37 static uint8 **RAMPtrs = NULL;
38 static uint32 PageSize;
39 static uint32 NumPages;
40
41 typedef struct __CHEATF
42 {
43 char *name;
44 char *conditions;
45
46 uint32 addr;
47 uint64 val;
48 uint64 compare;
49
50 unsigned int length;
51 bool bigendian;
52 unsigned int icount; // Instance count
53 char type; /* 'R' for replace, 'S' for substitute(GG), 'C' for substitute with compare */
54 int status;
55 } CHEATF;
56
57 static std::vector<CHEATF> cheats;
58 static int savecheats;
59 static uint32 resultsbytelen = 1;
60 static bool resultsbigendian = 0;
61 static bool CheatsActive = true;
62
63 bool SubCheatsOn = 0;
64 std::vector<SUBCHEAT> SubCheats[8];
65
RebuildSubCheats(void)66 static void RebuildSubCheats(void)
67 {
68 std::vector<CHEATF>::iterator chit;
69
70 SubCheatsOn = 0;
71 for(int x = 0; x < 8; x++)
72 SubCheats[x].clear();
73
74 if(!CheatsActive) return;
75
76 for(chit = cheats.begin(); chit != cheats.end(); chit++)
77 {
78 if(chit->status && chit->type != 'R')
79 {
80 for(unsigned int x = 0; x < chit->length; x++)
81 {
82 SUBCHEAT tmpsub;
83 unsigned int shiftie;
84
85 if(chit->bigendian)
86 shiftie = (chit->length - 1 - x) * 8;
87 else
88 shiftie = x * 8;
89
90 tmpsub.addr = chit->addr + x;
91 tmpsub.value = (chit->val >> shiftie) & 0xFF;
92 if(chit->type == 'C')
93 tmpsub.compare = (chit->compare >> shiftie) & 0xFF;
94 else
95 tmpsub.compare = -1;
96 SubCheats[(chit->addr + x) & 0x7].push_back(tmpsub);
97 SubCheatsOn = 1;
98 }
99 }
100 }
101 }
102
MDFNMP_Init(uint32 ps,uint32 numpages)103 bool MDFNMP_Init(uint32 ps, uint32 numpages)
104 {
105 PageSize = ps;
106 NumPages = numpages;
107
108 RAMPtrs = (uint8 **)calloc(numpages, sizeof(uint8 *));
109
110 CheatsActive = MDFN_GetSettingB("cheats");
111 return(1);
112 }
113
MDFNMP_Kill(void)114 void MDFNMP_Kill(void)
115 {
116 if(RAMPtrs)
117 {
118 free(RAMPtrs);
119 RAMPtrs = NULL;
120 }
121 }
122
123
MDFNMP_AddRAM(uint32 size,uint32 A,uint8 * RAM)124 void MDFNMP_AddRAM(uint32 size, uint32 A, uint8 *RAM)
125 {
126 uint32 AB = A / PageSize;
127
128 size /= PageSize;
129
130 for(unsigned int x = 0; x < size; x++)
131 {
132 RAMPtrs[AB + x] = RAM;
133 if(RAM) // Don't increment the RAM pointer if we're passed a NULL pointer
134 RAM += PageSize;
135 }
136 }
137
MDFNMP_RegSearchable(uint32 addr,uint32 size)138 void MDFNMP_RegSearchable(uint32 addr, uint32 size)
139 {
140 MDFNMP_AddRAM(size, addr, NULL);
141 }
142
MDFNMP_InstallReadPatches(void)143 void MDFNMP_InstallReadPatches(void)
144 {
145 if(!CheatsActive) return;
146
147 std::vector<SUBCHEAT>::iterator chit;
148
149 #if 0
150 for(unsigned int x = 0; x < 8; x++)
151 for(chit = SubCheats[x].begin(); chit != SubCheats[x].end(); chit++)
152 {
153 if(MDFNGameInfo->InstallReadPatch)
154 MDFNGameInfo->InstallReadPatch(chit->addr);
155 }
156 #endif
157 }
158
MDFNMP_RemoveReadPatches(void)159 void MDFNMP_RemoveReadPatches(void)
160 {
161 #if 0
162 if(MDFNGameInfo->RemoveReadPatches)
163 MDFNGameInfo->RemoveReadPatches();
164 #endif
165 }
166
167 /* This function doesn't allocate any memory for "name" */
AddCheatEntry(char * name,char * conditions,uint32 addr,uint64 val,uint64 compare,int status,char type,unsigned int length,bool bigendian)168 static int AddCheatEntry(char *name, char *conditions, uint32 addr, uint64 val, uint64 compare, int status, char type, unsigned int length, bool bigendian)
169 {
170 CHEATF temp;
171
172 memset(&temp, 0, sizeof(CHEATF));
173
174 temp.name=name;
175 temp.conditions = conditions;
176 temp.addr=addr;
177 temp.val=val;
178 temp.status=status;
179 temp.compare=compare;
180 temp.length = length;
181 temp.bigendian = bigendian;
182 temp.type=type;
183
184 cheats.push_back(temp);
185 return(1);
186 }
187
MDFN_LoadGameCheats(void * override_ptr)188 void MDFN_LoadGameCheats(void *override_ptr)
189 {
190 RebuildSubCheats();
191 }
192
MDFN_FlushGameCheats(int nosave)193 void MDFN_FlushGameCheats(int nosave)
194 {
195 std::vector<CHEATF>::iterator chit;
196
197 for(chit = cheats.begin(); chit != cheats.end(); chit++)
198 {
199 free(chit->name);
200 if(chit->conditions)
201 free(chit->conditions);
202 }
203 cheats.clear();
204
205 RebuildSubCheats();
206 }
207
MDFNI_AddCheat(const char * name,uint32 addr,uint64 val,uint64 compare,char type,unsigned int length,bool bigendian)208 int MDFNI_AddCheat(const char *name, uint32 addr, uint64 val, uint64 compare, char type, unsigned int length, bool bigendian)
209 {
210 char *t;
211
212 if(!(t = strdup(name)))
213 return(0);
214
215 if(!AddCheatEntry(t, NULL, addr,val,compare,1,type, length, bigendian))
216 {
217 free(t);
218 return(0);
219 }
220
221 savecheats = 1;
222
223 MDFNMP_RemoveReadPatches();
224 RebuildSubCheats();
225 MDFNMP_InstallReadPatches();
226
227 return(1);
228 }
229
MDFNI_DelCheat(uint32 which)230 int MDFNI_DelCheat(uint32 which)
231 {
232 free(cheats[which].name);
233 cheats.erase(cheats.begin() + which);
234
235 savecheats=1;
236
237 MDFNMP_RemoveReadPatches();
238 RebuildSubCheats();
239 MDFNMP_InstallReadPatches();
240
241 return(1);
242 }
243
244 /*
245 Condition format(ws = white space):
246
247 <variable size><ws><endian><ws><address><ws><operation><ws><value>
248 [,second condition...etc.]
249
250 Value should be unsigned integer, hex(with a 0x prefix) or
251 base-10.
252
253 Operations:
254 >=
255 <=
256 >
257 <
258 ==
259 !=
260 & // Result of AND between two values is nonzero
261 !& // Result of AND between two values is zero
262 ^ // same, XOR
263 !^
264 | // same, OR
265 !|
266
267 Full example:
268
269 2 L 0xADDE == 0xDEAD, 1 L 0xC000 == 0xA0
270
271 */
272
TestConditions(const char * string)273 static bool TestConditions(const char *string)
274 {
275 char address[64];
276 char operation[64];
277 char value[64];
278 char endian;
279 unsigned int bytelen;
280 bool passed = 1;
281
282 //printf("TR: %s\n", string);
283 while(sscanf(string, "%u %c %63s %63s %63s", &bytelen, &endian, address, operation, value) == 5 && passed)
284 {
285 uint64 v_value;
286 uint64 value_at_address;
287 #if 0
288 uint32 v_address;
289
290 if(address[0] == '0' && address[1] == 'x')
291 v_address = strtoul(address + 2, NULL, 16);
292 else
293 v_address = strtoul(address, NULL, 10);
294 #endif
295
296 if(value[0] == '0' && value[1] == 'x')
297 v_value = strtoull(value + 2, NULL, 16);
298 else
299 v_value = strtoull(value, NULL, 0);
300
301 value_at_address = 0;
302
303 #if 0
304 for(unsigned int x = 0; x < bytelen; x++)
305 {
306 unsigned int shiftie;
307
308 if(endian == 'B')
309 shiftie = (bytelen - 1 - x) * 8;
310 else
311 shiftie = x * 8;
312 value_at_address |= MDFNGameInfo->MemRead(v_address + x) << shiftie;
313 }
314 #endif
315
316 //printf("A: %08x, V: %08llx, VA: %08llx, OP: %s\n", v_address, v_value, value_at_address, operation);
317 if(!strcmp(operation, ">="))
318 {
319 if(!(value_at_address >= v_value))
320 passed = 0;
321 }
322 else if(!strcmp(operation, "<="))
323 {
324 if(!(value_at_address <= v_value))
325 passed = 0;
326 }
327 else if(!strcmp(operation, ">"))
328 {
329 if(!(value_at_address > v_value))
330 passed = 0;
331 }
332 else if(!strcmp(operation, "<"))
333 {
334 if(!(value_at_address < v_value))
335 passed = 0;
336 }
337 else if(!strcmp(operation, "=="))
338 {
339 if(!(value_at_address == v_value))
340 passed = 0;
341 }
342 else if(!strcmp(operation, "!="))
343 {
344 if(!(value_at_address != v_value))
345 passed = 0;
346 }
347 else if(!strcmp(operation, "&"))
348 {
349 if(!(value_at_address & v_value))
350 passed = 0;
351 }
352 else if(!strcmp(operation, "!&"))
353 {
354 if(value_at_address & v_value)
355 passed = 0;
356 }
357 else if(!strcmp(operation, "^"))
358 {
359 if(!(value_at_address ^ v_value))
360 passed = 0;
361 }
362 else if(!strcmp(operation, "!^"))
363 {
364 if(value_at_address ^ v_value)
365 passed = 0;
366 }
367 else if(!strcmp(operation, "|"))
368 {
369 if(!(value_at_address | v_value))
370 passed = 0;
371 }
372 else if(!strcmp(operation, "!|"))
373 {
374 if(value_at_address | v_value)
375 passed = 0;
376 }
377 else
378 puts("Invalid operation");
379 string = strchr(string, ',');
380 if(string == NULL)
381 break;
382 else
383 string++;
384 //printf("Foo: %s\n", string);
385 }
386
387 return(passed);
388 }
389
MDFNMP_ApplyPeriodicCheats(void)390 void MDFNMP_ApplyPeriodicCheats(void)
391 {
392 std::vector<CHEATF>::iterator chit;
393
394 if(!CheatsActive)
395 return;
396
397 for(chit = cheats.begin(); chit != cheats.end(); chit++)
398 {
399 if(chit->status && chit->type == 'R')
400 {
401 if(!chit->conditions || TestConditions(chit->conditions))
402 for(unsigned int x = 0; x < chit->length; x++)
403 {
404 uint32 page = ((chit->addr + x) / PageSize) % NumPages;
405 if(RAMPtrs[page])
406 {
407 uint64 tmpval = chit->val;
408
409 if(chit->bigendian)
410 tmpval >>= (chit->length - 1 - x) * 8;
411 else
412 tmpval >>= x * 8;
413
414 RAMPtrs[page][(chit->addr + x) % PageSize] = tmpval;
415 }
416 }
417 }
418 }
419 }
420
421
MDFNI_ListCheats(int (* callb)(char * name,uint32 a,uint64 v,uint64 compare,int s,char type,unsigned int length,bool bigendian,void * data),void * data)422 void MDFNI_ListCheats(int (*callb)(char *name, uint32 a, uint64 v, uint64 compare, int s, char type, unsigned int length, bool bigendian, void *data), void *data)
423 {
424 std::vector<CHEATF>::iterator chit;
425
426 for(chit = cheats.begin(); chit != cheats.end(); chit++)
427 {
428 if(!callb(chit->name, chit->addr, chit->val, chit->compare, chit->status, chit->type, chit->length, chit->bigendian, data)) break;
429 }
430 }
431
MDFNI_GetCheat(uint32 which,char ** name,uint32 * a,uint64 * v,uint64 * compare,int * s,char * type,unsigned int * length,bool * bigendian)432 int MDFNI_GetCheat(uint32 which, char **name, uint32 *a, uint64 *v, uint64 *compare, int *s, char *type, unsigned int *length, bool *bigendian)
433 {
434 CHEATF *next = &cheats[which];
435
436 if(name)
437 *name=next->name;
438 if(a)
439 *a=next->addr;
440 if(v)
441 *v=next->val;
442 if(s)
443 *s=next->status;
444 if(compare)
445 *compare=next->compare;
446 if(type)
447 *type=next->type;
448 if(length)
449 *length = next->length;
450 if(bigendian)
451 *bigendian = next->bigendian;
452 return(1);
453 }
454
CharToNibble(char thechar)455 static uint8 CharToNibble(char thechar)
456 {
457 const char lut[16] = {'0', '1', '2', '3', '4', '5', '6', '7', '8', '9', 'A', 'B', 'C', 'D', 'E', 'F' };
458
459 thechar = toupper(thechar);
460
461 for(int x = 0; x < 16; x++)
462 if(lut[x] == thechar)
463 return(x);
464
465 return(0xFF);
466 }
467
MDFNI_DecodeGBGG(const char * instr,uint32 * a,uint8 * v,uint8 * c,char * type)468 bool MDFNI_DecodeGBGG(const char *instr, uint32 *a, uint8 *v, uint8 *c, char *type)
469 {
470 char str[10];
471 int len;
472
473 for(int x = 0; x < 9; x++)
474 {
475 while(*instr && CharToNibble(*instr) == 255)
476 instr++;
477 if(!(str[x] = *instr)) break;
478 instr++;
479 }
480 str[9] = 0;
481
482 len = strlen(str);
483
484 if(len != 9 && len != 6)
485 return(0);
486
487 uint32 tmp_address;
488 uint8 tmp_value;
489 uint8 tmp_compare = 0;
490
491 tmp_address = (CharToNibble(str[5]) << 12) | (CharToNibble(str[2]) << 8) | (CharToNibble(str[3]) << 4) | (CharToNibble(str[4]) << 0);
492 tmp_address ^= 0xF000;
493 tmp_value = (CharToNibble(str[0]) << 4) | (CharToNibble(str[1]) << 0);
494
495 if(len == 9)
496 {
497 tmp_compare = (CharToNibble(str[6]) << 4) | (CharToNibble(str[8]) << 0);
498 tmp_compare = (tmp_compare >> 2) | ((tmp_compare << 6) & 0xC0);
499 tmp_compare ^= 0xBA;
500 }
501
502 *a = tmp_address;
503 *v = tmp_value;
504
505 if(len == 9)
506 {
507 *c = tmp_compare;
508 *type = 'C';
509 }
510 else
511 {
512 *c = 0;
513 *type = 'S';
514 }
515
516 return(1);
517 }
518
GGtobin(char c)519 static int GGtobin(char c)
520 {
521 static char lets[16]={'A','P','Z','L','G','I','T','Y','E','O','X','U','K','S','V','N'};
522 int x;
523
524 for(x=0;x<16;x++)
525 if(lets[x] == toupper(c)) return(x);
526 return(0);
527 }
528
529 /* Returns 1 on success, 0 on failure. Sets *a,*v,*c. */
MDFNI_DecodeGG(const char * str,uint32 * a,uint8 * v,uint8 * c,char * type)530 int MDFNI_DecodeGG(const char *str, uint32 *a, uint8 *v, uint8 *c, char *type)
531 {
532 uint16 A;
533 uint8 V,C;
534 uint8 t;
535 int s;
536
537 A=0x8000;
538 V=0;
539 C=0;
540
541 s=strlen(str);
542 if(s!=6 && s!=8) return(0);
543
544 t=GGtobin(*str++);
545 V|=(t&0x07);
546 V|=(t&0x08)<<4;
547
548 t=GGtobin(*str++);
549 V|=(t&0x07)<<4;
550 A|=(t&0x08)<<4;
551
552 t=GGtobin(*str++);
553 A|=(t&0x07)<<4;
554 //if(t&0x08) return(0); /* 8-character code?! */
555
556 t=GGtobin(*str++);
557 A|=(t&0x07)<<12;
558 A|=(t&0x08);
559
560 t=GGtobin(*str++);
561 A|=(t&0x07);
562 A|=(t&0x08)<<8;
563
564 if(s==6)
565 {
566 t=GGtobin(*str++);
567 A|=(t&0x07)<<8;
568 V|=(t&0x08);
569
570 *a=A;
571 *v=V;
572 *type = 'S';
573 *c = 0;
574 }
575 else
576 {
577 t=GGtobin(*str++);
578 A|=(t&0x07)<<8;
579 C|=(t&0x08);
580
581 t=GGtobin(*str++);
582 C|=(t&0x07);
583 C|=(t&0x08)<<4;
584
585 t=GGtobin(*str++);
586 C|=(t&0x07)<<4;
587 V|=(t&0x08);
588 *a=A;
589 *v=V;
590 *c=C;
591 *type = 'C';
592 }
593
594 return(1);
595 }
596
MDFNI_DecodePAR(const char * str,uint32 * a,uint8 * v,uint8 * c,char * type)597 int MDFNI_DecodePAR(const char *str, uint32 *a, uint8 *v, uint8 *c, char *type)
598 {
599 int boo[4];
600 if(strlen(str)!=8) return(0);
601
602 sscanf(str,"%02x%02x%02x%02x",boo,boo+1,boo+2,boo+3);
603
604 *c = 0;
605
606 if(1)
607 {
608 *a=(boo[3]<<8)|(boo[2]+0x7F);
609 *v=0;
610 }
611 else
612 {
613 *v=boo[3];
614 *a=boo[2]|(boo[1]<<8);
615 }
616
617 *type = 'S';
618 return(1);
619 }
620
621 /* name can be NULL if the name isn't going to be changed. */
MDFNI_SetCheat(uint32 which,const char * name,uint32 a,uint64 v,uint64 compare,int s,char type,unsigned int length,bool bigendian)622 int MDFNI_SetCheat(uint32 which, const char *name, uint32 a, uint64 v, uint64 compare, int s, char type, unsigned int length, bool bigendian)
623 {
624 CHEATF *next = &cheats[which];
625
626 if(name)
627 {
628 char *t;
629 if(!(t=(char *)realloc(next->name,strlen(name)+1)))
630 return(0);
631 next->name=t;
632 strcpy(next->name,name);
633 }
634 next->addr=a;
635 next->val=v;
636 next->status=s;
637 next->compare=compare;
638 next->type=type;
639 next->length = length;
640 next->bigendian = bigendian;
641
642 RebuildSubCheats();
643 savecheats=1;
644
645 return(1);
646 }
647
648 /* Convenience function. */
MDFNI_ToggleCheat(uint32 which)649 int MDFNI_ToggleCheat(uint32 which)
650 {
651 cheats[which].status = !cheats[which].status;
652 savecheats = 1;
653 RebuildSubCheats();
654
655 return(cheats[which].status);
656 }
657