526ebd434898ed2509b62871bda9ea3f583b8b78
[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 void BiosBootBypass() {
167         assert(psxRegs.pc == 0x80030000);
168
169         // skip BIOS logos and region check
170         psxCpu->Notify(R3000ACPU_NOTIFY_BEFORE_SAVE, NULL);
171         psxRegs.pc = psxRegs.GPR.n.ra;
172 }
173
174 static void getFromCnf(char *buf, const char *key, u32 *val)
175 {
176         buf = strstr(buf, key);
177         if (buf)
178                 buf = strchr(buf, '=');
179         if (buf) {
180                 unsigned long v;
181                 errno = 0;
182                 v = strtoul(buf + 1, NULL, 16);
183                 if (errno == 0)
184                         *val = v;
185         }
186 }
187
188 int LoadCdrom() {
189         union {
190                 EXE_HEADER h;
191                 u32 d[sizeof(EXE_HEADER) / sizeof(u32)];
192         } tmpHead;
193         struct iso_directory_record *dir;
194         u8 time[4], *buf;
195         u8 mdir[4096];
196         char exename[256];
197         u32 cnf_tcb = 4;
198         u32 cnf_event = 16;
199         u32 cnf_stack = 0;
200         u32 t_addr;
201         u32 t_size;
202         u32 sp = 0;
203         int i, ret;
204
205         if (!Config.HLE) {
206                 if (psxRegs.pc != 0x80030000) // BiosBootBypass'ed or custom BIOS?
207                         return 0;
208                 if (Config.SlowBoot)
209                         return 0;
210         }
211
212         time[0] = itob(0); time[1] = itob(2); time[2] = itob(0x10);
213
214         READTRACK();
215
216         // skip head and sub, and go to the root directory record
217         dir = (struct iso_directory_record*) &buf[12+156];
218
219         mmssdd(dir->extent, (char*)time);
220
221         READDIR(mdir);
222
223         // Load SYSTEM.CNF and scan for the main executable
224         if (GetCdromFile(mdir, time, "SYSTEM.CNF;1") == -1) {
225                 // if SYSTEM.CNF is missing, start an existing PSX.EXE
226                 if (GetCdromFile(mdir, time, "PSX.EXE;1") == -1) return -1;
227                 strcpy(exename, "PSX.EXE;1");
228
229                 READTRACK();
230         }
231         else {
232                 // read the SYSTEM.CNF
233                 READTRACK();
234                 buf[1023] = 0;
235
236                 ret = sscanf((char *)buf + 12, "BOOT = cdrom:\\%255s", exename);
237                 if (ret < 1 || GetCdromFile(mdir, time, exename) == -1) {
238                         ret = sscanf((char *)buf + 12, "BOOT = cdrom:%255s", exename);
239                         if (ret < 1 || GetCdromFile(mdir, time, exename) == -1) {
240                                 char *ptr = strstr((char *)buf + 12, "cdrom:");
241                                 if (ptr != NULL) {
242                                         ptr += 6;
243                                         while (*ptr == '\\' || *ptr == '/') ptr++;
244                                         strncpy(exename, ptr, 255);
245                                         exename[255] = '\0';
246                                         ptr = exename;
247                                         while (*ptr != '\0' && *ptr != '\r' && *ptr != '\n') ptr++;
248                                         *ptr = '\0';
249                                         if (GetCdromFile(mdir, time, exename) == -1)
250                                                 return -1;
251                                 } else
252                                         return -1;
253                         }
254                 }
255                 getFromCnf((char *)buf + 12, "TCB", &cnf_tcb);
256                 getFromCnf((char *)buf + 12, "EVENT", &cnf_event);
257                 getFromCnf((char *)buf + 12, "STACK", &cnf_stack);
258                 if (Config.HLE)
259                         psxBiosCnfLoaded(cnf_tcb, cnf_event, cnf_stack);
260
261                 // Read the EXE-Header
262                 READTRACK();
263         }
264
265         memcpy(&tmpHead, buf + 12, sizeof(EXE_HEADER));
266         for (i = 2; i < sizeof(tmpHead.d) / sizeof(tmpHead.d[0]); i++)
267                 tmpHead.d[i] = SWAP32(tmpHead.d[i]);
268
269         SysPrintf("manual booting '%s' pc=%x\n", exename, tmpHead.h.pc0);
270         sp = tmpHead.h.s_addr;
271         if (cnf_stack)
272                 sp = cnf_stack;
273         SetBootRegs(tmpHead.h.pc0, tmpHead.h.gp0, sp);
274
275         // Read the rest of the main executable
276         for (t_addr = tmpHead.h.t_addr, t_size = tmpHead.h.t_size; t_size & ~2047; ) {
277                 void *ptr = (void *)PSXM(t_addr);
278
279                 incTime();
280                 READTRACK();
281
282                 if (ptr != INVALID_PTR) memcpy(ptr, buf+12, 2048);
283
284                 t_addr += 2048;
285                 t_size -= 2048;
286         }
287
288         psxCpu->Clear(tmpHead.h.t_addr, tmpHead.h.t_size / 4);
289         //psxCpu->Reset();
290
291         if (Config.HLE)
292                 psxBiosCheckExe(tmpHead.h.t_addr, tmpHead.h.t_size, 0);
293
294         return 0;
295 }
296
297 int LoadCdromFile(const char *filename, EXE_HEADER *head) {
298         struct iso_directory_record *dir;
299         u8 time[4],*buf;
300         u8 mdir[4096];
301         char exename[256];
302         const char *p1, *p2;
303         u32 size, addr;
304         void *mem;
305
306         if (filename == INVALID_PTR)
307                 return -1;
308
309         p1 = filename;
310         if ((p2 = strchr(p1, ':')))
311                 p1 = p2 + 1;
312         while (*p1 == '\\')
313                 p1++;
314         snprintf(exename, sizeof(exename), "%s", p1);
315
316         time[0] = itob(0); time[1] = itob(2); time[2] = itob(0x10);
317
318         READTRACK();
319
320         // skip head and sub, and go to the root directory record
321         dir = (struct iso_directory_record *)&buf[12 + 156];
322
323         mmssdd(dir->extent, (char*)time);
324
325         READDIR(mdir);
326
327         if (GetCdromFile(mdir, time, exename) == -1) return -1;
328
329         READTRACK();
330
331         memcpy(head, buf + 12, sizeof(EXE_HEADER));
332         size = SWAP32(head->t_size);
333         addr = SWAP32(head->t_addr);
334
335         psxCpu->Clear(addr, size / 4);
336         //psxCpu->Reset();
337
338         while (size & ~2047) {
339                 incTime();
340                 READTRACK();
341
342                 mem = PSXM(addr);
343                 if (mem != INVALID_PTR)
344                         memcpy(mem, buf + 12, 2048);
345
346                 size -= 2048;
347                 addr += 2048;
348         }
349
350         return 0;
351 }
352
353 int CheckCdrom() {
354         struct iso_directory_record *dir;
355         unsigned char time[4];
356         char *buf;
357         unsigned char mdir[4096];
358         char exename[256];
359         int i, len, c;
360
361         FreePPFCache();
362
363         time[0] = itob(0);
364         time[1] = itob(2);
365         time[2] = itob(0x10);
366
367         READTRACK();
368
369         memset(CdromLabel, 0, sizeof(CdromLabel));
370         memset(CdromId, 0, sizeof(CdromId));
371         memset(exename, 0, sizeof(exename));
372
373         strncpy(CdromLabel, buf + 52, 32);
374
375         // skip head and sub, and go to the root directory record
376         dir = (struct iso_directory_record *)&buf[12 + 156];
377
378         mmssdd(dir->extent, (char *)time);
379
380         READDIR(mdir);
381
382         if (GetCdromFile(mdir, time, "SYSTEM.CNF;1") != -1) {
383                 READTRACK();
384
385                 sscanf(buf + 12, "BOOT = cdrom:\\%255s", exename);
386                 if (GetCdromFile(mdir, time, exename) == -1) {
387                         sscanf(buf + 12, "BOOT = cdrom:%255s", exename);
388                         if (GetCdromFile(mdir, time, exename) == -1) {
389                                 char *ptr = strstr(buf + 12, "cdrom:");                 // possibly the executable is in some subdir
390                                 if (ptr != NULL) {
391                                         ptr += 6;
392                                         while (*ptr == '\\' || *ptr == '/') ptr++;
393                                         strncpy(exename, ptr, 255);
394                                         exename[255] = '\0';
395                                         ptr = exename;
396                                         while (*ptr != '\0' && *ptr != '\r' && *ptr != '\n') ptr++;
397                                         *ptr = '\0';
398                                         if (GetCdromFile(mdir, time, exename) == -1)
399                                                 return -1;              // main executable not found
400                                 } else
401                                         return -1;
402                         }
403                 }
404                 /* Workaround for Wild Arms EU/US which has non-standard string causing incorrect region detection */
405                 if (exename[0] == 'E' && exename[1] == 'X' && exename[2] == 'E' && exename[3] == '\\') {
406                         size_t offset = 4;
407                         size_t i, len = strlen(exename) - offset;
408                         for (i = 0; i < len; i++)
409                                 exename[i] = exename[i + offset];
410                         exename[i] = '\0';
411                 }
412         } else if (GetCdromFile(mdir, time, "PSX.EXE;1") != -1) {
413                 strcpy(exename, "PSX.EXE;1");
414                 strcpy(CdromId, "SLUS99999");
415         } else
416                 return -1;              // SYSTEM.CNF and PSX.EXE not found
417
418         if (CdromId[0] == '\0') {
419                 len = strlen(exename);
420                 c = 0;
421                 for (i = 0; i < len; ++i) {
422                         if (exename[i] == ';' || c >= sizeof(CdromId) - 1)
423                                 break;
424                         if (isalnum(exename[i]))
425                                 CdromId[c++] = exename[i];
426                 }
427         }
428
429         if (CdromId[0] == '\0')
430                 strcpy(CdromId, "SLUS99999");
431
432         if (Config.PsxAuto) { // autodetect system (pal or ntsc)
433                 if (
434                         /* Make sure Wild Arms SCUS-94608 is not detected as a PAL game. */
435                         ((CdromId[0] == 's' || CdromId[0] == 'S') && (CdromId[2] == 'e' || CdromId[2] == 'E')) ||
436                         !strncmp(CdromId, "DTLS3035", 8) ||
437                         !strncmp(CdromId, "PBPX95001", 9) || // according to redump.org, these PAL
438                         !strncmp(CdromId, "PBPX95007", 9) || // discs have a non-standard ID;
439                         !strncmp(CdromId, "PBPX95008", 9))   // add more serials if they are discovered.
440                         Config.PsxType = PSX_TYPE_PAL; // pal
441                 else Config.PsxType = PSX_TYPE_NTSC; // ntsc
442         }
443
444         if (CdromLabel[0] == ' ') {
445                 strncpy(CdromLabel, CdromId, 9);
446         }
447         SysPrintf(_("CD-ROM Label: %.32s\n"), CdromLabel);
448         SysPrintf(_("CD-ROM ID: %.9s\n"), CdromId);
449         SysPrintf(_("CD-ROM EXE Name: %.255s\n"), exename);
450         
451         Apply_Hacks_Cdrom();
452
453         BuildPPFCache();
454
455         return 0;
456 }
457
458 static int PSXGetFileType(FILE *f) {
459         unsigned long current;
460         u8 mybuf[2048];
461         EXE_HEADER *exe_hdr;
462         FILHDR *coff_hdr;
463
464         current = ftell(f);
465         fseek(f, 0L, SEEK_SET);
466         if (fread(&mybuf, 1, sizeof(mybuf), f) != sizeof(mybuf))
467                 goto io_fail;
468         
469         fseek(f, current, SEEK_SET);
470
471         exe_hdr = (EXE_HEADER *)mybuf;
472         if (memcmp(exe_hdr->id, "PS-X EXE", 8) == 0)
473                 return PSX_EXE;
474
475         if (mybuf[0] == 'C' && mybuf[1] == 'P' && mybuf[2] == 'E')
476                 return CPE_EXE;
477
478         coff_hdr = (FILHDR *)mybuf;
479         if (SWAPu16(coff_hdr->f_magic) == 0x0162)
480                 return COFF_EXE;
481
482         return INVALID_EXE;
483
484 io_fail:
485 #ifndef NDEBUG
486         SysPrintf(_("File IO error in <%s:%s>.\n"), __FILE__, __func__);
487 #endif
488         return INVALID_EXE;
489 }
490
491 // temporary pandora workaround..
492 // FIXME: remove
493 size_t fread_to_ram(void *ptr, size_t size, size_t nmemb, FILE *stream)
494 {
495         void *tmp;
496         size_t ret = 0;
497
498         tmp = malloc(size * nmemb);
499         if (tmp) {
500                 ret = fread(tmp, size, nmemb, stream);
501                 memcpy(ptr, tmp, size * nmemb);
502                 free(tmp);
503         }
504         return ret;
505 }
506
507 int Load(const char *ExePath) {
508         FILE *tmpFile;
509         EXE_HEADER tmpHead;
510         int type;
511         int retval = 0;
512         u8 opcode;
513         u32 section_address, section_size;
514         void *mem;
515
516         strcpy(CdromId, "SLUS99999");
517         strcpy(CdromLabel, "SLUS_999.99");
518
519         tmpFile = fopen(ExePath, "rb");
520         if (tmpFile == NULL) {
521                 SysPrintf(_("Error opening file: %s.\n"), ExePath);
522                 retval = -1;
523         } else {
524                 type = PSXGetFileType(tmpFile);
525                 switch (type) {
526                         case PSX_EXE:
527                                 if (fread(&tmpHead, 1, sizeof(EXE_HEADER), tmpFile) != sizeof(EXE_HEADER))
528                                         goto fail_io;
529                                 section_address = SWAP32(tmpHead.t_addr);
530                                 section_size = SWAP32(tmpHead.t_size);
531                                 mem = PSXM(section_address);
532                                 if (mem != INVALID_PTR) {
533                                         fseek(tmpFile, 0x800, SEEK_SET);
534                                         fread_to_ram(mem, section_size, 1, tmpFile);
535                                         psxCpu->Clear(section_address, section_size / 4);
536                                 }
537                                 SetBootRegs(SWAP32(tmpHead.pc0), SWAP32(tmpHead.gp0),
538                                         SWAP32(tmpHead.s_addr));
539                                 retval = 0;
540                                 break;
541                         case CPE_EXE:
542                                 fseek(tmpFile, 6, SEEK_SET); /* Something tells me we should go to 4 and read the "08 00" here... */
543                                 do {
544                                         if (fread(&opcode, 1, sizeof(opcode), tmpFile) != sizeof(opcode))
545                                                 goto fail_io;
546                                         switch (opcode) {
547                                                 case 1: /* Section loading */
548                                                         if (fread(&section_address, 1, sizeof(section_address), tmpFile) != sizeof(section_address))
549                                                                 goto fail_io;
550                                                         if (fread(&section_size, 1, sizeof(section_size), tmpFile) != sizeof(section_size))
551                                                                 goto fail_io;
552                                                         section_address = SWAPu32(section_address);
553                                                         section_size = SWAPu32(section_size);
554 #ifdef EMU_LOG
555                                                         EMU_LOG("Loading %08X bytes from %08X to %08X\n", section_size, ftell(tmpFile), section_address);
556 #endif
557                                                         mem = PSXM(section_address);
558                                                         if (mem != INVALID_PTR) {
559                                                                 fread_to_ram(mem, section_size, 1, tmpFile);
560                                                                 psxCpu->Clear(section_address, section_size / 4);
561                                                         }
562                                                         break;
563                                                 case 3: /* register loading (PC only?) */
564                                                         fseek(tmpFile, 2, SEEK_CUR); /* unknown field */
565                                                         if (fread(&psxRegs.pc, 1, sizeof(psxRegs.pc), tmpFile) != sizeof(psxRegs.pc))
566                                                                 goto fail_io;
567                                                         psxRegs.pc = SWAPu32(psxRegs.pc);
568                                                         break;
569                                                 case 0: /* End of file */
570                                                         break;
571                                                 default:
572                                                         SysPrintf(_("Unknown CPE opcode %02x at position %08x.\n"), opcode, ftell(tmpFile) - 1);
573                                                         retval = -1;
574                                                         break;
575                                         }
576                                 } while (opcode != 0 && retval == 0);
577                                 break;
578                         case COFF_EXE:
579                                 SysPrintf(_("COFF files not supported.\n"));
580                                 retval = -1;
581                                 break;
582                         case INVALID_EXE:
583                                 SysPrintf(_("This file does not appear to be a valid PSX EXE file.\n"));
584                                 SysPrintf(_("(did you forget -cdfile ?)\n"));
585                                 retval = -1;
586                                 break;
587                 }
588         }
589
590         if (retval != 0) {
591                 CdromId[0] = '\0';
592                 CdromLabel[0] = '\0';
593         }
594
595         if (tmpFile)
596                 fclose(tmpFile);
597         return retval;
598
599 fail_io:
600 #ifndef NDEBUG
601         SysPrintf(_("File IO error in <%s:%s>.\n"), __FILE__, __func__);
602 #endif
603         fclose(tmpFile);
604         return -1;
605 }
606
607 // STATES
608
609 static void *zlib_open(const char *name, const char *mode)
610 {
611         return gzopen(name, mode);
612 }
613
614 static int zlib_read(void *file, void *buf, u32 len)
615 {
616         return gzread(file, buf, len);
617 }
618
619 static int zlib_write(void *file, const void *buf, u32 len)
620 {
621         return gzwrite(file, buf, len);
622 }
623
624 static long zlib_seek(void *file, long offs, int whence)
625 {
626         return gzseek(file, offs, whence);
627 }
628
629 static void zlib_close(void *file)
630 {
631         gzclose(file);
632 }
633
634 struct PcsxSaveFuncs SaveFuncs = {
635         zlib_open, zlib_read, zlib_write, zlib_seek, zlib_close
636 };
637
638 static const char PcsxHeader[32] = "STv4 PCSX v" PCSX_VERSION;
639
640 // Savestate Versioning!
641 // If you make changes to the savestate version, please increment the value below.
642 static const u32 SaveVersion = 0x8b410006;
643
644 #define MISC_MAGIC 0x4353494d
645 struct misc_save_data {
646         u32 magic;
647         u32 gteBusyCycle;
648         u32 muldivBusyCycle;
649         u32 biuReg;
650         u32 biosBranchCheck;
651         u32 gpuIdleAfter;
652         u32 gpuSr;
653         u32 frame_counter;
654         int CdromFrontendId;
655 };
656
657 int SaveState(const char *file) {
658         struct misc_save_data *misc = (void *)(psxH + 0xf000);
659         void *f;
660         GPUFreeze_t *gpufP = NULL;
661         SPUFreezeHdr_t spufH;
662         SPUFreeze_t *spufP = NULL;
663         unsigned char *pMem = NULL;
664         int result = -1;
665         int Size;
666
667         assert(!psxRegs.branching);
668         assert(!psxRegs.cpuInRecursion);
669         assert(!misc->magic);
670         misc->magic = MISC_MAGIC;
671         misc->gteBusyCycle = psxRegs.gteBusyCycle;
672         misc->muldivBusyCycle = psxRegs.muldivBusyCycle;
673         misc->biuReg = psxRegs.biuReg;
674         misc->biosBranchCheck = psxRegs.biosBranchCheck;
675         misc->gpuIdleAfter = psxRegs.gpuIdleAfter;
676         misc->gpuSr = HW_GPU_STATUS;
677         misc->frame_counter = frame_counter;
678         misc->CdromFrontendId = CdromFrontendId;
679
680         f = SaveFuncs.open(file, "wb");
681         if (f == NULL) return -1;
682
683         psxCpu->Notify(R3000ACPU_NOTIFY_BEFORE_SAVE, NULL);
684
685         SaveFuncs.write(f, (void *)PcsxHeader, 32);
686         SaveFuncs.write(f, (void *)&SaveVersion, sizeof(u32));
687         SaveFuncs.write(f, (void *)&Config.HLE, sizeof(boolean));
688
689         pMem = (unsigned char *)malloc(128 * 96 * 3);
690         if (pMem == NULL) goto cleanup;
691         GPU_getScreenPic(pMem);
692         SaveFuncs.write(f, pMem, 128 * 96 * 3);
693         free(pMem);
694
695         if (Config.HLE)
696                 psxBiosFreeze(1);
697
698         SaveFuncs.write(f, psxM, 0x00200000);
699         SaveFuncs.write(f, psxR, 0x00080000);
700         SaveFuncs.write(f, psxH, 0x00010000);
701         // only partial save of psxRegisters to maintain savestate compat
702         SaveFuncs.write(f, &psxRegs, offsetof(psxRegisters, gteBusyCycle));
703
704         // gpu
705         gpufP = (GPUFreeze_t *)malloc(sizeof(GPUFreeze_t));
706         if (gpufP == NULL) goto cleanup;
707         gpufP->ulFreezeVersion = 1;
708         GPU_freeze(1, gpufP);
709         SaveFuncs.write(f, gpufP, sizeof(GPUFreeze_t));
710         free(gpufP); gpufP = NULL;
711
712         // spu
713         SPU_freeze(2, (SPUFreeze_t *)&spufH, psxRegs.cycle);
714         Size = spufH.Size; SaveFuncs.write(f, &Size, 4);
715         spufP = (SPUFreeze_t *) malloc(Size);
716         if (spufP == NULL) goto cleanup;
717         SPU_freeze(1, spufP, psxRegs.cycle);
718         SaveFuncs.write(f, spufP, Size);
719         free(spufP); spufP = NULL;
720
721         sioFreeze(f, 1);
722         cdrFreeze(f, 1);
723         psxHwFreeze(f, 1);
724         psxRcntFreeze(f, 1);
725         mdecFreeze(f, 1);
726         new_dyna_freeze(f, 1);
727         padFreeze(f, 1);
728
729         result = 0;
730 cleanup:
731         memset(misc, 0, sizeof(*misc));
732         SaveFuncs.close(f);
733         return result;
734 }
735
736 int LoadState(const char *file) {
737         struct misc_save_data *misc = (void *)(psxH + 0xf000);
738         u32 biosBranchCheckOld = psxRegs.biosBranchCheck;
739         void *f;
740         GPUFreeze_t *gpufP = NULL;
741         SPUFreeze_t *spufP = NULL;
742         int Size;
743         char header[32];
744         u32 version;
745         boolean hle;
746         int result = -1;
747
748         f = SaveFuncs.open(file, "rb");
749         if (f == NULL) return -1;
750
751         SaveFuncs.read(f, header, sizeof(header));
752         SaveFuncs.read(f, &version, sizeof(u32));
753         SaveFuncs.read(f, &hle, sizeof(boolean));
754
755         if (strncmp("STv4 PCSX", header, 9) != 0 || version != SaveVersion) {
756                 SysPrintf("incompatible savestate version %x\n", version);
757                 goto cleanup;
758         }
759         Config.HLE = hle;
760
761         if (Config.HLE)
762                 psxBiosInit();
763
764         SaveFuncs.seek(f, 128 * 96 * 3, SEEK_CUR);
765         SaveFuncs.read(f, psxM, 0x00200000);
766         SaveFuncs.read(f, psxR, 0x00080000);
767         SaveFuncs.read(f, psxH, 0x00010000);
768         SaveFuncs.read(f, &psxRegs, offsetof(psxRegisters, gteBusyCycle));
769         psxRegs.gteBusyCycle = psxRegs.cycle;
770         psxRegs.biosBranchCheck = ~0;
771         psxRegs.gpuIdleAfter = psxRegs.cycle - 1;
772         HW_GPU_STATUS &= SWAP32(~PSXGPU_nBUSY);
773         if (misc->magic == MISC_MAGIC) {
774                 psxRegs.gteBusyCycle = misc->gteBusyCycle;
775                 psxRegs.muldivBusyCycle = misc->muldivBusyCycle;
776                 psxRegs.biuReg = misc->biuReg;
777                 psxRegs.biosBranchCheck = misc->biosBranchCheck;
778                 psxRegs.gpuIdleAfter = misc->gpuIdleAfter;
779                 HW_GPU_STATUS = misc->gpuSr;
780                 frame_counter = misc->frame_counter;
781                 CdromFrontendId = misc->CdromFrontendId;
782         }
783
784         psxCpu->Notify(R3000ACPU_NOTIFY_AFTER_LOAD, NULL);
785
786         if (Config.HLE)
787                 psxBiosFreeze(0);
788
789         // gpu
790         gpufP = (GPUFreeze_t *)malloc(sizeof(GPUFreeze_t));
791         if (gpufP == NULL) goto cleanup;
792         SaveFuncs.read(f, gpufP, sizeof(GPUFreeze_t));
793         GPU_freeze(0, gpufP);
794         free(gpufP);
795         gpuSyncPluginSR();
796
797         // spu
798         SaveFuncs.read(f, &Size, 4);
799         spufP = (SPUFreeze_t *)malloc(Size);
800         if (spufP == NULL) goto cleanup;
801         SaveFuncs.read(f, spufP, Size);
802         SPU_freeze(0, spufP, psxRegs.cycle);
803         free(spufP);
804
805         sioFreeze(f, 0);
806         cdrFreeze(f, 0);
807         psxHwFreeze(f, 0);
808         psxRcntFreeze(f, 0);
809         mdecFreeze(f, 0);
810         new_dyna_freeze(f, 0);
811         padFreeze(f, 0);
812
813         events_restore();
814         if (Config.HLE)
815                 psxBiosCheckExe(biosBranchCheckOld, 0x60, 1);
816
817         result = 0;
818 cleanup:
819         memset(misc, 0, sizeof(*misc));
820         SaveFuncs.close(f);
821         return result;
822 }
823
824 int CheckState(const char *file) {
825         void *f;
826         char header[32];
827         u32 version;
828         boolean hle;
829
830         f = SaveFuncs.open(file, "rb");
831         if (f == NULL) return -1;
832
833         SaveFuncs.read(f, header, sizeof(header));
834         SaveFuncs.read(f, &version, sizeof(u32));
835         SaveFuncs.read(f, &hle, sizeof(boolean));
836
837         SaveFuncs.close(f);
838
839         if (strncmp("STv4 PCSX", header, 9) != 0 || version != SaveVersion)
840                 return -1;
841
842         return 0;
843 }
844
845 // NET Function Helpers
846
847 int SendPcsxInfo() {
848         if (NET_recvData == NULL || NET_sendData == NULL)
849                 return 0;
850
851         boolean Sio_old = 0;
852         boolean SpuIrq_old = 0;
853         boolean RCntFix_old = 0;
854         NET_sendData(&Config.Xa, sizeof(Config.Xa), PSE_NET_BLOCKING);
855         NET_sendData(&Sio_old, sizeof(Sio_old), PSE_NET_BLOCKING);
856         NET_sendData(&SpuIrq_old, sizeof(SpuIrq_old), PSE_NET_BLOCKING);
857         NET_sendData(&RCntFix_old, sizeof(RCntFix_old), PSE_NET_BLOCKING);
858         NET_sendData(&Config.PsxType, sizeof(Config.PsxType), PSE_NET_BLOCKING);
859         NET_sendData(&Config.Cpu, sizeof(Config.Cpu), PSE_NET_BLOCKING);
860
861         return 0;
862 }
863
864 int RecvPcsxInfo() {
865         int tmp;
866
867         if (NET_recvData == NULL || NET_sendData == NULL)
868                 return 0;
869
870         boolean Sio_old = 0;
871         boolean SpuIrq_old = 0;
872         boolean RCntFix_old = 0;
873         NET_recvData(&Config.Xa, sizeof(Config.Xa), PSE_NET_BLOCKING);
874         NET_recvData(&Sio_old, sizeof(Sio_old), PSE_NET_BLOCKING);
875         NET_recvData(&SpuIrq_old, sizeof(SpuIrq_old), PSE_NET_BLOCKING);
876         NET_recvData(&RCntFix_old, sizeof(RCntFix_old), PSE_NET_BLOCKING);
877         NET_recvData(&Config.PsxType, sizeof(Config.PsxType), PSE_NET_BLOCKING);
878
879         tmp = Config.Cpu;
880         NET_recvData(&Config.Cpu, sizeof(Config.Cpu), PSE_NET_BLOCKING);
881         if (tmp != Config.Cpu) {
882                 psxCpu->Shutdown();
883 #ifndef DRC_DISABLE
884                 if (Config.Cpu == CPU_INTERPRETER) psxCpu = &psxInt;
885                 else psxCpu = &psxRec;
886 #else
887                 psxCpu = &psxInt;
888 #endif
889                 if (psxCpu->Init() == -1) {
890                         SysClose(); return -1;
891                 }
892                 psxCpu->Reset();
893                 psxCpu->Notify(R3000ACPU_NOTIFY_AFTER_LOAD, NULL);
894         }
895
896         return 0;
897 }
898
899 // remove the leading and trailing spaces in a string
900 void trim(char *str) {
901         int pos = 0;
902         char *dest = str;
903
904         // skip leading blanks
905         while (str[pos] <= ' ' && str[pos] > 0)
906                 pos++;
907
908         while (str[pos]) {
909                 *(dest++) = str[pos];
910                 pos++;
911         }
912
913         *(dest--) = '\0'; // store the null
914
915         // remove trailing blanks
916         while (dest >= str && *dest <= ' ' && *dest > 0)
917                 *(dest--) = '\0';
918 }
919
920 // lookup table for crc calculation
921 static unsigned short crctab[256] = {
922         0x0000, 0x1021, 0x2042, 0x3063, 0x4084, 0x50A5, 0x60C6, 0x70E7, 0x8108,
923         0x9129, 0xA14A, 0xB16B, 0xC18C, 0xD1AD, 0xE1CE, 0xF1EF, 0x1231, 0x0210,
924         0x3273, 0x2252, 0x52B5, 0x4294, 0x72F7, 0x62D6, 0x9339, 0x8318, 0xB37B,
925         0xA35A, 0xD3BD, 0xC39C, 0xF3FF, 0xE3DE, 0x2462, 0x3443, 0x0420, 0x1401,
926         0x64E6, 0x74C7, 0x44A4, 0x5485, 0xA56A, 0xB54B, 0x8528, 0x9509, 0xE5EE,
927         0xF5CF, 0xC5AC, 0xD58D, 0x3653, 0x2672, 0x1611, 0x0630, 0x76D7, 0x66F6,
928         0x5695, 0x46B4, 0xB75B, 0xA77A, 0x9719, 0x8738, 0xF7DF, 0xE7FE, 0xD79D,
929         0xC7BC, 0x48C4, 0x58E5, 0x6886, 0x78A7, 0x0840, 0x1861, 0x2802, 0x3823,
930         0xC9CC, 0xD9ED, 0xE98E, 0xF9AF, 0x8948, 0x9969, 0xA90A, 0xB92B, 0x5AF5,
931         0x4AD4, 0x7AB7, 0x6A96, 0x1A71, 0x0A50, 0x3A33, 0x2A12, 0xDBFD, 0xCBDC,
932         0xFBBF, 0xEB9E, 0x9B79, 0x8B58, 0xBB3B, 0xAB1A, 0x6CA6, 0x7C87, 0x4CE4,
933         0x5CC5, 0x2C22, 0x3C03, 0x0C60, 0x1C41, 0xEDAE, 0xFD8F, 0xCDEC, 0xDDCD,
934         0xAD2A, 0xBD0B, 0x8D68, 0x9D49, 0x7E97, 0x6EB6, 0x5ED5, 0x4EF4, 0x3E13,
935         0x2E32, 0x1E51, 0x0E70, 0xFF9F, 0xEFBE, 0xDFDD, 0xCFFC, 0xBF1B, 0xAF3A,
936         0x9F59, 0x8F78, 0x9188, 0x81A9, 0xB1CA, 0xA1EB, 0xD10C, 0xC12D, 0xF14E,
937         0xE16F, 0x1080, 0x00A1, 0x30C2, 0x20E3, 0x5004, 0x4025, 0x7046, 0x6067,
938         0x83B9, 0x9398, 0xA3FB, 0xB3DA, 0xC33D, 0xD31C, 0xE37F, 0xF35E, 0x02B1,
939         0x1290, 0x22F3, 0x32D2, 0x4235, 0x5214, 0x6277, 0x7256, 0xB5EA, 0xA5CB,
940         0x95A8, 0x8589, 0xF56E, 0xE54F, 0xD52C, 0xC50D, 0x34E2, 0x24C3, 0x14A0,
941         0x0481, 0x7466, 0x6447, 0x5424, 0x4405, 0xA7DB, 0xB7FA, 0x8799, 0x97B8,
942         0xE75F, 0xF77E, 0xC71D, 0xD73C, 0x26D3, 0x36F2, 0x0691, 0x16B0, 0x6657,
943         0x7676, 0x4615, 0x5634, 0xD94C, 0xC96D, 0xF90E, 0xE92F, 0x99C8, 0x89E9,
944         0xB98A, 0xA9AB, 0x5844, 0x4865, 0x7806, 0x6827, 0x18C0, 0x08E1, 0x3882,
945         0x28A3, 0xCB7D, 0xDB5C, 0xEB3F, 0xFB1E, 0x8BF9, 0x9BD8, 0xABBB, 0xBB9A,
946         0x4A75, 0x5A54, 0x6A37, 0x7A16, 0x0AF1, 0x1AD0, 0x2AB3, 0x3A92, 0xFD2E,
947         0xED0F, 0xDD6C, 0xCD4D, 0xBDAA, 0xAD8B, 0x9DE8, 0x8DC9, 0x7C26, 0x6C07,
948         0x5C64, 0x4C45, 0x3CA2, 0x2C83, 0x1CE0, 0x0CC1, 0xEF1F, 0xFF3E, 0xCF5D,
949         0xDF7C, 0xAF9B, 0xBFBA, 0x8FD9, 0x9FF8, 0x6E17, 0x7E36, 0x4E55, 0x5E74,
950         0x2E93, 0x3EB2, 0x0ED1, 0x1EF0
951 };
952
953 u16 calcCrc(u8 *d, int len) {
954         u16 crc = 0;
955         int i;
956
957         for (i = 0; i < len; i++) {
958                 crc = crctab[(crc >> 8) ^ d[i]] ^ (crc << 8);
959         }
960
961         return ~crc;
962 }