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