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