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