try to unbreak bigendian
[pcsx_rearmed.git] / libpcsxcore / misc.c
CommitLineData
ef79bbde
P
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
81dbbf4c 24#include <stddef.h>
7b75929b 25#include <assert.h>
ef79bbde
P
26#include "misc.h"
27#include "cdrom.h"
28#include "mdec.h"
ddbaf678 29#include "gpu.h"
ef79bbde 30#include "ppf.h"
eedfe806 31#include "database.h"
496d88d4 32#include <zlib.h>
ef79bbde
P
33
34char CdromId[10] = "";
35char 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
45struct 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
1c654475 59static void mmssdd( char *b, char *p )
ef79bbde
P
60{
61 int m, s, d;
1c654475 62 unsigned char *ub = (void *)b;
63 int block = (ub[3] << 24) | (ub[2] << 16) | (ub[1] << 8) | ub[0];
ef79bbde
P
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;
7a8d521f 73 d = ((d / 10) << 4) | d % 10;
ef79bbde
P
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() \
415213c9 94 if (!CDR_readTrack(time)) return -1; \
305c8c93 95 buf = (void *)CDR_getBuffer(); \
96 if (buf == NULL) return -1; \
97 else CheckPPFCache((u8 *)buf, time[0], time[1], time[2]);
ef79bbde
P
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
305c8c93 107int GetCdromFile(u8 *mdir, u8 *time, char *filename) {
ef79bbde 108 struct iso_directory_record *dir;
5be6eaeb 109 int retval = -1;
305c8c93 110 u8 ddir[4096];
ef79bbde
P
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 }
1c2c2bfb 123 i += (u8)dir->length[0];
ef79bbde
P
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);
5be6eaeb 139 retval = 0;
ef79bbde
P
140 break;
141 }
142 }
143 }
5be6eaeb 144 return retval;
ef79bbde
P
145}
146
6d75addf 147static 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;
14b3bd95 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;
6d75addf 159
160 psxCpu->Notify(R3000ACPU_NOTIFY_AFTER_LOAD, NULL);
161}
162
7b75929b 163void 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
dc4fa8bc 171static 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
ef79bbde
P
180int LoadCdrom() {
181 EXE_HEADER tmpHead;
182 struct iso_directory_record *dir;
183 u8 time[4], *buf;
184 u8 mdir[4096];
305c8c93 185 char exename[256];
dc4fa8bc 186 u32 cnf_tcb = 4;
187 u32 cnf_event = 16;
188 u32 cnf_stack = 0;
189 u32 sp = 0;
190 int ret;
ef79bbde 191
7b75929b 192 if (!Config.HLE) {
da65071f 193 if (psxRegs.pc != 0x80030000) // BiosBootBypass'ed or custom BIOS?
194 return 0;
195 if (Config.SlowBoot)
196 return 0;
ef79bbde
P
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
7a8d521f 204 dir = (struct iso_directory_record*) &buf[12+156];
ef79bbde
P
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;
7b75929b 214 strcpy(exename, "PSX.EXE;1");
ef79bbde
P
215
216 READTRACK();
217 }
218 else {
219 // read the SYSTEM.CNF
220 READTRACK();
dc4fa8bc 221 buf[1023] = 0;
ef79bbde 222
dc4fa8bc 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) {
305c8c93 227 char *ptr = strstr((char *)buf + 12, "cdrom:");
ef79bbde
P
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 }
dc4fa8bc 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);
ef79bbde
P
247
248 // Read the EXE-Header
249 READTRACK();
250 }
251
252 memcpy(&tmpHead, buf + 12, sizeof(EXE_HEADER));
253
14b3bd95 254 SysPrintf("manual booting '%s' pc=%x\n", exename, SWAP32(tmpHead.pc0));
dc4fa8bc 255 sp = SWAP32(tmpHead.s_addr);
256 if (cnf_stack)
257 sp = cnf_stack;
258 SetBootRegs(SWAP32(tmpHead.pc0), SWAP32(tmpHead.gp0), sp);
ef79bbde
P
259
260 tmpHead.t_size = SWAP32(tmpHead.t_size);
261 tmpHead.t_addr = SWAP32(tmpHead.t_addr);
262
ebe6ff2b 263 psxCpu->Clear(tmpHead.t_addr, tmpHead.t_size / 4);
86c70511 264 //psxCpu->Reset();
ebe6ff2b 265
ef79bbde 266 // Read the rest of the main executable
ebe6ff2b 267 while (tmpHead.t_size & ~2047) {
ef79bbde
P
268 void *ptr = (void *)PSXM(tmpHead.t_addr);
269
270 incTime();
271 READTRACK();
272
7a8d521f 273 if (ptr != INVALID_PTR) memcpy(ptr, buf+12, 2048);
ef79bbde
P
274
275 tmpHead.t_size -= 2048;
276 tmpHead.t_addr += 2048;
277 }
278
279 return 0;
280}
281
282int LoadCdromFile(const char *filename, EXE_HEADER *head) {
283 struct iso_directory_record *dir;
284 u8 time[4],*buf;
305c8c93 285 u8 mdir[4096];
286 char exename[256];
457a46ec 287 const char *p1, *p2;
ef79bbde 288 u32 size, addr;
305c8c93 289 void *mem;
ef79bbde 290
86c70511 291 if (filename == INVALID_PTR)
292 return -1;
293
457a46ec 294 p1 = filename;
295 if ((p2 = strchr(p1, ':')))
296 p1 = p2 + 1;
297 while (*p1 == '\\')
298 p1++;
299 snprintf(exename, sizeof(exename), "%s", p1);
ef79bbde
P
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
7a8d521f 306 dir = (struct iso_directory_record *)&buf[12 + 156];
ef79bbde
P
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));
86c70511 317 size = SWAP32(head->t_size);
318 addr = SWAP32(head->t_addr);
ef79bbde 319
ebe6ff2b 320 psxCpu->Clear(addr, size / 4);
86c70511 321 //psxCpu->Reset();
ebe6ff2b 322
323 while (size & ~2047) {
ef79bbde
P
324 incTime();
325 READTRACK();
326
305c8c93 327 mem = PSXM(addr);
7a8d521f 328 if (mem != INVALID_PTR)
305c8c93 329 memcpy(mem, buf + 12, 2048);
ef79bbde
P
330
331 size -= 2048;
332 addr += 2048;
333 }
334
335 return 0;
336}
337
338int CheckCdrom() {
339 struct iso_directory_record *dir;
305c8c93 340 unsigned char time[4];
341 char *buf;
ef79bbde
P
342 unsigned char mdir[4096];
343 char exename[256];
e06ee7f4 344 int i, len, c;
ef79bbde
P
345
346 FreePPFCache();
347
348 time[0] = itob(0);
349 time[1] = itob(2);
350 time[2] = itob(0x10);
351
352 READTRACK();
353
e06ee7f4 354 memset(CdromLabel, 0, sizeof(CdromLabel));
355 memset(CdromId, 0, sizeof(CdromId));
356 memset(exename, 0, sizeof(exename));
ef79bbde
P
357
358 strncpy(CdromLabel, buf + 52, 32);
359
360 // skip head and sub, and go to the root directory record
7a8d521f 361 dir = (struct iso_directory_record *)&buf[12 + 156];
ef79bbde
P
362
363 mmssdd(dir->extent, (char *)time);
364
365 READDIR(mdir);
366
367 if (GetCdromFile(mdir, time, "SYSTEM.CNF;1") != -1) {
368 READTRACK();
369
9a9bcd78 370 sscanf(buf + 12, "BOOT = cdrom:\\%255s", exename);
ef79bbde 371 if (GetCdromFile(mdir, time, exename) == -1) {
9a9bcd78 372 sscanf(buf + 12, "BOOT = cdrom:%255s", exename);
ef79bbde
P
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 }
7a8d521f 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 }
ef79bbde
P
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') {
e06ee7f4 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];
ef79bbde
P
411 }
412 }
413
0e2e3f49 414 if (CdromId[0] == '\0')
415 strcpy(CdromId, "SLUS99999");
416
ef79bbde 417 if (Config.PsxAuto) { // autodetect system (pal or ntsc)
40337130 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.
ef79bbde
P
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);
e06ee7f4 434 SysPrintf(_("CD-ROM EXE Name: %.255s\n"), exename);
eedfe806 435
436 Apply_Hacks_Cdrom();
ef79bbde
P
437
438 BuildPPFCache();
439
440 return 0;
441}
442
443static 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);
7a8d521f 451 if (fread(&mybuf, 1, sizeof(mybuf), f) != sizeof(mybuf))
452 goto io_fail;
453
ef79bbde
P
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;
7a8d521f 468
469io_fail:
470#ifndef NDEBUG
471 SysPrintf(_("File IO error in <%s:%s>.\n"), __FILE__, __func__);
472#endif
473 return INVALID_EXE;
ef79bbde
P
474}
475
96bef96f 476// temporary pandora workaround..
477// FIXME: remove
478size_t fread_to_ram(void *ptr, size_t size, size_t nmemb, FILE *stream)
479{
480 void *tmp;
481 size_t ret = 0;
7a8d521f 482
96bef96f 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
ef79bbde
P
492int 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;
ebe6ff2b 499 void *mem;
ef79bbde 500
7a8d521f 501 strcpy(CdromId, "SLUS99999");
502 strcpy(CdromLabel, "SLUS_999.99");
ef79bbde
P
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:
7a8d521f 512 if (fread(&tmpHead, 1, sizeof(EXE_HEADER), tmpFile) != sizeof(EXE_HEADER))
513 goto fail_io;
ebe6ff2b 514 section_address = SWAP32(tmpHead.t_addr);
515 section_size = SWAP32(tmpHead.t_size);
516 mem = PSXM(section_address);
7a8d521f 517 if (mem != INVALID_PTR) {
518 fseek(tmpFile, 0x800, SEEK_SET);
96bef96f 519 fread_to_ram(mem, section_size, 1, tmpFile);
ebe6ff2b 520 psxCpu->Clear(section_address, section_size / 4);
521 }
6d75addf 522 SetBootRegs(SWAP32(tmpHead.pc0), SWAP32(tmpHead.gp0),
523 SWAP32(tmpHead.s_addr));
ef79bbde
P
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 {
7a8d521f 529 if (fread(&opcode, 1, sizeof(opcode), tmpFile) != sizeof(opcode))
530 goto fail_io;
ef79bbde
P
531 switch (opcode) {
532 case 1: /* Section loading */
7a8d521f 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;
ef79bbde
P
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
ebe6ff2b 542 mem = PSXM(section_address);
7a8d521f 543 if (mem != INVALID_PTR) {
96bef96f 544 fread_to_ram(mem, section_size, 1, tmpFile);
ebe6ff2b 545 psxCpu->Clear(section_address, section_size / 4);
546 }
ef79bbde
P
547 break;
548 case 3: /* register loading (PC only?) */
549 fseek(tmpFile, 2, SEEK_CUR); /* unknown field */
7a8d521f 550 if (fread(&psxRegs.pc, 1, sizeof(psxRegs.pc), tmpFile) != sizeof(psxRegs.pc))
551 goto fail_io;
ef79bbde
P
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:
8f00f96c 568 SysPrintf(_("This file does not appear to be a valid PSX EXE file.\n"));
569 SysPrintf(_("(did you forget -cdfile ?)\n"));
ef79bbde
P
570 retval = -1;
571 break;
572 }
573 }
574
575 if (retval != 0) {
576 CdromId[0] = '\0';
577 CdromLabel[0] = '\0';
578 }
579
7a8d521f 580 if (tmpFile)
581 fclose(tmpFile);
ef79bbde 582 return retval;
7a8d521f 583
584fail_io:
585#ifndef NDEBUG
586 SysPrintf(_("File IO error in <%s:%s>.\n"), __FILE__, __func__);
587#endif
588 fclose(tmpFile);
589 return -1;
ef79bbde
P
590}
591
592// STATES
593
496d88d4 594static void *zlib_open(const char *name, const char *mode)
595{
596 return gzopen(name, mode);
597}
598
599static int zlib_read(void *file, void *buf, u32 len)
600{
601 return gzread(file, buf, len);
602}
603
604static int zlib_write(void *file, const void *buf, u32 len)
605{
606 return gzwrite(file, buf, len);
607}
608
609static long zlib_seek(void *file, long offs, int whence)
610{
611 return gzseek(file, offs, whence);
612}
613
614static void zlib_close(void *file)
615{
616 gzclose(file);
617}
618
619struct PcsxSaveFuncs SaveFuncs = {
620 zlib_open, zlib_read, zlib_write, zlib_seek, zlib_close
621};
622
7a8d521f 623static const char PcsxHeader[32] = "STv4 PCSX v" PCSX_VERSION;
ef79bbde
P
624
625// Savestate Versioning!
626// If you make changes to the savestate version, please increment the value below.
df656dde 627static const u32 SaveVersion = 0x8b410006;
ef79bbde
P
628
629int SaveState(const char *file) {
496d88d4 630 void *f;
cfa5a2af 631 GPUFreeze_t *gpufP = NULL;
632 SPUFreezeHdr_t spufH;
633 SPUFreeze_t *spufP = NULL;
634 unsigned char *pMem = NULL;
635 int result = -1;
ef79bbde 636 int Size;
ef79bbde 637
496d88d4 638 f = SaveFuncs.open(file, "wb");
ef79bbde
P
639 if (f == NULL) return -1;
640
980f7a58 641 psxCpu->Notify(R3000ACPU_NOTIFY_BEFORE_SAVE, NULL);
52082bc1 642
496d88d4 643 SaveFuncs.write(f, (void *)PcsxHeader, 32);
644 SaveFuncs.write(f, (void *)&SaveVersion, sizeof(u32));
645 SaveFuncs.write(f, (void *)&Config.HLE, sizeof(boolean));
ef79bbde
P
646
647 pMem = (unsigned char *)malloc(128 * 96 * 3);
cfa5a2af 648 if (pMem == NULL) goto cleanup;
ef79bbde 649 GPU_getScreenPic(pMem);
496d88d4 650 SaveFuncs.write(f, pMem, 128 * 96 * 3);
ef79bbde
P
651 free(pMem);
652
653 if (Config.HLE)
654 psxBiosFreeze(1);
655
496d88d4 656 SaveFuncs.write(f, psxM, 0x00200000);
657 SaveFuncs.write(f, psxR, 0x00080000);
658 SaveFuncs.write(f, psxH, 0x00010000);
81dbbf4c 659 // only partial save of psxRegisters to maintain savestate compat
660 SaveFuncs.write(f, &psxRegs, offsetof(psxRegisters, gteBusyCycle));
ef79bbde
P
661
662 // gpu
663 gpufP = (GPUFreeze_t *)malloc(sizeof(GPUFreeze_t));
cfa5a2af 664 if (gpufP == NULL) goto cleanup;
ef79bbde
P
665 gpufP->ulFreezeVersion = 1;
666 GPU_freeze(1, gpufP);
496d88d4 667 SaveFuncs.write(f, gpufP, sizeof(GPUFreeze_t));
cfa5a2af 668 free(gpufP); gpufP = NULL;
ef79bbde
P
669
670 // spu
cfa5a2af 671 SPU_freeze(2, (SPUFreeze_t *)&spufH, psxRegs.cycle);
672 Size = spufH.Size; SaveFuncs.write(f, &Size, 4);
ef79bbde 673 spufP = (SPUFreeze_t *) malloc(Size);
cfa5a2af 674 if (spufP == NULL) goto cleanup;
650adfd2 675 SPU_freeze(1, spufP, psxRegs.cycle);
496d88d4 676 SaveFuncs.write(f, spufP, Size);
cfa5a2af 677 free(spufP); spufP = NULL;
ef79bbde
P
678
679 sioFreeze(f, 1);
680 cdrFreeze(f, 1);
681 psxHwFreeze(f, 1);
682 psxRcntFreeze(f, 1);
683 mdecFreeze(f, 1);
03f55e6b 684 new_dyna_freeze(f, 1);
ef79bbde 685
cfa5a2af 686 result = 0;
687cleanup:
496d88d4 688 SaveFuncs.close(f);
cfa5a2af 689 return result;
ef79bbde
P
690}
691
692int LoadState(const char *file) {
496d88d4 693 void *f;
cfa5a2af 694 GPUFreeze_t *gpufP = NULL;
695 SPUFreeze_t *spufP = NULL;
ef79bbde
P
696 int Size;
697 char header[32];
698 u32 version;
699 boolean hle;
cfa5a2af 700 int result = -1;
ef79bbde 701
496d88d4 702 f = SaveFuncs.open(file, "rb");
ef79bbde
P
703 if (f == NULL) return -1;
704
496d88d4 705 SaveFuncs.read(f, header, sizeof(header));
706 SaveFuncs.read(f, &version, sizeof(u32));
707 SaveFuncs.read(f, &hle, sizeof(boolean));
ef79bbde 708
33f56da1 709 if (strncmp("STv4 PCSX", header, 9) != 0 || version != SaveVersion) {
cfa5a2af 710 SysPrintf("incompatible savestate version %x\n", version);
711 goto cleanup;
ef79bbde 712 }
33f56da1 713 Config.HLE = hle;
714
715 if (Config.HLE)
716 psxBiosInit();
ef79bbde 717
496d88d4 718 SaveFuncs.seek(f, 128 * 96 * 3, SEEK_CUR);
496d88d4 719 SaveFuncs.read(f, psxM, 0x00200000);
720 SaveFuncs.read(f, psxR, 0x00080000);
721 SaveFuncs.read(f, psxH, 0x00010000);
81dbbf4c 722 SaveFuncs.read(f, &psxRegs, offsetof(psxRegisters, gteBusyCycle));
723 psxRegs.gteBusyCycle = psxRegs.cycle;
ef79bbde 724
980f7a58 725 psxCpu->Notify(R3000ACPU_NOTIFY_AFTER_LOAD, NULL);
726
ef79bbde
P
727 if (Config.HLE)
728 psxBiosFreeze(0);
729
730 // gpu
731 gpufP = (GPUFreeze_t *)malloc(sizeof(GPUFreeze_t));
cfa5a2af 732 if (gpufP == NULL) goto cleanup;
496d88d4 733 SaveFuncs.read(f, gpufP, sizeof(GPUFreeze_t));
ef79bbde
P
734 GPU_freeze(0, gpufP);
735 free(gpufP);
ddbaf678 736 if (HW_GPU_STATUS == 0)
086adfff 737 HW_GPU_STATUS = SWAP32(GPU_readStatus());
ef79bbde
P
738
739 // spu
496d88d4 740 SaveFuncs.read(f, &Size, 4);
ef79bbde 741 spufP = (SPUFreeze_t *)malloc(Size);
cfa5a2af 742 if (spufP == NULL) goto cleanup;
496d88d4 743 SaveFuncs.read(f, spufP, Size);
650adfd2 744 SPU_freeze(0, spufP, psxRegs.cycle);
ef79bbde
P
745 free(spufP);
746
747 sioFreeze(f, 0);
748 cdrFreeze(f, 0);
749 psxHwFreeze(f, 0);
750 psxRcntFreeze(f, 0);
751 mdecFreeze(f, 0);
03f55e6b 752 new_dyna_freeze(f, 0);
ef79bbde 753
cfa5a2af 754 result = 0;
755cleanup:
496d88d4 756 SaveFuncs.close(f);
cfa5a2af 757 return result;
ef79bbde
P
758}
759
760int CheckState(const char *file) {
496d88d4 761 void *f;
ef79bbde
P
762 char header[32];
763 u32 version;
764 boolean hle;
765
496d88d4 766 f = SaveFuncs.open(file, "rb");
ef79bbde
P
767 if (f == NULL) return -1;
768
496d88d4 769 SaveFuncs.read(f, header, sizeof(header));
770 SaveFuncs.read(f, &version, sizeof(u32));
771 SaveFuncs.read(f, &hle, sizeof(boolean));
ef79bbde 772
496d88d4 773 SaveFuncs.close(f);
ef79bbde 774
33f56da1 775 if (strncmp("STv4 PCSX", header, 9) != 0 || version != SaveVersion)
ef79bbde
P
776 return -1;
777
778 return 0;
779}
780
781// NET Function Helpers
782
783int SendPcsxInfo() {
784 if (NET_recvData == NULL || NET_sendData == NULL)
785 return 0;
786
d014a471 787 boolean Sio_old = 0;
788 boolean SpuIrq_old = 0;
789 boolean RCntFix_old = 0;
ef79bbde 790 NET_sendData(&Config.Xa, sizeof(Config.Xa), PSE_NET_BLOCKING);
d014a471 791 NET_sendData(&Sio_old, sizeof(Sio_old), PSE_NET_BLOCKING);
792 NET_sendData(&SpuIrq_old, sizeof(SpuIrq_old), PSE_NET_BLOCKING);
793 NET_sendData(&RCntFix_old, sizeof(RCntFix_old), PSE_NET_BLOCKING);
ef79bbde
P
794 NET_sendData(&Config.PsxType, sizeof(Config.PsxType), PSE_NET_BLOCKING);
795 NET_sendData(&Config.Cpu, sizeof(Config.Cpu), PSE_NET_BLOCKING);
796
797 return 0;
798}
799
800int RecvPcsxInfo() {
801 int tmp;
802
803 if (NET_recvData == NULL || NET_sendData == NULL)
804 return 0;
805
d014a471 806 boolean Sio_old = 0;
807 boolean SpuIrq_old = 0;
808 boolean RCntFix_old = 0;
ef79bbde 809 NET_recvData(&Config.Xa, sizeof(Config.Xa), PSE_NET_BLOCKING);
d014a471 810 NET_recvData(&Sio_old, sizeof(Sio_old), PSE_NET_BLOCKING);
811 NET_recvData(&SpuIrq_old, sizeof(SpuIrq_old), PSE_NET_BLOCKING);
812 NET_recvData(&RCntFix_old, sizeof(RCntFix_old), PSE_NET_BLOCKING);
ef79bbde
P
813 NET_recvData(&Config.PsxType, sizeof(Config.PsxType), PSE_NET_BLOCKING);
814
ef79bbde
P
815 tmp = Config.Cpu;
816 NET_recvData(&Config.Cpu, sizeof(Config.Cpu), PSE_NET_BLOCKING);
817 if (tmp != Config.Cpu) {
818 psxCpu->Shutdown();
41e82ad4 819#ifndef DRC_DISABLE
ef79bbde
P
820 if (Config.Cpu == CPU_INTERPRETER) psxCpu = &psxInt;
821 else psxCpu = &psxRec;
822#else
823 psxCpu = &psxInt;
824#endif
825 if (psxCpu->Init() == -1) {
826 SysClose(); return -1;
827 }
828 psxCpu->Reset();
980f7a58 829 psxCpu->Notify(R3000ACPU_NOTIFY_AFTER_LOAD, NULL);
ef79bbde
P
830 }
831
832 return 0;
833}
834
835// remove the leading and trailing spaces in a string
836void trim(char *str) {
837 int pos = 0;
838 char *dest = str;
839
840 // skip leading blanks
841 while (str[pos] <= ' ' && str[pos] > 0)
842 pos++;
843
844 while (str[pos]) {
845 *(dest++) = str[pos];
846 pos++;
847 }
848
849 *(dest--) = '\0'; // store the null
850
851 // remove trailing blanks
852 while (dest >= str && *dest <= ' ' && *dest > 0)
853 *(dest--) = '\0';
854}
855
856// lookup table for crc calculation
857static unsigned short crctab[256] = {
858 0x0000, 0x1021, 0x2042, 0x3063, 0x4084, 0x50A5, 0x60C6, 0x70E7, 0x8108,
859 0x9129, 0xA14A, 0xB16B, 0xC18C, 0xD1AD, 0xE1CE, 0xF1EF, 0x1231, 0x0210,
860 0x3273, 0x2252, 0x52B5, 0x4294, 0x72F7, 0x62D6, 0x9339, 0x8318, 0xB37B,
861 0xA35A, 0xD3BD, 0xC39C, 0xF3FF, 0xE3DE, 0x2462, 0x3443, 0x0420, 0x1401,
862 0x64E6, 0x74C7, 0x44A4, 0x5485, 0xA56A, 0xB54B, 0x8528, 0x9509, 0xE5EE,
863 0xF5CF, 0xC5AC, 0xD58D, 0x3653, 0x2672, 0x1611, 0x0630, 0x76D7, 0x66F6,
864 0x5695, 0x46B4, 0xB75B, 0xA77A, 0x9719, 0x8738, 0xF7DF, 0xE7FE, 0xD79D,
865 0xC7BC, 0x48C4, 0x58E5, 0x6886, 0x78A7, 0x0840, 0x1861, 0x2802, 0x3823,
866 0xC9CC, 0xD9ED, 0xE98E, 0xF9AF, 0x8948, 0x9969, 0xA90A, 0xB92B, 0x5AF5,
867 0x4AD4, 0x7AB7, 0x6A96, 0x1A71, 0x0A50, 0x3A33, 0x2A12, 0xDBFD, 0xCBDC,
868 0xFBBF, 0xEB9E, 0x9B79, 0x8B58, 0xBB3B, 0xAB1A, 0x6CA6, 0x7C87, 0x4CE4,
869 0x5CC5, 0x2C22, 0x3C03, 0x0C60, 0x1C41, 0xEDAE, 0xFD8F, 0xCDEC, 0xDDCD,
870 0xAD2A, 0xBD0B, 0x8D68, 0x9D49, 0x7E97, 0x6EB6, 0x5ED5, 0x4EF4, 0x3E13,
871 0x2E32, 0x1E51, 0x0E70, 0xFF9F, 0xEFBE, 0xDFDD, 0xCFFC, 0xBF1B, 0xAF3A,
872 0x9F59, 0x8F78, 0x9188, 0x81A9, 0xB1CA, 0xA1EB, 0xD10C, 0xC12D, 0xF14E,
873 0xE16F, 0x1080, 0x00A1, 0x30C2, 0x20E3, 0x5004, 0x4025, 0x7046, 0x6067,
874 0x83B9, 0x9398, 0xA3FB, 0xB3DA, 0xC33D, 0xD31C, 0xE37F, 0xF35E, 0x02B1,
875 0x1290, 0x22F3, 0x32D2, 0x4235, 0x5214, 0x6277, 0x7256, 0xB5EA, 0xA5CB,
876 0x95A8, 0x8589, 0xF56E, 0xE54F, 0xD52C, 0xC50D, 0x34E2, 0x24C3, 0x14A0,
877 0x0481, 0x7466, 0x6447, 0x5424, 0x4405, 0xA7DB, 0xB7FA, 0x8799, 0x97B8,
878 0xE75F, 0xF77E, 0xC71D, 0xD73C, 0x26D3, 0x36F2, 0x0691, 0x16B0, 0x6657,
879 0x7676, 0x4615, 0x5634, 0xD94C, 0xC96D, 0xF90E, 0xE92F, 0x99C8, 0x89E9,
880 0xB98A, 0xA9AB, 0x5844, 0x4865, 0x7806, 0x6827, 0x18C0, 0x08E1, 0x3882,
881 0x28A3, 0xCB7D, 0xDB5C, 0xEB3F, 0xFB1E, 0x8BF9, 0x9BD8, 0xABBB, 0xBB9A,
882 0x4A75, 0x5A54, 0x6A37, 0x7A16, 0x0AF1, 0x1AD0, 0x2AB3, 0x3A92, 0xFD2E,
883 0xED0F, 0xDD6C, 0xCD4D, 0xBDAA, 0xAD8B, 0x9DE8, 0x8DC9, 0x7C26, 0x6C07,
884 0x5C64, 0x4C45, 0x3CA2, 0x2C83, 0x1CE0, 0x0CC1, 0xEF1F, 0xFF3E, 0xCF5D,
885 0xDF7C, 0xAF9B, 0xBFBA, 0x8FD9, 0x9FF8, 0x6E17, 0x7E36, 0x4E55, 0x5E74,
886 0x2E93, 0x3EB2, 0x0ED1, 0x1EF0
887};
888
889u16 calcCrc(u8 *d, int len) {
890 u16 crc = 0;
891 int i;
892
893 for (i = 0; i < len; i++) {
894 crc = crctab[(crc >> 8) ^ d[i]] ^ (crc << 8);
895 }
896
897 return ~crc;
898}