25eb1d60edb9049ba29fb6e85694889bc1b38299
[pcsx_rearmed.git] / libpcsxcore / misc.c
1 /***************************************************************************
2  *   Copyright (C) 2007 Ryan Schultz, PCSX-df Team, PCSX team              *
3  *                                                                         *
4  *   This program is free software; you can redistribute it and/or modify  *
5  *   it under the terms of the GNU General Public License as published by  *
6  *   the Free Software Foundation; either version 2 of the License, or     *
7  *   (at your option) any later version.                                   *
8  *                                                                         *
9  *   This program is distributed in the hope that it will be useful,       *
10  *   but WITHOUT ANY WARRANTY; without even the implied warranty of        *
11  *   MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE.  See the         *
12  *   GNU General Public License for more details.                          *
13  *                                                                         *
14  *   You should have received a copy of the GNU General Public License     *
15  *   along with this program; if not, write to the                         *
16  *   Free Software Foundation, Inc.,                                       *
17  *   51 Franklin Street, Fifth Floor, Boston, MA 02111-1307 USA.           *
18  ***************************************************************************/
19
20 /*
21 * Miscellaneous functions, including savestates and CD-ROM loading.
22 */
23
24 #include <stddef.h>
25 #include <stdio.h>
26 #include <ctype.h>
27 #include <errno.h>
28 #include <assert.h>
29 #include "misc.h"
30 #include "cdrom.h"
31 #include "cdrom-async.h"
32 #include "mdec.h"
33 #include "gpu.h"
34 #include "ppf.h"
35 #include "psxbios.h"
36 #include "database.h"
37 #include <zlib.h>
38 #include "revision.h"
39
40 char CdromId[10] = "";
41 char CdromLabel[33] = "";
42 int  CdromFrontendId; // for frontend use
43
44 static u32 save_counter;
45
46 // PSX Executable types
47 #define PSX_EXE     1
48 #define CPE_EXE     2
49 #define COFF_EXE    3
50 #define INVALID_EXE 4
51
52 #define ISODCL(from, to) (to - from + 1)
53
54 struct iso_directory_record {
55         char length                     [ISODCL (1, 1)]; /* 711 */
56         char ext_attr_length            [ISODCL (2, 2)]; /* 711 */
57         char extent                     [ISODCL (3, 10)]; /* 733 */
58         char size                       [ISODCL (11, 18)]; /* 733 */
59         char date                       [ISODCL (19, 25)]; /* 7 by 711 */
60         char flags                      [ISODCL (26, 26)];
61         char file_unit_size             [ISODCL (27, 27)]; /* 711 */
62         char interleave                 [ISODCL (28, 28)]; /* 711 */
63         char volume_sequence_number     [ISODCL (29, 32)]; /* 723 */
64         unsigned char name_len          [ISODCL (33, 33)]; /* 711 */
65         char name                       [1];
66 };
67
68 static void mmssdd(const char *b, char *p)
69 {
70         const unsigned char *ub = (void *)b;
71         int block = (ub[3] << 24) | (ub[2] << 16) | (ub[1] << 8) | ub[0];
72         int m, s, d;
73
74         block += 150;
75         m = block / 4500;                       // minutes
76         block = block - m * 4500;       // minutes rest
77         s = block / 75;                         // seconds
78         d = block - s * 75;                     // seconds rest
79
80         p[0] = m;
81         p[1] = s;
82         p[2] = d;
83 }
84
85 #define incTime() \
86         time[2]++; \
87         if(time[2] == 75) { \
88                 time[2] = 0; \
89                 time[1]++; \
90                 if (time[1] == 60) { \
91                         time[1] = 0; \
92                         time[0]++; \
93                 } \
94         } \
95
96 #define READTRACK() \
97         if (cdra_readTrack(time)) return -1; \
98         buf = cdra_getBuffer(); \
99         if (buf == NULL) return -1; \
100         else CheckPPFCache((u8 *)buf, time[0], time[1], time[2]);
101
102 #define READDIR(_dir) \
103         READTRACK(); \
104         memcpy(_dir, buf + 12, 2048); \
105  \
106         incTime(); \
107         READTRACK(); \
108         memcpy(_dir + 2048, buf + 12, 2048);
109
110 int GetCdromFile(u8 *mdir, u8 *time, char *filename) {
111         struct iso_directory_record *dir;
112         int retval = -1;
113         u8 ddir[4096];
114         u8 *buf;
115         int i;
116
117         // only try to scan if a filename is given
118         if (filename == INVALID_PTR || !strlen(filename)) return -1;
119
120         i = 0;
121         while (i < 4096) {
122                 dir = (struct iso_directory_record*) &mdir[i];
123                 if (dir->length[0] == 0) {
124                         return -1;
125                 }
126                 i += (u8)dir->length[0];
127
128                 if (dir->flags[0] & 0x2) { // it's a dir
129                         if (!strnicmp((char *)&dir->name[0], filename, dir->name_len[0])) {
130                                 if (filename[dir->name_len[0]] != '\\') continue;
131
132                                 filename += dir->name_len[0] + 1;
133
134                                 mmssdd(dir->extent, (char *)time);
135                                 READDIR(ddir);
136                                 i = 0;
137                                 mdir = ddir;
138                         }
139                 } else {
140                         if (!strnicmp((char *)&dir->name[0], filename, strlen(filename))) {
141                                 mmssdd(dir->extent, (char *)time);
142                                 retval = 0;
143                                 break;
144                         }
145                 }
146         }
147         return retval;
148 }
149
150 static void SetBootRegs(u32 pc, u32 gp, u32 sp)
151 {
152         //printf("%s %08x %08x %08x\n", __func__, pc, gp, sp);
153         psxCpu->Notify(R3000ACPU_NOTIFY_BEFORE_SAVE, NULL);
154
155         psxRegs.pc = pc;
156         psxRegs.GPR.n.gp = gp;
157         psxRegs.GPR.n.sp = sp ? sp : 0x801fff00;
158         psxRegs.GPR.n.fp = psxRegs.GPR.n.sp;
159
160         psxRegs.GPR.n.t0 = psxRegs.GPR.n.sp; // mimic A(43)
161         psxRegs.GPR.n.t3 = pc;
162
163         psxCpu->Notify(R3000ACPU_NOTIFY_AFTER_LOAD, NULL);
164 }
165
166 int BiosBootBypass() {
167         struct CdrStat stat = { 0, 0, };
168         assert(psxRegs.pc == 0x80030000);
169
170         // no bypass if the lid is open
171         CDR__getStatus(&stat);
172         if (stat.Status & 0x10)
173                 return 0;
174
175         // skip BIOS logos and region check
176         psxCpu->Notify(R3000ACPU_NOTIFY_BEFORE_SAVE, NULL);
177         psxRegs.pc = psxRegs.GPR.n.ra;
178         return 1;
179 }
180
181 static void getFromCnf(char *buf, const char *key, u32 *val)
182 {
183         buf = strstr(buf, key);
184         if (buf)
185                 buf = strchr(buf, '=');
186         if (buf) {
187                 unsigned long v;
188                 errno = 0;
189                 v = strtoul(buf + 1, NULL, 16);
190                 if (errno == 0)
191                         *val = v;
192         }
193 }
194
195 int LoadCdrom() {
196         union {
197                 EXE_HEADER h;
198                 u32 d[sizeof(EXE_HEADER) / sizeof(u32)];
199         } tmpHead;
200         struct iso_directory_record *dir;
201         u8 time[4], *buf;
202         u8 mdir[4096];
203         char exename[256];
204         u32 cnf_tcb = 4;
205         u32 cnf_event = 16;
206         u32 cnf_stack = 0;
207         u32 t_addr;
208         u32 t_size;
209         u32 sp = 0;
210         int i, ret;
211
212         save_counter = 0;
213
214         if (!Config.HLE) {
215                 if (psxRegs.pc != 0x80030000) // BiosBootBypass'ed or custom BIOS?
216                         return 0;
217                 if (Config.SlowBoot)
218                         return 0;
219         }
220
221         time[0] = 0; time[1] = 2; time[2] = 0x10;
222
223         READTRACK();
224
225         // skip head and sub, and go to the root directory record
226         dir = (struct iso_directory_record*) &buf[12+156];
227
228         mmssdd(dir->extent, (char*)time);
229
230         READDIR(mdir);
231
232         // Load SYSTEM.CNF and scan for the main executable
233         if (GetCdromFile(mdir, time, "SYSTEM.CNF;1") == -1) {
234                 // if SYSTEM.CNF is missing, start an existing PSX.EXE
235                 if (GetCdromFile(mdir, time, "PSX.EXE;1") == -1) return -1;
236                 strcpy(exename, "PSX.EXE;1");
237
238                 READTRACK();
239         }
240         else {
241                 // read the SYSTEM.CNF
242                 READTRACK();
243                 buf[1023] = 0;
244
245                 ret = sscanf((char *)buf + 12, "BOOT = cdrom:\\%255s", exename);
246                 if (ret < 1 || GetCdromFile(mdir, time, exename) == -1) {
247                         ret = sscanf((char *)buf + 12, "BOOT = cdrom:%255s", exename);
248                         if (ret < 1 || GetCdromFile(mdir, time, exename) == -1) {
249                                 char *ptr = strstr((char *)buf + 12, "cdrom:");
250                                 if (ptr != NULL) {
251                                         ptr += 6;
252                                         while (*ptr == '\\' || *ptr == '/') ptr++;
253                                         strncpy(exename, ptr, 255);
254                                         exename[255] = '\0';
255                                         ptr = exename;
256                                         while (*ptr != '\0' && *ptr != '\r' && *ptr != '\n') ptr++;
257                                         *ptr = '\0';
258                                         if (GetCdromFile(mdir, time, exename) == -1)
259                                                 return -1;
260                                 } else
261                                         return -1;
262                         }
263                 }
264                 getFromCnf((char *)buf + 12, "TCB", &cnf_tcb);
265                 getFromCnf((char *)buf + 12, "EVENT", &cnf_event);
266                 getFromCnf((char *)buf + 12, "STACK", &cnf_stack);
267                 if (Config.HLE)
268                         psxBiosCnfLoaded(cnf_tcb, cnf_event, cnf_stack);
269
270                 // Read the EXE-Header
271                 READTRACK();
272         }
273
274         memcpy(&tmpHead, buf + 12, sizeof(EXE_HEADER));
275         for (i = 2; i < sizeof(tmpHead.d) / sizeof(tmpHead.d[0]); i++)
276                 tmpHead.d[i] = SWAP32(tmpHead.d[i]);
277
278         SysPrintf("manual booting '%s' pc=%x\n", exename, tmpHead.h.pc0);
279         sp = tmpHead.h.s_addr;
280         if (cnf_stack)
281                 sp = cnf_stack;
282         SetBootRegs(tmpHead.h.pc0, tmpHead.h.gp0, sp);
283
284         // Read the rest of the main executable
285         for (t_addr = tmpHead.h.t_addr, t_size = tmpHead.h.t_size; t_size & ~2047; ) {
286                 void *ptr = (void *)PSXM(t_addr);
287
288                 incTime();
289                 READTRACK();
290
291                 if (ptr != INVALID_PTR) memcpy(ptr, buf+12, 2048);
292
293                 t_addr += 2048;
294                 t_size -= 2048;
295         }
296
297         psxCpu->Clear(tmpHead.h.t_addr, tmpHead.h.t_size / 4);
298         //psxCpu->Reset();
299
300         if (Config.HLE)
301                 psxBiosCheckExe(tmpHead.h.t_addr, tmpHead.h.t_size, 0);
302
303         return 0;
304 }
305
306 int LoadCdromFile(const char *filename, EXE_HEADER *head, u8 *time_bcd_out) {
307         struct iso_directory_record *dir;
308         u8 time[4],*buf;
309         u8 mdir[4096];
310         char exename[256];
311         const char *p1, *p2;
312         u32 size, addr;
313         void *mem;
314
315         if (filename == INVALID_PTR)
316                 return -1;
317
318         p1 = filename;
319         if ((p2 = strchr(p1, ':')))
320                 p1 = p2 + 1;
321         while (*p1 == '\\')
322                 p1++;
323         snprintf(exename, sizeof(exename), "%s", p1);
324
325         time[0] = 0; time[1] = 2; time[2] = 0x10;
326
327         READTRACK();
328
329         // skip head and sub, and go to the root directory record
330         dir = (struct iso_directory_record *)&buf[12 + 156];
331
332         mmssdd(dir->extent, (char*)time);
333
334         READDIR(mdir);
335
336         if (GetCdromFile(mdir, time, exename) == -1) return -1;
337
338         READTRACK();
339         incTime();
340
341         memcpy(head, buf + 12, sizeof(EXE_HEADER));
342         size = SWAP32(head->t_size);
343         addr = SWAP32(head->t_addr);
344
345         psxCpu->Clear(addr, size / 4);
346         //psxCpu->Reset();
347
348         while (size & ~2047) {
349                 READTRACK();
350                 incTime();
351
352                 mem = PSXM(addr);
353                 if (mem != INVALID_PTR)
354                         memcpy(mem, buf + 12, 2048);
355
356                 size -= 2048;
357                 addr += 2048;
358         }
359         if (time_bcd_out) {
360                 time_bcd_out[0] = itob(time[0]);
361                 time_bcd_out[1] = itob(time[1]);
362                 time_bcd_out[2] = itob(time[2]);
363         }
364
365         return 0;
366 }
367
368 int CheckCdrom() {
369         struct iso_directory_record *dir;
370         struct CdrStat stat = { 0, 0, };
371         unsigned char time[4] = { 0, 2, 4 };
372         char *buf;
373         unsigned char mdir[4096];
374         char exename[256];
375         int psxtype_from_lic = -1;
376         struct { u8 region; const char *str; } lic_strings[] = {
377                 { PSX_REGION_JP, "Inc." },
378                 { PSX_REGION_US, "Amer  ica" },
379                 { PSX_REGION_EU, "Euro pe" }
380         };
381         size_t i, len, c;
382
383         FreePPFCache();
384         memset(CdromLabel, 0, sizeof(CdromLabel));
385         memset(CdromId, 0, sizeof(CdromId));
386         memset(exename, 0, sizeof(exename));
387         Config.PsxRegion = PSX_REGION_US;
388
389         if (!Config.HLE && Config.SlowBoot) {
390                 // boot to BIOS in case of CDDA or lid is open
391                 cdra_getStatus(&stat);
392                 if ((stat.Status & 0x10) || stat.Type == 2 || cdra_readTrack(time))
393                         return 0;
394         }
395         time[0] = 0;
396         time[1] = 2;
397         time[2] = 4;
398         READTRACK();
399         for (i = 0; i < sizeof(lic_strings) / sizeof(lic_strings[0]); i++) {
400                 len = strlen(lic_strings[i].str);
401                 if (strncmp((char *)buf + 12 + 60, lic_strings[i].str, len) == 0) {
402                         Config.PsxRegion = lic_strings[i].region;
403                         psxtype_from_lic = Config.PsxRegion == PSX_REGION_EU
404                                 ? PSX_TYPE_PAL : PSX_TYPE_NTSC;
405                         break;
406                 }
407         }
408         if (psxtype_from_lic < 0)
409                 SysPrintf("CheckCdrom: missing lic sector?\n");
410
411         time[0] = 0;
412         time[1] = 2;
413         time[2] = 0x10;
414         READTRACK();
415
416         strncpy(CdromLabel, buf + 52, 32);
417
418         // skip head and sub, and go to the root directory record
419         dir = (struct iso_directory_record *)&buf[12 + 156];
420
421         mmssdd(dir->extent, (char *)time);
422
423         READDIR(mdir);
424
425         if (GetCdromFile(mdir, time, "SYSTEM.CNF;1") != -1) {
426                 READTRACK();
427
428                 sscanf(buf + 12, "BOOT = cdrom:\\%255s", exename);
429                 if (GetCdromFile(mdir, time, exename) == -1) {
430                         sscanf(buf + 12, "BOOT = cdrom:%255s", exename);
431                         if (GetCdromFile(mdir, time, exename) == -1) {
432                                 char *ptr = strstr(buf + 12, "cdrom:");                 // possibly the executable is in some subdir
433                                 if (ptr != NULL) {
434                                         ptr += 6;
435                                         while (*ptr == '\\' || *ptr == '/') ptr++;
436                                         strncpy(exename, ptr, 255);
437                                         exename[255] = '\0';
438                                         ptr = exename;
439                                         while (*ptr != '\0' && *ptr != '\r' && *ptr != '\n') ptr++;
440                                         *ptr = '\0';
441                                         if (GetCdromFile(mdir, time, exename) == -1)
442                                                 return -1;              // main executable not found
443                                 } else
444                                         return -1;
445                         }
446                 }
447                 /* Workaround for Wild Arms EU/US which has non-standard string causing incorrect region detection */
448                 if (exename[0] == 'E' && exename[1] == 'X' && exename[2] == 'E' && exename[3] == '\\') {
449                         size_t offset = 4;
450                         len = strlen(exename) - offset;
451                         for (i = 0; i < len; i++)
452                                 exename[i] = exename[i + offset];
453                         exename[i] = '\0';
454                 }
455         } else if (GetCdromFile(mdir, time, "PSX.EXE;1") != -1) {
456                 strcpy(exename, "PSX.EXE;1");
457                 strcpy(CdromId, "SLUS99999");
458         } else
459                 return -1;              // SYSTEM.CNF and PSX.EXE not found
460
461         if (CdromId[0] == '\0') {
462                 len = strlen(exename);
463                 c = 0;
464                 for (i = 0; i < len; ++i) {
465                         if (exename[i] == ';' || c >= sizeof(CdromId) - 1)
466                                 break;
467                         if (isalnum((int)exename[i]))
468                                 CdromId[c++] = exename[i];
469                 }
470         }
471
472         if (CdromId[0] == '\0')
473                 strcpy(CdromId, "SLUS99999");
474
475         if (Config.PsxAuto) { // autodetect system (pal or ntsc)
476                 if (psxtype_from_lic >= 0)
477                         Config.PsxType = psxtype_from_lic;
478                 else if (
479                         /* Make sure Wild Arms SCUS-94608 is not detected as a PAL game. */
480                         ((CdromId[0] == 's' || CdromId[0] == 'S') && (CdromId[2] == 'e' || CdromId[2] == 'E')) ||
481                         !strncmp(CdromId, "DTLS3035", 8) ||
482                         !strncmp(CdromId, "PBPX95001", 9) || // according to redump.org, these PAL
483                         !strncmp(CdromId, "PBPX95007", 9) || // discs have a non-standard ID;
484                         !strncmp(CdromId, "PBPX95008", 9))   // add more serials if they are discovered.
485                         Config.PsxType = PSX_TYPE_PAL; // pal
486                 else Config.PsxType = PSX_TYPE_NTSC; // ntsc
487         }
488
489         if (CdromLabel[0] == ' ') {
490                 strncpy(CdromLabel, CdromId, 9);
491         }
492         SysPrintf(_("CD-ROM Label: %.32s\n"), CdromLabel);
493         SysPrintf(_("CD-ROM ID: %.9s\n"), CdromId);
494         SysPrintf(_("CD-ROM EXE Name: %.255s\n"), exename);
495         
496         Apply_Hacks_Cdrom();
497
498         BuildPPFCache(NULL);
499
500         return 0;
501 }
502
503 static int PSXGetFileType(FILE *f) {
504         unsigned long current;
505         u8 mybuf[2048];
506         EXE_HEADER *exe_hdr;
507         FILHDR *coff_hdr;
508
509         current = ftell(f);
510         fseek(f, 0L, SEEK_SET);
511         if (fread(&mybuf, 1, sizeof(mybuf), f) != sizeof(mybuf))
512                 goto io_fail;
513         
514         fseek(f, current, SEEK_SET);
515
516         exe_hdr = (EXE_HEADER *)mybuf;
517         if (memcmp(exe_hdr->id, "PS-X EXE", 8) == 0)
518                 return PSX_EXE;
519
520         if (mybuf[0] == 'C' && mybuf[1] == 'P' && mybuf[2] == 'E')
521                 return CPE_EXE;
522
523         coff_hdr = (FILHDR *)mybuf;
524         if (SWAPu16(coff_hdr->f_magic) == 0x0162)
525                 return COFF_EXE;
526
527         return INVALID_EXE;
528
529 io_fail:
530 #ifndef NDEBUG
531         SysPrintf(_("File IO error in <%s:%s>.\n"), __FILE__, __func__);
532 #endif
533         return INVALID_EXE;
534 }
535
536 // temporary pandora workaround..
537 // FIXME: remove
538 size_t fread_to_ram(void *ptr, size_t size, size_t nmemb, FILE *stream)
539 {
540         void *tmp;
541         size_t ret = 0;
542
543         tmp = malloc(size * nmemb);
544         if (tmp) {
545                 ret = fread(tmp, size, nmemb, stream);
546                 memcpy(ptr, tmp, size * nmemb);
547                 free(tmp);
548         }
549         else
550                 ret = fread(ptr, size, nmemb, stream);
551         return ret;
552 }
553
554 int Load(const char *ExePath) {
555         FILE *tmpFile;
556         EXE_HEADER tmpHead;
557         int type;
558         int retval = 0;
559         u8 opcode;
560         u32 section_address, section_size;
561         void *mem;
562
563         strcpy(CdromId, "SLUS99999");
564         strcpy(CdromLabel, "SLUS_999.99");
565
566         tmpFile = fopen(ExePath, "rb");
567         if (tmpFile == NULL) {
568                 SysPrintf(_("Error opening file: %s.\n"), ExePath);
569                 retval = -1;
570         } else {
571                 type = PSXGetFileType(tmpFile);
572                 switch (type) {
573                         case PSX_EXE:
574                                 if (fread(&tmpHead, 1, sizeof(EXE_HEADER), tmpFile) != sizeof(EXE_HEADER))
575                                         goto fail_io;
576                                 section_address = SWAP32(tmpHead.t_addr);
577                                 section_size = SWAP32(tmpHead.t_size);
578                                 mem = PSXM(section_address);
579                                 if (mem != INVALID_PTR) {
580                                         fseek(tmpFile, 0x800, SEEK_SET);
581                                         fread_to_ram(mem, section_size, 1, tmpFile);
582                                         psxCpu->Clear(section_address, section_size / 4);
583                                 }
584                                 SetBootRegs(SWAP32(tmpHead.pc0), SWAP32(tmpHead.gp0),
585                                         SWAP32(tmpHead.s_addr));
586                                 retval = 0;
587                                 break;
588                         case CPE_EXE:
589                                 fseek(tmpFile, 6, SEEK_SET); /* Something tells me we should go to 4 and read the "08 00" here... */
590                                 do {
591                                         if (fread(&opcode, 1, sizeof(opcode), tmpFile) != sizeof(opcode))
592                                                 goto fail_io;
593                                         switch (opcode) {
594                                                 case 1: /* Section loading */
595                                                         if (fread(&section_address, 1, sizeof(section_address), tmpFile) != sizeof(section_address))
596                                                                 goto fail_io;
597                                                         if (fread(&section_size, 1, sizeof(section_size), tmpFile) != sizeof(section_size))
598                                                                 goto fail_io;
599                                                         section_address = SWAPu32(section_address);
600                                                         section_size = SWAPu32(section_size);
601                                                         //printf("Loading %08X bytes from %08X to %08X\n", section_size, ftell(tmpFile), section_address);
602                                                         mem = PSXM(section_address);
603                                                         if (mem != INVALID_PTR) {
604                                                                 fread_to_ram(mem, section_size, 1, tmpFile);
605                                                                 psxCpu->Clear(section_address, section_size / 4);
606                                                         }
607                                                         break;
608                                                 case 3: /* register loading (PC only?) */
609                                                         fseek(tmpFile, 2, SEEK_CUR); /* unknown field */
610                                                         if (fread(&psxRegs.pc, 1, sizeof(psxRegs.pc), tmpFile) != sizeof(psxRegs.pc))
611                                                                 goto fail_io;
612                                                         psxRegs.pc = SWAPu32(psxRegs.pc);
613                                                         break;
614                                                 case 0: /* End of file */
615                                                         break;
616                                                 default:
617                                                         SysPrintf(_("Unknown CPE opcode %02x at position %08lx.\n"), opcode, ftell(tmpFile) - 1);
618                                                         retval = -1;
619                                                         break;
620                                         }
621                                 } while (opcode != 0 && retval == 0);
622                                 break;
623                         case COFF_EXE:
624                                 SysPrintf(_("COFF files not supported.\n"));
625                                 retval = -1;
626                                 break;
627                         case INVALID_EXE:
628                                 SysPrintf(_("This file does not appear to be a valid PSX EXE file.\n"));
629                                 SysPrintf(_("(did you forget -cdfile ?)\n"));
630                                 retval = -1;
631                                 break;
632                 }
633         }
634
635         if (retval != 0) {
636                 CdromId[0] = '\0';
637                 CdromLabel[0] = '\0';
638         }
639
640         if (tmpFile)
641                 fclose(tmpFile);
642         return retval;
643
644 fail_io:
645 #ifndef NDEBUG
646         SysPrintf(_("File IO error in <%s:%s>.\n"), __FILE__, __func__);
647 #endif
648         fclose(tmpFile);
649         return -1;
650 }
651
652 // STATES
653
654 static void *zlib_open(const char *name, const char *mode)
655 {
656         return gzopen(name, mode);
657 }
658
659 static int zlib_read(void *file, void *buf, u32 len)
660 {
661         return gzread(file, buf, len);
662 }
663
664 static int zlib_write(void *file, const void *buf, u32 len)
665 {
666         return gzwrite(file, buf, len);
667 }
668
669 static long zlib_seek(void *file, long offs, int whence)
670 {
671         return gzseek(file, offs, whence);
672 }
673
674 static void zlib_close(void *file)
675 {
676         gzclose(file);
677 }
678
679 struct PcsxSaveFuncs SaveFuncs = {
680         zlib_open, zlib_read, zlib_write, zlib_seek, zlib_close
681 };
682
683 static const char PcsxHeader[32] = "STv4 PCSXra " REV;
684
685 // Savestate Versioning!
686 // If you make changes to the savestate version, please increment the value below.
687 static const u32 SaveVersion = 0x8b410006;
688
689 struct origin_info {
690         boolean icache_emulation;
691         boolean DisableStalls;
692         boolean PreciseExceptions;
693         boolean TurboCD;
694         s8 GpuListWalking;
695         s8 FractionalFramerate;
696         u8 Cpu;
697         u8 PsxType;
698         char build_info[64];
699 };
700
701 #define MISC_MAGIC 0x4353494d
702 struct misc_save_data {
703         u32 magic;
704         u32 gteBusyCycle;
705         u32 muldivBusyCycle;
706         u32 biuReg;
707         u32 biosBranchCheck;
708         u32 gpuIdleAfter;
709         u32 gpuSr;
710         u32 frame_counter;
711         int CdromFrontendId;
712         u32 save_counter;
713 };
714
715 #define EX_SCREENPIC_SIZE (128 * 96 * 3)
716
717 int SaveState(const char *file) {
718         struct misc_save_data *misc = (void *)(psxRegs.ptrs.psxH + 0xf000);
719         struct origin_info oi = { 0, };
720         union {
721                 // save stack space
722                 u8 buf[EX_SCREENPIC_SIZE];
723                 GPUFreeze_t gpu_hdr;
724                 struct {
725                         SPUFreeze_t spu_hdr;
726                         u8 spu_part2[SPUFREEZE_F2_MAX_SIZE];
727                 };
728         } u;
729         unsigned short *spuram = NULL;
730         uint16_t *vram = NULL;
731         void *f;
732
733         assert(!psxRegs.branching);
734         assert(!psxRegs.cpuInRecursion);
735         assert(!misc->magic);
736
737         f = SaveFuncs.open(file, "wb");
738         if (f == NULL) return -1;
739
740         misc->magic = MISC_MAGIC;
741         misc->gteBusyCycle = psxRegs.gteBusyCycle;
742         misc->muldivBusyCycle = psxRegs.muldivBusyCycle;
743         misc->biuReg = psxRegs.biuReg;
744         misc->biosBranchCheck = psxRegs.biosBranchCheck;
745         misc->gpuIdleAfter = psxRegs.gpuIdleAfter;
746         misc->gpuSr = HW_GPU_STATUS;
747         misc->frame_counter = frame_counter;
748         misc->CdromFrontendId = CdromFrontendId;
749         misc->save_counter = ++save_counter;
750
751         psxCpu->Notify(R3000ACPU_NOTIFY_BEFORE_SAVE, NULL);
752
753         SaveFuncs.write(f, (void *)PcsxHeader, 32);
754         SaveFuncs.write(f, (void *)&SaveVersion, sizeof(u32));
755         SaveFuncs.write(f, (void *)&Config.HLE, sizeof(boolean));
756
757         oi.icache_emulation = Config.icache_emulation;
758         oi.DisableStalls = Config.DisableStalls;
759         oi.PreciseExceptions = Config.PreciseExceptions;
760         oi.TurboCD = Config.TurboCD;
761         oi.GpuListWalking = Config.GpuListWalking;
762         oi.FractionalFramerate = Config.FractionalFramerate;
763         oi.Cpu = Config.Cpu;
764         oi.PsxType = Config.PsxType;
765         snprintf(oi.build_info, sizeof(oi.build_info), "%s", get_build_info());
766
767         // this was space for ScreenPic
768         assert(sizeof(oi) - 3 <= EX_SCREENPIC_SIZE);
769         memset(u.buf, 0, sizeof(u.buf));
770         memcpy(u.buf + 3, &oi, sizeof(oi));
771         SaveFuncs.write(f, u.buf, sizeof(u.buf));
772
773         if (Config.HLE)
774                 psxBiosFreeze(1);
775
776         SaveFuncs.write(f, psxRegs.ptrs.psxM, 0x00200000);
777         SaveFuncs.write(f, psxRegs.ptrs.psxR, 0x00080000);
778         SaveFuncs.write(f, psxRegs.ptrs.psxH, 0x00010000);
779         // only partial save of psxRegisters to maintain savestate compat
780         SaveFuncs.write(f, &psxRegs, offsetof(psxRegisters, gteBusyCycle));
781
782         // gpu
783         u.gpu_hdr.ulFreezeVersion = 1;
784         u.gpu_hdr.ulStatus = 0;
785         memset(u.gpu_hdr.ulControl, 0, sizeof(u.gpu_hdr.ulControl));
786         GPU_freeze(1, &u.gpu_hdr, &vram);
787         SaveFuncs.write(f, &u.gpu_hdr, sizeof(u.gpu_hdr));
788         SaveFuncs.write(f, vram, 1024*512*2);
789
790         // spu
791         SPU_freeze(1, &u.spu_hdr, &spuram, u.spu_part2, psxRegs.cycle);
792         assert(u.spu_hdr.Size > sizeof(u.spu_hdr) + 512*1024);
793         assert(u.spu_hdr.Size <= sizeof(u.spu_hdr) + 512*1024 + sizeof(u.spu_part2));
794         assert(spuram);
795         SaveFuncs.write(f, &u.spu_hdr.Size, 4); // redundant, for compat
796         SaveFuncs.write(f, &u.spu_hdr, sizeof(u.spu_hdr));
797         SaveFuncs.write(f, spuram, 512*1024);
798         SaveFuncs.write(f, &u.spu_part2, u.spu_hdr.Size - sizeof(u.spu_hdr) - 512*1024);
799
800         sioFreeze(f, 1);
801         cdrFreeze(f, 1);
802         psxHwFreeze(f, 1);
803         psxRcntFreeze(f, 1);
804         mdecFreeze(f, 1);
805         ndrc_freeze(f, 1);
806         padFreeze(f, 1);
807
808         memset(misc, 0, sizeof(*misc));
809         SaveFuncs.close(f);
810         return 0;
811 }
812
813 int LoadState(const char *file) {
814         struct misc_save_data *misc = (void *)(psxRegs.ptrs.psxH + 0xf000);
815         u32 biosBranchCheckOld = psxRegs.biosBranchCheck;
816         union {
817                 // save stack space
818                 GPUFreeze_t gpu_hdr;
819                 struct {
820                         SPUFreeze_t spu_hdr;
821                         u8 spu_part2[SPUFREEZE_F2_MAX_SIZE];
822                 };
823         } u;
824         unsigned short *spuram = NULL;
825         uint16_t *vram = NULL;
826         boolean hle, oldhle;
827         int Size;
828         char header[32];
829         u32 version;
830         int result = -1;
831         void *f;
832
833         f = SaveFuncs.open(file, "rb");
834         if (f == NULL) return -1;
835
836         if (!file)
837                 file = "(stream)";
838         memset(header, 0, sizeof(header));
839         SaveFuncs.read(f, header, 16);
840         if (strncmp("RASTATE", header, 7) == 0) {
841                 // looks like RA header, normal savestate should follow
842                 SysPrintf("%s: trying to skip RASTATE header\n", file);
843                 SaveFuncs.read(f, header, 16);
844         }
845         SaveFuncs.read(f, header + 16, 16);
846         SaveFuncs.read(f, &version, sizeof(u32));
847         SaveFuncs.read(f, &hle, sizeof(boolean));
848
849         if (strncmp("STv4 PCSX", header, 9) != 0) {
850                 SysPrintf("%s: is not a savestate?\n", file);
851                 goto cleanup;
852         }
853         if (version != SaveVersion) {
854                 SysPrintf("%s: incompatible savestate version %x\n", file, version);
855                 goto cleanup;
856         }
857         oldhle = Config.HLE;
858         Config.HLE = hle;
859
860         if (Config.HLE)
861                 psxBiosInit();
862         else if (oldhle)
863                 psxBiosResetTables();
864
865         // ex-ScreenPic space
866         SaveFuncs.seek(f, EX_SCREENPIC_SIZE, SEEK_CUR);
867
868         SaveFuncs.read(f, psxRegs.ptrs.psxM, 0x00200000);
869         SaveFuncs.read(f, psxRegs.ptrs.psxR, 0x00080000);
870         SaveFuncs.read(f, psxRegs.ptrs.psxH, 0x00010000);
871         SaveFuncs.read(f, &psxRegs, offsetof(psxRegisters, gteBusyCycle));
872         psxRegs.gteBusyCycle = psxRegs.cycle;
873         psxRegs.branching = 0;
874         psxRegs.biosBranchCheck = ~0;
875         psxRegs.cpuInRecursion = 0;
876         psxRegs.gpuIdleAfter = psxRegs.cycle - 1;
877         HW_GPU_STATUS &= SWAP32(~PSXGPU_nBUSY);
878         if (misc->magic == MISC_MAGIC) {
879                 psxRegs.gteBusyCycle = misc->gteBusyCycle;
880                 psxRegs.muldivBusyCycle = misc->muldivBusyCycle;
881                 psxRegs.biuReg = misc->biuReg;
882                 psxRegs.biosBranchCheck = misc->biosBranchCheck;
883                 psxRegs.gpuIdleAfter = misc->gpuIdleAfter;
884                 HW_GPU_STATUS = misc->gpuSr;
885                 frame_counter = misc->frame_counter;
886                 CdromFrontendId = misc->CdromFrontendId;
887                 if (misc->save_counter)
888                         save_counter = misc->save_counter;
889         }
890
891         if (Config.HLE)
892                 psxBiosFreeze(0);
893
894         // gpu
895         SaveFuncs.read(f, &u.gpu_hdr, sizeof(u.gpu_hdr));
896         GPU_freeze(0, &u.gpu_hdr, &vram);
897         assert(vram);
898         SaveFuncs.read(f, vram, 1024*512*2);
899         gpuSyncPluginSR();
900
901         // spu
902         SaveFuncs.read(f, &Size, 4);
903         if (sizeof(u.spu_hdr) + 512*1024 < Size &&
904             (uint32_t)Size <= sizeof(u.spu_hdr) + 512*1024u + sizeof(u.spu_part2))
905         {
906                 SPU_freeze(0, NULL, &spuram, NULL, psxRegs.cycle);
907                 assert(spuram);
908                 SaveFuncs.read(f, &u.spu_hdr, sizeof(u.spu_hdr));
909                 SaveFuncs.read(f, spuram, 512*1024);
910                 SaveFuncs.read(f, &u.spu_part2, Size - sizeof(u.spu_hdr) - 512*1024);
911                 SPU_freeze(0, &u.spu_hdr, &spuram, u.spu_part2, psxRegs.cycle);
912         }
913         else
914         {
915                 SysPrintf("broken spu save size %d, attempting to skip\n", Size);
916                 SaveFuncs.seek(f, Size, SEEK_CUR);
917         }
918
919         sioFreeze(f, 0);
920         cdrFreeze(f, 0);
921         psxHwFreeze(f, 0);
922         psxRcntFreeze(f, 0);
923         mdecFreeze(f, 0);
924
925         if (Config.HLE != oldhle) {
926                 // at least ari64 drc compiles differently so hard reset
927                 psxCpu->Shutdown();
928                 psxCpu->Init();
929         }
930         ndrc_freeze(f, 0);
931         padFreeze(f, 0);
932
933         events_restore();
934         if (Config.HLE)
935                 psxBiosCheckExe(biosBranchCheckOld, 0x60, 1);
936
937         psxCpu->Notify(R3000ACPU_NOTIFY_AFTER_LOAD_STATE, NULL);
938
939         result = 0;
940 cleanup:
941         memset(misc, 0, sizeof(*misc));
942         SaveFuncs.close(f);
943         return result;
944 }
945
946 int CheckState(const char *file) {
947         void *f;
948         char header[32];
949         u32 version;
950         boolean hle;
951
952         f = SaveFuncs.open(file, "rb");
953         if (f == NULL) return -1;
954
955         memset(header, 0, sizeof(header));
956         SaveFuncs.read(f, header, 16);
957         if (strncmp("RASTATE", header, 7) == 0)
958                 SaveFuncs.read(f, header, 16);
959         SaveFuncs.read(f, header + 16, 16);
960         SaveFuncs.read(f, &version, sizeof(u32));
961         SaveFuncs.read(f, &hle, sizeof(boolean));
962
963         SaveFuncs.close(f);
964
965         if (strncmp("STv4 PCSX", header, 9) != 0 || version != SaveVersion)
966                 return -1;
967
968         return 0;
969 }
970
971 // remove the leading and trailing spaces in a string
972 void trim(char *str) {
973         int pos = 0;
974         char *dest = str;
975
976         // skip leading blanks
977         while (str[pos] <= ' ' && str[pos] > 0)
978                 pos++;
979
980         while (str[pos]) {
981                 *(dest++) = str[pos];
982                 pos++;
983         }
984
985         *(dest--) = '\0'; // store the null
986
987         // remove trailing blanks
988         while (dest >= str && *dest <= ' ' && *dest > 0)
989                 *(dest--) = '\0';
990 }
991
992 // lookup table for crc calculation
993 static unsigned short crctab[256] = {
994         0x0000, 0x1021, 0x2042, 0x3063, 0x4084, 0x50A5, 0x60C6, 0x70E7, 0x8108,
995         0x9129, 0xA14A, 0xB16B, 0xC18C, 0xD1AD, 0xE1CE, 0xF1EF, 0x1231, 0x0210,
996         0x3273, 0x2252, 0x52B5, 0x4294, 0x72F7, 0x62D6, 0x9339, 0x8318, 0xB37B,
997         0xA35A, 0xD3BD, 0xC39C, 0xF3FF, 0xE3DE, 0x2462, 0x3443, 0x0420, 0x1401,
998         0x64E6, 0x74C7, 0x44A4, 0x5485, 0xA56A, 0xB54B, 0x8528, 0x9509, 0xE5EE,
999         0xF5CF, 0xC5AC, 0xD58D, 0x3653, 0x2672, 0x1611, 0x0630, 0x76D7, 0x66F6,
1000         0x5695, 0x46B4, 0xB75B, 0xA77A, 0x9719, 0x8738, 0xF7DF, 0xE7FE, 0xD79D,
1001         0xC7BC, 0x48C4, 0x58E5, 0x6886, 0x78A7, 0x0840, 0x1861, 0x2802, 0x3823,
1002         0xC9CC, 0xD9ED, 0xE98E, 0xF9AF, 0x8948, 0x9969, 0xA90A, 0xB92B, 0x5AF5,
1003         0x4AD4, 0x7AB7, 0x6A96, 0x1A71, 0x0A50, 0x3A33, 0x2A12, 0xDBFD, 0xCBDC,
1004         0xFBBF, 0xEB9E, 0x9B79, 0x8B58, 0xBB3B, 0xAB1A, 0x6CA6, 0x7C87, 0x4CE4,
1005         0x5CC5, 0x2C22, 0x3C03, 0x0C60, 0x1C41, 0xEDAE, 0xFD8F, 0xCDEC, 0xDDCD,
1006         0xAD2A, 0xBD0B, 0x8D68, 0x9D49, 0x7E97, 0x6EB6, 0x5ED5, 0x4EF4, 0x3E13,
1007         0x2E32, 0x1E51, 0x0E70, 0xFF9F, 0xEFBE, 0xDFDD, 0xCFFC, 0xBF1B, 0xAF3A,
1008         0x9F59, 0x8F78, 0x9188, 0x81A9, 0xB1CA, 0xA1EB, 0xD10C, 0xC12D, 0xF14E,
1009         0xE16F, 0x1080, 0x00A1, 0x30C2, 0x20E3, 0x5004, 0x4025, 0x7046, 0x6067,
1010         0x83B9, 0x9398, 0xA3FB, 0xB3DA, 0xC33D, 0xD31C, 0xE37F, 0xF35E, 0x02B1,
1011         0x1290, 0x22F3, 0x32D2, 0x4235, 0x5214, 0x6277, 0x7256, 0xB5EA, 0xA5CB,
1012         0x95A8, 0x8589, 0xF56E, 0xE54F, 0xD52C, 0xC50D, 0x34E2, 0x24C3, 0x14A0,
1013         0x0481, 0x7466, 0x6447, 0x5424, 0x4405, 0xA7DB, 0xB7FA, 0x8799, 0x97B8,
1014         0xE75F, 0xF77E, 0xC71D, 0xD73C, 0x26D3, 0x36F2, 0x0691, 0x16B0, 0x6657,
1015         0x7676, 0x4615, 0x5634, 0xD94C, 0xC96D, 0xF90E, 0xE92F, 0x99C8, 0x89E9,
1016         0xB98A, 0xA9AB, 0x5844, 0x4865, 0x7806, 0x6827, 0x18C0, 0x08E1, 0x3882,
1017         0x28A3, 0xCB7D, 0xDB5C, 0xEB3F, 0xFB1E, 0x8BF9, 0x9BD8, 0xABBB, 0xBB9A,
1018         0x4A75, 0x5A54, 0x6A37, 0x7A16, 0x0AF1, 0x1AD0, 0x2AB3, 0x3A92, 0xFD2E,
1019         0xED0F, 0xDD6C, 0xCD4D, 0xBDAA, 0xAD8B, 0x9DE8, 0x8DC9, 0x7C26, 0x6C07,
1020         0x5C64, 0x4C45, 0x3CA2, 0x2C83, 0x1CE0, 0x0CC1, 0xEF1F, 0xFF3E, 0xCF5D,
1021         0xDF7C, 0xAF9B, 0xBFBA, 0x8FD9, 0x9FF8, 0x6E17, 0x7E36, 0x4E55, 0x5E74,
1022         0x2E93, 0x3EB2, 0x0ED1, 0x1EF0
1023 };
1024
1025 u16 calcCrc(const u8 *d, int len) {
1026         u16 crc = 0;
1027         int i;
1028
1029         for (i = 0; i < len; i++) {
1030                 crc = crctab[(crc >> 8) ^ d[i]] ^ (crc << 8);
1031         }
1032
1033         return ~crc;
1034 }
1035
1036 #define MKSTR2(x) #x
1037 #define MKSTR(x) MKSTR2(x)
1038 const char *get_build_info(void)
1039 {
1040         return ""
1041 #ifdef __VERSION__
1042                 "cc " __VERSION__ " "
1043 #endif
1044 #if defined(__SIZEOF_POINTER__) && __SIZEOF_POINTER__ == 8
1045                 "64bit "
1046 #elif defined(__SIZEOF_POINTER__) && __SIZEOF_POINTER__ == 4
1047                 "32bit "
1048 #endif
1049 #if defined(__BYTE_ORDER__) && __BYTE_ORDER__ == __ORDER_BIG_ENDIAN__
1050                 "be "
1051 #endif
1052 #if defined(__PIE__) || defined(__pie__)
1053                 "pie "
1054 #endif
1055 #if defined(__PIC__) || defined(__pic__)
1056                 "pic "
1057 #endif
1058 #if defined(__aarch64__)
1059                 "arm64"
1060 #elif defined(__arm__)
1061                 "arm"
1062 #endif
1063 #ifdef __ARM_ARCH
1064                 "v" MKSTR(__ARM_ARCH) " "
1065 #endif
1066 #ifdef __thumb__
1067                 "thumb "
1068 #endif
1069 #if defined(__AVX__)
1070                 "avx "
1071 #elif defined(__SSSE3__)
1072                 "ssse3 "
1073 #elif defined(__ARM_NEON) || defined(__ARM_NEON__)
1074                 "neon "
1075 #endif
1076 #if defined(__ARM_FEATURE_SVE) && __ARM_FEATURE_SVE
1077                 "sve "
1078 #endif
1079 #if defined(LIGHTREC)
1080                 "lightrec "
1081 #elif !defined(DRC_DISABLE)
1082                 "ari64 "
1083 #endif
1084                 "gpu=" MKSTR(BUILTIN_GPU);
1085 }