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