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