1 /***************************************************************************
2 * Copyright (C) 2007 Ryan Schultz, PCSX-df Team, PCSX team *
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. *
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. *
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 ***************************************************************************/
21 * Miscellaneous functions, including savestates and CD-ROM loading.
36 char CdromId[10] = "";
37 char CdromLabel[33] = "";
38 int CdromFrontendId; // for frontend use
40 // PSX Executable types
46 #define ISODCL(from, to) (to - from + 1)
48 struct iso_directory_record {
49 char length [ISODCL (1, 1)]; /* 711 */
50 char ext_attr_length [ISODCL (2, 2)]; /* 711 */
51 char extent [ISODCL (3, 10)]; /* 733 */
52 char size [ISODCL (11, 18)]; /* 733 */
53 char date [ISODCL (19, 25)]; /* 7 by 711 */
54 char flags [ISODCL (26, 26)];
55 char file_unit_size [ISODCL (27, 27)]; /* 711 */
56 char interleave [ISODCL (28, 28)]; /* 711 */
57 char volume_sequence_number [ISODCL (29, 32)]; /* 723 */
58 unsigned char name_len [ISODCL (33, 33)]; /* 711 */
62 static void mmssdd( char *b, char *p )
65 unsigned char *ub = (void *)b;
66 int block = (ub[3] << 24) | (ub[2] << 16) | (ub[1] << 8) | ub[0];
69 m = block / 4500; // minutes
70 block = block - m * 4500; // minutes rest
71 s = block / 75; // seconds
72 d = block - s * 75; // seconds rest
74 m = ((m / 10) << 4) | m % 10;
75 s = ((s / 10) << 4) | s % 10;
76 d = ((d / 10) << 4) | d % 10;
84 time[0] = btoi(time[0]); time[1] = btoi(time[1]); time[2] = btoi(time[2]); \
89 if (time[1] == 60) { \
94 time[0] = itob(time[0]); time[1] = itob(time[1]); time[2] = itob(time[2]);
97 if (!CDR_readTrack(time)) return -1; \
98 buf = (void *)CDR_getBuffer(); \
99 if (buf == NULL) return -1; \
100 else CheckPPFCache((u8 *)buf, time[0], time[1], time[2]);
102 #define READDIR(_dir) \
104 memcpy(_dir, buf + 12, 2048); \
108 memcpy(_dir + 2048, buf + 12, 2048);
110 int GetCdromFile(u8 *mdir, u8 *time, char *filename) {
111 struct iso_directory_record *dir;
117 // only try to scan if a filename is given
118 if (filename == INVALID_PTR || !strlen(filename)) return -1;
122 dir = (struct iso_directory_record*) &mdir[i];
123 if (dir->length[0] == 0) {
126 i += (u8)dir->length[0];
128 if (dir->flags[0] & 0x2) { // it's a dir
129 if (!strnicmp((char *)&dir->name[0], filename, dir->name_len[0])) {
130 if (filename[dir->name_len[0]] != '\\') continue;
132 filename += dir->name_len[0] + 1;
134 mmssdd(dir->extent, (char *)time);
140 if (!strnicmp((char *)&dir->name[0], filename, strlen(filename))) {
141 mmssdd(dir->extent, (char *)time);
150 static void SetBootRegs(u32 pc, u32 gp, u32 sp)
152 //printf("%s %08x %08x %08x\n", __func__, pc, gp, sp);
153 psxCpu->Notify(R3000ACPU_NOTIFY_BEFORE_SAVE, NULL);
156 psxRegs.GPR.n.gp = gp;
157 psxRegs.GPR.n.sp = sp ? sp : 0x801fff00;
158 psxRegs.GPR.n.fp = psxRegs.GPR.n.sp;
160 psxRegs.GPR.n.t0 = psxRegs.GPR.n.sp; // mimic A(43)
161 psxRegs.GPR.n.t3 = pc;
163 psxCpu->Notify(R3000ACPU_NOTIFY_AFTER_LOAD, NULL);
166 int BiosBootBypass() {
167 struct CdrStat stat = { 0, 0, };
168 assert(psxRegs.pc == 0x80030000);
170 // no bypass if the lid is open
171 CDR__getStatus(&stat);
172 if (stat.Status & 0x10)
175 // skip BIOS logos and region check
176 psxCpu->Notify(R3000ACPU_NOTIFY_BEFORE_SAVE, NULL);
177 psxRegs.pc = psxRegs.GPR.n.ra;
181 static void getFromCnf(char *buf, const char *key, u32 *val)
183 buf = strstr(buf, key);
185 buf = strchr(buf, '=');
189 v = strtoul(buf + 1, NULL, 16);
198 u32 d[sizeof(EXE_HEADER) / sizeof(u32)];
200 struct iso_directory_record *dir;
213 if (psxRegs.pc != 0x80030000) // BiosBootBypass'ed or custom BIOS?
219 time[0] = itob(0); time[1] = itob(2); time[2] = itob(0x10);
223 // skip head and sub, and go to the root directory record
224 dir = (struct iso_directory_record*) &buf[12+156];
226 mmssdd(dir->extent, (char*)time);
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");
239 // read the SYSTEM.CNF
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:");
250 while (*ptr == '\\' || *ptr == '/') ptr++;
251 strncpy(exename, ptr, 255);
254 while (*ptr != '\0' && *ptr != '\r' && *ptr != '\n') ptr++;
256 if (GetCdromFile(mdir, time, exename) == -1)
262 getFromCnf((char *)buf + 12, "TCB", &cnf_tcb);
263 getFromCnf((char *)buf + 12, "EVENT", &cnf_event);
264 getFromCnf((char *)buf + 12, "STACK", &cnf_stack);
266 psxBiosCnfLoaded(cnf_tcb, cnf_event, cnf_stack);
268 // Read the EXE-Header
272 memcpy(&tmpHead, buf + 12, sizeof(EXE_HEADER));
273 for (i = 2; i < sizeof(tmpHead.d) / sizeof(tmpHead.d[0]); i++)
274 tmpHead.d[i] = SWAP32(tmpHead.d[i]);
276 SysPrintf("manual booting '%s' pc=%x\n", exename, tmpHead.h.pc0);
277 sp = tmpHead.h.s_addr;
280 SetBootRegs(tmpHead.h.pc0, tmpHead.h.gp0, sp);
282 // Read the rest of the main executable
283 for (t_addr = tmpHead.h.t_addr, t_size = tmpHead.h.t_size; t_size & ~2047; ) {
284 void *ptr = (void *)PSXM(t_addr);
289 if (ptr != INVALID_PTR) memcpy(ptr, buf+12, 2048);
295 psxCpu->Clear(tmpHead.h.t_addr, tmpHead.h.t_size / 4);
299 psxBiosCheckExe(tmpHead.h.t_addr, tmpHead.h.t_size, 0);
304 int LoadCdromFile(const char *filename, EXE_HEADER *head, u8 *time_bcd_out) {
305 struct iso_directory_record *dir;
313 if (filename == INVALID_PTR)
317 if ((p2 = strchr(p1, ':')))
321 snprintf(exename, sizeof(exename), "%s", p1);
323 time[0] = itob(0); time[1] = itob(2); time[2] = itob(0x10);
327 // skip head and sub, and go to the root directory record
328 dir = (struct iso_directory_record *)&buf[12 + 156];
330 mmssdd(dir->extent, (char*)time);
334 if (GetCdromFile(mdir, time, exename) == -1) return -1;
339 memcpy(head, buf + 12, sizeof(EXE_HEADER));
340 size = SWAP32(head->t_size);
341 addr = SWAP32(head->t_addr);
343 psxCpu->Clear(addr, size / 4);
346 while (size & ~2047) {
351 if (mem != INVALID_PTR)
352 memcpy(mem, buf + 12, 2048);
358 memcpy(time_bcd_out, time, 3);
364 struct iso_directory_record *dir;
365 unsigned char time[4];
367 unsigned char mdir[4096];
375 time[2] = itob(0x10);
379 memset(CdromLabel, 0, sizeof(CdromLabel));
380 memset(CdromId, 0, sizeof(CdromId));
381 memset(exename, 0, sizeof(exename));
383 strncpy(CdromLabel, buf + 52, 32);
385 // skip head and sub, and go to the root directory record
386 dir = (struct iso_directory_record *)&buf[12 + 156];
388 mmssdd(dir->extent, (char *)time);
392 if (GetCdromFile(mdir, time, "SYSTEM.CNF;1") != -1) {
395 sscanf(buf + 12, "BOOT = cdrom:\\%255s", exename);
396 if (GetCdromFile(mdir, time, exename) == -1) {
397 sscanf(buf + 12, "BOOT = cdrom:%255s", exename);
398 if (GetCdromFile(mdir, time, exename) == -1) {
399 char *ptr = strstr(buf + 12, "cdrom:"); // possibly the executable is in some subdir
402 while (*ptr == '\\' || *ptr == '/') ptr++;
403 strncpy(exename, ptr, 255);
406 while (*ptr != '\0' && *ptr != '\r' && *ptr != '\n') ptr++;
408 if (GetCdromFile(mdir, time, exename) == -1)
409 return -1; // main executable not found
414 /* Workaround for Wild Arms EU/US which has non-standard string causing incorrect region detection */
415 if (exename[0] == 'E' && exename[1] == 'X' && exename[2] == 'E' && exename[3] == '\\') {
417 size_t i, len = strlen(exename) - offset;
418 for (i = 0; i < len; i++)
419 exename[i] = exename[i + offset];
422 } else if (GetCdromFile(mdir, time, "PSX.EXE;1") != -1) {
423 strcpy(exename, "PSX.EXE;1");
424 strcpy(CdromId, "SLUS99999");
426 return -1; // SYSTEM.CNF and PSX.EXE not found
428 if (CdromId[0] == '\0') {
429 len = strlen(exename);
431 for (i = 0; i < len; ++i) {
432 if (exename[i] == ';' || c >= sizeof(CdromId) - 1)
434 if (isalnum(exename[i]))
435 CdromId[c++] = exename[i];
439 if (CdromId[0] == '\0')
440 strcpy(CdromId, "SLUS99999");
442 if (Config.PsxAuto) { // autodetect system (pal or ntsc)
444 /* Make sure Wild Arms SCUS-94608 is not detected as a PAL game. */
445 ((CdromId[0] == 's' || CdromId[0] == 'S') && (CdromId[2] == 'e' || CdromId[2] == 'E')) ||
446 !strncmp(CdromId, "DTLS3035", 8) ||
447 !strncmp(CdromId, "PBPX95001", 9) || // according to redump.org, these PAL
448 !strncmp(CdromId, "PBPX95007", 9) || // discs have a non-standard ID;
449 !strncmp(CdromId, "PBPX95008", 9)) // add more serials if they are discovered.
450 Config.PsxType = PSX_TYPE_PAL; // pal
451 else Config.PsxType = PSX_TYPE_NTSC; // ntsc
454 if (CdromLabel[0] == ' ') {
455 strncpy(CdromLabel, CdromId, 9);
457 SysPrintf(_("CD-ROM Label: %.32s\n"), CdromLabel);
458 SysPrintf(_("CD-ROM ID: %.9s\n"), CdromId);
459 SysPrintf(_("CD-ROM EXE Name: %.255s\n"), exename);
468 static int PSXGetFileType(FILE *f) {
469 unsigned long current;
475 fseek(f, 0L, SEEK_SET);
476 if (fread(&mybuf, 1, sizeof(mybuf), f) != sizeof(mybuf))
479 fseek(f, current, SEEK_SET);
481 exe_hdr = (EXE_HEADER *)mybuf;
482 if (memcmp(exe_hdr->id, "PS-X EXE", 8) == 0)
485 if (mybuf[0] == 'C' && mybuf[1] == 'P' && mybuf[2] == 'E')
488 coff_hdr = (FILHDR *)mybuf;
489 if (SWAPu16(coff_hdr->f_magic) == 0x0162)
496 SysPrintf(_("File IO error in <%s:%s>.\n"), __FILE__, __func__);
501 // temporary pandora workaround..
503 size_t fread_to_ram(void *ptr, size_t size, size_t nmemb, FILE *stream)
508 tmp = malloc(size * nmemb);
510 ret = fread(tmp, size, nmemb, stream);
511 memcpy(ptr, tmp, size * nmemb);
517 int Load(const char *ExePath) {
523 u32 section_address, section_size;
526 strcpy(CdromId, "SLUS99999");
527 strcpy(CdromLabel, "SLUS_999.99");
529 tmpFile = fopen(ExePath, "rb");
530 if (tmpFile == NULL) {
531 SysPrintf(_("Error opening file: %s.\n"), ExePath);
534 type = PSXGetFileType(tmpFile);
537 if (fread(&tmpHead, 1, sizeof(EXE_HEADER), tmpFile) != sizeof(EXE_HEADER))
539 section_address = SWAP32(tmpHead.t_addr);
540 section_size = SWAP32(tmpHead.t_size);
541 mem = PSXM(section_address);
542 if (mem != INVALID_PTR) {
543 fseek(tmpFile, 0x800, SEEK_SET);
544 fread_to_ram(mem, section_size, 1, tmpFile);
545 psxCpu->Clear(section_address, section_size / 4);
547 SetBootRegs(SWAP32(tmpHead.pc0), SWAP32(tmpHead.gp0),
548 SWAP32(tmpHead.s_addr));
552 fseek(tmpFile, 6, SEEK_SET); /* Something tells me we should go to 4 and read the "08 00" here... */
554 if (fread(&opcode, 1, sizeof(opcode), tmpFile) != sizeof(opcode))
557 case 1: /* Section loading */
558 if (fread(§ion_address, 1, sizeof(section_address), tmpFile) != sizeof(section_address))
560 if (fread(§ion_size, 1, sizeof(section_size), tmpFile) != sizeof(section_size))
562 section_address = SWAPu32(section_address);
563 section_size = SWAPu32(section_size);
565 EMU_LOG("Loading %08X bytes from %08X to %08X\n", section_size, ftell(tmpFile), section_address);
567 mem = PSXM(section_address);
568 if (mem != INVALID_PTR) {
569 fread_to_ram(mem, section_size, 1, tmpFile);
570 psxCpu->Clear(section_address, section_size / 4);
573 case 3: /* register loading (PC only?) */
574 fseek(tmpFile, 2, SEEK_CUR); /* unknown field */
575 if (fread(&psxRegs.pc, 1, sizeof(psxRegs.pc), tmpFile) != sizeof(psxRegs.pc))
577 psxRegs.pc = SWAPu32(psxRegs.pc);
579 case 0: /* End of file */
582 SysPrintf(_("Unknown CPE opcode %02x at position %08x.\n"), opcode, ftell(tmpFile) - 1);
586 } while (opcode != 0 && retval == 0);
589 SysPrintf(_("COFF files not supported.\n"));
593 SysPrintf(_("This file does not appear to be a valid PSX EXE file.\n"));
594 SysPrintf(_("(did you forget -cdfile ?)\n"));
602 CdromLabel[0] = '\0';
611 SysPrintf(_("File IO error in <%s:%s>.\n"), __FILE__, __func__);
619 static void *zlib_open(const char *name, const char *mode)
621 return gzopen(name, mode);
624 static int zlib_read(void *file, void *buf, u32 len)
626 return gzread(file, buf, len);
629 static int zlib_write(void *file, const void *buf, u32 len)
631 return gzwrite(file, buf, len);
634 static long zlib_seek(void *file, long offs, int whence)
636 return gzseek(file, offs, whence);
639 static void zlib_close(void *file)
644 struct PcsxSaveFuncs SaveFuncs = {
645 zlib_open, zlib_read, zlib_write, zlib_seek, zlib_close
648 static const char PcsxHeader[32] = "STv4 PCSX v" PCSX_VERSION;
650 // Savestate Versioning!
651 // If you make changes to the savestate version, please increment the value below.
652 static const u32 SaveVersion = 0x8b410006;
654 #define MISC_MAGIC 0x4353494d
655 struct misc_save_data {
667 int SaveState(const char *file) {
668 struct misc_save_data *misc = (void *)(psxH + 0xf000);
670 GPUFreeze_t *gpufP = NULL;
671 SPUFreezeHdr_t spufH;
672 SPUFreeze_t *spufP = NULL;
673 unsigned char *pMem = NULL;
677 assert(!psxRegs.branching);
678 assert(!psxRegs.cpuInRecursion);
679 assert(!misc->magic);
680 misc->magic = MISC_MAGIC;
681 misc->gteBusyCycle = psxRegs.gteBusyCycle;
682 misc->muldivBusyCycle = psxRegs.muldivBusyCycle;
683 misc->biuReg = psxRegs.biuReg;
684 misc->biosBranchCheck = psxRegs.biosBranchCheck;
685 misc->gpuIdleAfter = psxRegs.gpuIdleAfter;
686 misc->gpuSr = HW_GPU_STATUS;
687 misc->frame_counter = frame_counter;
688 misc->CdromFrontendId = CdromFrontendId;
690 f = SaveFuncs.open(file, "wb");
691 if (f == NULL) return -1;
693 psxCpu->Notify(R3000ACPU_NOTIFY_BEFORE_SAVE, NULL);
695 SaveFuncs.write(f, (void *)PcsxHeader, 32);
696 SaveFuncs.write(f, (void *)&SaveVersion, sizeof(u32));
697 SaveFuncs.write(f, (void *)&Config.HLE, sizeof(boolean));
699 pMem = (unsigned char *)malloc(128 * 96 * 3);
700 if (pMem == NULL) goto cleanup;
701 GPU_getScreenPic(pMem);
702 SaveFuncs.write(f, pMem, 128 * 96 * 3);
708 SaveFuncs.write(f, psxM, 0x00200000);
709 SaveFuncs.write(f, psxR, 0x00080000);
710 SaveFuncs.write(f, psxH, 0x00010000);
711 // only partial save of psxRegisters to maintain savestate compat
712 SaveFuncs.write(f, &psxRegs, offsetof(psxRegisters, gteBusyCycle));
715 gpufP = (GPUFreeze_t *)malloc(sizeof(GPUFreeze_t));
716 if (gpufP == NULL) goto cleanup;
717 gpufP->ulFreezeVersion = 1;
718 GPU_freeze(1, gpufP);
719 SaveFuncs.write(f, gpufP, sizeof(GPUFreeze_t));
720 free(gpufP); gpufP = NULL;
723 SPU_freeze(2, (SPUFreeze_t *)&spufH, psxRegs.cycle);
724 Size = spufH.Size; SaveFuncs.write(f, &Size, 4);
725 spufP = (SPUFreeze_t *) malloc(Size);
726 if (spufP == NULL) goto cleanup;
727 SPU_freeze(1, spufP, psxRegs.cycle);
728 SaveFuncs.write(f, spufP, Size);
729 free(spufP); spufP = NULL;
736 new_dyna_freeze(f, 1);
741 memset(misc, 0, sizeof(*misc));
746 int LoadState(const char *file) {
747 struct misc_save_data *misc = (void *)(psxH + 0xf000);
748 u32 biosBranchCheckOld = psxRegs.biosBranchCheck;
750 GPUFreeze_t *gpufP = NULL;
751 SPUFreeze_t *spufP = NULL;
758 f = SaveFuncs.open(file, "rb");
759 if (f == NULL) return -1;
761 SaveFuncs.read(f, header, sizeof(header));
762 SaveFuncs.read(f, &version, sizeof(u32));
763 SaveFuncs.read(f, &hle, sizeof(boolean));
765 if (strncmp("STv4 PCSX", header, 9) != 0 || version != SaveVersion) {
766 SysPrintf("incompatible savestate version %x\n", version);
774 SaveFuncs.seek(f, 128 * 96 * 3, SEEK_CUR);
775 SaveFuncs.read(f, psxM, 0x00200000);
776 SaveFuncs.read(f, psxR, 0x00080000);
777 SaveFuncs.read(f, psxH, 0x00010000);
778 SaveFuncs.read(f, &psxRegs, offsetof(psxRegisters, gteBusyCycle));
779 psxRegs.gteBusyCycle = psxRegs.cycle;
780 psxRegs.biosBranchCheck = ~0;
781 psxRegs.gpuIdleAfter = psxRegs.cycle - 1;
782 HW_GPU_STATUS &= SWAP32(~PSXGPU_nBUSY);
783 if (misc->magic == MISC_MAGIC) {
784 psxRegs.gteBusyCycle = misc->gteBusyCycle;
785 psxRegs.muldivBusyCycle = misc->muldivBusyCycle;
786 psxRegs.biuReg = misc->biuReg;
787 psxRegs.biosBranchCheck = misc->biosBranchCheck;
788 psxRegs.gpuIdleAfter = misc->gpuIdleAfter;
789 HW_GPU_STATUS = misc->gpuSr;
790 frame_counter = misc->frame_counter;
791 CdromFrontendId = misc->CdromFrontendId;
794 psxCpu->Notify(R3000ACPU_NOTIFY_AFTER_LOAD, NULL);
800 gpufP = (GPUFreeze_t *)malloc(sizeof(GPUFreeze_t));
801 if (gpufP == NULL) goto cleanup;
802 SaveFuncs.read(f, gpufP, sizeof(GPUFreeze_t));
803 GPU_freeze(0, gpufP);
808 SaveFuncs.read(f, &Size, 4);
809 spufP = (SPUFreeze_t *)malloc(Size);
810 if (spufP == NULL) goto cleanup;
811 SaveFuncs.read(f, spufP, Size);
812 SPU_freeze(0, spufP, psxRegs.cycle);
820 new_dyna_freeze(f, 0);
825 psxBiosCheckExe(biosBranchCheckOld, 0x60, 1);
829 memset(misc, 0, sizeof(*misc));
834 int CheckState(const char *file) {
840 f = SaveFuncs.open(file, "rb");
841 if (f == NULL) return -1;
843 SaveFuncs.read(f, header, sizeof(header));
844 SaveFuncs.read(f, &version, sizeof(u32));
845 SaveFuncs.read(f, &hle, sizeof(boolean));
849 if (strncmp("STv4 PCSX", header, 9) != 0 || version != SaveVersion)
855 // NET Function Helpers
858 if (NET_recvData == NULL || NET_sendData == NULL)
862 boolean SpuIrq_old = 0;
863 boolean RCntFix_old = 0;
864 NET_sendData(&Config.Xa, sizeof(Config.Xa), PSE_NET_BLOCKING);
865 NET_sendData(&Sio_old, sizeof(Sio_old), PSE_NET_BLOCKING);
866 NET_sendData(&SpuIrq_old, sizeof(SpuIrq_old), PSE_NET_BLOCKING);
867 NET_sendData(&RCntFix_old, sizeof(RCntFix_old), PSE_NET_BLOCKING);
868 NET_sendData(&Config.PsxType, sizeof(Config.PsxType), PSE_NET_BLOCKING);
869 NET_sendData(&Config.Cpu, sizeof(Config.Cpu), PSE_NET_BLOCKING);
877 if (NET_recvData == NULL || NET_sendData == NULL)
881 boolean SpuIrq_old = 0;
882 boolean RCntFix_old = 0;
883 NET_recvData(&Config.Xa, sizeof(Config.Xa), PSE_NET_BLOCKING);
884 NET_recvData(&Sio_old, sizeof(Sio_old), PSE_NET_BLOCKING);
885 NET_recvData(&SpuIrq_old, sizeof(SpuIrq_old), PSE_NET_BLOCKING);
886 NET_recvData(&RCntFix_old, sizeof(RCntFix_old), PSE_NET_BLOCKING);
887 NET_recvData(&Config.PsxType, sizeof(Config.PsxType), PSE_NET_BLOCKING);
890 NET_recvData(&Config.Cpu, sizeof(Config.Cpu), PSE_NET_BLOCKING);
891 if (tmp != Config.Cpu) {
894 if (Config.Cpu == CPU_INTERPRETER) psxCpu = &psxInt;
895 else psxCpu = &psxRec;
899 if (psxCpu->Init() == -1) {
900 SysClose(); return -1;
903 psxCpu->Notify(R3000ACPU_NOTIFY_AFTER_LOAD, NULL);
909 // remove the leading and trailing spaces in a string
910 void trim(char *str) {
914 // skip leading blanks
915 while (str[pos] <= ' ' && str[pos] > 0)
919 *(dest++) = str[pos];
923 *(dest--) = '\0'; // store the null
925 // remove trailing blanks
926 while (dest >= str && *dest <= ' ' && *dest > 0)
930 // lookup table for crc calculation
931 static unsigned short crctab[256] = {
932 0x0000, 0x1021, 0x2042, 0x3063, 0x4084, 0x50A5, 0x60C6, 0x70E7, 0x8108,
933 0x9129, 0xA14A, 0xB16B, 0xC18C, 0xD1AD, 0xE1CE, 0xF1EF, 0x1231, 0x0210,
934 0x3273, 0x2252, 0x52B5, 0x4294, 0x72F7, 0x62D6, 0x9339, 0x8318, 0xB37B,
935 0xA35A, 0xD3BD, 0xC39C, 0xF3FF, 0xE3DE, 0x2462, 0x3443, 0x0420, 0x1401,
936 0x64E6, 0x74C7, 0x44A4, 0x5485, 0xA56A, 0xB54B, 0x8528, 0x9509, 0xE5EE,
937 0xF5CF, 0xC5AC, 0xD58D, 0x3653, 0x2672, 0x1611, 0x0630, 0x76D7, 0x66F6,
938 0x5695, 0x46B4, 0xB75B, 0xA77A, 0x9719, 0x8738, 0xF7DF, 0xE7FE, 0xD79D,
939 0xC7BC, 0x48C4, 0x58E5, 0x6886, 0x78A7, 0x0840, 0x1861, 0x2802, 0x3823,
940 0xC9CC, 0xD9ED, 0xE98E, 0xF9AF, 0x8948, 0x9969, 0xA90A, 0xB92B, 0x5AF5,
941 0x4AD4, 0x7AB7, 0x6A96, 0x1A71, 0x0A50, 0x3A33, 0x2A12, 0xDBFD, 0xCBDC,
942 0xFBBF, 0xEB9E, 0x9B79, 0x8B58, 0xBB3B, 0xAB1A, 0x6CA6, 0x7C87, 0x4CE4,
943 0x5CC5, 0x2C22, 0x3C03, 0x0C60, 0x1C41, 0xEDAE, 0xFD8F, 0xCDEC, 0xDDCD,
944 0xAD2A, 0xBD0B, 0x8D68, 0x9D49, 0x7E97, 0x6EB6, 0x5ED5, 0x4EF4, 0x3E13,
945 0x2E32, 0x1E51, 0x0E70, 0xFF9F, 0xEFBE, 0xDFDD, 0xCFFC, 0xBF1B, 0xAF3A,
946 0x9F59, 0x8F78, 0x9188, 0x81A9, 0xB1CA, 0xA1EB, 0xD10C, 0xC12D, 0xF14E,
947 0xE16F, 0x1080, 0x00A1, 0x30C2, 0x20E3, 0x5004, 0x4025, 0x7046, 0x6067,
948 0x83B9, 0x9398, 0xA3FB, 0xB3DA, 0xC33D, 0xD31C, 0xE37F, 0xF35E, 0x02B1,
949 0x1290, 0x22F3, 0x32D2, 0x4235, 0x5214, 0x6277, 0x7256, 0xB5EA, 0xA5CB,
950 0x95A8, 0x8589, 0xF56E, 0xE54F, 0xD52C, 0xC50D, 0x34E2, 0x24C3, 0x14A0,
951 0x0481, 0x7466, 0x6447, 0x5424, 0x4405, 0xA7DB, 0xB7FA, 0x8799, 0x97B8,
952 0xE75F, 0xF77E, 0xC71D, 0xD73C, 0x26D3, 0x36F2, 0x0691, 0x16B0, 0x6657,
953 0x7676, 0x4615, 0x5634, 0xD94C, 0xC96D, 0xF90E, 0xE92F, 0x99C8, 0x89E9,
954 0xB98A, 0xA9AB, 0x5844, 0x4865, 0x7806, 0x6827, 0x18C0, 0x08E1, 0x3882,
955 0x28A3, 0xCB7D, 0xDB5C, 0xEB3F, 0xFB1E, 0x8BF9, 0x9BD8, 0xABBB, 0xBB9A,
956 0x4A75, 0x5A54, 0x6A37, 0x7A16, 0x0AF1, 0x1AD0, 0x2AB3, 0x3A92, 0xFD2E,
957 0xED0F, 0xDD6C, 0xCD4D, 0xBDAA, 0xAD8B, 0x9DE8, 0x8DC9, 0x7C26, 0x6C07,
958 0x5C64, 0x4C45, 0x3CA2, 0x2C83, 0x1CE0, 0x0CC1, 0xEF1F, 0xFF3E, 0xCF5D,
959 0xDF7C, 0xAF9B, 0xBFBA, 0x8FD9, 0x9FF8, 0x6E17, 0x7E36, 0x4E55, 0x5E74,
960 0x2E93, 0x3EB2, 0x0ED1, 0x1EF0
963 u16 calcCrc(u8 *d, int len) {
967 for (i = 0; i < len; i++) {
968 crc = crctab[(crc >> 8) ^ d[i]] ^ (crc << 8);