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