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