bigendian again
[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;
ef79bbde 741
980f7a58 742 psxCpu->Notify(R3000ACPU_NOTIFY_AFTER_LOAD, NULL);
743
ef79bbde
P
744 if (Config.HLE)
745 psxBiosFreeze(0);
746
747 // gpu
748 gpufP = (GPUFreeze_t *)malloc(sizeof(GPUFreeze_t));
cfa5a2af 749 if (gpufP == NULL) goto cleanup;
496d88d4 750 SaveFuncs.read(f, gpufP, sizeof(GPUFreeze_t));
ef79bbde
P
751 GPU_freeze(0, gpufP);
752 free(gpufP);
ddbaf678 753 if (HW_GPU_STATUS == 0)
086adfff 754 HW_GPU_STATUS = SWAP32(GPU_readStatus());
ef79bbde
P
755
756 // spu
496d88d4 757 SaveFuncs.read(f, &Size, 4);
ef79bbde 758 spufP = (SPUFreeze_t *)malloc(Size);
cfa5a2af 759 if (spufP == NULL) goto cleanup;
496d88d4 760 SaveFuncs.read(f, spufP, Size);
650adfd2 761 SPU_freeze(0, spufP, psxRegs.cycle);
ef79bbde
P
762 free(spufP);
763
764 sioFreeze(f, 0);
765 cdrFreeze(f, 0);
766 psxHwFreeze(f, 0);
767 psxRcntFreeze(f, 0);
768 mdecFreeze(f, 0);
03f55e6b 769 new_dyna_freeze(f, 0);
3faf5c23 770 padFreeze(f, 0);
ef79bbde 771
de74f599 772 if (Config.HLE)
b34d6a80 773 psxBiosCheckExe(biosBranchCheckOld, 0x60, 1);
de74f599 774
cfa5a2af 775 result = 0;
776cleanup:
496d88d4 777 SaveFuncs.close(f);
cfa5a2af 778 return result;
ef79bbde
P
779}
780
781int CheckState(const char *file) {
496d88d4 782 void *f;
ef79bbde
P
783 char header[32];
784 u32 version;
785 boolean hle;
786
496d88d4 787 f = SaveFuncs.open(file, "rb");
ef79bbde
P
788 if (f == NULL) return -1;
789
496d88d4 790 SaveFuncs.read(f, header, sizeof(header));
791 SaveFuncs.read(f, &version, sizeof(u32));
792 SaveFuncs.read(f, &hle, sizeof(boolean));
ef79bbde 793
496d88d4 794 SaveFuncs.close(f);
ef79bbde 795
33f56da1 796 if (strncmp("STv4 PCSX", header, 9) != 0 || version != SaveVersion)
ef79bbde
P
797 return -1;
798
799 return 0;
800}
801
802// NET Function Helpers
803
804int SendPcsxInfo() {
805 if (NET_recvData == NULL || NET_sendData == NULL)
806 return 0;
807
d014a471 808 boolean Sio_old = 0;
809 boolean SpuIrq_old = 0;
810 boolean RCntFix_old = 0;
ef79bbde 811 NET_sendData(&Config.Xa, sizeof(Config.Xa), PSE_NET_BLOCKING);
d014a471 812 NET_sendData(&Sio_old, sizeof(Sio_old), PSE_NET_BLOCKING);
813 NET_sendData(&SpuIrq_old, sizeof(SpuIrq_old), PSE_NET_BLOCKING);
814 NET_sendData(&RCntFix_old, sizeof(RCntFix_old), PSE_NET_BLOCKING);
ef79bbde
P
815 NET_sendData(&Config.PsxType, sizeof(Config.PsxType), PSE_NET_BLOCKING);
816 NET_sendData(&Config.Cpu, sizeof(Config.Cpu), PSE_NET_BLOCKING);
817
818 return 0;
819}
820
821int RecvPcsxInfo() {
822 int tmp;
823
824 if (NET_recvData == NULL || NET_sendData == NULL)
825 return 0;
826
d014a471 827 boolean Sio_old = 0;
828 boolean SpuIrq_old = 0;
829 boolean RCntFix_old = 0;
ef79bbde 830 NET_recvData(&Config.Xa, sizeof(Config.Xa), PSE_NET_BLOCKING);
d014a471 831 NET_recvData(&Sio_old, sizeof(Sio_old), PSE_NET_BLOCKING);
832 NET_recvData(&SpuIrq_old, sizeof(SpuIrq_old), PSE_NET_BLOCKING);
833 NET_recvData(&RCntFix_old, sizeof(RCntFix_old), PSE_NET_BLOCKING);
ef79bbde
P
834 NET_recvData(&Config.PsxType, sizeof(Config.PsxType), PSE_NET_BLOCKING);
835
ef79bbde
P
836 tmp = Config.Cpu;
837 NET_recvData(&Config.Cpu, sizeof(Config.Cpu), PSE_NET_BLOCKING);
838 if (tmp != Config.Cpu) {
839 psxCpu->Shutdown();
41e82ad4 840#ifndef DRC_DISABLE
ef79bbde
P
841 if (Config.Cpu == CPU_INTERPRETER) psxCpu = &psxInt;
842 else psxCpu = &psxRec;
843#else
844 psxCpu = &psxInt;
845#endif
846 if (psxCpu->Init() == -1) {
847 SysClose(); return -1;
848 }
849 psxCpu->Reset();
980f7a58 850 psxCpu->Notify(R3000ACPU_NOTIFY_AFTER_LOAD, NULL);
ef79bbde
P
851 }
852
853 return 0;
854}
855
856// remove the leading and trailing spaces in a string
857void trim(char *str) {
858 int pos = 0;
859 char *dest = str;
860
861 // skip leading blanks
862 while (str[pos] <= ' ' && str[pos] > 0)
863 pos++;
864
865 while (str[pos]) {
866 *(dest++) = str[pos];
867 pos++;
868 }
869
870 *(dest--) = '\0'; // store the null
871
872 // remove trailing blanks
873 while (dest >= str && *dest <= ' ' && *dest > 0)
874 *(dest--) = '\0';
875}
876
877// lookup table for crc calculation
878static unsigned short crctab[256] = {
879 0x0000, 0x1021, 0x2042, 0x3063, 0x4084, 0x50A5, 0x60C6, 0x70E7, 0x8108,
880 0x9129, 0xA14A, 0xB16B, 0xC18C, 0xD1AD, 0xE1CE, 0xF1EF, 0x1231, 0x0210,
881 0x3273, 0x2252, 0x52B5, 0x4294, 0x72F7, 0x62D6, 0x9339, 0x8318, 0xB37B,
882 0xA35A, 0xD3BD, 0xC39C, 0xF3FF, 0xE3DE, 0x2462, 0x3443, 0x0420, 0x1401,
883 0x64E6, 0x74C7, 0x44A4, 0x5485, 0xA56A, 0xB54B, 0x8528, 0x9509, 0xE5EE,
884 0xF5CF, 0xC5AC, 0xD58D, 0x3653, 0x2672, 0x1611, 0x0630, 0x76D7, 0x66F6,
885 0x5695, 0x46B4, 0xB75B, 0xA77A, 0x9719, 0x8738, 0xF7DF, 0xE7FE, 0xD79D,
886 0xC7BC, 0x48C4, 0x58E5, 0x6886, 0x78A7, 0x0840, 0x1861, 0x2802, 0x3823,
887 0xC9CC, 0xD9ED, 0xE98E, 0xF9AF, 0x8948, 0x9969, 0xA90A, 0xB92B, 0x5AF5,
888 0x4AD4, 0x7AB7, 0x6A96, 0x1A71, 0x0A50, 0x3A33, 0x2A12, 0xDBFD, 0xCBDC,
889 0xFBBF, 0xEB9E, 0x9B79, 0x8B58, 0xBB3B, 0xAB1A, 0x6CA6, 0x7C87, 0x4CE4,
890 0x5CC5, 0x2C22, 0x3C03, 0x0C60, 0x1C41, 0xEDAE, 0xFD8F, 0xCDEC, 0xDDCD,
891 0xAD2A, 0xBD0B, 0x8D68, 0x9D49, 0x7E97, 0x6EB6, 0x5ED5, 0x4EF4, 0x3E13,
892 0x2E32, 0x1E51, 0x0E70, 0xFF9F, 0xEFBE, 0xDFDD, 0xCFFC, 0xBF1B, 0xAF3A,
893 0x9F59, 0x8F78, 0x9188, 0x81A9, 0xB1CA, 0xA1EB, 0xD10C, 0xC12D, 0xF14E,
894 0xE16F, 0x1080, 0x00A1, 0x30C2, 0x20E3, 0x5004, 0x4025, 0x7046, 0x6067,
895 0x83B9, 0x9398, 0xA3FB, 0xB3DA, 0xC33D, 0xD31C, 0xE37F, 0xF35E, 0x02B1,
896 0x1290, 0x22F3, 0x32D2, 0x4235, 0x5214, 0x6277, 0x7256, 0xB5EA, 0xA5CB,
897 0x95A8, 0x8589, 0xF56E, 0xE54F, 0xD52C, 0xC50D, 0x34E2, 0x24C3, 0x14A0,
898 0x0481, 0x7466, 0x6447, 0x5424, 0x4405, 0xA7DB, 0xB7FA, 0x8799, 0x97B8,
899 0xE75F, 0xF77E, 0xC71D, 0xD73C, 0x26D3, 0x36F2, 0x0691, 0x16B0, 0x6657,
900 0x7676, 0x4615, 0x5634, 0xD94C, 0xC96D, 0xF90E, 0xE92F, 0x99C8, 0x89E9,
901 0xB98A, 0xA9AB, 0x5844, 0x4865, 0x7806, 0x6827, 0x18C0, 0x08E1, 0x3882,
902 0x28A3, 0xCB7D, 0xDB5C, 0xEB3F, 0xFB1E, 0x8BF9, 0x9BD8, 0xABBB, 0xBB9A,
903 0x4A75, 0x5A54, 0x6A37, 0x7A16, 0x0AF1, 0x1AD0, 0x2AB3, 0x3A92, 0xFD2E,
904 0xED0F, 0xDD6C, 0xCD4D, 0xBDAA, 0xAD8B, 0x9DE8, 0x8DC9, 0x7C26, 0x6C07,
905 0x5C64, 0x4C45, 0x3CA2, 0x2C83, 0x1CE0, 0x0CC1, 0xEF1F, 0xFF3E, 0xCF5D,
906 0xDF7C, 0xAF9B, 0xBFBA, 0x8FD9, 0x9FF8, 0x6E17, 0x7E36, 0x4E55, 0x5E74,
907 0x2E93, 0x3EB2, 0x0ED1, 0x1EF0
908};
909
910u16 calcCrc(u8 *d, int len) {
911 u16 crc = 0;
912 int i;
913
914 for (i = 0; i < len; i++) {
915 crc = crctab[(crc >> 8) ^ d[i]] ^ (crc << 8);
916 }
917
918 return ~crc;
919}