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