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