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