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