misc: allow slow-booting to cdda or whatever
[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 "mdec.h"
30 #include "gpu.h"
31 #include "ppf.h"
32 #include "psxbios.h"
33 #include "database.h"
34 #include <zlib.h>
35
36 char CdromId[10] = "";
37 char CdromLabel[33] = "";
38 int  CdromFrontendId; // for frontend use
39
40 // PSX Executable types
41 #define PSX_EXE     1
42 #define CPE_EXE     2
43 #define COFF_EXE    3
44 #define INVALID_EXE 4
45
46 #define ISODCL(from, to) (to - from + 1)
47
48 struct iso_directory_record {
49         char length                     [ISODCL (1, 1)]; /* 711 */
50         char ext_attr_length            [ISODCL (2, 2)]; /* 711 */
51         char extent                     [ISODCL (3, 10)]; /* 733 */
52         char size                       [ISODCL (11, 18)]; /* 733 */
53         char date                       [ISODCL (19, 25)]; /* 7 by 711 */
54         char flags                      [ISODCL (26, 26)];
55         char file_unit_size             [ISODCL (27, 27)]; /* 711 */
56         char interleave                 [ISODCL (28, 28)]; /* 711 */
57         char volume_sequence_number     [ISODCL (29, 32)]; /* 723 */
58         unsigned char name_len          [ISODCL (33, 33)]; /* 711 */
59         char name                       [1];
60 };
61
62 static void mmssdd( char *b, char *p )
63 {
64         int m, s, d;
65         unsigned char *ub = (void *)b;
66         int block = (ub[3] << 24) | (ub[2] << 16) | (ub[1] << 8) | ub[0];
67
68         block += 150;
69         m = block / 4500;                       // minutes
70         block = block - m * 4500;       // minutes rest
71         s = block / 75;                         // seconds
72         d = block - s * 75;                     // seconds rest
73
74         m = ((m / 10) << 4) | m % 10;
75         s = ((s / 10) << 4) | s % 10;
76         d = ((d / 10) << 4) | d % 10;
77
78         p[0] = m;
79         p[1] = s;
80         p[2] = d;
81 }
82
83 #define incTime() \
84         time[0] = btoi(time[0]); time[1] = btoi(time[1]); time[2] = btoi(time[2]); \
85         time[2]++; \
86         if(time[2] == 75) { \
87                 time[2] = 0; \
88                 time[1]++; \
89                 if (time[1] == 60) { \
90                         time[1] = 0; \
91                         time[0]++; \
92                 } \
93         } \
94         time[0] = itob(time[0]); time[1] = itob(time[1]); time[2] = itob(time[2]);
95
96 #define READTRACK() \
97         if (!CDR_readTrack(time)) return -1; \
98         buf = (void *)CDR_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         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] = itob(0); time[1] = itob(2); time[2] = itob(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] = itob(0); time[1] = itob(2); time[2] = itob(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                 memcpy(time_bcd_out, time, 3);
359
360         return 0;
361 }
362
363 int CheckCdrom() {
364         struct iso_directory_record *dir;
365         struct CdrStat stat = { 0, 0, };
366         unsigned char time[4];
367         char *buf;
368         unsigned char mdir[4096];
369         char exename[256];
370         int i, len, c;
371
372         FreePPFCache();
373         memset(CdromLabel, 0, sizeof(CdromLabel));
374         memset(CdromId, 0, sizeof(CdromId));
375         memset(exename, 0, sizeof(exename));
376
377         time[0] = itob(0);
378         time[1] = itob(2);
379         time[2] = itob(0x10);
380
381         if (!Config.HLE && Config.SlowBoot) {
382                 // boot to BIOS in case of CDDA ir lid open
383                 CDR_getStatus(&stat);
384                 if ((stat.Status & 0x10) || stat.Type == 2 || !CDR_readTrack(time))
385                         return 0;
386         }
387         READTRACK();
388
389         strncpy(CdromLabel, buf + 52, 32);
390
391         // skip head and sub, and go to the root directory record
392         dir = (struct iso_directory_record *)&buf[12 + 156];
393
394         mmssdd(dir->extent, (char *)time);
395
396         READDIR(mdir);
397
398         if (GetCdromFile(mdir, time, "SYSTEM.CNF;1") != -1) {
399                 READTRACK();
400
401                 sscanf(buf + 12, "BOOT = cdrom:\\%255s", exename);
402                 if (GetCdromFile(mdir, time, exename) == -1) {
403                         sscanf(buf + 12, "BOOT = cdrom:%255s", exename);
404                         if (GetCdromFile(mdir, time, exename) == -1) {
405                                 char *ptr = strstr(buf + 12, "cdrom:");                 // possibly the executable is in some subdir
406                                 if (ptr != NULL) {
407                                         ptr += 6;
408                                         while (*ptr == '\\' || *ptr == '/') ptr++;
409                                         strncpy(exename, ptr, 255);
410                                         exename[255] = '\0';
411                                         ptr = exename;
412                                         while (*ptr != '\0' && *ptr != '\r' && *ptr != '\n') ptr++;
413                                         *ptr = '\0';
414                                         if (GetCdromFile(mdir, time, exename) == -1)
415                                                 return -1;              // main executable not found
416                                 } else
417                                         return -1;
418                         }
419                 }
420                 /* Workaround for Wild Arms EU/US which has non-standard string causing incorrect region detection */
421                 if (exename[0] == 'E' && exename[1] == 'X' && exename[2] == 'E' && exename[3] == '\\') {
422                         size_t offset = 4;
423                         size_t i, len = strlen(exename) - offset;
424                         for (i = 0; i < len; i++)
425                                 exename[i] = exename[i + offset];
426                         exename[i] = '\0';
427                 }
428         } else if (GetCdromFile(mdir, time, "PSX.EXE;1") != -1) {
429                 strcpy(exename, "PSX.EXE;1");
430                 strcpy(CdromId, "SLUS99999");
431         } else
432                 return -1;              // SYSTEM.CNF and PSX.EXE not found
433
434         if (CdromId[0] == '\0') {
435                 len = strlen(exename);
436                 c = 0;
437                 for (i = 0; i < len; ++i) {
438                         if (exename[i] == ';' || c >= sizeof(CdromId) - 1)
439                                 break;
440                         if (isalnum(exename[i]))
441                                 CdromId[c++] = exename[i];
442                 }
443         }
444
445         if (CdromId[0] == '\0')
446                 strcpy(CdromId, "SLUS99999");
447
448         if (Config.PsxAuto) { // autodetect system (pal or ntsc)
449                 if (
450                         /* Make sure Wild Arms SCUS-94608 is not detected as a PAL game. */
451                         ((CdromId[0] == 's' || CdromId[0] == 'S') && (CdromId[2] == 'e' || CdromId[2] == 'E')) ||
452                         !strncmp(CdromId, "DTLS3035", 8) ||
453                         !strncmp(CdromId, "PBPX95001", 9) || // according to redump.org, these PAL
454                         !strncmp(CdromId, "PBPX95007", 9) || // discs have a non-standard ID;
455                         !strncmp(CdromId, "PBPX95008", 9))   // add more serials if they are discovered.
456                         Config.PsxType = PSX_TYPE_PAL; // pal
457                 else Config.PsxType = PSX_TYPE_NTSC; // ntsc
458         }
459
460         if (CdromLabel[0] == ' ') {
461                 strncpy(CdromLabel, CdromId, 9);
462         }
463         SysPrintf(_("CD-ROM Label: %.32s\n"), CdromLabel);
464         SysPrintf(_("CD-ROM ID: %.9s\n"), CdromId);
465         SysPrintf(_("CD-ROM EXE Name: %.255s\n"), exename);
466         
467         Apply_Hacks_Cdrom();
468
469         BuildPPFCache();
470
471         return 0;
472 }
473
474 static int PSXGetFileType(FILE *f) {
475         unsigned long current;
476         u8 mybuf[2048];
477         EXE_HEADER *exe_hdr;
478         FILHDR *coff_hdr;
479
480         current = ftell(f);
481         fseek(f, 0L, SEEK_SET);
482         if (fread(&mybuf, 1, sizeof(mybuf), f) != sizeof(mybuf))
483                 goto io_fail;
484         
485         fseek(f, current, SEEK_SET);
486
487         exe_hdr = (EXE_HEADER *)mybuf;
488         if (memcmp(exe_hdr->id, "PS-X EXE", 8) == 0)
489                 return PSX_EXE;
490
491         if (mybuf[0] == 'C' && mybuf[1] == 'P' && mybuf[2] == 'E')
492                 return CPE_EXE;
493
494         coff_hdr = (FILHDR *)mybuf;
495         if (SWAPu16(coff_hdr->f_magic) == 0x0162)
496                 return COFF_EXE;
497
498         return INVALID_EXE;
499
500 io_fail:
501 #ifndef NDEBUG
502         SysPrintf(_("File IO error in <%s:%s>.\n"), __FILE__, __func__);
503 #endif
504         return INVALID_EXE;
505 }
506
507 // temporary pandora workaround..
508 // FIXME: remove
509 size_t fread_to_ram(void *ptr, size_t size, size_t nmemb, FILE *stream)
510 {
511         void *tmp;
512         size_t ret = 0;
513
514         tmp = malloc(size * nmemb);
515         if (tmp) {
516                 ret = fread(tmp, size, nmemb, stream);
517                 memcpy(ptr, tmp, size * nmemb);
518                 free(tmp);
519         }
520         return ret;
521 }
522
523 int Load(const char *ExePath) {
524         FILE *tmpFile;
525         EXE_HEADER tmpHead;
526         int type;
527         int retval = 0;
528         u8 opcode;
529         u32 section_address, section_size;
530         void *mem;
531
532         strcpy(CdromId, "SLUS99999");
533         strcpy(CdromLabel, "SLUS_999.99");
534
535         tmpFile = fopen(ExePath, "rb");
536         if (tmpFile == NULL) {
537                 SysPrintf(_("Error opening file: %s.\n"), ExePath);
538                 retval = -1;
539         } else {
540                 type = PSXGetFileType(tmpFile);
541                 switch (type) {
542                         case PSX_EXE:
543                                 if (fread(&tmpHead, 1, sizeof(EXE_HEADER), tmpFile) != sizeof(EXE_HEADER))
544                                         goto fail_io;
545                                 section_address = SWAP32(tmpHead.t_addr);
546                                 section_size = SWAP32(tmpHead.t_size);
547                                 mem = PSXM(section_address);
548                                 if (mem != INVALID_PTR) {
549                                         fseek(tmpFile, 0x800, SEEK_SET);
550                                         fread_to_ram(mem, section_size, 1, tmpFile);
551                                         psxCpu->Clear(section_address, section_size / 4);
552                                 }
553                                 SetBootRegs(SWAP32(tmpHead.pc0), SWAP32(tmpHead.gp0),
554                                         SWAP32(tmpHead.s_addr));
555                                 retval = 0;
556                                 break;
557                         case CPE_EXE:
558                                 fseek(tmpFile, 6, SEEK_SET); /* Something tells me we should go to 4 and read the "08 00" here... */
559                                 do {
560                                         if (fread(&opcode, 1, sizeof(opcode), tmpFile) != sizeof(opcode))
561                                                 goto fail_io;
562                                         switch (opcode) {
563                                                 case 1: /* Section loading */
564                                                         if (fread(&section_address, 1, sizeof(section_address), tmpFile) != sizeof(section_address))
565                                                                 goto fail_io;
566                                                         if (fread(&section_size, 1, sizeof(section_size), tmpFile) != sizeof(section_size))
567                                                                 goto fail_io;
568                                                         section_address = SWAPu32(section_address);
569                                                         section_size = SWAPu32(section_size);
570 #ifdef EMU_LOG
571                                                         EMU_LOG("Loading %08X bytes from %08X to %08X\n", section_size, ftell(tmpFile), section_address);
572 #endif
573                                                         mem = PSXM(section_address);
574                                                         if (mem != INVALID_PTR) {
575                                                                 fread_to_ram(mem, section_size, 1, tmpFile);
576                                                                 psxCpu->Clear(section_address, section_size / 4);
577                                                         }
578                                                         break;
579                                                 case 3: /* register loading (PC only?) */
580                                                         fseek(tmpFile, 2, SEEK_CUR); /* unknown field */
581                                                         if (fread(&psxRegs.pc, 1, sizeof(psxRegs.pc), tmpFile) != sizeof(psxRegs.pc))
582                                                                 goto fail_io;
583                                                         psxRegs.pc = SWAPu32(psxRegs.pc);
584                                                         break;
585                                                 case 0: /* End of file */
586                                                         break;
587                                                 default:
588                                                         SysPrintf(_("Unknown CPE opcode %02x at position %08x.\n"), opcode, ftell(tmpFile) - 1);
589                                                         retval = -1;
590                                                         break;
591                                         }
592                                 } while (opcode != 0 && retval == 0);
593                                 break;
594                         case COFF_EXE:
595                                 SysPrintf(_("COFF files not supported.\n"));
596                                 retval = -1;
597                                 break;
598                         case INVALID_EXE:
599                                 SysPrintf(_("This file does not appear to be a valid PSX EXE file.\n"));
600                                 SysPrintf(_("(did you forget -cdfile ?)\n"));
601                                 retval = -1;
602                                 break;
603                 }
604         }
605
606         if (retval != 0) {
607                 CdromId[0] = '\0';
608                 CdromLabel[0] = '\0';
609         }
610
611         if (tmpFile)
612                 fclose(tmpFile);
613         return retval;
614
615 fail_io:
616 #ifndef NDEBUG
617         SysPrintf(_("File IO error in <%s:%s>.\n"), __FILE__, __func__);
618 #endif
619         fclose(tmpFile);
620         return -1;
621 }
622
623 // STATES
624
625 static void *zlib_open(const char *name, const char *mode)
626 {
627         return gzopen(name, mode);
628 }
629
630 static int zlib_read(void *file, void *buf, u32 len)
631 {
632         return gzread(file, buf, len);
633 }
634
635 static int zlib_write(void *file, const void *buf, u32 len)
636 {
637         return gzwrite(file, buf, len);
638 }
639
640 static long zlib_seek(void *file, long offs, int whence)
641 {
642         return gzseek(file, offs, whence);
643 }
644
645 static void zlib_close(void *file)
646 {
647         gzclose(file);
648 }
649
650 struct PcsxSaveFuncs SaveFuncs = {
651         zlib_open, zlib_read, zlib_write, zlib_seek, zlib_close
652 };
653
654 static const char PcsxHeader[32] = "STv4 PCSX v" PCSX_VERSION;
655
656 // Savestate Versioning!
657 // If you make changes to the savestate version, please increment the value below.
658 static const u32 SaveVersion = 0x8b410006;
659
660 #define MISC_MAGIC 0x4353494d
661 struct misc_save_data {
662         u32 magic;
663         u32 gteBusyCycle;
664         u32 muldivBusyCycle;
665         u32 biuReg;
666         u32 biosBranchCheck;
667         u32 gpuIdleAfter;
668         u32 gpuSr;
669         u32 frame_counter;
670         int CdromFrontendId;
671 };
672
673 int SaveState(const char *file) {
674         struct misc_save_data *misc = (void *)(psxH + 0xf000);
675         void *f;
676         GPUFreeze_t *gpufP = NULL;
677         SPUFreezeHdr_t spufH;
678         SPUFreeze_t *spufP = NULL;
679         unsigned char *pMem = NULL;
680         int result = -1;
681         int Size;
682
683         assert(!psxRegs.branching);
684         assert(!psxRegs.cpuInRecursion);
685         assert(!misc->magic);
686
687         f = SaveFuncs.open(file, "wb");
688         if (f == NULL) return -1;
689
690         misc->magic = MISC_MAGIC;
691         misc->gteBusyCycle = psxRegs.gteBusyCycle;
692         misc->muldivBusyCycle = psxRegs.muldivBusyCycle;
693         misc->biuReg = psxRegs.biuReg;
694         misc->biosBranchCheck = psxRegs.biosBranchCheck;
695         misc->gpuIdleAfter = psxRegs.gpuIdleAfter;
696         misc->gpuSr = HW_GPU_STATUS;
697         misc->frame_counter = frame_counter;
698         misc->CdromFrontendId = CdromFrontendId;
699
700         psxCpu->Notify(R3000ACPU_NOTIFY_BEFORE_SAVE, NULL);
701
702         SaveFuncs.write(f, (void *)PcsxHeader, 32);
703         SaveFuncs.write(f, (void *)&SaveVersion, sizeof(u32));
704         SaveFuncs.write(f, (void *)&Config.HLE, sizeof(boolean));
705
706         pMem = (unsigned char *)malloc(128 * 96 * 3);
707         if (pMem == NULL) goto cleanup;
708         GPU_getScreenPic(pMem);
709         SaveFuncs.write(f, pMem, 128 * 96 * 3);
710         free(pMem);
711
712         if (Config.HLE)
713                 psxBiosFreeze(1);
714
715         SaveFuncs.write(f, psxM, 0x00200000);
716         SaveFuncs.write(f, psxR, 0x00080000);
717         SaveFuncs.write(f, psxH, 0x00010000);
718         // only partial save of psxRegisters to maintain savestate compat
719         SaveFuncs.write(f, &psxRegs, offsetof(psxRegisters, gteBusyCycle));
720
721         // gpu
722         gpufP = (GPUFreeze_t *)malloc(sizeof(GPUFreeze_t));
723         if (gpufP == NULL) goto cleanup;
724         gpufP->ulFreezeVersion = 1;
725         GPU_freeze(1, gpufP);
726         SaveFuncs.write(f, gpufP, sizeof(GPUFreeze_t));
727         free(gpufP); gpufP = NULL;
728
729         // spu
730         SPU_freeze(2, (SPUFreeze_t *)&spufH, psxRegs.cycle);
731         Size = spufH.Size; SaveFuncs.write(f, &Size, 4);
732         spufP = (SPUFreeze_t *) malloc(Size);
733         if (spufP == NULL) goto cleanup;
734         SPU_freeze(1, spufP, psxRegs.cycle);
735         SaveFuncs.write(f, spufP, Size);
736         free(spufP); spufP = NULL;
737
738         sioFreeze(f, 1);
739         cdrFreeze(f, 1);
740         psxHwFreeze(f, 1);
741         psxRcntFreeze(f, 1);
742         mdecFreeze(f, 1);
743         new_dyna_freeze(f, 1);
744         padFreeze(f, 1);
745
746         result = 0;
747 cleanup:
748         memset(misc, 0, sizeof(*misc));
749         SaveFuncs.close(f);
750         return result;
751 }
752
753 int LoadState(const char *file) {
754         struct misc_save_data *misc = (void *)(psxH + 0xf000);
755         u32 biosBranchCheckOld = psxRegs.biosBranchCheck;
756         void *f;
757         GPUFreeze_t *gpufP = NULL;
758         SPUFreeze_t *spufP = NULL;
759         int Size;
760         char header[32];
761         u32 version;
762         boolean hle;
763         int result = -1;
764
765         f = SaveFuncs.open(file, "rb");
766         if (f == NULL) return -1;
767
768         SaveFuncs.read(f, header, sizeof(header));
769         SaveFuncs.read(f, &version, sizeof(u32));
770         SaveFuncs.read(f, &hle, sizeof(boolean));
771
772         if (strncmp("STv4 PCSX", header, 9) != 0 || version != SaveVersion) {
773                 SysPrintf("incompatible savestate version %x\n", version);
774                 goto cleanup;
775         }
776         Config.HLE = hle;
777
778         if (Config.HLE)
779                 psxBiosInit();
780
781         SaveFuncs.seek(f, 128 * 96 * 3, SEEK_CUR);
782         SaveFuncs.read(f, psxM, 0x00200000);
783         SaveFuncs.read(f, psxR, 0x00080000);
784         SaveFuncs.read(f, psxH, 0x00010000);
785         SaveFuncs.read(f, &psxRegs, offsetof(psxRegisters, gteBusyCycle));
786         psxRegs.gteBusyCycle = psxRegs.cycle;
787         psxRegs.biosBranchCheck = ~0;
788         psxRegs.gpuIdleAfter = psxRegs.cycle - 1;
789         HW_GPU_STATUS &= SWAP32(~PSXGPU_nBUSY);
790         if (misc->magic == MISC_MAGIC) {
791                 psxRegs.gteBusyCycle = misc->gteBusyCycle;
792                 psxRegs.muldivBusyCycle = misc->muldivBusyCycle;
793                 psxRegs.biuReg = misc->biuReg;
794                 psxRegs.biosBranchCheck = misc->biosBranchCheck;
795                 psxRegs.gpuIdleAfter = misc->gpuIdleAfter;
796                 HW_GPU_STATUS = misc->gpuSr;
797                 frame_counter = misc->frame_counter;
798                 CdromFrontendId = misc->CdromFrontendId;
799         }
800
801         psxCpu->Notify(R3000ACPU_NOTIFY_AFTER_LOAD, NULL);
802
803         if (Config.HLE)
804                 psxBiosFreeze(0);
805
806         // gpu
807         gpufP = (GPUFreeze_t *)malloc(sizeof(GPUFreeze_t));
808         if (gpufP == NULL) goto cleanup;
809         SaveFuncs.read(f, gpufP, sizeof(GPUFreeze_t));
810         GPU_freeze(0, gpufP);
811         free(gpufP);
812         gpuSyncPluginSR();
813
814         // spu
815         SaveFuncs.read(f, &Size, 4);
816         spufP = (SPUFreeze_t *)malloc(Size);
817         if (spufP == NULL) goto cleanup;
818         SaveFuncs.read(f, spufP, Size);
819         SPU_freeze(0, spufP, psxRegs.cycle);
820         free(spufP);
821
822         sioFreeze(f, 0);
823         cdrFreeze(f, 0);
824         psxHwFreeze(f, 0);
825         psxRcntFreeze(f, 0);
826         mdecFreeze(f, 0);
827         new_dyna_freeze(f, 0);
828         padFreeze(f, 0);
829
830         events_restore();
831         if (Config.HLE)
832                 psxBiosCheckExe(biosBranchCheckOld, 0x60, 1);
833
834         result = 0;
835 cleanup:
836         memset(misc, 0, sizeof(*misc));
837         SaveFuncs.close(f);
838         return result;
839 }
840
841 int CheckState(const char *file) {
842         void *f;
843         char header[32];
844         u32 version;
845         boolean hle;
846
847         f = SaveFuncs.open(file, "rb");
848         if (f == NULL) return -1;
849
850         SaveFuncs.read(f, header, sizeof(header));
851         SaveFuncs.read(f, &version, sizeof(u32));
852         SaveFuncs.read(f, &hle, sizeof(boolean));
853
854         SaveFuncs.close(f);
855
856         if (strncmp("STv4 PCSX", header, 9) != 0 || version != SaveVersion)
857                 return -1;
858
859         return 0;
860 }
861
862 // NET Function Helpers
863
864 int SendPcsxInfo() {
865         if (NET_recvData == NULL || NET_sendData == NULL)
866                 return 0;
867
868         boolean Sio_old = 0;
869         boolean SpuIrq_old = 0;
870         boolean RCntFix_old = 0;
871         NET_sendData(&Config.Xa, sizeof(Config.Xa), PSE_NET_BLOCKING);
872         NET_sendData(&Sio_old, sizeof(Sio_old), PSE_NET_BLOCKING);
873         NET_sendData(&SpuIrq_old, sizeof(SpuIrq_old), PSE_NET_BLOCKING);
874         NET_sendData(&RCntFix_old, sizeof(RCntFix_old), PSE_NET_BLOCKING);
875         NET_sendData(&Config.PsxType, sizeof(Config.PsxType), PSE_NET_BLOCKING);
876         NET_sendData(&Config.Cpu, sizeof(Config.Cpu), PSE_NET_BLOCKING);
877
878         return 0;
879 }
880
881 int RecvPcsxInfo() {
882         int tmp;
883
884         if (NET_recvData == NULL || NET_sendData == NULL)
885                 return 0;
886
887         boolean Sio_old = 0;
888         boolean SpuIrq_old = 0;
889         boolean RCntFix_old = 0;
890         NET_recvData(&Config.Xa, sizeof(Config.Xa), PSE_NET_BLOCKING);
891         NET_recvData(&Sio_old, sizeof(Sio_old), PSE_NET_BLOCKING);
892         NET_recvData(&SpuIrq_old, sizeof(SpuIrq_old), PSE_NET_BLOCKING);
893         NET_recvData(&RCntFix_old, sizeof(RCntFix_old), PSE_NET_BLOCKING);
894         NET_recvData(&Config.PsxType, sizeof(Config.PsxType), PSE_NET_BLOCKING);
895
896         tmp = Config.Cpu;
897         NET_recvData(&Config.Cpu, sizeof(Config.Cpu), PSE_NET_BLOCKING);
898         if (tmp != Config.Cpu) {
899                 psxCpu->Shutdown();
900 #ifndef DRC_DISABLE
901                 if (Config.Cpu == CPU_INTERPRETER) psxCpu = &psxInt;
902                 else psxCpu = &psxRec;
903 #else
904                 psxCpu = &psxInt;
905 #endif
906                 if (psxCpu->Init() == -1) {
907                         SysClose(); return -1;
908                 }
909                 psxCpu->Reset();
910                 psxCpu->Notify(R3000ACPU_NOTIFY_AFTER_LOAD, NULL);
911         }
912
913         return 0;
914 }
915
916 // remove the leading and trailing spaces in a string
917 void trim(char *str) {
918         int pos = 0;
919         char *dest = str;
920
921         // skip leading blanks
922         while (str[pos] <= ' ' && str[pos] > 0)
923                 pos++;
924
925         while (str[pos]) {
926                 *(dest++) = str[pos];
927                 pos++;
928         }
929
930         *(dest--) = '\0'; // store the null
931
932         // remove trailing blanks
933         while (dest >= str && *dest <= ' ' && *dest > 0)
934                 *(dest--) = '\0';
935 }
936
937 // lookup table for crc calculation
938 static unsigned short crctab[256] = {
939         0x0000, 0x1021, 0x2042, 0x3063, 0x4084, 0x50A5, 0x60C6, 0x70E7, 0x8108,
940         0x9129, 0xA14A, 0xB16B, 0xC18C, 0xD1AD, 0xE1CE, 0xF1EF, 0x1231, 0x0210,
941         0x3273, 0x2252, 0x52B5, 0x4294, 0x72F7, 0x62D6, 0x9339, 0x8318, 0xB37B,
942         0xA35A, 0xD3BD, 0xC39C, 0xF3FF, 0xE3DE, 0x2462, 0x3443, 0x0420, 0x1401,
943         0x64E6, 0x74C7, 0x44A4, 0x5485, 0xA56A, 0xB54B, 0x8528, 0x9509, 0xE5EE,
944         0xF5CF, 0xC5AC, 0xD58D, 0x3653, 0x2672, 0x1611, 0x0630, 0x76D7, 0x66F6,
945         0x5695, 0x46B4, 0xB75B, 0xA77A, 0x9719, 0x8738, 0xF7DF, 0xE7FE, 0xD79D,
946         0xC7BC, 0x48C4, 0x58E5, 0x6886, 0x78A7, 0x0840, 0x1861, 0x2802, 0x3823,
947         0xC9CC, 0xD9ED, 0xE98E, 0xF9AF, 0x8948, 0x9969, 0xA90A, 0xB92B, 0x5AF5,
948         0x4AD4, 0x7AB7, 0x6A96, 0x1A71, 0x0A50, 0x3A33, 0x2A12, 0xDBFD, 0xCBDC,
949         0xFBBF, 0xEB9E, 0x9B79, 0x8B58, 0xBB3B, 0xAB1A, 0x6CA6, 0x7C87, 0x4CE4,
950         0x5CC5, 0x2C22, 0x3C03, 0x0C60, 0x1C41, 0xEDAE, 0xFD8F, 0xCDEC, 0xDDCD,
951         0xAD2A, 0xBD0B, 0x8D68, 0x9D49, 0x7E97, 0x6EB6, 0x5ED5, 0x4EF4, 0x3E13,
952         0x2E32, 0x1E51, 0x0E70, 0xFF9F, 0xEFBE, 0xDFDD, 0xCFFC, 0xBF1B, 0xAF3A,
953         0x9F59, 0x8F78, 0x9188, 0x81A9, 0xB1CA, 0xA1EB, 0xD10C, 0xC12D, 0xF14E,
954         0xE16F, 0x1080, 0x00A1, 0x30C2, 0x20E3, 0x5004, 0x4025, 0x7046, 0x6067,
955         0x83B9, 0x9398, 0xA3FB, 0xB3DA, 0xC33D, 0xD31C, 0xE37F, 0xF35E, 0x02B1,
956         0x1290, 0x22F3, 0x32D2, 0x4235, 0x5214, 0x6277, 0x7256, 0xB5EA, 0xA5CB,
957         0x95A8, 0x8589, 0xF56E, 0xE54F, 0xD52C, 0xC50D, 0x34E2, 0x24C3, 0x14A0,
958         0x0481, 0x7466, 0x6447, 0x5424, 0x4405, 0xA7DB, 0xB7FA, 0x8799, 0x97B8,
959         0xE75F, 0xF77E, 0xC71D, 0xD73C, 0x26D3, 0x36F2, 0x0691, 0x16B0, 0x6657,
960         0x7676, 0x4615, 0x5634, 0xD94C, 0xC96D, 0xF90E, 0xE92F, 0x99C8, 0x89E9,
961         0xB98A, 0xA9AB, 0x5844, 0x4865, 0x7806, 0x6827, 0x18C0, 0x08E1, 0x3882,
962         0x28A3, 0xCB7D, 0xDB5C, 0xEB3F, 0xFB1E, 0x8BF9, 0x9BD8, 0xABBB, 0xBB9A,
963         0x4A75, 0x5A54, 0x6A37, 0x7A16, 0x0AF1, 0x1AD0, 0x2AB3, 0x3A92, 0xFD2E,
964         0xED0F, 0xDD6C, 0xCD4D, 0xBDAA, 0xAD8B, 0x9DE8, 0x8DC9, 0x7C26, 0x6C07,
965         0x5C64, 0x4C45, 0x3CA2, 0x2C83, 0x1CE0, 0x0CC1, 0xEF1F, 0xFF3E, 0xCF5D,
966         0xDF7C, 0xAF9B, 0xBFBA, 0x8FD9, 0x9FF8, 0x6E17, 0x7E36, 0x4E55, 0x5E74,
967         0x2E93, 0x3EB2, 0x0ED1, 0x1EF0
968 };
969
970 u16 calcCrc(u8 *d, int len) {
971         u16 crc = 0;
972         int i;
973
974         for (i = 0; i < len; i++) {
975                 crc = crctab[(crc >> 8) ^ d[i]] ^ (crc << 8);
976         }
977
978         return ~crc;
979 }