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