libretro: Cleanup Makefile.libretro.
[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:\\%256s", exename);
212                 if (GetCdromFile(mdir, time, exename) == -1) {
213                         sscanf((char *)buf + 12, "BOOT = cdrom:%256s", 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:\\%256s", 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, 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         CdromLabel[0] = '\0';
326         CdromId[0] = '\0';
327
328         strncpy(CdromLabel, buf + 52, 32);
329
330         // skip head and sub, and go to the root directory record
331         dir = (struct iso_directory_record *)&buf[12 + 156]; 
332
333         mmssdd(dir->extent, (char *)time);
334
335         READDIR(mdir);
336
337         if (GetCdromFile(mdir, time, "SYSTEM.CNF;1") != -1) {
338                 READTRACK();
339
340                 sscanf(buf + 12, "BOOT = cdrom:\\%256s", exename);
341                 if (GetCdromFile(mdir, time, exename) == -1) {
342                         sscanf(buf + 12, "BOOT = cdrom:%256s", exename);
343                         if (GetCdromFile(mdir, time, exename) == -1) {
344                                 char *ptr = strstr(buf + 12, "cdrom:");                 // possibly the executable is in some subdir
345                                 if (ptr != NULL) {
346                                         ptr += 6;
347                                         while (*ptr == '\\' || *ptr == '/') ptr++;
348                                         strncpy(exename, ptr, 255);
349                                         exename[255] = '\0';
350                                         ptr = exename;
351                                         while (*ptr != '\0' && *ptr != '\r' && *ptr != '\n') ptr++;
352                                         *ptr = '\0';
353                                         if (GetCdromFile(mdir, time, exename) == -1)
354                                                 return -1;              // main executable not found
355                                 } else
356                                         return -1;
357                         }
358                 }
359         } else if (GetCdromFile(mdir, time, "PSX.EXE;1") != -1) {
360                 strcpy(exename, "PSX.EXE;1");
361                 strcpy(CdromId, "SLUS99999");
362         } else
363                 return -1;              // SYSTEM.CNF and PSX.EXE not found
364
365         if (CdromId[0] == '\0') {
366                 i = strlen(exename);
367                 if (i >= 2) {
368                         if (exename[i - 2] == ';') i-= 2;
369                         c = 8; i--;
370                         while (i >= 0 && c >= 0) {
371                                 if (isalnum(exename[i])) CdromId[c--] = exename[i];
372                                 i--;
373                         }
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
392         BuildPPFCache();
393
394         return 0;
395 }
396
397 static int PSXGetFileType(FILE *f) {
398         unsigned long current;
399         u8 mybuf[2048];
400         EXE_HEADER *exe_hdr;
401         FILHDR *coff_hdr;
402
403         current = ftell(f);
404         fseek(f, 0L, SEEK_SET);
405         fread(mybuf, 2048, 1, f);
406         fseek(f, current, SEEK_SET);
407
408         exe_hdr = (EXE_HEADER *)mybuf;
409         if (memcmp(exe_hdr->id, "PS-X EXE", 8) == 0)
410                 return PSX_EXE;
411
412         if (mybuf[0] == 'C' && mybuf[1] == 'P' && mybuf[2] == 'E')
413                 return CPE_EXE;
414
415         coff_hdr = (FILHDR *)mybuf;
416         if (SWAPu16(coff_hdr->f_magic) == 0x0162)
417                 return COFF_EXE;
418
419         return INVALID_EXE;
420 }
421
422 // temporary pandora workaround..
423 // FIXME: remove
424 size_t fread_to_ram(void *ptr, size_t size, size_t nmemb, FILE *stream)
425 {
426         void *tmp;
427         size_t ret = 0;
428         
429         tmp = malloc(size * nmemb);
430         if (tmp) {
431                 ret = fread(tmp, size, nmemb, stream);
432                 memcpy(ptr, tmp, size * nmemb);
433                 free(tmp);
434         }
435         return ret;
436 }
437
438 int Load(const char *ExePath) {
439         FILE *tmpFile;
440         EXE_HEADER tmpHead;
441         int type;
442         int retval = 0;
443         u8 opcode;
444         u32 section_address, section_size;
445         void *mem;
446
447         strncpy(CdromId, "SLUS99999", 9);
448         strncpy(CdromLabel, "SLUS_999.99", 11);
449
450         tmpFile = fopen(ExePath, "rb");
451         if (tmpFile == NULL) {
452                 SysPrintf(_("Error opening file: %s.\n"), ExePath);
453                 retval = -1;
454         } else {
455                 type = PSXGetFileType(tmpFile);
456                 switch (type) {
457                         case PSX_EXE:
458                                 fread(&tmpHead,sizeof(EXE_HEADER),1,tmpFile);
459                                 section_address = SWAP32(tmpHead.t_addr);
460                                 section_size = SWAP32(tmpHead.t_size);
461                                 mem = PSXM(section_address);
462                                 if (mem != NULL) {
463                                         fseek(tmpFile, 0x800, SEEK_SET);                
464                                         fread_to_ram(mem, section_size, 1, tmpFile);
465                                         psxCpu->Clear(section_address, section_size / 4);
466                                 }
467                                 fclose(tmpFile);
468                                 psxRegs.pc = SWAP32(tmpHead.pc0);
469                                 psxRegs.GPR.n.gp = SWAP32(tmpHead.gp0);
470                                 psxRegs.GPR.n.sp = SWAP32(tmpHead.s_addr); 
471                                 if (psxRegs.GPR.n.sp == 0)
472                                         psxRegs.GPR.n.sp = 0x801fff00;
473                                 retval = 0;
474                                 break;
475                         case CPE_EXE:
476                                 fseek(tmpFile, 6, SEEK_SET); /* Something tells me we should go to 4 and read the "08 00" here... */
477                                 do {
478                                         fread(&opcode, 1, 1, tmpFile);
479                                         switch (opcode) {
480                                                 case 1: /* Section loading */
481                                                         fread(&section_address, 4, 1, tmpFile);
482                                                         fread(&section_size, 4, 1, tmpFile);
483                                                         section_address = SWAPu32(section_address);
484                                                         section_size = SWAPu32(section_size);
485 #ifdef EMU_LOG
486                                                         EMU_LOG("Loading %08X bytes from %08X to %08X\n", section_size, ftell(tmpFile), section_address);
487 #endif
488                                                         mem = PSXM(section_address);
489                                                         if (mem != NULL) {
490                                                                 fread_to_ram(mem, section_size, 1, tmpFile);
491                                                                 psxCpu->Clear(section_address, section_size / 4);
492                                                         }
493                                                         break;
494                                                 case 3: /* register loading (PC only?) */
495                                                         fseek(tmpFile, 2, SEEK_CUR); /* unknown field */
496                                                         fread(&psxRegs.pc, 4, 1, tmpFile);
497                                                         psxRegs.pc = SWAPu32(psxRegs.pc);
498                                                         break;
499                                                 case 0: /* End of file */
500                                                         break;
501                                                 default:
502                                                         SysPrintf(_("Unknown CPE opcode %02x at position %08x.\n"), opcode, ftell(tmpFile) - 1);
503                                                         retval = -1;
504                                                         break;
505                                         }
506                                 } while (opcode != 0 && retval == 0);
507                                 break;
508                         case COFF_EXE:
509                                 SysPrintf(_("COFF files not supported.\n"));
510                                 retval = -1;
511                                 break;
512                         case INVALID_EXE:
513                                 SysPrintf(_("This file does not appear to be a valid PSX EXE file.\n"));
514                                 SysPrintf(_("(did you forget -cdfile ?)\n"));
515                                 retval = -1;
516                                 break;
517                 }
518         }
519
520         if (retval != 0) {
521                 CdromId[0] = '\0';
522                 CdromLabel[0] = '\0';
523         }
524
525         return retval;
526 }
527
528 // STATES
529
530 static void *zlib_open(const char *name, const char *mode)
531 {
532         return gzopen(name, mode);
533 }
534
535 static int zlib_read(void *file, void *buf, u32 len)
536 {
537         return gzread(file, buf, len);
538 }
539
540 static int zlib_write(void *file, const void *buf, u32 len)
541 {
542         return gzwrite(file, buf, len);
543 }
544
545 static long zlib_seek(void *file, long offs, int whence)
546 {
547         return gzseek(file, offs, whence);
548 }
549
550 static void zlib_close(void *file)
551 {
552         gzclose(file);
553 }
554
555 struct PcsxSaveFuncs SaveFuncs = {
556         zlib_open, zlib_read, zlib_write, zlib_seek, zlib_close
557 };
558
559 static const char PcsxHeader[32] = "STv4 PCSX v" PACKAGE_VERSION;
560
561 // Savestate Versioning!
562 // If you make changes to the savestate version, please increment the value below.
563 static const u32 SaveVersion = 0x8b410006;
564
565 int SaveState(const char *file) {
566         void *f;
567         GPUFreeze_t *gpufP;
568         SPUFreeze_t *spufP;
569         int Size;
570         unsigned char *pMem;
571
572         f = SaveFuncs.open(file, "wb");
573         if (f == NULL) return -1;
574
575         new_dyna_save();
576
577         SaveFuncs.write(f, (void *)PcsxHeader, 32);
578         SaveFuncs.write(f, (void *)&SaveVersion, sizeof(u32));
579         SaveFuncs.write(f, (void *)&Config.HLE, sizeof(boolean));
580
581         pMem = (unsigned char *)malloc(128 * 96 * 3);
582         if (pMem == NULL) return -1;
583         GPU_getScreenPic(pMem);
584         SaveFuncs.write(f, pMem, 128 * 96 * 3);
585         free(pMem);
586
587         if (Config.HLE)
588                 psxBiosFreeze(1);
589
590         SaveFuncs.write(f, psxM, 0x00200000);
591         SaveFuncs.write(f, psxR, 0x00080000);
592         SaveFuncs.write(f, psxH, 0x00010000);
593         SaveFuncs.write(f, (void *)&psxRegs, sizeof(psxRegs));
594
595         // gpu
596         gpufP = (GPUFreeze_t *)malloc(sizeof(GPUFreeze_t));
597         gpufP->ulFreezeVersion = 1;
598         GPU_freeze(1, gpufP);
599         SaveFuncs.write(f, gpufP, sizeof(GPUFreeze_t));
600         free(gpufP);
601
602         // spu
603         spufP = (SPUFreeze_t *) malloc(16);
604         SPU_freeze(2, spufP, psxRegs.cycle);
605         Size = spufP->Size; SaveFuncs.write(f, &Size, 4);
606         free(spufP);
607         spufP = (SPUFreeze_t *) malloc(Size);
608         SPU_freeze(1, spufP, psxRegs.cycle);
609         SaveFuncs.write(f, spufP, Size);
610         free(spufP);
611
612         sioFreeze(f, 1);
613         cdrFreeze(f, 1);
614         psxHwFreeze(f, 1);
615         psxRcntFreeze(f, 1);
616         mdecFreeze(f, 1);
617
618         SaveFuncs.close(f);
619
620         new_dyna_after_save();
621
622         return 0;
623 }
624
625 int LoadState(const char *file) {
626         void *f;
627         GPUFreeze_t *gpufP;
628         SPUFreeze_t *spufP;
629         int Size;
630         char header[32];
631         u32 version;
632         boolean hle;
633
634         f = SaveFuncs.open(file, "rb");
635         if (f == NULL) return -1;
636
637         SaveFuncs.read(f, header, sizeof(header));
638         SaveFuncs.read(f, &version, sizeof(u32));
639         SaveFuncs.read(f, &hle, sizeof(boolean));
640
641         if (strncmp("STv4 PCSX", header, 9) != 0 || version != SaveVersion) {
642                 SaveFuncs.close(f);
643                 return -1;
644         }
645         Config.HLE = hle;
646
647         if (Config.HLE)
648                 psxBiosInit();
649
650         psxCpu->Reset();
651         SaveFuncs.seek(f, 128 * 96 * 3, SEEK_CUR);
652
653         SaveFuncs.read(f, psxM, 0x00200000);
654         SaveFuncs.read(f, psxR, 0x00080000);
655         SaveFuncs.read(f, psxH, 0x00010000);
656         SaveFuncs.read(f, (void *)&psxRegs, sizeof(psxRegs));
657
658         if (Config.HLE)
659                 psxBiosFreeze(0);
660
661         // gpu
662         gpufP = (GPUFreeze_t *)malloc(sizeof(GPUFreeze_t));
663         SaveFuncs.read(f, gpufP, sizeof(GPUFreeze_t));
664         GPU_freeze(0, gpufP);
665         free(gpufP);
666         if (HW_GPU_STATUS == 0)
667                 HW_GPU_STATUS = GPU_readStatus();
668
669         // spu
670         SaveFuncs.read(f, &Size, 4);
671         spufP = (SPUFreeze_t *)malloc(Size);
672         SaveFuncs.read(f, spufP, Size);
673         SPU_freeze(0, spufP, psxRegs.cycle);
674         free(spufP);
675
676         sioFreeze(f, 0);
677         cdrFreeze(f, 0);
678         psxHwFreeze(f, 0);
679         psxRcntFreeze(f, 0);
680         mdecFreeze(f, 0);
681
682         SaveFuncs.close(f);
683         new_dyna_restore();
684
685         return 0;
686 }
687
688 int CheckState(const char *file) {
689         void *f;
690         char header[32];
691         u32 version;
692         boolean hle;
693
694         f = SaveFuncs.open(file, "rb");
695         if (f == NULL) return -1;
696
697         SaveFuncs.read(f, header, sizeof(header));
698         SaveFuncs.read(f, &version, sizeof(u32));
699         SaveFuncs.read(f, &hle, sizeof(boolean));
700
701         SaveFuncs.close(f);
702
703         if (strncmp("STv4 PCSX", header, 9) != 0 || version != SaveVersion)
704                 return -1;
705
706         return 0;
707 }
708
709 // NET Function Helpers
710
711 int SendPcsxInfo() {
712         if (NET_recvData == NULL || NET_sendData == NULL)
713                 return 0;
714
715         NET_sendData(&Config.Xa, sizeof(Config.Xa), PSE_NET_BLOCKING);
716         NET_sendData(&Config.Sio, sizeof(Config.Sio), PSE_NET_BLOCKING);
717         NET_sendData(&Config.SpuIrq, sizeof(Config.SpuIrq), PSE_NET_BLOCKING);
718         NET_sendData(&Config.RCntFix, sizeof(Config.RCntFix), PSE_NET_BLOCKING);
719         NET_sendData(&Config.PsxType, sizeof(Config.PsxType), PSE_NET_BLOCKING);
720         NET_sendData(&Config.Cpu, sizeof(Config.Cpu), PSE_NET_BLOCKING);
721
722         return 0;
723 }
724
725 int RecvPcsxInfo() {
726         int tmp;
727
728         if (NET_recvData == NULL || NET_sendData == NULL)
729                 return 0;
730
731         NET_recvData(&Config.Xa, sizeof(Config.Xa), PSE_NET_BLOCKING);
732         NET_recvData(&Config.Sio, sizeof(Config.Sio), PSE_NET_BLOCKING);
733         NET_recvData(&Config.SpuIrq, sizeof(Config.SpuIrq), PSE_NET_BLOCKING);
734         NET_recvData(&Config.RCntFix, sizeof(Config.RCntFix), PSE_NET_BLOCKING);
735         NET_recvData(&Config.PsxType, sizeof(Config.PsxType), PSE_NET_BLOCKING);
736
737         SysUpdate();
738
739         tmp = Config.Cpu;
740         NET_recvData(&Config.Cpu, sizeof(Config.Cpu), PSE_NET_BLOCKING);
741         if (tmp != Config.Cpu) {
742                 psxCpu->Shutdown();
743 #ifdef PSXREC
744                 if (Config.Cpu == CPU_INTERPRETER) psxCpu = &psxInt;
745                 else psxCpu = &psxRec;
746 #else
747                 psxCpu = &psxInt;
748 #endif
749                 if (psxCpu->Init() == -1) {
750                         SysClose(); return -1;
751                 }
752                 psxCpu->Reset();
753         }
754
755         return 0;
756 }
757
758 // remove the leading and trailing spaces in a string
759 void trim(char *str) {
760         int pos = 0;
761         char *dest = str;
762
763         // skip leading blanks
764         while (str[pos] <= ' ' && str[pos] > 0)
765                 pos++;
766
767         while (str[pos]) {
768                 *(dest++) = str[pos];
769                 pos++;
770         }
771
772         *(dest--) = '\0'; // store the null
773
774         // remove trailing blanks
775         while (dest >= str && *dest <= ' ' && *dest > 0)
776                 *(dest--) = '\0';
777 }
778
779 // lookup table for crc calculation
780 static unsigned short crctab[256] = {
781         0x0000, 0x1021, 0x2042, 0x3063, 0x4084, 0x50A5, 0x60C6, 0x70E7, 0x8108,
782         0x9129, 0xA14A, 0xB16B, 0xC18C, 0xD1AD, 0xE1CE, 0xF1EF, 0x1231, 0x0210,
783         0x3273, 0x2252, 0x52B5, 0x4294, 0x72F7, 0x62D6, 0x9339, 0x8318, 0xB37B,
784         0xA35A, 0xD3BD, 0xC39C, 0xF3FF, 0xE3DE, 0x2462, 0x3443, 0x0420, 0x1401,
785         0x64E6, 0x74C7, 0x44A4, 0x5485, 0xA56A, 0xB54B, 0x8528, 0x9509, 0xE5EE,
786         0xF5CF, 0xC5AC, 0xD58D, 0x3653, 0x2672, 0x1611, 0x0630, 0x76D7, 0x66F6,
787         0x5695, 0x46B4, 0xB75B, 0xA77A, 0x9719, 0x8738, 0xF7DF, 0xE7FE, 0xD79D,
788         0xC7BC, 0x48C4, 0x58E5, 0x6886, 0x78A7, 0x0840, 0x1861, 0x2802, 0x3823,
789         0xC9CC, 0xD9ED, 0xE98E, 0xF9AF, 0x8948, 0x9969, 0xA90A, 0xB92B, 0x5AF5,
790         0x4AD4, 0x7AB7, 0x6A96, 0x1A71, 0x0A50, 0x3A33, 0x2A12, 0xDBFD, 0xCBDC,
791         0xFBBF, 0xEB9E, 0x9B79, 0x8B58, 0xBB3B, 0xAB1A, 0x6CA6, 0x7C87, 0x4CE4,
792         0x5CC5, 0x2C22, 0x3C03, 0x0C60, 0x1C41, 0xEDAE, 0xFD8F, 0xCDEC, 0xDDCD,
793         0xAD2A, 0xBD0B, 0x8D68, 0x9D49, 0x7E97, 0x6EB6, 0x5ED5, 0x4EF4, 0x3E13,
794         0x2E32, 0x1E51, 0x0E70, 0xFF9F, 0xEFBE, 0xDFDD, 0xCFFC, 0xBF1B, 0xAF3A,
795         0x9F59, 0x8F78, 0x9188, 0x81A9, 0xB1CA, 0xA1EB, 0xD10C, 0xC12D, 0xF14E,
796         0xE16F, 0x1080, 0x00A1, 0x30C2, 0x20E3, 0x5004, 0x4025, 0x7046, 0x6067,
797         0x83B9, 0x9398, 0xA3FB, 0xB3DA, 0xC33D, 0xD31C, 0xE37F, 0xF35E, 0x02B1,
798         0x1290, 0x22F3, 0x32D2, 0x4235, 0x5214, 0x6277, 0x7256, 0xB5EA, 0xA5CB,
799         0x95A8, 0x8589, 0xF56E, 0xE54F, 0xD52C, 0xC50D, 0x34E2, 0x24C3, 0x14A0,
800         0x0481, 0x7466, 0x6447, 0x5424, 0x4405, 0xA7DB, 0xB7FA, 0x8799, 0x97B8,
801         0xE75F, 0xF77E, 0xC71D, 0xD73C, 0x26D3, 0x36F2, 0x0691, 0x16B0, 0x6657,
802         0x7676, 0x4615, 0x5634, 0xD94C, 0xC96D, 0xF90E, 0xE92F, 0x99C8, 0x89E9,
803         0xB98A, 0xA9AB, 0x5844, 0x4865, 0x7806, 0x6827, 0x18C0, 0x08E1, 0x3882,
804         0x28A3, 0xCB7D, 0xDB5C, 0xEB3F, 0xFB1E, 0x8BF9, 0x9BD8, 0xABBB, 0xBB9A,
805         0x4A75, 0x5A54, 0x6A37, 0x7A16, 0x0AF1, 0x1AD0, 0x2AB3, 0x3A92, 0xFD2E,
806         0xED0F, 0xDD6C, 0xCD4D, 0xBDAA, 0xAD8B, 0x9DE8, 0x8DC9, 0x7C26, 0x6C07,
807         0x5C64, 0x4C45, 0x3CA2, 0x2C83, 0x1CE0, 0x0CC1, 0xEF1F, 0xFF3E, 0xCF5D,
808         0xDF7C, 0xAF9B, 0xBFBA, 0x8FD9, 0x9FF8, 0x6E17, 0x7E36, 0x4E55, 0x5E74,
809         0x2E93, 0x3EB2, 0x0ED1, 0x1EF0
810 };
811
812 u16 calcCrc(u8 *d, int len) {
813         u16 crc = 0;
814         int i;
815
816         for (i = 0; i < len; i++) {
817                 crc = crctab[(crc >> 8) ^ d[i]] ^ (crc << 8);
818         }
819
820         return ~crc;
821 }