remove "slow" text from Enhanced Resolution
[pcsx_rearmed.git] / libpcsxcore / misc.c
... / ...
CommitLineData
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
24#include <stddef.h>
25#include <assert.h>
26#include "misc.h"
27#include "cdrom.h"
28#include "mdec.h"
29#include "gpu.h"
30#include "ppf.h"
31#include "database.h"
32#include <zlib.h>
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
59static void mmssdd( char *b, char *p )
60{
61 int m, s, d;
62 unsigned char *ub = (void *)b;
63 int block = (ub[3] << 24) | (ub[2] << 16) | (ub[1] << 8) | ub[0];
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;
73 d = ((d / 10) << 4) | d % 10;
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() \
94 if (!CDR_readTrack(time)) return -1; \
95 buf = (void *)CDR_getBuffer(); \
96 if (buf == NULL) return -1; \
97 else CheckPPFCache((u8 *)buf, time[0], time[1], time[2]);
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
107int GetCdromFile(u8 *mdir, u8 *time, char *filename) {
108 struct iso_directory_record *dir;
109 int retval = -1;
110 u8 ddir[4096];
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 }
123 i += (u8)dir->length[0];
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);
139 retval = 0;
140 break;
141 }
142 }
143 }
144 return retval;
145}
146
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
158void BiosLikeGPUSetup()
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 HW_GPU_STATUS |= SWAP32(PSXGPU_nBUSY);
169}
170
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
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
191int LoadCdrom() {
192 EXE_HEADER tmpHead;
193 struct iso_directory_record *dir;
194 u8 time[4], *buf;
195 u8 mdir[4096];
196 char exename[256];
197
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;
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
209 dir = (struct iso_directory_record*) &buf[12+156];
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;
219 strcpy(exename, "PSX.EXE;1");
220
221 READTRACK();
222 }
223 else {
224 // read the SYSTEM.CNF
225 READTRACK();
226
227 sscanf((char *)buf + 12, "BOOT = cdrom:\\%255s", exename);
228 if (GetCdromFile(mdir, time, exename) == -1) {
229 sscanf((char *)buf + 12, "BOOT = cdrom:%255s", exename);
230 if (GetCdromFile(mdir, time, exename) == -1) {
231 char *ptr = strstr((char *)buf + 12, "cdrom:");
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
253 SysPrintf("manual booting '%s'\n", exename);
254 SetBootRegs(SWAP32(tmpHead.pc0), SWAP32(tmpHead.gp0), SWAP32(tmpHead.s_addr));
255
256 tmpHead.t_size = SWAP32(tmpHead.t_size);
257 tmpHead.t_addr = SWAP32(tmpHead.t_addr);
258
259 psxCpu->Clear(tmpHead.t_addr, tmpHead.t_size / 4);
260 psxCpu->Reset();
261
262 // Read the rest of the main executable
263 while (tmpHead.t_size & ~2047) {
264 void *ptr = (void *)PSXM(tmpHead.t_addr);
265
266 incTime();
267 READTRACK();
268
269 if (ptr != INVALID_PTR) memcpy(ptr, buf+12, 2048);
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;
281 u8 mdir[4096];
282 char exename[256];
283 u32 size, addr;
284 void *mem;
285
286 sscanf(filename, "cdrom:\\%255s", exename);
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
293 dir = (struct iso_directory_record *)&buf[12 + 156];
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
307 psxCpu->Clear(addr, size / 4);
308 psxCpu->Reset();
309
310 while (size & ~2047) {
311 incTime();
312 READTRACK();
313
314 mem = PSXM(addr);
315 if (mem != INVALID_PTR)
316 memcpy(mem, buf + 12, 2048);
317
318 size -= 2048;
319 addr += 2048;
320 }
321
322 return 0;
323}
324
325int CheckCdrom() {
326 struct iso_directory_record *dir;
327 unsigned char time[4];
328 char *buf;
329 unsigned char mdir[4096];
330 char exename[256];
331 int i, len, c;
332
333 FreePPFCache();
334
335 time[0] = itob(0);
336 time[1] = itob(2);
337 time[2] = itob(0x10);
338
339 READTRACK();
340
341 memset(CdromLabel, 0, sizeof(CdromLabel));
342 memset(CdromId, 0, sizeof(CdromId));
343 memset(exename, 0, sizeof(exename));
344
345 strncpy(CdromLabel, buf + 52, 32);
346
347 // skip head and sub, and go to the root directory record
348 dir = (struct iso_directory_record *)&buf[12 + 156];
349
350 mmssdd(dir->extent, (char *)time);
351
352 READDIR(mdir);
353
354 if (GetCdromFile(mdir, time, "SYSTEM.CNF;1") != -1) {
355 READTRACK();
356
357 sscanf(buf + 12, "BOOT = cdrom:\\%255s", exename);
358 if (GetCdromFile(mdir, time, exename) == -1) {
359 sscanf(buf + 12, "BOOT = cdrom:%255s", exename);
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 }
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 }
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') {
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];
398 }
399 }
400
401 if (CdromId[0] == '\0')
402 strcpy(CdromId, "SLUS99999");
403
404 if (Config.PsxAuto) { // autodetect system (pal or ntsc)
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.
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);
421 SysPrintf(_("CD-ROM EXE Name: %.255s\n"), exename);
422
423 Apply_Hacks_Cdrom();
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);
438 if (fread(&mybuf, 1, sizeof(mybuf), f) != sizeof(mybuf))
439 goto io_fail;
440
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;
455
456io_fail:
457#ifndef NDEBUG
458 SysPrintf(_("File IO error in <%s:%s>.\n"), __FILE__, __func__);
459#endif
460 return INVALID_EXE;
461}
462
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;
469
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
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;
486 void *mem;
487
488 strcpy(CdromId, "SLUS99999");
489 strcpy(CdromLabel, "SLUS_999.99");
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:
499 if (fread(&tmpHead, 1, sizeof(EXE_HEADER), tmpFile) != sizeof(EXE_HEADER))
500 goto fail_io;
501 section_address = SWAP32(tmpHead.t_addr);
502 section_size = SWAP32(tmpHead.t_size);
503 mem = PSXM(section_address);
504 if (mem != INVALID_PTR) {
505 fseek(tmpFile, 0x800, SEEK_SET);
506 fread_to_ram(mem, section_size, 1, tmpFile);
507 psxCpu->Clear(section_address, section_size / 4);
508 }
509 SetBootRegs(SWAP32(tmpHead.pc0), SWAP32(tmpHead.gp0),
510 SWAP32(tmpHead.s_addr));
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 {
516 if (fread(&opcode, 1, sizeof(opcode), tmpFile) != sizeof(opcode))
517 goto fail_io;
518 switch (opcode) {
519 case 1: /* Section loading */
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;
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
529 mem = PSXM(section_address);
530 if (mem != INVALID_PTR) {
531 fread_to_ram(mem, section_size, 1, tmpFile);
532 psxCpu->Clear(section_address, section_size / 4);
533 }
534 break;
535 case 3: /* register loading (PC only?) */
536 fseek(tmpFile, 2, SEEK_CUR); /* unknown field */
537 if (fread(&psxRegs.pc, 1, sizeof(psxRegs.pc), tmpFile) != sizeof(psxRegs.pc))
538 goto fail_io;
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:
555 SysPrintf(_("This file does not appear to be a valid PSX EXE file.\n"));
556 SysPrintf(_("(did you forget -cdfile ?)\n"));
557 retval = -1;
558 break;
559 }
560 }
561
562 if (retval != 0) {
563 CdromId[0] = '\0';
564 CdromLabel[0] = '\0';
565 }
566
567 if (tmpFile)
568 fclose(tmpFile);
569 return retval;
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;
577}
578
579// STATES
580
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
610static const char PcsxHeader[32] = "STv4 PCSX v" PCSX_VERSION;
611
612// Savestate Versioning!
613// If you make changes to the savestate version, please increment the value below.
614static const u32 SaveVersion = 0x8b410006;
615
616int SaveState(const char *file) {
617 void *f;
618 GPUFreeze_t *gpufP;
619 SPUFreezeHdr_t *spufH;
620 SPUFreeze_t *spufP;
621 int Size;
622 unsigned char *pMem;
623
624 f = SaveFuncs.open(file, "wb");
625 if (f == NULL) return -1;
626
627 psxCpu->Notify(R3000ACPU_NOTIFY_BEFORE_SAVE, NULL);
628
629 SaveFuncs.write(f, (void *)PcsxHeader, 32);
630 SaveFuncs.write(f, (void *)&SaveVersion, sizeof(u32));
631 SaveFuncs.write(f, (void *)&Config.HLE, sizeof(boolean));
632
633 pMem = (unsigned char *)malloc(128 * 96 * 3);
634 if (pMem == NULL) return -1;
635 GPU_getScreenPic(pMem);
636 SaveFuncs.write(f, pMem, 128 * 96 * 3);
637 free(pMem);
638
639 if (Config.HLE)
640 psxBiosFreeze(1);
641
642 SaveFuncs.write(f, psxM, 0x00200000);
643 SaveFuncs.write(f, psxR, 0x00080000);
644 SaveFuncs.write(f, psxH, 0x00010000);
645 // only partial save of psxRegisters to maintain savestate compat
646 SaveFuncs.write(f, &psxRegs, offsetof(psxRegisters, gteBusyCycle));
647
648 // gpu
649 gpufP = (GPUFreeze_t *)malloc(sizeof(GPUFreeze_t));
650 gpufP->ulFreezeVersion = 1;
651 GPU_freeze(1, gpufP);
652 SaveFuncs.write(f, gpufP, sizeof(GPUFreeze_t));
653 free(gpufP);
654
655 // spu
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);
660 spufP = (SPUFreeze_t *) malloc(Size);
661 SPU_freeze(1, spufP, psxRegs.cycle);
662 SaveFuncs.write(f, spufP, Size);
663 free(spufP);
664
665 sioFreeze(f, 1);
666 cdrFreeze(f, 1);
667 psxHwFreeze(f, 1);
668 psxRcntFreeze(f, 1);
669 mdecFreeze(f, 1);
670 new_dyna_freeze(f, 1);
671
672 SaveFuncs.close(f);
673
674 return 0;
675}
676
677int LoadState(const char *file) {
678 void *f;
679 GPUFreeze_t *gpufP;
680 SPUFreeze_t *spufP;
681 int Size;
682 char header[32];
683 u32 version;
684 boolean hle;
685
686 f = SaveFuncs.open(file, "rb");
687 if (f == NULL) return -1;
688
689 SaveFuncs.read(f, header, sizeof(header));
690 SaveFuncs.read(f, &version, sizeof(u32));
691 SaveFuncs.read(f, &hle, sizeof(boolean));
692
693 if (strncmp("STv4 PCSX", header, 9) != 0 || version != SaveVersion) {
694 SaveFuncs.close(f);
695 return -1;
696 }
697 Config.HLE = hle;
698
699 if (Config.HLE)
700 psxBiosInit();
701
702 SaveFuncs.seek(f, 128 * 96 * 3, SEEK_CUR);
703 SaveFuncs.read(f, psxM, 0x00200000);
704 SaveFuncs.read(f, psxR, 0x00080000);
705 SaveFuncs.read(f, psxH, 0x00010000);
706 SaveFuncs.read(f, &psxRegs, offsetof(psxRegisters, gteBusyCycle));
707 psxRegs.gteBusyCycle = psxRegs.cycle;
708
709 psxCpu->Notify(R3000ACPU_NOTIFY_AFTER_LOAD, NULL);
710
711 if (Config.HLE)
712 psxBiosFreeze(0);
713
714 // gpu
715 gpufP = (GPUFreeze_t *)malloc(sizeof(GPUFreeze_t));
716 SaveFuncs.read(f, gpufP, sizeof(GPUFreeze_t));
717 GPU_freeze(0, gpufP);
718 free(gpufP);
719 if (HW_GPU_STATUS == 0)
720 HW_GPU_STATUS = SWAP32(GPU_readStatus());
721
722 // spu
723 SaveFuncs.read(f, &Size, 4);
724 spufP = (SPUFreeze_t *)malloc(Size);
725 SaveFuncs.read(f, spufP, Size);
726 SPU_freeze(0, spufP, psxRegs.cycle);
727 free(spufP);
728
729 sioFreeze(f, 0);
730 cdrFreeze(f, 0);
731 psxHwFreeze(f, 0);
732 psxRcntFreeze(f, 0);
733 mdecFreeze(f, 0);
734 new_dyna_freeze(f, 0);
735
736 SaveFuncs.close(f);
737
738 return 0;
739}
740
741int CheckState(const char *file) {
742 void *f;
743 char header[32];
744 u32 version;
745 boolean hle;
746
747 f = SaveFuncs.open(file, "rb");
748 if (f == NULL) return -1;
749
750 SaveFuncs.read(f, header, sizeof(header));
751 SaveFuncs.read(f, &version, sizeof(u32));
752 SaveFuncs.read(f, &hle, sizeof(boolean));
753
754 SaveFuncs.close(f);
755
756 if (strncmp("STv4 PCSX", header, 9) != 0 || version != SaveVersion)
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 boolean Sio_old = 0;
769 boolean SpuIrq_old = 0;
770 boolean RCntFix_old = 0;
771 NET_sendData(&Config.Xa, sizeof(Config.Xa), PSE_NET_BLOCKING);
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);
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
787 boolean Sio_old = 0;
788 boolean SpuIrq_old = 0;
789 boolean RCntFix_old = 0;
790 NET_recvData(&Config.Xa, sizeof(Config.Xa), PSE_NET_BLOCKING);
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);
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();
802#ifndef DRC_DISABLE
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();
812 psxCpu->Notify(R3000ACPU_NOTIFY_AFTER_LOAD, NULL);
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}