try some overscan display option
[pcsx_rearmed.git] / libpcsxcore / ppf.c
CommitLineData
ef79bbde
P
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"
de74f599 24#include "misc.h"
ef79bbde
P
25#include "cdrom.h"
26
27typedef struct tagPPF_DATA {
28 s32 addr;
29 s32 pos;
30 s32 anz;
31 struct tagPPF_DATA *pNext;
32} PPF_DATA;
33
34typedef struct tagPPF_CACHE {
35 s32 addr;
36 struct tagPPF_DATA *pNext;
37} PPF_CACHE;
38
39static PPF_CACHE *ppfCache = NULL;
40static PPF_DATA *ppfHead = NULL, *ppfLast = NULL;
41static int iPPFNum = 0;
42
43// using a linked data list, and address array
44static 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));
cfa5a2af 62 if (pc == NULL) return;
ef79bbde
P
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
79void 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
95void 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
135static void AddToPPF(s32 ladr, s32 pos, s32 anz, unsigned char *ppfmem) {
136 if (ppfHead == NULL) {
137 ppfHead = (PPF_DATA *)malloc(sizeof(PPF_DATA) + anz);
cfa5a2af 138 if (ppfHead == NULL) return;
ef79bbde
P
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);
cfa5a2af 170 if (padd == NULL) return;
ef79bbde
P
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
e1fbc1a8 184void BuildPPFCache(const char *fname) {
ef79bbde
P
185 FILE *ppffile;
186 char buffer[12];
187 char method, undo = 0, blockcheck = 0;
188 int dizlen, dizyn;
189 unsigned char ppfmem[512];
7a8d521f 190 char szPPF[MAXPATHLEN * 2];
ef79bbde
P
191 int count, seekpos, pos;
192 u32 anz; // use 32-bit to avoid stupid overflows
193 s32 ladr, off, anx;
194
195 FreePPFCache();
196
4e44d6f6 197 if (CdromId[0] == '\0') return;
198
e1fbc1a8 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");
ef79bbde
P
218 if (ppffile == NULL) return;
219
220 memset(buffer, 0, 5);
7a8d521f 221 if (fread(buffer, 1, 3, ppffile) != 3)
222 goto fail_io;
ef79bbde
P
223
224 if (strcmp(buffer, "PPF") != 0) {
e1fbc1a8 225 SysPrintf(_("Invalid PPF patch: %s.\n"), fname);
ef79bbde
P
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);
7a8d521f 245 if (fread(buffer, 1, 4, ppffile) != 4)
246 goto fail_io;
ef79bbde
P
247
248 if (strcmp(".DIZ", buffer) != 0) {
249 dizyn = 0;
250 } else {
7a8d521f 251 if (fread(&dizlen, 1, 4, ppffile) != 4)
252 goto fail_io;
ef79bbde
P
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);
7a8d521f 278 if (fread(buffer, 1, 4, ppffile) != 4)
279 goto fail_io;
ef79bbde
P
280 dizlen = 0;
281
282 if (strcmp(".DIZ", buffer) == 0) {
283 fseek(ppffile, -2, SEEK_END);
7a8d521f 284 // TODO: Endian/size unsafe?
285 if (fread(&dizlen, 1, 2, ppffile) != 2)
286 goto fail_io;
ef79bbde
P
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);
7a8d521f 313 if (fread(&pos, 1, sizeof(pos), ppffile) != sizeof(pos))
314 goto fail_io;
ef79bbde
P
315 pos = SWAP32(pos);
316
7a8d521f 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 }
ef79bbde
P
322
323 anz = fgetc(ppffile);
7a8d521f 324 if (fread(ppfmem, 1, anz, ppffile) != anz)
325 goto fail_io;
ef79bbde
P
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
e1fbc1a8 351 SysPrintf(_("Loaded PPF %d.0 patch: %s.\n"), method + 1, fname);
352 return;
7a8d521f 353
354fail_io:
355#ifndef NDEBUG
356 SysPrintf(_("File IO error in <%s:%s>.\n"), __FILE__, __func__);
357#endif
358 fclose(ppffile);
ef79bbde 359}
ae4e7dc9 360
361// redump.org SBI files, slightly different handling from PCSX-Reloaded
362unsigned char *sbi_sectors;
f3746eea 363int sbi_len;
ae4e7dc9 364
365int LoadSBI(const char *fname, int sector_count) {
4904809d 366 int good_sectors = 0;
367 int clean_eof = 0;
ab948f7e 368 char buffer[16];
ae4e7dc9 369 FILE *sbihandle;
eaa895dc 370 u8 sbitime[3], t;
ae4e7dc9 371 int s;
372
373 sbihandle = fopen(fname, "rb");
374 if (sbihandle == NULL)
375 return -1;
376
f3746eea 377 sbi_len = (sector_count + 7) / 8;
378 sbi_sectors = calloc(1, sbi_len);
4904809d 379 if (sbi_sectors == NULL)
380 goto end;
ae4e7dc9 381
382 // 4-byte SBI header
7a8d521f 383 if (fread(buffer, 1, 4, sbihandle) != 4)
4904809d 384 goto end;
7a8d521f 385
eaa895dc 386 while (1) {
387 s = fread(sbitime, 1, 3, sbihandle);
388 if (s != 3)
4904809d 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
7a8d521f 403 if (fread(&t, 1, sizeof(t), sbihandle) != sizeof(t))
4904809d 404 break;
405 s = -1;
eaa895dc 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 }
4904809d 416 if (s < 0)
417 break;
418 if (fseek(sbihandle, s, SEEK_CUR))
419 break;
ae4e7dc9 420 }
421
4904809d 422end:
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;
f3746eea 428 sbi_len = 0;
4904809d 429 }
7a8d521f 430 fclose(sbihandle);
4904809d 431 return sbi_sectors ? 0 : -1;
ae4e7dc9 432}
433
434void UnloadSBI(void) {
435 if (sbi_sectors) {
436 free(sbi_sectors);
437 sbi_sectors = NULL;
f3746eea 438 sbi_len = 0;
ae4e7dc9 439 }
440}