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