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