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