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