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