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