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