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