start using libpicofe, move some files
[fceu.git] / cheat.c
CommitLineData
c62d2810 1/* FCE Ultra - NES/Famicom Emulator
2 *
3 * Copyright notice for this file:
92764e62 4 * Copyright (C) 2002 Xodnizel
c62d2810 5 *
6 * This program is free software; you can redistribute it and/or modify
7 * it under the terms of the GNU General Public License as published by
8 * the Free Software Foundation; either version 2 of the License, or
9 * (at your option) any later version.
10 *
11 * This program is distributed in the hope that it will be useful,
12 * but WITHOUT ANY WARRANTY; without even the implied warranty of
13 * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
14 * GNU General Public License for more details.
15 *
16 * You should have received a copy of the GNU General Public License
17 * along with this program; if not, write to the Free Software
18 * Foundation, Inc., 59 Temple Place, Suite 330, Boston, MA 02111-1307 USA
19 */
20
21#include <stdlib.h>
22#include <string.h>
23#include <stdio.h>
92764e62 24#include <ctype.h>
c62d2810 25
26#include "types.h"
27#include "x6502.h"
28#include "cheat.h"
29#include "fce.h"
c62d2810 30#include "general.h"
31#include "cart.h"
32#include "memory.h"
33
92764e62 34#include "file.h"
35#include "svga.h"
36
c62d2810 37static uint8 *CheatRPtrs[64];
38
39void FCEU_CheatResetRAM(void)
40{
41 int x;
42
92764e62 43 for(x=0;x<64;x++)
c62d2810 44 CheatRPtrs[x]=0;
45}
46
47void FCEU_CheatAddRAM(int s, uint32 A, uint8 *p)
48{
49 uint32 AB=A>>10;
50 int x;
51
52 for(x=s-1;x>=0;x--)
53 CheatRPtrs[AB+x]=p-A;
54}
55
56
57struct CHEATF {
58 struct CHEATF *next;
59 char *name;
60 uint16 addr;
61 uint8 val;
92764e62 62 int compare; /* -1 for no compare. */
63 int type; /* 0 for replace, 1 for substitute(GG). */
c62d2810 64 int status;
65};
66
92764e62 67typedef struct {
68 uint16 addr;
69 uint8 val;
70 int compare;
71 readfunc PrevRead;
72} CHEATF_SUBFAST;
73
74
75static CHEATF_SUBFAST SubCheats[256];
76static int numsubcheats=0;
c62d2810 77struct CHEATF *cheats=0,*cheatsl=0;
78
79
80#define CHEATC_NONE 0x8000
81#define CHEATC_EXCLUDED 0x4000
82#define CHEATC_NOSHOW 0xC000
83
84static uint16 *CheatComp=0;
85static int savecheats;
86
92764e62 87static DECLFR(SubCheatsRead)
88{
89 CHEATF_SUBFAST *s=SubCheats;
90 int x=numsubcheats;
91
92 do
93 {
94 if(s->addr==A)
95 {
96 if(s->compare>=0)
97 {
98 uint8 pv=s->PrevRead(A);
99
100 if(pv==s->compare)
101 return(s->val);
102 else return(pv);
103 }
104 else return(s->val);
105 }
106 s++;
107 } while(--x);
108 return(0); /* We should never get here. */
109}
110
111void RebuildSubCheats(void)
112{
113 int x;
114 struct CHEATF *c=cheats;
115
116 for(x=0;x<numsubcheats;x++)
117 SetReadHandler(SubCheats[x].addr,SubCheats[x].addr,SubCheats[x].PrevRead);
118
119 numsubcheats=0;
120 while(c)
121 {
122 if(c->type==1 && c->status)
123 {
124 if(GetReadHandler(c->addr)==SubCheatsRead)
125 {
126 /* Prevent a catastrophe by this check. */
127 //FCEU_DispMessage("oops");
128 }
129 else
130 {
131 SubCheats[numsubcheats].PrevRead=GetReadHandler(c->addr);
132 SubCheats[numsubcheats].addr=c->addr;
133 SubCheats[numsubcheats].val=c->val;
134 SubCheats[numsubcheats].compare=c->compare;
135 SetReadHandler(c->addr,c->addr,SubCheatsRead);
136 numsubcheats++;
137 }
138 }
139 c=c->next;
140 }
141}
c62d2810 142
92764e62 143void FCEU_PowerCheats()
144{
145 numsubcheats=0; /* Quick hack to prevent setting of ancient read addresses. */
146 RebuildSubCheats();
147}
148
149static int AddCheatEntry(char *name, uint32 addr, uint8 val, int compare, int status, int type);
c62d2810 150static void CheatMemErr(void)
151{
152 FCEUD_PrintError("Error allocating memory for cheat data.");
153}
154
155/* This function doesn't allocate any memory for "name" */
92764e62 156static int AddCheatEntry(char *name, uint32 addr, uint8 val, int compare, int status, int type)
c62d2810 157{
158 struct CHEATF *temp;
92764e62 159 if(!(temp=(struct CHEATF *)malloc(sizeof(struct CHEATF))))
c62d2810 160 {
161 CheatMemErr();
162 return(0);
163 }
164 temp->name=name;
165 temp->addr=addr;
166 temp->val=val;
167 temp->status=status;
92764e62 168 temp->compare=compare;
169 temp->type=type;
c62d2810 170 temp->next=0;
171
172 if(cheats)
173 {
174 cheatsl->next=temp;
175 cheatsl=temp;
176 }
177 else
178 cheats=cheatsl=temp;
179
180 return(1);
181}
182
92764e62 183void FCEU_LoadGameCheats(FILE *override)
c62d2810 184{
185 FILE *fp;
c62d2810 186 unsigned int addr;
187 unsigned int val;
188 unsigned int status;
92764e62 189 unsigned int type;
190 unsigned int compare;
c62d2810 191 int x;
192
92764e62 193 char linebuf[2048];
194 char *namebuf;
c62d2810 195 int tc=0;
92764e62 196 char *fn;
197
198 numsubcheats=savecheats=0;
199
200 if(override)
201 fp = override;
202 else
203 {
204 fn=FCEU_MakeFName(FCEUMKF_CHEAT,0,0);
205 fp=FCEUD_UTF8fopen(fn,"rb");
206 free(fn);
207 if(!fp) return;
208 }
c62d2810 209
92764e62 210 while(fgets(linebuf,2048,fp)>0)
211 {
212 char *tbuf=linebuf;
213 int doc=0;
214
215 addr=val=compare=status=type=0;
216
217 if(tbuf[0]=='S')
c62d2810 218 {
92764e62 219 tbuf++;
220 type=1;
221 }
222 else type=0;
223
224 if(tbuf[0]=='C')
225 {
226 tbuf++;
227 doc=1;
228 }
229
230 if(tbuf[0]==':')
231 {
232 tbuf++;
c62d2810 233 status=0;
234 }
92764e62 235 else status=1;
236
237 if(doc)
238 {
239 char *neo=&tbuf[4+2+2+1+1+1];
240 if(sscanf(tbuf,"%04x%*[:]%02x%*[:]%02x",&addr,&val,&compare)!=3)
241 continue;
242 namebuf=malloc(strlen(neo)+1);
243 strcpy(namebuf,neo);
244 }
c62d2810 245 else
246 {
92764e62 247 char *neo=&tbuf[4+2+1+1];
248 if(sscanf(tbuf,"%04x%*[:]%02x",&addr,&val)!=2)
249 continue;
250 namebuf=malloc(strlen(neo)+1);
251 strcpy(namebuf,neo);
c62d2810 252 }
92764e62 253
254 for(x=0;x<strlen(namebuf);x++)
c62d2810 255 {
256 if(namebuf[x]==10 || namebuf[x]==13)
257 {
258 namebuf[x]=0;
259 break;
260 }
92764e62 261 else if(namebuf[x]<0x20) namebuf[x]=' ';
c62d2810 262 }
92764e62 263
264 AddCheatEntry(namebuf,addr,val,doc?compare:-1,status,type);
265 tc++;
c62d2810 266 }
92764e62 267 RebuildSubCheats();
268 if(!override)
269 fclose(fp);
c62d2810 270}
271
92764e62 272void FCEU_FlushGameCheats(FILE *override, int nosave)
c62d2810 273{
274 if(CheatComp)
275 {
276 free(CheatComp);
277 CheatComp=0;
278 }
92764e62 279 if((!savecheats || nosave) && !override) /* Always save cheats if we're being overridden. */
c62d2810 280 {
281 if(cheats)
282 {
283 struct CHEATF *next=cheats;
284 for(;;)
92764e62 285 {
286 struct CHEATF *last=next;
c62d2810 287 next=next->next;
92764e62 288 free(last->name);
289 free(last);
c62d2810 290 if(!next) break;
291 }
292 cheats=cheatsl=0;
293 }
294 }
295 else
296 {
92764e62 297 char *fn = 0;
298
299 if(!override)
300 fn = FCEU_MakeFName(FCEUMKF_CHEAT,0,0);
301
c62d2810 302 if(cheats)
303 {
304 struct CHEATF *next=cheats;
305 FILE *fp;
92764e62 306
307 if(override)
308 fp = override;
309 else
310 fp=FCEUD_UTF8fopen(fn,"wb");
311
312 if(fp)
c62d2810 313 {
314 for(;;)
315 {
316 struct CHEATF *t;
92764e62 317 if(next->type)
318 fputc('S',fp);
319 if(next->compare>=0)
320 fputc('C',fp);
321
322 if(!next->status)
323 fputc(':',fp);
324
325 if(next->compare>=0)
326 fprintf(fp,"%04x:%02x:%02x:%s\n",next->addr,next->val,next->compare,next->name);
c62d2810 327 else
92764e62 328 fprintf(fp,"%04x:%02x:%s\n",next->addr,next->val,next->name);
329
c62d2810 330 free(next->name);
331 t=next;
332 next=next->next;
333 free(t);
334 if(!next) break;
335 }
92764e62 336 if(!override)
337 fclose(fp);
c62d2810 338 }
339 else
340 FCEUD_PrintError("Error saving cheats.");
341 cheats=cheatsl=0;
342 }
92764e62 343 else if(!override)
344 remove(fn);
345 if(!override)
346 free(fn);
c62d2810 347 }
92764e62 348
349 RebuildSubCheats(); /* Remove memory handlers. */
350
c62d2810 351}
352
353
92764e62 354int FCEUI_AddCheat(const char *name, uint32 addr, uint8 val, int compare, int type)
c62d2810 355{
356 char *t;
357
92764e62 358 if(!(t=(char *)malloc(strlen(name)+1)))
c62d2810 359 {
360 CheatMemErr();
361 return(0);
362 }
363 strcpy(t,name);
92764e62 364 if(!AddCheatEntry(t,addr,val,compare,1,type))
c62d2810 365 {
366 free(t);
367 return(0);
368 }
369 savecheats=1;
92764e62 370 RebuildSubCheats();
c62d2810 371 return(1);
372}
373
374int FCEUI_DelCheat(uint32 which)
375{
376 struct CHEATF *prev;
377 struct CHEATF *cur;
378 uint32 x=0;
379
380 for(prev=0,cur=cheats;;)
381 {
382 if(x==which) // Remove this cheat.
383 {
384 if(prev) // Update pointer to this cheat.
385 {
386 if(cur->next) // More cheats.
387 prev->next=cur->next;
388 else // No more.
389 {
390 prev->next=0;
391 cheatsl=prev; // Set the previous cheat as the last cheat.
392 }
393 }
394 else // This is the first cheat.
395 {
396 if(cur->next) // More cheats
397 cheats=cur->next;
398 else
399 cheats=cheatsl=0; // No (more) cheats.
400 }
401 free(cur->name); // Now that all references to this cheat are removed,
92764e62 402 free(cur); // free the memory.
c62d2810 403 break;
404 } // *END REMOVE THIS CHEAT*
405
406
407 if(!cur->next) // No more cheats to go through(this shouldn't ever happen...)
408 return(0);
409 prev=cur;
410 cur=prev->next;
411 x++;
412 }
413
414 savecheats=1;
92764e62 415 RebuildSubCheats();
416
c62d2810 417 return(1);
418}
419
92764e62 420void FCEU_ApplyPeriodicCheats(void)
c62d2810 421{
422 struct CHEATF *cur=cheats;
423 if(!cur) return;
424
425 for(;;)
426 {
92764e62 427 if(cur->status && !(cur->type))
c62d2810 428 if(CheatRPtrs[cur->addr>>10])
429 CheatRPtrs[cur->addr>>10][cur->addr]=cur->val;
430 if(cur->next)
431 cur=cur->next;
432 else
433 break;
434 }
435}
436
437
92764e62 438void FCEUI_ListCheats(int (*callb)(char *name, uint32 a, uint8 v, int compare, int s, int type, void *data), void *data)
c62d2810 439{
440 struct CHEATF *next=cheats;
441
442 while(next)
443 {
92764e62 444 if(!callb(next->name,next->addr,next->val,next->compare,next->status,next->type,data)) break;
c62d2810 445 next=next->next;
446 }
447}
448
92764e62 449int FCEUI_GetCheat(uint32 which, char **name, uint32 *a, uint8 *v, int *compare, int *s, int *type)
c62d2810 450{
451 struct CHEATF *next=cheats;
452 uint32 x=0;
453
454 while(next)
455 {
456 if(x==which)
457 {
458 if(name)
459 *name=next->name;
460 if(a)
92764e62 461 *a=next->addr;
c62d2810 462 if(v)
463 *v=next->val;
464 if(s)
465 *s=next->status;
92764e62 466 if(compare)
467 *compare=next->compare;
468 if(type)
469 *type=next->type;
c62d2810 470 return(1);
471 }
472 next=next->next;
473 x++;
474 }
475 return(0);
476}
477
92764e62 478static int GGtobin(char c)
479{
480 static char lets[16]={'A','P','Z','L','G','I','T','Y','E','O','X','U','K','S','V','N'};
481 int x;
482
483 for(x=0;x<16;x++)
484 if(lets[x] == toupper(c)) return(x);
485 return(0);
486}
487
488/* Returns 1 on success, 0 on failure. Sets *a,*v,*c. */
489int FCEUI_DecodeGG(const char *str, uint16 *a, uint8 *v, int *c)
490{
491 uint16 A;
492 uint8 V,C;
493 uint8 t;
494 int s;
495
496 A=0x8000;
497 V=0;
498 C=0;
499
500 s=strlen(str);
501 if(s!=6 && s!=8) return(0);
502
503 t=GGtobin(*str++);
504 V|=(t&0x07);
505 V|=(t&0x08)<<4;
506
507 t=GGtobin(*str++);
508 V|=(t&0x07)<<4;
509 A|=(t&0x08)<<4;
510
511 t=GGtobin(*str++);
512 A|=(t&0x07)<<4;
513 //if(t&0x08) return(0); /* 8-character code?! */
514
515 t=GGtobin(*str++);
516 A|=(t&0x07)<<12;
517 A|=(t&0x08);
518
519 t=GGtobin(*str++);
520 A|=(t&0x07);
521 A|=(t&0x08)<<8;
522
523 if(s==6)
524 {
525 t=GGtobin(*str++);
526 A|=(t&0x07)<<8;
527 V|=(t&0x08);
528
529 *a=A;
530 *v=V;
531 *c=-1;
532 return(1);
533 }
534 else
535 {
536 t=GGtobin(*str++);
537 A|=(t&0x07)<<8;
538 C|=(t&0x08);
539
540 t=GGtobin(*str++);
541 C|=(t&0x07);
542 C|=(t&0x08)<<4;
543
544 t=GGtobin(*str++);
545 C|=(t&0x07)<<4;
546 V|=(t&0x08);
547 *a=A;
548 *v=V;
549 *c=C;
550 return(1);
551 }
552 return(0);
553}
554
555int FCEUI_DecodePAR(const char *str, uint16 *a, uint8 *v, int *c, int *type)
556{
557 int boo[4];
558 if(strlen(str)!=8) return(0);
559
560 sscanf(str,"%02x%02x%02x%02x",boo,boo+1,boo+2,boo+3);
561
562 *c=-1;
563
564 if(1)
565 {
566 *a=(boo[3]<<8)|(boo[2]+0x7F);
567 *v=0;
568 }
569 else
570 {
571 *v=boo[3];
572 *a=boo[2]|(boo[1]<<8);
573 }
574 /* Zero-page addressing modes don't go through the normal read/write handlers in FCEU, so
575 we must do the old hacky method of RAM cheats.
576 */
577 if(*a<0x0100)
578 *type=0;
579 else
580 *type=1;
581 return(1);
582}
583
c62d2810 584/* name can be NULL if the name isn't going to be changed. */
585/* same goes for a, v, and s(except the values of each one must be <0) */
586
92764e62 587int FCEUI_SetCheat(uint32 which, const char *name, int32 a, int32 v, int compare,int s, int type)
c62d2810 588{
589 struct CHEATF *next=cheats;
590 uint32 x=0;
591
592 while(next)
593 {
594 if(x==which)
595 {
596 if(name)
597 {
598 char *t;
599
92764e62 600 if((t=(char *)realloc(next->name,strlen(name+1))))
c62d2810 601 {
602 next->name=t;
603 strcpy(next->name,name);
604 }
605 else
606 return(0);
607 }
608 if(a>=0)
609 next->addr=a;
610 if(v>=0)
611 next->val=v;
612 if(s>=0)
613 next->status=s;
92764e62 614 next->compare=compare;
615 next->type=type;
616
c62d2810 617 savecheats=1;
92764e62 618 RebuildSubCheats();
619
c62d2810 620 return(1);
621 }
622 next=next->next;
623 x++;
624 }
625 return(0);
626}
627
92764e62 628/* Convenience function. */
629int FCEUI_ToggleCheat(uint32 which)
630{
631 struct CHEATF *next=cheats;
632 uint32 x=0;
633
634 while(next)
635 {
636 if(x==which)
637 {
638 next->status=!next->status;
639 savecheats=1;
640 RebuildSubCheats();
641 return(next->status);
642 }
643 next=next->next;
644 x++;
645 }
646
647 return(-1);
648}
c62d2810 649
650static int InitCheatComp(void)
651{
652 uint32 x;
653
92764e62 654 CheatComp=(uint16*)malloc(65536*sizeof(uint16));
c62d2810 655 if(!CheatComp)
656 {
657 CheatMemErr();
658 return(0);
659 }
660 for(x=0;x<65536;x++)
661 CheatComp[x]=CHEATC_NONE;
92764e62 662
c62d2810 663 return(1);
664}
665
666void FCEUI_CheatSearchSetCurrentAsOriginal(void)
667{
668 uint32 x;
669 for(x=0x000;x<0x10000;x++)
670 if(!(CheatComp[x]&CHEATC_NOSHOW))
671 {
672 if(CheatRPtrs[x>>10])
673 CheatComp[x]=CheatRPtrs[x>>10][x];
674 else
675 CheatComp[x]|=CHEATC_NONE;
676 }
677}
678
679void FCEUI_CheatSearchShowExcluded(void)
680{
681 uint32 x;
682
683 for(x=0x000;x<0x10000;x++)
684 CheatComp[x]&=~CHEATC_EXCLUDED;
685}
686
687
688int32 FCEUI_CheatSearchGetCount(void)
689{
690 uint32 x,c=0;
691
692 if(CheatComp)
693 {
694 for(x=0x0000;x<0x10000;x++)
695 if(!(CheatComp[x]&CHEATC_NOSHOW) && CheatRPtrs[x>>10])
696 c++;
697 }
698
699 return c;
700}
701/* This function will give the initial value of the search and the current value at a location. */
702
92764e62 703void FCEUI_CheatSearchGet(int (*callb)(uint32 a, uint8 last, uint8 current, void *data),void *data)
c62d2810 704{
705 uint32 x;
706
707 if(!CheatComp)
708 {
709 if(!InitCheatComp())
710 CheatMemErr();
711 return;
712 }
713
714 for(x=0;x<0x10000;x++)
715 if(!(CheatComp[x]&CHEATC_NOSHOW) && CheatRPtrs[x>>10])
92764e62 716 if(!callb(x,CheatComp[x],CheatRPtrs[x>>10][x],data))
c62d2810 717 break;
718}
719
720void FCEUI_CheatSearchGetRange(uint32 first, uint32 last, int (*callb)(uint32 a, uint8 last, uint8 current))
721{
722 uint32 x;
723 uint32 in=0;
724
725 if(!CheatComp)
726 {
727 if(!InitCheatComp())
728 CheatMemErr();
729 return;
730 }
731
732 for(x=0;x<0x10000;x++)
733 if(!(CheatComp[x]&CHEATC_NOSHOW) && CheatRPtrs[x>>10])
734 {
735 if(in>=first)
736 if(!callb(x,CheatComp[x],CheatRPtrs[x>>10][x]))
737 break;
738 in++;
739 if(in>last) return;
740 }
741}
742
743void FCEUI_CheatSearchBegin(void)
744{
745 uint32 x;
746
747 if(!CheatComp)
748 {
749 if(!InitCheatComp())
750 {
751 CheatMemErr();
752 return;
753 }
754 }
755 for(x=0;x<0x10000;x++)
756 {
757 if(CheatRPtrs[x>>10])
758 CheatComp[x]=CheatRPtrs[x>>10][x];
759 else
760 CheatComp[x]=CHEATC_NONE;
761 }
762}
763
764
765static int INLINE CAbs(int x)
766{
767 if(x<0)
768 return(0-x);
769 return x;
770}
771
772void FCEUI_CheatSearchEnd(int type, uint8 v1, uint8 v2)
773{
774 uint32 x;
775
776 if(!CheatComp)
777 {
778 if(!InitCheatComp())
779 {
780 CheatMemErr();
781 return;
782 }
783 }
784
785
786 if(!type) // Change to a specific value.
787 {
788 for(x=0;x<0x10000;x++)
789 if(!(CheatComp[x]&CHEATC_NOSHOW))
790 {
791 if(CheatComp[x]==v1 && CheatRPtrs[x>>10][x]==v2)
792 {
793
794 }
795 else
796 CheatComp[x]|=CHEATC_EXCLUDED;
797 }
798 }
799 else if(type==1) // Search for relative change(between values).
800 {
801 for(x=0;x<0x10000;x++)
802 if(!(CheatComp[x]&CHEATC_NOSHOW))
803 {
804 if(CheatComp[x]==v1 && CAbs(CheatComp[x]-CheatRPtrs[x>>10][x])==v2)
805 {
806
807 }
808 else
809 CheatComp[x]|=CHEATC_EXCLUDED;
810 }
811 }
812 else if(type==2) // Purely relative change.
813 {
814 for(x=0x000;x<0x10000;x++)
815 if(!(CheatComp[x]&CHEATC_NOSHOW))
816 {
817 if(CAbs(CheatComp[x]-CheatRPtrs[x>>10][x])==v2)
818 {
819
820 }
821 else
822 CheatComp[x]|=CHEATC_EXCLUDED;
823 }
824 }
825 else if(type==3) // Any change.
826 {
92764e62 827 for(x=0;x<0x10000;x++)
c62d2810 828 if(!(CheatComp[x]&CHEATC_NOSHOW))
829 {
830 if(CheatComp[x]!=CheatRPtrs[x>>10][x])
831 {
832
833 }
834 else
835 CheatComp[x]|=CHEATC_EXCLUDED;
836 }
c62d2810 837 }
92764e62 838 else if(type==4) // Value decreased.
839 {
840 for(x=0;x<0x10000;x++)
841 if(!(CheatComp[x]&CHEATC_NOSHOW))
842 {
843 if(!(CheatRPtrs[x>>10][x]<CheatComp[x]))
844 CheatComp[x]|=CHEATC_EXCLUDED;
845 }
846 }
847 else if(type==5) // Value increased.
848 {
849 for(x=0;x<0x10000;x++)
850 if(!(CheatComp[x]&CHEATC_NOSHOW))
851 {
852 if(!(CheatRPtrs[x>>10][x]>CheatComp[x]))
853 CheatComp[x]|=CHEATC_EXCLUDED;
854 }
855 }
856 if(type>4)
857 FCEUI_CheatSearchSetCurrentAsOriginal();
c62d2810 858}