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