merge from libretro fork
[pcsx_rearmed.git] / libpcsxcore / ppf.c
1 /*  PPF Patch Support for PCSX-Reloaded
2  *  Copyright (c) 2009, Wei Mingzhi <whistler_wmz@users.sf.net>.
3  *
4  *  Based on P.E.Op.S CDR Plugin by Pete Bernert.
5  *  Copyright (c) 2002, Pete Bernert.
6  *
7  *  This program is free software; you can redistribute it and/or modify
8  *  it under the terms of the GNU General Public License as published by
9  *  the Free Software Foundation; either version 2 of the License, or
10  *  (at your option) any later version.
11  *
12  *  This program is distributed in the hope that it will be useful,
13  *  but WITHOUT ANY WARRANTY; without even the implied warranty of
14  *  MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE.  See the
15  *  GNU General Public License for more details.
16  *
17  *  You should have received a copy of the GNU General Public License
18  *  along with this program; if not, write to the Free Software
19  *  Foundation, Inc., 51 Franklin Street, Fifth Floor, Boston, MA 02111-1307 USA
20  */
21
22 #include "psxcommon.h"
23 #include "ppf.h"
24 #include "cdrom.h"
25
26 typedef struct tagPPF_DATA {
27         s32                                     addr;
28         s32                                     pos;
29         s32                                     anz;
30         struct tagPPF_DATA      *pNext;
31 } PPF_DATA;
32
33 typedef struct tagPPF_CACHE {
34         s32                                     addr;
35         struct tagPPF_DATA      *pNext;
36 } PPF_CACHE;
37
38 static PPF_CACHE                *ppfCache = NULL;
39 static PPF_DATA                 *ppfHead = NULL, *ppfLast = NULL;
40 static int                              iPPFNum = 0;
41
42 // using a linked data list, and address array
43 static void FillPPFCache() {
44         PPF_DATA                *p;
45         PPF_CACHE               *pc;
46         s32                             lastaddr;
47
48         p = ppfHead;
49         lastaddr = -1;
50         iPPFNum = 0;
51
52         while (p != NULL) {
53                 if (p->addr != lastaddr) iPPFNum++;
54                 lastaddr = p->addr;
55                 p = p->pNext;
56         }
57
58         if (iPPFNum <= 0) return;
59
60         pc = ppfCache = (PPF_CACHE *)malloc(iPPFNum * sizeof(PPF_CACHE));
61
62         iPPFNum--;
63         p = ppfHead;
64         lastaddr = -1;
65
66         while (p != NULL) {
67                 if (p->addr != lastaddr) {
68                         pc->addr = p->addr;
69                         pc->pNext = p;
70                         pc++;
71                 }
72                 lastaddr = p->addr;
73                 p = p->pNext;
74         }
75 }
76
77 void FreePPFCache() {
78         PPF_DATA *p = ppfHead;
79         void *pn;
80
81         while (p != NULL) {
82                 pn = p->pNext;
83                 free(p);
84                 p = (PPF_DATA *)pn;
85         }
86         ppfHead = NULL;
87         ppfLast = NULL;
88
89         if (ppfCache != NULL) free(ppfCache);
90         ppfCache = NULL;
91 }
92
93 void CheckPPFCache(unsigned char *pB, unsigned char m, unsigned char s, unsigned char f) {
94         PPF_CACHE *pcstart, *pcend, *pcpos;
95         int addr = MSF2SECT(btoi(m), btoi(s), btoi(f)), pos, anz, start;
96
97         if (ppfCache == NULL) return;
98
99         pcstart = ppfCache;
100         if (addr < pcstart->addr) return;
101         pcend = ppfCache + iPPFNum;
102         if (addr > pcend->addr) return;
103
104         while (1) {
105                 if (addr == pcend->addr) { pcpos = pcend; break; }
106
107                 pcpos = pcstart + (pcend - pcstart) / 2;
108                 if (pcpos == pcstart) break;
109                 if (addr < pcpos->addr) {
110                         pcend = pcpos;
111                         continue;
112                 }
113                 if (addr > pcpos->addr) {
114                         pcstart = pcpos;
115                         continue;
116                 }
117                 break;
118         }
119
120         if (addr == pcpos->addr) {
121                 PPF_DATA *p = pcpos->pNext;
122                 while (p != NULL && p->addr == addr) {
123                         pos = p->pos - (CD_FRAMESIZE_RAW - DATA_SIZE);
124                         anz = p->anz;
125                         if (pos < 0) { start = -pos; pos = 0; anz -= start; }
126                         else start = 0;
127                         memcpy(pB + pos, (unsigned char *)(p + 1) + start, anz);
128                         p = p->pNext;
129                 }
130         }
131 }
132
133 static void AddToPPF(s32 ladr, s32 pos, s32 anz, unsigned char *ppfmem) {
134         if (ppfHead == NULL) {
135                 ppfHead = (PPF_DATA *)malloc(sizeof(PPF_DATA) + anz);
136                 ppfHead->addr = ladr;
137                 ppfHead->pNext = NULL;
138                 ppfHead->pos = pos;
139                 ppfHead->anz = anz;
140                 memcpy(ppfHead + 1, ppfmem, anz);
141                 iPPFNum = 1;
142                 ppfLast = ppfHead;
143         } else {
144                 PPF_DATA *p = ppfHead;
145                 PPF_DATA *plast = NULL;
146                 PPF_DATA *padd;
147
148                 if (ladr > ppfLast->addr || (ladr == ppfLast->addr && pos > ppfLast->pos)) {
149                         p = NULL;
150                         plast = ppfLast;
151                 } else {
152                         while (p != NULL) {
153                                 if (ladr < p->addr) break;
154                                 if (ladr == p->addr) {
155                                         while (p && ladr == p->addr && pos > p->pos) {
156                                                 plast = p;
157                                                 p = p->pNext;
158                                         }
159                                         break;
160                                 }
161                                 plast = p;
162                                 p = p->pNext;
163                         }
164                 }
165
166                 padd = (PPF_DATA *)malloc(sizeof(PPF_DATA) + anz);
167                 padd->addr = ladr;
168                 padd->pNext = p;
169                 padd->pos = pos;
170                 padd->anz = anz;
171                 memcpy(padd + 1, ppfmem, anz);
172                 iPPFNum++;
173                 if (plast == NULL) ppfHead = padd;
174                 else plast->pNext = padd;
175
176                 if (padd->pNext == NULL) ppfLast = padd;
177         }
178 }
179
180 void BuildPPFCache() {
181         FILE                    *ppffile;
182         char                    buffer[12];
183         char                    method, undo = 0, blockcheck = 0;
184         int                             dizlen, dizyn;
185         unsigned char   ppfmem[512];
186         char                    szPPF[MAXPATHLEN * 2];
187         int                             count, seekpos, pos;
188         u32                             anz; // use 32-bit to avoid stupid overflows
189         s32                             ladr, off, anx;
190
191         FreePPFCache();
192
193         if (CdromId[0] == '\0') return;
194
195         // Generate filename in the format of SLUS_123.45
196         buffer[0] = toupper(CdromId[0]);
197         buffer[1] = toupper(CdromId[1]);
198         buffer[2] = toupper(CdromId[2]);
199         buffer[3] = toupper(CdromId[3]);
200         buffer[4] = '_';
201         buffer[5] = CdromId[4];
202         buffer[6] = CdromId[5];
203         buffer[7] = CdromId[6];
204         buffer[8] = '.';
205         buffer[9] = CdromId[7];
206         buffer[10] = CdromId[8];
207         buffer[11] = '\0';
208
209         sprintf(szPPF, "%s%s", Config.PatchesDir, buffer);
210
211         ppffile = fopen(szPPF, "rb");
212         if (ppffile == NULL) return;
213
214         memset(buffer, 0, 5);
215         if (fread(buffer, 1, 3, ppffile) != 3)
216                 goto fail_io;
217
218         if (strcmp(buffer, "PPF") != 0) {
219                 SysPrintf(_("Invalid PPF patch: %s.\n"), szPPF);
220                 fclose(ppffile);
221                 return;
222         }
223
224         fseek(ppffile, 5, SEEK_SET);
225         method = fgetc(ppffile);
226
227         switch (method) {
228                 case 0: // ppf1
229                         fseek(ppffile, 0, SEEK_END);
230                         count = ftell(ppffile);
231                         count -= 56;
232                         seekpos = 56;
233                         break;
234
235                 case 1: // ppf2
236                         fseek(ppffile, -8, SEEK_END);
237
238                         memset(buffer, 0, 5);
239                         if (fread(buffer, 1, 4, ppffile) != 4)
240                                 goto fail_io;
241
242                         if (strcmp(".DIZ", buffer) != 0) {
243                                 dizyn = 0;
244                         } else {
245                                 if (fread(&dizlen, 1, 4, ppffile) != 4)
246                                         goto fail_io;
247                                 dizlen = SWAP32(dizlen);
248                                 dizyn = 1;
249                         }
250
251                         fseek(ppffile, 0, SEEK_END);
252                         count = ftell(ppffile);
253
254                         if (dizyn == 0) {
255                                 count -= 1084;
256                                 seekpos = 1084;
257                         } else {
258                                 count -= 1084;
259                                 count -= 38;
260                                 count -= dizlen;
261                                 seekpos = 1084;
262                         }
263                         break;
264
265                 case 2: // ppf3
266                         fseek(ppffile, 57, SEEK_SET);
267                         blockcheck = fgetc(ppffile);
268                         undo = fgetc(ppffile);
269
270                         fseek(ppffile, -6, SEEK_END);
271                         memset(buffer, 0, 5);
272                         if (fread(buffer, 1, 4, ppffile) != 4)
273                                 goto fail_io;
274                         dizlen = 0;
275
276                         if (strcmp(".DIZ", buffer) == 0) {
277                                 fseek(ppffile, -2, SEEK_END);
278                                 // TODO: Endian/size unsafe?
279                                 if (fread(&dizlen, 1, 2, ppffile) != 2)
280                                         goto fail_io;
281                                 dizlen = SWAP32(dizlen);
282                                 dizlen += 36;
283                         }
284
285                         fseek(ppffile, 0, SEEK_END);
286                         count = ftell(ppffile);
287                         count -= dizlen;
288
289                         if (blockcheck) {
290                                 seekpos = 1084;
291                                 count -= 1084;
292                         } else {
293                                 seekpos = 60;
294                                 count -= 60;
295                         }
296                         break;
297
298                 default:
299                         fclose(ppffile);
300                         SysPrintf(_("Unsupported PPF version (%d).\n"), method + 1);
301                         return;
302         }
303
304         // now do the data reading
305         do {                                                
306                 fseek(ppffile, seekpos, SEEK_SET);
307                 if (fread(&pos, 1, sizeof(pos), ppffile) != sizeof(pos))
308                         goto fail_io;
309                 pos = SWAP32(pos);
310
311                 if (method == 2) {
312                         // skip 4 bytes on ppf3 (no int64 support here)
313                         if (fread(buffer, 1, 4, ppffile) != 4)
314                                 goto fail_io;
315                 }
316
317                 anz = fgetc(ppffile);
318                 if (fread(ppfmem, 1, anz, ppffile) != anz)
319                         goto fail_io;
320
321                 ladr = pos / CD_FRAMESIZE_RAW;
322                 off = pos % CD_FRAMESIZE_RAW;
323
324                 if (off + anz > CD_FRAMESIZE_RAW) {
325                         anx = off + anz - CD_FRAMESIZE_RAW;
326                         anz -= (unsigned char)anx;
327                         AddToPPF(ladr + 1, 0, anx, &ppfmem[anz]);
328                 }
329
330                 AddToPPF(ladr, off, anz, ppfmem); // add to link list
331
332                 if (method == 2) {
333                         if (undo) anz += anz;
334                         anz += 4;
335                 }
336
337                 seekpos = seekpos + 5 + anz;
338                 count = count - 5 - anz;
339         } while (count != 0); // loop til end
340
341         fclose(ppffile);
342
343         FillPPFCache(); // build address array
344
345         SysPrintf(_("Loaded PPF %d.0 patch: %s.\n"), method + 1, szPPF);
346
347 fail_io:
348 #ifndef NDEBUG
349         SysPrintf(_("File IO error in <%s:%s>.\n"), __FILE__, __func__);
350 #endif
351         fclose(ppffile);
352 }
353
354 // redump.org SBI files, slightly different handling from PCSX-Reloaded
355 unsigned char *sbi_sectors;
356
357 int LoadSBI(const char *fname, int sector_count) {
358         char buffer[16];
359         FILE *sbihandle;
360         u8 sbitime[3], t;
361         int s;
362
363         sbihandle = fopen(fname, "rb");
364         if (sbihandle == NULL)
365                 return -1;
366
367         sbi_sectors = calloc(1, sector_count / 8);
368         if (sbi_sectors == NULL) {
369                 fclose(sbihandle);
370                 return -1;
371         }
372
373         // 4-byte SBI header
374         if (fread(buffer, 1, 4, sbihandle) != 4)
375                 goto fail_io;
376
377         while (1) {
378                 s = fread(sbitime, 1, 3, sbihandle);
379                 if (s != 3)
380                         goto fail_io;
381                 if (fread(&t, 1, sizeof(t), sbihandle) != sizeof(t))
382                         goto fail_io;
383                 switch (t) {
384                 default:
385                 case 1:
386                         s = 10;
387                         break;
388                 case 2:
389                 case 3:
390                         s = 3;
391                         break;
392                 }
393                 fseek(sbihandle, s, SEEK_CUR);
394
395                 s = MSF2SECT(btoi(sbitime[0]), btoi(sbitime[1]), btoi(sbitime[2]));
396                 if (s < sector_count)
397                         sbi_sectors[s >> 3] |= 1 << (s&7);
398                 else
399                         SysPrintf(_("SBI sector %d >= %d?\n"), s, sector_count);
400         }
401
402         fclose(sbihandle);
403         return 0;
404
405 fail_io:
406 #ifndef NDEBUG
407         SysPrintf(_("File IO error in <%s:%s>.\n"), __FILE__, __func__);
408 #endif
409         fclose(sbihandle);
410         return -1;
411 }
412
413 void UnloadSBI(void) {
414         if (sbi_sectors) {
415                 free(sbi_sectors);
416                 sbi_sectors = NULL;
417         }
418 }