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