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