cdrom: maybe more accurate lid behavior
[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
630b122b 24#include <stddef.h>
38f70ea4 25#include <errno.h>
f5f5f5dd 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"
584fc319 32#include "psxbios.h"
630b122b 33#include "database.h"
496d88d4 34#include <zlib.h>
ef79bbde
P
35
36char CdromId[10] = "";
37char CdromLabel[33] = "";
ec92465a 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
6d8f88c4 62static void mmssdd( char *b, char *p )
ef79bbde
P
63{
64 int m, s, d;
6d8f88c4 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;
3e42b115 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() \
1290ebe4 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
c35a25f6 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
6c62131f 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;
7575a4aa 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;
6c62131f 162
163 psxCpu->Notify(R3000ACPU_NOTIFY_AFTER_LOAD, NULL);
164}
165
f5f5f5dd 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
9415f066 174static void getFromCnf(char *buf, const char *key, u32 *val)
175{
176 buf = strstr(buf, key);
177 if (buf)
178 buf = strchr(buf, '=');
38f70ea4 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 }
9415f066 186}
187
ef79bbde 188int LoadCdrom() {
584fc319 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];
9415f066 197 u32 cnf_tcb = 4;
198 u32 cnf_event = 16;
199 u32 cnf_stack = 0;
584fc319 200 u32 t_addr;
201 u32 t_size;
9415f066 202 u32 sp = 0;
584fc319 203 int i, ret;
ef79bbde 204
f5f5f5dd 205 if (!Config.HLE) {
f3bc907d 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
3e42b115 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;
f5f5f5dd 227 strcpy(exename, "PSX.EXE;1");
ef79bbde
P
228
229 READTRACK();
230 }
231 else {
232 // read the SYSTEM.CNF
233 READTRACK();
9415f066 234 buf[1023] = 0;
ef79bbde 235
9415f066 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 }
9415f066 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)
c89fac55 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));
584fc319 266 for (i = 2; i < sizeof(tmpHead.d) / sizeof(tmpHead.d[0]); i++)
267 tmpHead.d[i] = SWAP32(tmpHead.d[i]);
ef79bbde 268
584fc319 269 SysPrintf("manual booting '%s' pc=%x\n", exename, tmpHead.h.pc0);
270 sp = tmpHead.h.s_addr;
9415f066 271 if (cnf_stack)
272 sp = cnf_stack;
584fc319 273 SetBootRegs(tmpHead.h.pc0, tmpHead.h.gp0, sp);
ebe6ff2b 274
ef79bbde 275 // Read the rest of the main executable
584fc319 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
b012a437 282 if (ptr != INVALID_PTR) memcpy(ptr, buf+12, 2048);
ef79bbde 283
584fc319 284 t_addr += 2048;
285 t_size -= 2048;
ef79bbde
P
286 }
287
584fc319 288 psxCpu->Clear(tmpHead.h.t_addr, tmpHead.h.t_size / 4);
289 //psxCpu->Reset();
290
291 if (Config.HLE)
403a6290 292 psxBiosCheckExe(tmpHead.h.t_addr, tmpHead.h.t_size, 0);
584fc319 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];
867297ae 302 const char *p1, *p2;
ef79bbde 303 u32 size, addr;
305c8c93 304 void *mem;
ef79bbde 305
de6bb534 306 if (filename == INVALID_PTR)
307 return -1;
308
867297ae 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
3e42b115 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));
de6bb534 332 size = SWAP32(head->t_size);
333 addr = SWAP32(head->t_addr);
ef79bbde 334
ebe6ff2b 335 psxCpu->Clear(addr, size / 4);
de6bb534 336 //psxCpu->Reset();
ebe6ff2b 337
338 while (size & ~2047) {
ef79bbde
P
339 incTime();
340 READTRACK();
341
305c8c93 342 mem = PSXM(addr);
b012a437 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
3e42b115 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 }
1893f408 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)
630b122b 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] == ' ') {
630b122b 445 strncpy(CdromLabel, CdromId, 9);
ef79bbde
P
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);
630b122b 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);
5b568098 466 if (fread(&mybuf, 1, sizeof(mybuf), f) != sizeof(mybuf))
9361a5aa
EC
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;
9361a5aa
EC
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;
3e42b115 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
3e42b115 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:
5b568098 527 if (fread(&tmpHead, 1, sizeof(EXE_HEADER), tmpFile) != sizeof(EXE_HEADER))
9361a5aa 528 goto fail_io;
ebe6ff2b 529 section_address = SWAP32(tmpHead.t_addr);
530 section_size = SWAP32(tmpHead.t_size);
531 mem = PSXM(section_address);
b012a437 532 if (mem != INVALID_PTR) {
3e42b115 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 }
6c62131f 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 {
5b568098 544 if (fread(&opcode, 1, sizeof(opcode), tmpFile) != sizeof(opcode))
9361a5aa 545 goto fail_io;
ef79bbde
P
546 switch (opcode) {
547 case 1: /* Section loading */
5b568098 548 if (fread(&section_address, 1, sizeof(section_address), tmpFile) != sizeof(section_address))
9361a5aa 549 goto fail_io;
5b568098 550 if (fread(&section_size, 1, sizeof(section_size), tmpFile) != sizeof(section_size))
9361a5aa 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);
b012a437 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 */
5b568098 565 if (fread(&psxRegs.pc, 1, sizeof(psxRegs.pc), tmpFile) != sizeof(psxRegs.pc))
9361a5aa 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
5b568098 595 if (tmpFile)
596 fclose(tmpFile);
ef79bbde 597 return retval;
9361a5aa
EC
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
202b29d4 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
ec92465a 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) {
ec92465a 658 struct misc_save_data *misc = (void *)(psxH + 0xf000);
496d88d4 659 void *f;
ab91a058 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
ec92465a 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
20196899 683 psxCpu->Notify(R3000ACPU_NOTIFY_BEFORE_SAVE, NULL);
6962f770 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);
ab91a058 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);
630b122b 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));
ab91a058 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));
ab91a058 710 free(gpufP); gpufP = NULL;
ef79bbde
P
711
712 // spu
ab91a058 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);
ab91a058 716 if (spufP == NULL) goto cleanup;
650adfd2 717 SPU_freeze(1, spufP, psxRegs.cycle);
496d88d4 718 SaveFuncs.write(f, spufP, Size);
ab91a058 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);
bdeee1a1 727 padFreeze(f, 1);
ef79bbde 728
ab91a058 729 result = 0;
730cleanup:
ec92465a 731 memset(misc, 0, sizeof(*misc));
496d88d4 732 SaveFuncs.close(f);
ab91a058 733 return result;
ef79bbde
P
734}
735
736int LoadState(const char *file) {
ec92465a 737 struct misc_save_data *misc = (void *)(psxH + 0xf000);
584fc319 738 u32 biosBranchCheckOld = psxRegs.biosBranchCheck;
496d88d4 739 void *f;
ab91a058 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;
ab91a058 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) {
ab91a058 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);
630b122b 768 SaveFuncs.read(f, &psxRegs, offsetof(psxRegisters, gteBusyCycle));
769 psxRegs.gteBusyCycle = psxRegs.cycle;
584fc319 770 psxRegs.biosBranchCheck = ~0;
8cba0a22 771 psxRegs.gpuIdleAfter = psxRegs.cycle - 1;
772 HW_GPU_STATUS &= SWAP32(~PSXGPU_nBUSY);
ec92465a 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
20196899 784 psxCpu->Notify(R3000ACPU_NOTIFY_AFTER_LOAD, NULL);
6962f770 785
ef79bbde
P
786 if (Config.HLE)
787 psxBiosFreeze(0);
788
789 // gpu
790 gpufP = (GPUFreeze_t *)malloc(sizeof(GPUFreeze_t));
ab91a058 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);
8cba0a22 795 gpuSyncPluginSR();
ef79bbde
P
796
797 // spu
496d88d4 798 SaveFuncs.read(f, &Size, 4);
ef79bbde 799 spufP = (SPUFreeze_t *)malloc(Size);
ab91a058 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);
bdeee1a1 811 padFreeze(f, 0);
ef79bbde 812
c6a249e3 813 events_restore();
584fc319 814 if (Config.HLE)
403a6290 815 psxBiosCheckExe(biosBranchCheckOld, 0x60, 1);
584fc319 816
ab91a058 817 result = 0;
818cleanup:
ec92465a 819 memset(misc, 0, sizeof(*misc));
496d88d4 820 SaveFuncs.close(f);
ab91a058 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
840639a5 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);
840639a5 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
840639a5 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);
840639a5 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();
630b122b 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();
20196899 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}