psxbios: assorted changes
[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;
ef79bbde 631 GPUFreeze_t *gpufP;
6d75addf 632 SPUFreezeHdr_t *spufH;
ef79bbde
P
633 SPUFreeze_t *spufP;
634 int Size;
635 unsigned char *pMem;
636
496d88d4 637 f = SaveFuncs.open(file, "wb");
ef79bbde
P
638 if (f == NULL) return -1;
639
980f7a58 640 psxCpu->Notify(R3000ACPU_NOTIFY_BEFORE_SAVE, NULL);
52082bc1 641
496d88d4 642 SaveFuncs.write(f, (void *)PcsxHeader, 32);
643 SaveFuncs.write(f, (void *)&SaveVersion, sizeof(u32));
644 SaveFuncs.write(f, (void *)&Config.HLE, sizeof(boolean));
ef79bbde
P
645
646 pMem = (unsigned char *)malloc(128 * 96 * 3);
647 if (pMem == NULL) return -1;
648 GPU_getScreenPic(pMem);
496d88d4 649 SaveFuncs.write(f, pMem, 128 * 96 * 3);
ef79bbde
P
650 free(pMem);
651
652 if (Config.HLE)
653 psxBiosFreeze(1);
654
496d88d4 655 SaveFuncs.write(f, psxM, 0x00200000);
656 SaveFuncs.write(f, psxR, 0x00080000);
657 SaveFuncs.write(f, psxH, 0x00010000);
81dbbf4c 658 // only partial save of psxRegisters to maintain savestate compat
659 SaveFuncs.write(f, &psxRegs, offsetof(psxRegisters, gteBusyCycle));
ef79bbde
P
660
661 // gpu
662 gpufP = (GPUFreeze_t *)malloc(sizeof(GPUFreeze_t));
663 gpufP->ulFreezeVersion = 1;
664 GPU_freeze(1, gpufP);
496d88d4 665 SaveFuncs.write(f, gpufP, sizeof(GPUFreeze_t));
ef79bbde
P
666 free(gpufP);
667
668 // spu
6d75addf 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);
ef79bbde 673 spufP = (SPUFreeze_t *) malloc(Size);
650adfd2 674 SPU_freeze(1, spufP, psxRegs.cycle);
496d88d4 675 SaveFuncs.write(f, spufP, Size);
ef79bbde
P
676 free(spufP);
677
678 sioFreeze(f, 1);
679 cdrFreeze(f, 1);
680 psxHwFreeze(f, 1);
681 psxRcntFreeze(f, 1);
682 mdecFreeze(f, 1);
03f55e6b 683 new_dyna_freeze(f, 1);
ef79bbde 684
496d88d4 685 SaveFuncs.close(f);
ef79bbde
P
686
687 return 0;
688}
689
690int LoadState(const char *file) {
496d88d4 691 void *f;
ef79bbde
P
692 GPUFreeze_t *gpufP;
693 SPUFreeze_t *spufP;
694 int Size;
695 char header[32];
696 u32 version;
697 boolean hle;
698
496d88d4 699 f = SaveFuncs.open(file, "rb");
ef79bbde
P
700 if (f == NULL) return -1;
701
496d88d4 702 SaveFuncs.read(f, header, sizeof(header));
703 SaveFuncs.read(f, &version, sizeof(u32));
704 SaveFuncs.read(f, &hle, sizeof(boolean));
ef79bbde 705
33f56da1 706 if (strncmp("STv4 PCSX", header, 9) != 0 || version != SaveVersion) {
496d88d4 707 SaveFuncs.close(f);
ef79bbde
P
708 return -1;
709 }
33f56da1 710 Config.HLE = hle;
711
712 if (Config.HLE)
713 psxBiosInit();
ef79bbde 714
496d88d4 715 SaveFuncs.seek(f, 128 * 96 * 3, SEEK_CUR);
496d88d4 716 SaveFuncs.read(f, psxM, 0x00200000);
717 SaveFuncs.read(f, psxR, 0x00080000);
718 SaveFuncs.read(f, psxH, 0x00010000);
81dbbf4c 719 SaveFuncs.read(f, &psxRegs, offsetof(psxRegisters, gteBusyCycle));
720 psxRegs.gteBusyCycle = psxRegs.cycle;
ef79bbde 721
980f7a58 722 psxCpu->Notify(R3000ACPU_NOTIFY_AFTER_LOAD, NULL);
723
ef79bbde
P
724 if (Config.HLE)
725 psxBiosFreeze(0);
726
727 // gpu
728 gpufP = (GPUFreeze_t *)malloc(sizeof(GPUFreeze_t));
496d88d4 729 SaveFuncs.read(f, gpufP, sizeof(GPUFreeze_t));
ef79bbde
P
730 GPU_freeze(0, gpufP);
731 free(gpufP);
ddbaf678 732 if (HW_GPU_STATUS == 0)
086adfff 733 HW_GPU_STATUS = SWAP32(GPU_readStatus());
ef79bbde
P
734
735 // spu
496d88d4 736 SaveFuncs.read(f, &Size, 4);
ef79bbde 737 spufP = (SPUFreeze_t *)malloc(Size);
496d88d4 738 SaveFuncs.read(f, spufP, Size);
650adfd2 739 SPU_freeze(0, spufP, psxRegs.cycle);
ef79bbde
P
740 free(spufP);
741
742 sioFreeze(f, 0);
743 cdrFreeze(f, 0);
744 psxHwFreeze(f, 0);
745 psxRcntFreeze(f, 0);
746 mdecFreeze(f, 0);
03f55e6b 747 new_dyna_freeze(f, 0);
ef79bbde 748
496d88d4 749 SaveFuncs.close(f);
ef79bbde
P
750
751 return 0;
752}
753
754int CheckState(const char *file) {
496d88d4 755 void *f;
ef79bbde
P
756 char header[32];
757 u32 version;
758 boolean hle;
759
496d88d4 760 f = SaveFuncs.open(file, "rb");
ef79bbde
P
761 if (f == NULL) return -1;
762
496d88d4 763 SaveFuncs.read(f, header, sizeof(header));
764 SaveFuncs.read(f, &version, sizeof(u32));
765 SaveFuncs.read(f, &hle, sizeof(boolean));
ef79bbde 766
496d88d4 767 SaveFuncs.close(f);
ef79bbde 768
33f56da1 769 if (strncmp("STv4 PCSX", header, 9) != 0 || version != SaveVersion)
ef79bbde
P
770 return -1;
771
772 return 0;
773}
774
775// NET Function Helpers
776
777int SendPcsxInfo() {
778 if (NET_recvData == NULL || NET_sendData == NULL)
779 return 0;
780
d014a471 781 boolean Sio_old = 0;
782 boolean SpuIrq_old = 0;
783 boolean RCntFix_old = 0;
ef79bbde 784 NET_sendData(&Config.Xa, sizeof(Config.Xa), PSE_NET_BLOCKING);
d014a471 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);
ef79bbde
P
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
794int RecvPcsxInfo() {
795 int tmp;
796
797 if (NET_recvData == NULL || NET_sendData == NULL)
798 return 0;
799
d014a471 800 boolean Sio_old = 0;
801 boolean SpuIrq_old = 0;
802 boolean RCntFix_old = 0;
ef79bbde 803 NET_recvData(&Config.Xa, sizeof(Config.Xa), PSE_NET_BLOCKING);
d014a471 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);
ef79bbde
P
807 NET_recvData(&Config.PsxType, sizeof(Config.PsxType), PSE_NET_BLOCKING);
808
ef79bbde
P
809 tmp = Config.Cpu;
810 NET_recvData(&Config.Cpu, sizeof(Config.Cpu), PSE_NET_BLOCKING);
811 if (tmp != Config.Cpu) {
812 psxCpu->Shutdown();
41e82ad4 813#ifndef DRC_DISABLE
ef79bbde
P
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();
980f7a58 823 psxCpu->Notify(R3000ACPU_NOTIFY_AFTER_LOAD, NULL);
ef79bbde
P
824 }
825
826 return 0;
827}
828
829// remove the leading and trailing spaces in a string
830void 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
851static 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
883u16 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}