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