psxbios: assorted changes
[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"
24#include "cdrom.h"
25
26typedef struct tagPPF_DATA {
27 s32 addr;
28 s32 pos;
29 s32 anz;
30 struct tagPPF_DATA *pNext;
31} PPF_DATA;
32
33typedef struct tagPPF_CACHE {
34 s32 addr;
35 struct tagPPF_DATA *pNext;
36} PPF_CACHE;
37
38static PPF_CACHE *ppfCache = NULL;
39static PPF_DATA *ppfHead = NULL, *ppfLast = NULL;
40static int iPPFNum = 0;
41
42// using a linked data list, and address array
43static 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
77void 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
93void 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
133static 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
180void BuildPPFCache() {
181 FILE *ppffile;
182 char buffer[12];
183 char method, undo = 0, blockcheck = 0;
184 int dizlen, dizyn;
185 unsigned char ppfmem[512];
7a8d521f 186 char szPPF[MAXPATHLEN * 2];
ef79bbde
P
187 int count, seekpos, pos;
188 u32 anz; // use 32-bit to avoid stupid overflows
189 s32 ladr, off, anx;
190
191 FreePPFCache();
192
4e44d6f6 193 if (CdromId[0] == '\0') return;
194
ef79bbde
P
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);
7a8d521f 215 if (fread(buffer, 1, 3, ppffile) != 3)
216 goto fail_io;
ef79bbde
P
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);
7a8d521f 239 if (fread(buffer, 1, 4, ppffile) != 4)
240 goto fail_io;
ef79bbde
P
241
242 if (strcmp(".DIZ", buffer) != 0) {
243 dizyn = 0;
244 } else {
7a8d521f 245 if (fread(&dizlen, 1, 4, ppffile) != 4)
246 goto fail_io;
ef79bbde
P
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);
7a8d521f 272 if (fread(buffer, 1, 4, ppffile) != 4)
273 goto fail_io;
ef79bbde
P
274 dizlen = 0;
275
276 if (strcmp(".DIZ", buffer) == 0) {
277 fseek(ppffile, -2, SEEK_END);
7a8d521f 278 // TODO: Endian/size unsafe?
279 if (fread(&dizlen, 1, 2, ppffile) != 2)
280 goto fail_io;
ef79bbde
P
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);
7a8d521f 307 if (fread(&pos, 1, sizeof(pos), ppffile) != sizeof(pos))
308 goto fail_io;
ef79bbde
P
309 pos = SWAP32(pos);
310
7a8d521f 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 }
ef79bbde
P
316
317 anz = fgetc(ppffile);
7a8d521f 318 if (fread(ppfmem, 1, anz, ppffile) != anz)
319 goto fail_io;
ef79bbde
P
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);
7a8d521f 346
347fail_io:
348#ifndef NDEBUG
349 SysPrintf(_("File IO error in <%s:%s>.\n"), __FILE__, __func__);
350#endif
351 fclose(ppffile);
ef79bbde 352}
ae4e7dc9 353
354// redump.org SBI files, slightly different handling from PCSX-Reloaded
355unsigned char *sbi_sectors;
356
357int LoadSBI(const char *fname, int sector_count) {
4904809d 358 int good_sectors = 0;
359 int clean_eof = 0;
ab948f7e 360 char buffer[16];
ae4e7dc9 361 FILE *sbihandle;
eaa895dc 362 u8 sbitime[3], t;
ae4e7dc9 363 int s;
364
365 sbihandle = fopen(fname, "rb");
366 if (sbihandle == NULL)
367 return -1;
368
ae4e7dc9 369 sbi_sectors = calloc(1, sector_count / 8);
4904809d 370 if (sbi_sectors == NULL)
371 goto end;
ae4e7dc9 372
373 // 4-byte SBI header
7a8d521f 374 if (fread(buffer, 1, 4, sbihandle) != 4)
4904809d 375 goto end;
7a8d521f 376
eaa895dc 377 while (1) {
378 s = fread(sbitime, 1, 3, sbihandle);
379 if (s != 3)
4904809d 380 {
381 if (s == 0)
382 clean_eof = 1;
383 break;
384 }
385 s = MSF2SECT(btoi(sbitime[0]), btoi(sbitime[1]), btoi(sbitime[2]));
386 if (s < sector_count) {
387 sbi_sectors[s >> 3] |= 1 << (s&7);
388 good_sectors++;
389 }
390 else
391 SysPrintf(_("SBI sector %d >= %d?\n"), s, sector_count);
392
393 // skip to the next record
7a8d521f 394 if (fread(&t, 1, sizeof(t), sbihandle) != sizeof(t))
4904809d 395 break;
396 s = -1;
eaa895dc 397 switch (t) {
398 default:
399 case 1:
400 s = 10;
401 break;
402 case 2:
403 case 3:
404 s = 3;
405 break;
406 }
4904809d 407 if (s < 0)
408 break;
409 if (fseek(sbihandle, s, SEEK_CUR))
410 break;
ae4e7dc9 411 }
412
413 fclose(sbihandle);
ae4e7dc9 414 return 0;
7a8d521f 415
4904809d 416end:
417 if (!clean_eof)
418 SysPrintf(_("SBI: parse failure at 0x%lx\n"), ftell(sbihandle));
419 if (!good_sectors) {
420 free(sbi_sectors);
421 sbi_sectors = NULL;
422 }
7a8d521f 423 fclose(sbihandle);
4904809d 424 return sbi_sectors ? 0 : -1;
ae4e7dc9 425}
426
427void UnloadSBI(void) {
428 if (sbi_sectors) {
429 free(sbi_sectors);
430 sbi_sectors = NULL;
431 }
432}