1 /** EMULib Emulation Library *********************************/
2 /**                                                         **/
3 /**                         Hunt.c                          **/
4 /**                                                         **/
5 /** This file implements mechanism for searching possible   **/
6 /** cheats inside running game data. Also see Hunt.h.       **/
7 /**                                                         **/
8 /** Copyright (C) Marat Fayzullin 2013-2016                 **/
9 /**     You are not allowed to distribute this software     **/
10 /**     commercially. Please, notify me, if you make any    **/
11 /**     changes to this file.                               **/
12 /*************************************************************/
13 #include "Hunt.h"
14 #include <stdio.h>
15 
16 #if defined(VGBA)
17 #include "ARM.h"
18 #define MEMREAD32(A) QRdARM(A)
19 #define MEMREAD16(A) WRdARM(A)
20 #define MEMREAD8(A)  BRdARM(A)
21 #elif defined(INES)
22 #include "M6502.h"
23 #define MEMREAD32(A) (Rd6502(A)+((int)Rd6502(A+1)<<8)+((int)Rd6502(A+2)<<16)+((int)Rd6502(A+3)<<24))
24 #define MEMREAD16(A) (Rd6502(A)+((int)Rd6502(A+1)<<8))
25 #define MEMREAD8(A)  Rd6502(A)
26 #elif defined(VGB) || defined(MG) || defined(COLEM) || defined(SPECCY) || defined(FMSX)
27 #include "Z80.h"
28 #define MEMREAD32(A) (RdZ80(A)+((int)RdZ80(A+1)<<8)+((int)RdZ80(A+2)<<16)+((int)RdZ80(A+3)<<24))
29 #define MEMREAD16(A) (RdZ80(A)+((int)RdZ80(A+1)<<8))
30 #define MEMREAD8(A)  RdZ80(A)
31 #else
32 #define MEMREAD32(A) (0)
33 #define MEMREAD16(A) (0)
34 #define MEMREAD8(A)  (0)
35 #endif
36 
37 static HUNTEntry Buf[HUNT_BUFSIZE];
38 static int Count;
39 
40 /** InitHUNT() ***********************************************/
41 /** Initialize cheat search, clearing all data.             **/
42 /*************************************************************/
InitHUNT(void)43 void InitHUNT(void) { Count=0; }
44 
45 /** TotalHUNT() **********************************************/
46 /** Get total number of currently watched locations.        **/
47 /*************************************************************/
TotalHUNT(void)48 int TotalHUNT(void) { return(Count); }
49 
50 /** GetHUNT() ************************************************/
51 /** Get Nth memory location. Returns 0 for invalid N.       **/
52 /*************************************************************/
GetHUNT(int N)53 HUNTEntry *GetHUNT(int N)
54 { return((N>=0)&&(N<Count)? &Buf[N]:0); }
55 
56 /** AddHUNT() ************************************************/
57 /** Add a new value to search for, with the address range   **/
58 /** to search in. Returns number of memory locations found. **/
59 /*************************************************************/
AddHUNT(unsigned int Addr,unsigned int Size,unsigned int Value,unsigned int NewValue,unsigned int Flags)60 int AddHUNT(unsigned int Addr,unsigned int Size,unsigned int Value,unsigned int NewValue,unsigned int Flags)
61 {
62   unsigned int J,M;
63   int I;
64 
65   /* Force 32bit/16bit mode for large values */
66   if((Value>=0x10000)||(NewValue>=0x10000))
67     Flags = (Flags&~HUNT_MASK_SIZE)|HUNT_32BIT;
68   else if((Value>=0x100)||(NewValue>=0x100))
69     Flags = (Flags&~HUNT_MASK_SIZE)|HUNT_16BIT;
70 
71   /* Compute mask for given value size and truncate value */
72   M = Flags&HUNT_32BIT? 0xFFFFFFFF:Flags&HUNT_16BIT? 0xFFFF:0x00FF;
73 
74 #ifdef VGBA
75   /* ARM aligns data to the size boundary */
76   if(M>0xFFFF) Addr&=~3; else if(M>0xFF) Addr&=~1;
77 #endif
78 
79   /* Scan memory for given value */
80   for(Size+=Addr,I=0;(Addr<Size)&&(Count<HUNT_BUFSIZE);++Addr)
81   {
82     J = (M>0xFFFF? MEMREAD32(Addr):M>0xFF? MEMREAD16(Addr):MEMREAD8(Addr))&M;
83 
84     if((J==Value)||(J==((Value-1)&M)))
85     {
86       Buf[Count].Addr  = Addr;
87       Buf[Count].Flags = Flags;
88       Buf[Count].Value = J;
89       Buf[Count].Orig  = J==Value? NewValue:((NewValue-1)&M);
90       Buf[Count].Count = 0;
91       ++Count;
92       ++I;
93     }
94 
95 #ifdef VGBA
96     /* ARM aligns data to the size boundary */
97     if(M>0xFFFF) Addr+=3; else if(M>0xFF) Addr+=1;
98 #endif
99   }
100 
101   /* Return the number of matches found */
102   return(I);
103 }
104 
105 /** ScanHUNT() ***********************************************/
106 /** Scan memory for changed values and update search data.  **/
107 /** Returns number of memory locations updated.             **/
108 /*************************************************************/
ScanHUNT(void)109 int ScanHUNT(void)
110 {
111   unsigned int K,L;
112   int J,I;
113 
114   /* Scan active search entries */
115   for(J=I=0;J<Count;++J)
116   {
117     L = Buf[J].Flags&HUNT_32BIT? 0xFFFFFFFF:Buf[J].Flags&HUNT_16BIT? 0xFFFF:0x00FF;
118     K = (L>0xFFFF? MEMREAD32(Buf[J].Addr):L>0xFF? MEMREAD16(Buf[J].Addr):MEMREAD8(Buf[J].Addr))&L;
119 
120     /* Check for expected changes */
121     switch(Buf[J].Flags&HUNT_MASK_CHANGE)
122     {
123       case HUNT_PLUSONE:   L = K==((Buf[J].Value+1)&L);break;
124       case HUNT_PLUSMANY:  L = K>Buf[J].Value;break;
125       case HUNT_MINUSONE:  L = K==((Buf[J].Value-1)&L);break;
126       case HUNT_MINUSMANY: L = K<Buf[J].Value;break;
127       default:
128       case HUNT_CONSTANT:  L = K==Buf[J].Value;break;
129     }
130 
131     /* Delete any entry that does not change as expected */
132     if(L)
133     {
134       if(Buf[J].Count<(1<<(sizeof(Buf[J].Count)<<3))-1) ++Buf[J].Count;
135       Buf[J].Value = K;
136       Buf[I++] = Buf[J];
137     }
138   }
139 
140   /* Return number of successfully updated entries */
141   return(Count=I);
142 }
143 
144 /** HUNT2Cheat() *********************************************/
145 /** Create cheat code from Nth hunt entry. Returns 0 if the **/
146 /** entry is invalid.                                       **/
147 /*************************************************************/
HUNT2Cheat(int N,unsigned int Type)148 const char *HUNT2Cheat(int N,unsigned int Type)
149 {
150   static char Buf[32];
151   HUNTEntry *HE;
152 
153   /* Must have a valid entry */
154   if(!(HE=GetHUNT(N))) return(0);
155 
156   /* Depending on cheat type... */
157   switch(Type)
158   {
159 /** GameBoy Advance ******************************************/
160 /** There are two versions of GameShark (v1/v3) differing   **/
161 /** by encryption and CodeBreaker. Not encrypting here.     **/
162 /*************************************************************/
163     case HUNT_GBA_GS:
164       /* 00AAAAAA NNNNNNDD - Multiple 8bit RAM Write   */
165       /* 02AAAAAA NNNNDDDD - Multiple 816bit RAM Write */
166       sprintf(Buf,"0%c%06X 0000%04X",
167         HE->Flags&HUNT_16BIT? '2':'0',
168         (HE->Addr&0x000FFFFF)|((HE->Addr&0x0F000000)>>4),
169         HE->Orig
170       );
171       return(Buf);
172 
173     case HUNT_GBA_CB:
174       /* 2AAAAAAA XXXX - 16bit RAM Write */
175       /* 3AAAAAAA 00XX - 8bit RAM Write  */
176       sprintf(Buf,"%c%07X %04X",
177         HE->Flags&HUNT_16BIT? '2':'3',
178         HE->Addr&0x0FFFFFFF,
179         HE->Orig
180       );
181       return(Buf);
182 
183 /** GameBoy **************************************************/
184 /** GameBoy has GameShark and GameGenie, but only GameShark **/
185 /** can modify RAM.                                         **/
186 /*************************************************************/
187     case HUNT_GB_GS:
188       /* 00DDAAAA - 8bit RAM Write, No Bank */
189       sprintf(Buf,"00%02X%02X%02X",(HE->Orig)&0xFF,HE->Addr&0x00FF,(HE->Addr&0xFF00)>>8);
190       if(HE->Flags&HUNT_16BIT)
191         sprintf(Buf+8,";00%02X%02X%02X",HE->Orig>>8,(HE->Addr+1)&0x00FF,((HE->Addr+1)&0xFF00)>>8);
192       return(Buf);
193 
194 /** NES ******************************************************/
195 /** NES has both Pro Action Replay and GameGenie, but only  **/
196 /** Pro Action Replay can modify RAM.                       **/
197 /*************************************************************/
198     case HUNT_NES_AR:
199       /* 00AAAADD - 8bit RAM Write */
200       sprintf(Buf,"00%04X%02X",HE->Addr&0xFFFF,HE->Orig&0xFF);
201       if(HE->Flags&HUNT_16BIT)
202         sprintf(Buf+8,";00%04X%02X",(HE->Addr+1)&0xFFFF,HE->Orig>>8);
203       return(Buf);
204 
205 /** Sega MasterSystem and GameGear ***************************/
206 /** MasterSystem has Pro Action Replay, while GameGear has  **/
207 /** GameGenie. Only Pro Action Replay can modify RAM.       **/
208 /*************************************************************/
209     case HUNT_SMS_AR:
210       /* 00AA-AADD - 8bit RAM Write */
211       sprintf(Buf,"00%02X-%02X%02X",(HE->Addr&0xFF00)>>8,HE->Addr&0x00FF,HE->Orig&0xFF);
212       if(HE->Flags&HUNT_16BIT)
213         sprintf(Buf+9,";00%02X-%02X%02X",((HE->Addr+1)&0xFF00)>>8,(HE->Addr+1)&0x00FF,HE->Orig>>8);
214       return(Buf);
215 
216 /** ColecoVision, MSX, ZX Spectrum ***************************/
217 /** There was no official cheating hardware for these       **/
218 /** systems, so we come up with our own "hardware".         **/
219 /*************************************************************/
220     case HUNT_MSX:
221     case HUNT_ZXS:
222     case HUNT_COLECO:
223       /* AAAA-DD[DD] - 8bit[16bit] RAM Write */
224       if(HE->Flags&HUNT_16BIT)
225         sprintf(Buf,"%04X-%04X",HE->Addr,HE->Orig&0xFFFF);
226       else
227         sprintf(Buf,"%04X-%02X",HE->Addr,HE->Orig&0x00FF);
228       return(Buf);
229   }
230 
231   /* Invalid cheat type */
232   return(0);
233 }
234