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