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