reduce differences from upstream
[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"
70939d49 31#include "lightrec/plugin.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
6d8f88c4 59static void mmssdd( char *b, char *p )
ef79bbde
P
60{
61 int m, s, d;
6d8f88c4 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;
3e42b115 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() \
1290ebe4 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
158static void fake_bios_gpu_setup(void)
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
ef79bbde
P
169int LoadCdrom() {
170 EXE_HEADER tmpHead;
171 struct iso_directory_record *dir;
172 u8 time[4], *buf;
173 u8 mdir[4096];
305c8c93 174 char exename[256];
ef79bbde 175
f3a78e7e 176 // not the best place to do it, but since BIOS boot logo killer
177 // is just below, do it here
178 fake_bios_gpu_setup();
179
f422f444 180 if (!Config.HLE && !Config.SlowBoot) {
f3a78e7e 181 // skip BIOS logos
ef79bbde
P
182 psxRegs.pc = psxRegs.GPR.n.ra;
183 return 0;
184 }
185
186 time[0] = itob(0); time[1] = itob(2); time[2] = itob(0x10);
187
188 READTRACK();
189
190 // skip head and sub, and go to the root directory record
3e42b115 191 dir = (struct iso_directory_record*) &buf[12+156];
ef79bbde
P
192
193 mmssdd(dir->extent, (char*)time);
194
195 READDIR(mdir);
196
197 // Load SYSTEM.CNF and scan for the main executable
198 if (GetCdromFile(mdir, time, "SYSTEM.CNF;1") == -1) {
199 // if SYSTEM.CNF is missing, start an existing PSX.EXE
200 if (GetCdromFile(mdir, time, "PSX.EXE;1") == -1) return -1;
201
202 READTRACK();
203 }
204 else {
205 // read the SYSTEM.CNF
206 READTRACK();
207
9a9bcd78 208 sscanf((char *)buf + 12, "BOOT = cdrom:\\%255s", exename);
ef79bbde 209 if (GetCdromFile(mdir, time, exename) == -1) {
9a9bcd78 210 sscanf((char *)buf + 12, "BOOT = cdrom:%255s", exename);
ef79bbde 211 if (GetCdromFile(mdir, time, exename) == -1) {
305c8c93 212 char *ptr = strstr((char *)buf + 12, "cdrom:");
ef79bbde
P
213 if (ptr != NULL) {
214 ptr += 6;
215 while (*ptr == '\\' || *ptr == '/') ptr++;
216 strncpy(exename, ptr, 255);
217 exename[255] = '\0';
218 ptr = exename;
219 while (*ptr != '\0' && *ptr != '\r' && *ptr != '\n') ptr++;
220 *ptr = '\0';
221 if (GetCdromFile(mdir, time, exename) == -1)
222 return -1;
223 } else
224 return -1;
225 }
226 }
227
228 // Read the EXE-Header
229 READTRACK();
230 }
231
232 memcpy(&tmpHead, buf + 12, sizeof(EXE_HEADER));
233
234 psxRegs.pc = SWAP32(tmpHead.pc0);
235 psxRegs.GPR.n.gp = SWAP32(tmpHead.gp0);
3e42b115 236 psxRegs.GPR.n.sp = SWAP32(tmpHead.s_addr);
ef79bbde
P
237 if (psxRegs.GPR.n.sp == 0) psxRegs.GPR.n.sp = 0x801fff00;
238
239 tmpHead.t_size = SWAP32(tmpHead.t_size);
240 tmpHead.t_addr = SWAP32(tmpHead.t_addr);
241
ebe6ff2b 242 psxCpu->Clear(tmpHead.t_addr, tmpHead.t_size / 4);
952943a7 243 psxCpu->Reset();
ebe6ff2b 244
ef79bbde 245 // Read the rest of the main executable
ebe6ff2b 246 while (tmpHead.t_size & ~2047) {
ef79bbde
P
247 void *ptr = (void *)PSXM(tmpHead.t_addr);
248
249 incTime();
250 READTRACK();
251
b012a437 252 if (ptr != INVALID_PTR) memcpy(ptr, buf+12, 2048);
ef79bbde
P
253
254 tmpHead.t_size -= 2048;
255 tmpHead.t_addr += 2048;
256 }
257
258 return 0;
259}
260
261int LoadCdromFile(const char *filename, EXE_HEADER *head) {
262 struct iso_directory_record *dir;
263 u8 time[4],*buf;
305c8c93 264 u8 mdir[4096];
265 char exename[256];
ef79bbde 266 u32 size, addr;
305c8c93 267 void *mem;
ef79bbde 268
9a9bcd78 269 sscanf(filename, "cdrom:\\%255s", exename);
ef79bbde
P
270
271 time[0] = itob(0); time[1] = itob(2); time[2] = itob(0x10);
272
273 READTRACK();
274
275 // skip head and sub, and go to the root directory record
3e42b115 276 dir = (struct iso_directory_record *)&buf[12 + 156];
ef79bbde
P
277
278 mmssdd(dir->extent, (char*)time);
279
280 READDIR(mdir);
281
282 if (GetCdromFile(mdir, time, exename) == -1) return -1;
283
284 READTRACK();
285
286 memcpy(head, buf + 12, sizeof(EXE_HEADER));
287 size = head->t_size;
288 addr = head->t_addr;
289
ebe6ff2b 290 psxCpu->Clear(addr, size / 4);
952943a7 291 psxCpu->Reset();
ebe6ff2b 292
293 while (size & ~2047) {
ef79bbde
P
294 incTime();
295 READTRACK();
296
305c8c93 297 mem = PSXM(addr);
b012a437 298 if (mem != INVALID_PTR)
305c8c93 299 memcpy(mem, buf + 12, 2048);
ef79bbde
P
300
301 size -= 2048;
302 addr += 2048;
303 }
304
305 return 0;
306}
307
308int CheckCdrom() {
309 struct iso_directory_record *dir;
305c8c93 310 unsigned char time[4];
311 char *buf;
ef79bbde
P
312 unsigned char mdir[4096];
313 char exename[256];
e06ee7f4 314 int i, len, c;
ef79bbde
P
315
316 FreePPFCache();
317
318 time[0] = itob(0);
319 time[1] = itob(2);
320 time[2] = itob(0x10);
321
322 READTRACK();
323
e06ee7f4 324 memset(CdromLabel, 0, sizeof(CdromLabel));
325 memset(CdromId, 0, sizeof(CdromId));
326 memset(exename, 0, sizeof(exename));
ef79bbde
P
327
328 strncpy(CdromLabel, buf + 52, 32);
329
330 // skip head and sub, and go to the root directory record
3e42b115 331 dir = (struct iso_directory_record *)&buf[12 + 156];
ef79bbde
P
332
333 mmssdd(dir->extent, (char *)time);
334
335 READDIR(mdir);
336
337 if (GetCdromFile(mdir, time, "SYSTEM.CNF;1") != -1) {
338 READTRACK();
339
9a9bcd78 340 sscanf(buf + 12, "BOOT = cdrom:\\%255s", exename);
ef79bbde 341 if (GetCdromFile(mdir, time, exename) == -1) {
9a9bcd78 342 sscanf(buf + 12, "BOOT = cdrom:%255s", exename);
ef79bbde
P
343 if (GetCdromFile(mdir, time, exename) == -1) {
344 char *ptr = strstr(buf + 12, "cdrom:"); // possibly the executable is in some subdir
345 if (ptr != NULL) {
346 ptr += 6;
347 while (*ptr == '\\' || *ptr == '/') ptr++;
348 strncpy(exename, ptr, 255);
349 exename[255] = '\0';
350 ptr = exename;
351 while (*ptr != '\0' && *ptr != '\r' && *ptr != '\n') ptr++;
352 *ptr = '\0';
353 if (GetCdromFile(mdir, time, exename) == -1)
354 return -1; // main executable not found
355 } else
356 return -1;
357 }
358 }
1893f408 359 /* Workaround for Wild Arms EU/US which has non-standard string causing incorrect region detection */
360 if (exename[0] == 'E' && exename[1] == 'X' && exename[2] == 'E' && exename[3] == '\\') {
361 size_t offset = 4;
362 size_t i, len = strlen(exename) - offset;
363 for (i = 0; i < len; i++)
364 exename[i] = exename[i + offset];
365 exename[i] = '\0';
366 }
ef79bbde
P
367 } else if (GetCdromFile(mdir, time, "PSX.EXE;1") != -1) {
368 strcpy(exename, "PSX.EXE;1");
369 strcpy(CdromId, "SLUS99999");
370 } else
371 return -1; // SYSTEM.CNF and PSX.EXE not found
372
373 if (CdromId[0] == '\0') {
e06ee7f4 374 len = strlen(exename);
375 c = 0;
376 for (i = 0; i < len; ++i) {
377 if (exename[i] == ';' || c >= sizeof(CdromId) - 1)
378 break;
379 if (isalnum(exename[i]))
380 CdromId[c++] = exename[i];
ef79bbde
P
381 }
382 }
383
0e2e3f49 384 if (CdromId[0] == '\0')
385 strcpy(CdromId, "SLUS99999");
386
ef79bbde 387 if (Config.PsxAuto) { // autodetect system (pal or ntsc)
630b122b 388 if (
389 /* Make sure Wild Arms SCUS-94608 is not detected as a PAL game. */
390 ((CdromId[0] == 's' || CdromId[0] == 'S') && (CdromId[2] == 'e' || CdromId[2] == 'E')) ||
391 !strncmp(CdromId, "DTLS3035", 8) ||
392 !strncmp(CdromId, "PBPX95001", 9) || // according to redump.org, these PAL
393 !strncmp(CdromId, "PBPX95007", 9) || // discs have a non-standard ID;
394 !strncmp(CdromId, "PBPX95008", 9)) // add more serials if they are discovered.
ef79bbde
P
395 Config.PsxType = PSX_TYPE_PAL; // pal
396 else Config.PsxType = PSX_TYPE_NTSC; // ntsc
397 }
398
399 if (CdromLabel[0] == ' ') {
630b122b 400 strncpy(CdromLabel, CdromId, 9);
ef79bbde
P
401 }
402 SysPrintf(_("CD-ROM Label: %.32s\n"), CdromLabel);
403 SysPrintf(_("CD-ROM ID: %.9s\n"), CdromId);
e06ee7f4 404 SysPrintf(_("CD-ROM EXE Name: %.255s\n"), exename);
630b122b 405
406 Apply_Hacks_Cdrom();
ef79bbde
P
407
408 BuildPPFCache();
409
410 return 0;
411}
412
413static int PSXGetFileType(FILE *f) {
414 unsigned long current;
415 u8 mybuf[2048];
416 EXE_HEADER *exe_hdr;
417 FILHDR *coff_hdr;
418
419 current = ftell(f);
420 fseek(f, 0L, SEEK_SET);
5b568098 421 if (fread(&mybuf, 1, sizeof(mybuf), f) != sizeof(mybuf))
9361a5aa
EC
422 goto io_fail;
423
ef79bbde
P
424 fseek(f, current, SEEK_SET);
425
426 exe_hdr = (EXE_HEADER *)mybuf;
427 if (memcmp(exe_hdr->id, "PS-X EXE", 8) == 0)
428 return PSX_EXE;
429
430 if (mybuf[0] == 'C' && mybuf[1] == 'P' && mybuf[2] == 'E')
431 return CPE_EXE;
432
433 coff_hdr = (FILHDR *)mybuf;
434 if (SWAPu16(coff_hdr->f_magic) == 0x0162)
435 return COFF_EXE;
436
437 return INVALID_EXE;
9361a5aa
EC
438
439io_fail:
440#ifndef NDEBUG
441 SysPrintf(_("File IO error in <%s:%s>.\n"), __FILE__, __func__);
442#endif
443 return INVALID_EXE;
ef79bbde
P
444}
445
96bef96f 446// temporary pandora workaround..
447// FIXME: remove
448size_t fread_to_ram(void *ptr, size_t size, size_t nmemb, FILE *stream)
449{
450 void *tmp;
451 size_t ret = 0;
3e42b115 452
96bef96f 453 tmp = malloc(size * nmemb);
454 if (tmp) {
455 ret = fread(tmp, size, nmemb, stream);
456 memcpy(ptr, tmp, size * nmemb);
457 free(tmp);
458 }
459 return ret;
460}
461
ef79bbde
P
462int Load(const char *ExePath) {
463 FILE *tmpFile;
464 EXE_HEADER tmpHead;
465 int type;
466 int retval = 0;
467 u8 opcode;
468 u32 section_address, section_size;
ebe6ff2b 469 void *mem;
ef79bbde 470
3e42b115 471 strcpy(CdromId, "SLUS99999");
472 strcpy(CdromLabel, "SLUS_999.99");
ef79bbde
P
473
474 tmpFile = fopen(ExePath, "rb");
475 if (tmpFile == NULL) {
476 SysPrintf(_("Error opening file: %s.\n"), ExePath);
477 retval = -1;
478 } else {
479 type = PSXGetFileType(tmpFile);
480 switch (type) {
481 case PSX_EXE:
5b568098 482 if (fread(&tmpHead, 1, sizeof(EXE_HEADER), tmpFile) != sizeof(EXE_HEADER))
9361a5aa 483 goto fail_io;
ebe6ff2b 484 section_address = SWAP32(tmpHead.t_addr);
485 section_size = SWAP32(tmpHead.t_size);
486 mem = PSXM(section_address);
b012a437 487 if (mem != INVALID_PTR) {
3e42b115 488 fseek(tmpFile, 0x800, SEEK_SET);
96bef96f 489 fread_to_ram(mem, section_size, 1, tmpFile);
ebe6ff2b 490 psxCpu->Clear(section_address, section_size / 4);
491 }
ef79bbde
P
492 psxRegs.pc = SWAP32(tmpHead.pc0);
493 psxRegs.GPR.n.gp = SWAP32(tmpHead.gp0);
3e42b115 494 psxRegs.GPR.n.sp = SWAP32(tmpHead.s_addr);
ef79bbde
P
495 if (psxRegs.GPR.n.sp == 0)
496 psxRegs.GPR.n.sp = 0x801fff00;
497 retval = 0;
498 break;
499 case CPE_EXE:
500 fseek(tmpFile, 6, SEEK_SET); /* Something tells me we should go to 4 and read the "08 00" here... */
501 do {
5b568098 502 if (fread(&opcode, 1, sizeof(opcode), tmpFile) != sizeof(opcode))
9361a5aa 503 goto fail_io;
ef79bbde
P
504 switch (opcode) {
505 case 1: /* Section loading */
5b568098 506 if (fread(&section_address, 1, sizeof(section_address), tmpFile) != sizeof(section_address))
9361a5aa 507 goto fail_io;
5b568098 508 if (fread(&section_size, 1, sizeof(section_size), tmpFile) != sizeof(section_size))
9361a5aa 509 goto fail_io;
ef79bbde
P
510 section_address = SWAPu32(section_address);
511 section_size = SWAPu32(section_size);
512#ifdef EMU_LOG
513 EMU_LOG("Loading %08X bytes from %08X to %08X\n", section_size, ftell(tmpFile), section_address);
514#endif
ebe6ff2b 515 mem = PSXM(section_address);
b012a437 516 if (mem != INVALID_PTR) {
96bef96f 517 fread_to_ram(mem, section_size, 1, tmpFile);
ebe6ff2b 518 psxCpu->Clear(section_address, section_size / 4);
519 }
ef79bbde
P
520 break;
521 case 3: /* register loading (PC only?) */
522 fseek(tmpFile, 2, SEEK_CUR); /* unknown field */
5b568098 523 if (fread(&psxRegs.pc, 1, sizeof(psxRegs.pc), tmpFile) != sizeof(psxRegs.pc))
9361a5aa 524 goto fail_io;
ef79bbde
P
525 psxRegs.pc = SWAPu32(psxRegs.pc);
526 break;
527 case 0: /* End of file */
528 break;
529 default:
530 SysPrintf(_("Unknown CPE opcode %02x at position %08x.\n"), opcode, ftell(tmpFile) - 1);
531 retval = -1;
532 break;
533 }
534 } while (opcode != 0 && retval == 0);
535 break;
536 case COFF_EXE:
537 SysPrintf(_("COFF files not supported.\n"));
538 retval = -1;
539 break;
540 case INVALID_EXE:
8f00f96c 541 SysPrintf(_("This file does not appear to be a valid PSX EXE file.\n"));
542 SysPrintf(_("(did you forget -cdfile ?)\n"));
ef79bbde
P
543 retval = -1;
544 break;
545 }
546 }
547
548 if (retval != 0) {
549 CdromId[0] = '\0';
550 CdromLabel[0] = '\0';
551 }
552
5b568098 553 if (tmpFile)
554 fclose(tmpFile);
ef79bbde 555 return retval;
9361a5aa
EC
556
557fail_io:
558#ifndef NDEBUG
559 SysPrintf(_("File IO error in <%s:%s>.\n"), __FILE__, __func__);
560#endif
561 fclose(tmpFile);
562 return -1;
ef79bbde
P
563}
564
565// STATES
566
496d88d4 567static void *zlib_open(const char *name, const char *mode)
568{
569 return gzopen(name, mode);
570}
571
572static int zlib_read(void *file, void *buf, u32 len)
573{
574 return gzread(file, buf, len);
575}
576
577static int zlib_write(void *file, const void *buf, u32 len)
578{
579 return gzwrite(file, buf, len);
580}
581
582static long zlib_seek(void *file, long offs, int whence)
583{
584 return gzseek(file, offs, whence);
585}
586
587static void zlib_close(void *file)
588{
589 gzclose(file);
590}
591
592struct PcsxSaveFuncs SaveFuncs = {
593 zlib_open, zlib_read, zlib_write, zlib_seek, zlib_close
594};
595
202b29d4 596static const char PcsxHeader[32] = "STv4 PCSX v" PCSX_VERSION;
ef79bbde
P
597
598// Savestate Versioning!
599// If you make changes to the savestate version, please increment the value below.
df656dde 600static const u32 SaveVersion = 0x8b410006;
ef79bbde
P
601
602int SaveState(const char *file) {
496d88d4 603 void *f;
ef79bbde
P
604 GPUFreeze_t *gpufP;
605 SPUFreeze_t *spufP;
606 int Size;
607 unsigned char *pMem;
608
496d88d4 609 f = SaveFuncs.open(file, "wb");
ef79bbde
P
610 if (f == NULL) return -1;
611
03f55e6b 612 new_dyna_before_save();
52082bc1 613
02dbc869 614 if (drc_is_lightrec() && Config.Cpu != CPU_INTERPRETER)
d0abba5d 615 lightrec_plugin_sync_regs_to_pcsx();
6962f770 616
496d88d4 617 SaveFuncs.write(f, (void *)PcsxHeader, 32);
618 SaveFuncs.write(f, (void *)&SaveVersion, sizeof(u32));
619 SaveFuncs.write(f, (void *)&Config.HLE, sizeof(boolean));
ef79bbde
P
620
621 pMem = (unsigned char *)malloc(128 * 96 * 3);
622 if (pMem == NULL) return -1;
623 GPU_getScreenPic(pMem);
496d88d4 624 SaveFuncs.write(f, pMem, 128 * 96 * 3);
ef79bbde
P
625 free(pMem);
626
627 if (Config.HLE)
628 psxBiosFreeze(1);
629
496d88d4 630 SaveFuncs.write(f, psxM, 0x00200000);
631 SaveFuncs.write(f, psxR, 0x00080000);
632 SaveFuncs.write(f, psxH, 0x00010000);
630b122b 633 // only partial save of psxRegisters to maintain savestate compat
634 SaveFuncs.write(f, &psxRegs, offsetof(psxRegisters, gteBusyCycle));
ef79bbde
P
635
636 // gpu
637 gpufP = (GPUFreeze_t *)malloc(sizeof(GPUFreeze_t));
638 gpufP->ulFreezeVersion = 1;
639 GPU_freeze(1, gpufP);
496d88d4 640 SaveFuncs.write(f, gpufP, sizeof(GPUFreeze_t));
ef79bbde
P
641 free(gpufP);
642
643 // spu
644 spufP = (SPUFreeze_t *) malloc(16);
650adfd2 645 SPU_freeze(2, spufP, psxRegs.cycle);
496d88d4 646 Size = spufP->Size; SaveFuncs.write(f, &Size, 4);
ef79bbde
P
647 free(spufP);
648 spufP = (SPUFreeze_t *) malloc(Size);
650adfd2 649 SPU_freeze(1, spufP, psxRegs.cycle);
496d88d4 650 SaveFuncs.write(f, spufP, Size);
ef79bbde
P
651 free(spufP);
652
653 sioFreeze(f, 1);
654 cdrFreeze(f, 1);
655 psxHwFreeze(f, 1);
656 psxRcntFreeze(f, 1);
657 mdecFreeze(f, 1);
03f55e6b 658 new_dyna_freeze(f, 1);
ef79bbde 659
496d88d4 660 SaveFuncs.close(f);
ef79bbde 661
5b8c000f 662 new_dyna_after_save();
663
ef79bbde
P
664 return 0;
665}
666
667int LoadState(const char *file) {
496d88d4 668 void *f;
ef79bbde
P
669 GPUFreeze_t *gpufP;
670 SPUFreeze_t *spufP;
671 int Size;
672 char header[32];
673 u32 version;
674 boolean hle;
675
496d88d4 676 f = SaveFuncs.open(file, "rb");
ef79bbde
P
677 if (f == NULL) return -1;
678
496d88d4 679 SaveFuncs.read(f, header, sizeof(header));
680 SaveFuncs.read(f, &version, sizeof(u32));
681 SaveFuncs.read(f, &hle, sizeof(boolean));
ef79bbde 682
33f56da1 683 if (strncmp("STv4 PCSX", header, 9) != 0 || version != SaveVersion) {
496d88d4 684 SaveFuncs.close(f);
ef79bbde
P
685 return -1;
686 }
33f56da1 687 Config.HLE = hle;
688
689 if (Config.HLE)
690 psxBiosInit();
ef79bbde 691
6962f770
PC
692 if (!drc_is_lightrec() || Config.Cpu == CPU_INTERPRETER)
693 psxCpu->Reset();
496d88d4 694 SaveFuncs.seek(f, 128 * 96 * 3, SEEK_CUR);
ef79bbde 695
496d88d4 696 SaveFuncs.read(f, psxM, 0x00200000);
697 SaveFuncs.read(f, psxR, 0x00080000);
698 SaveFuncs.read(f, psxH, 0x00010000);
630b122b 699 SaveFuncs.read(f, &psxRegs, offsetof(psxRegisters, gteBusyCycle));
700 psxRegs.gteBusyCycle = psxRegs.cycle;
ef79bbde 701
6962f770 702 if (drc_is_lightrec() && Config.Cpu != CPU_INTERPRETER)
d0abba5d 703 lightrec_plugin_sync_regs_from_pcsx();
6962f770 704
ef79bbde
P
705 if (Config.HLE)
706 psxBiosFreeze(0);
707
708 // gpu
709 gpufP = (GPUFreeze_t *)malloc(sizeof(GPUFreeze_t));
496d88d4 710 SaveFuncs.read(f, gpufP, sizeof(GPUFreeze_t));
ef79bbde
P
711 GPU_freeze(0, gpufP);
712 free(gpufP);
ddbaf678 713 if (HW_GPU_STATUS == 0)
adb7d7ac 714 HW_GPU_STATUS = SWAP32(GPU_readStatus());
ef79bbde
P
715
716 // spu
496d88d4 717 SaveFuncs.read(f, &Size, 4);
ef79bbde 718 spufP = (SPUFreeze_t *)malloc(Size);
496d88d4 719 SaveFuncs.read(f, spufP, Size);
650adfd2 720 SPU_freeze(0, spufP, psxRegs.cycle);
ef79bbde
P
721 free(spufP);
722
723 sioFreeze(f, 0);
724 cdrFreeze(f, 0);
725 psxHwFreeze(f, 0);
726 psxRcntFreeze(f, 0);
727 mdecFreeze(f, 0);
03f55e6b 728 new_dyna_freeze(f, 0);
ef79bbde 729
496d88d4 730 SaveFuncs.close(f);
ef79bbde
P
731
732 return 0;
733}
734
735int CheckState(const char *file) {
496d88d4 736 void *f;
ef79bbde
P
737 char header[32];
738 u32 version;
739 boolean hle;
740
496d88d4 741 f = SaveFuncs.open(file, "rb");
ef79bbde
P
742 if (f == NULL) return -1;
743
496d88d4 744 SaveFuncs.read(f, header, sizeof(header));
745 SaveFuncs.read(f, &version, sizeof(u32));
746 SaveFuncs.read(f, &hle, sizeof(boolean));
ef79bbde 747
496d88d4 748 SaveFuncs.close(f);
ef79bbde 749
33f56da1 750 if (strncmp("STv4 PCSX", header, 9) != 0 || version != SaveVersion)
ef79bbde
P
751 return -1;
752
753 return 0;
754}
755
756// NET Function Helpers
757
758int SendPcsxInfo() {
759 if (NET_recvData == NULL || NET_sendData == NULL)
760 return 0;
761
840639a5 762 boolean Sio_old = 0;
763 boolean SpuIrq_old = 0;
764 boolean RCntFix_old = 0;
ef79bbde 765 NET_sendData(&Config.Xa, sizeof(Config.Xa), PSE_NET_BLOCKING);
840639a5 766 NET_sendData(&Sio_old, sizeof(Sio_old), PSE_NET_BLOCKING);
767 NET_sendData(&SpuIrq_old, sizeof(SpuIrq_old), PSE_NET_BLOCKING);
768 NET_sendData(&RCntFix_old, sizeof(RCntFix_old), PSE_NET_BLOCKING);
ef79bbde
P
769 NET_sendData(&Config.PsxType, sizeof(Config.PsxType), PSE_NET_BLOCKING);
770 NET_sendData(&Config.Cpu, sizeof(Config.Cpu), PSE_NET_BLOCKING);
771
772 return 0;
773}
774
775int RecvPcsxInfo() {
776 int tmp;
777
778 if (NET_recvData == NULL || NET_sendData == NULL)
779 return 0;
780
840639a5 781 boolean Sio_old = 0;
782 boolean SpuIrq_old = 0;
783 boolean RCntFix_old = 0;
ef79bbde 784 NET_recvData(&Config.Xa, sizeof(Config.Xa), PSE_NET_BLOCKING);
840639a5 785 NET_recvData(&Sio_old, sizeof(Sio_old), PSE_NET_BLOCKING);
786 NET_recvData(&SpuIrq_old, sizeof(SpuIrq_old), PSE_NET_BLOCKING);
787 NET_recvData(&RCntFix_old, sizeof(RCntFix_old), PSE_NET_BLOCKING);
ef79bbde
P
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}