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
630b122b 24#include <stddef.h>
f5f5f5dd 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"
630b122b 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
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
f5f5f5dd 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]);
4db82c07 167
168 HW_GPU_STATUS |= SWAP32(PSXGPU_nBUSY);
f3a78e7e 169}
170
6c62131f 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
f5f5f5dd 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
f5f5f5dd 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
3e42b115 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;
f5f5f5dd 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
f5f5f5dd 253 SysPrintf("manual booting '%s'\n", exename);
6c62131f 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);
952943a7 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
b012a437 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
3e42b115 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);
952943a7 308 psxCpu->Reset();
ebe6ff2b 309
310 while (size & ~2047) {
ef79bbde
P
311 incTime();
312 READTRACK();
313
305c8c93 314 mem = PSXM(addr);
b012a437 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
3e42b115 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 }
1893f408 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)
630b122b 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] == ' ') {
630b122b 417 strncpy(CdromLabel, CdromId, 9);
ef79bbde
P
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);
630b122b 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);
5b568098 438 if (fread(&mybuf, 1, sizeof(mybuf), f) != sizeof(mybuf))
9361a5aa
EC
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;
9361a5aa
EC
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;
3e42b115 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
3e42b115 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:
5b568098 499 if (fread(&tmpHead, 1, sizeof(EXE_HEADER), tmpFile) != sizeof(EXE_HEADER))
9361a5aa 500 goto fail_io;
ebe6ff2b 501 section_address = SWAP32(tmpHead.t_addr);
502 section_size = SWAP32(tmpHead.t_size);
503 mem = PSXM(section_address);
b012a437 504 if (mem != INVALID_PTR) {
3e42b115 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 }
6c62131f 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 {
5b568098 516 if (fread(&opcode, 1, sizeof(opcode), tmpFile) != sizeof(opcode))
9361a5aa 517 goto fail_io;
ef79bbde
P
518 switch (opcode) {
519 case 1: /* Section loading */
5b568098 520 if (fread(&section_address, 1, sizeof(section_address), tmpFile) != sizeof(section_address))
9361a5aa 521 goto fail_io;
5b568098 522 if (fread(&section_size, 1, sizeof(section_size), tmpFile) != sizeof(section_size))
9361a5aa 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);
b012a437 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 */
5b568098 537 if (fread(&psxRegs.pc, 1, sizeof(psxRegs.pc), tmpFile) != sizeof(psxRegs.pc))
9361a5aa 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
5b568098 567 if (tmpFile)
568 fclose(tmpFile);
ef79bbde 569 return retval;
9361a5aa
EC
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
202b29d4 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;
6c62131f 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
20196899 627 psxCpu->Notify(R3000ACPU_NOTIFY_BEFORE_SAVE, NULL);
6962f770 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);
630b122b 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
6c62131f 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);
630b122b 706 SaveFuncs.read(f, &psxRegs, offsetof(psxRegisters, gteBusyCycle));
707 psxRegs.gteBusyCycle = psxRegs.cycle;
ef79bbde 708
20196899 709 psxCpu->Notify(R3000ACPU_NOTIFY_AFTER_LOAD, NULL);
6962f770 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
840639a5 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);
840639a5 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
840639a5 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);
840639a5 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();
630b122b 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();
20196899 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}