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