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.
31 char CdromId[10] = "";
32 char CdromLabel[33] = "";
34 // PSX Executable types
40 #define ISODCL(from, to) (to - from + 1)
42 struct iso_directory_record {
43 char length [ISODCL (1, 1)]; /* 711 */
44 char ext_attr_length [ISODCL (2, 2)]; /* 711 */
45 char extent [ISODCL (3, 10)]; /* 733 */
46 char size [ISODCL (11, 18)]; /* 733 */
47 char date [ISODCL (19, 25)]; /* 7 by 711 */
48 char flags [ISODCL (26, 26)];
49 char file_unit_size [ISODCL (27, 27)]; /* 711 */
50 char interleave [ISODCL (28, 28)]; /* 711 */
51 char volume_sequence_number [ISODCL (29, 32)]; /* 723 */
52 unsigned char name_len [ISODCL (33, 33)]; /* 711 */
56 void mmssdd( char *b, char *p )
60 int block = (b[3] << 24) | (b[2] << 16) | (b[1] << 8) | b[0];
61 #elif defined(__BIGENDIAN__)
62 int block = (b[0] & 0xff) | ((b[1] & 0xff) << 8) | ((b[2] & 0xff) << 16) | (b[3] << 24);
64 int block = *((int*)b);
68 m = block / 4500; // minutes
69 block = block - m * 4500; // minutes rest
70 s = block / 75; // seconds
71 d = block - s * 75; // seconds rest
73 m = ((m / 10) << 4) | m % 10;
74 s = ((s / 10) << 4) | s % 10;
75 d = ((d / 10) << 4) | d % 10;
83 time[0] = btoi(time[0]); time[1] = btoi(time[1]); time[2] = btoi(time[2]); \
88 if (time[1] == 60) { \
93 time[0] = itob(time[0]); time[1] = itob(time[1]); time[2] = itob(time[2]);
96 if (CDR_readTrack(time) == -1) return -1; \
97 buf = (void *)CDR_getBuffer(); \
98 if (buf == NULL) return -1; \
99 else CheckPPFCache((u8 *)buf, time[0], time[1], time[2]);
101 #define READDIR(_dir) \
103 memcpy(_dir, buf + 12, 2048); \
107 memcpy(_dir + 2048, buf + 12, 2048);
109 int GetCdromFile(u8 *mdir, u8 *time, char *filename) {
110 struct iso_directory_record *dir;
115 // only try to scan if a filename is given
116 if (!strlen(filename)) return -1;
120 dir = (struct iso_directory_record*) &mdir[i];
121 if (dir->length[0] == 0) {
126 if (dir->flags[0] & 0x2) { // it's a dir
127 if (!strnicmp((char *)&dir->name[0], filename, dir->name_len[0])) {
128 if (filename[dir->name_len[0]] != '\\') continue;
130 filename += dir->name_len[0] + 1;
132 mmssdd(dir->extent, (char *)time);
138 if (!strnicmp((char *)&dir->name[0], filename, strlen(filename))) {
139 mmssdd(dir->extent, (char *)time);
147 static const unsigned int gpu_ctl_def[] = {
148 0x00000000, 0x01000000, 0x03000000, 0x04000000,
149 0x05000800, 0x06c60260, 0x0703fc10, 0x08000027,
152 static const unsigned int gpu_data_def[] = {
153 0xe100360b, 0xe2000000, 0xe3000800, 0xe4077e7f,
154 0xe5001000, 0xe6000000,
155 0x02000000, 0x00000000, 0x01ff03ff,
158 static void fake_bios_gpu_setup(void)
162 for (i = 0; i < sizeof(gpu_ctl_def) / sizeof(gpu_ctl_def[0]); i++)
163 GPU_writeStatus(gpu_ctl_def[i]);
165 for (i = 0; i < sizeof(gpu_data_def) / sizeof(gpu_data_def[0]); i++)
166 GPU_writeData(gpu_data_def[i]);
171 struct iso_directory_record *dir;
176 // not the best place to do it, but since BIOS boot logo killer
177 // is just below, do it here
178 fake_bios_gpu_setup();
182 psxRegs.pc = psxRegs.GPR.n.ra;
186 time[0] = itob(0); time[1] = itob(2); time[2] = itob(0x10);
190 // skip head and sub, and go to the root directory record
191 dir = (struct iso_directory_record*) &buf[12+156];
193 mmssdd(dir->extent, (char*)time);
197 // Load SYSTEM.CNF and scan for the main executable
198 if (GetCdromFile(mdir, time, "SYSTEM.CNF;1") == -1) {
199 // if SYSTEM.CNF is missing, start an existing PSX.EXE
200 if (GetCdromFile(mdir, time, "PSX.EXE;1") == -1) return -1;
205 // read the SYSTEM.CNF
208 sscanf((char *)buf + 12, "BOOT = cdrom:\\%256s", exename);
209 if (GetCdromFile(mdir, time, exename) == -1) {
210 sscanf((char *)buf + 12, "BOOT = cdrom:%256s", exename);
211 if (GetCdromFile(mdir, time, exename) == -1) {
212 char *ptr = strstr((char *)buf + 12, "cdrom:");
215 while (*ptr == '\\' || *ptr == '/') ptr++;
216 strncpy(exename, ptr, 255);
219 while (*ptr != '\0' && *ptr != '\r' && *ptr != '\n') ptr++;
221 if (GetCdromFile(mdir, time, exename) == -1)
228 // Read the EXE-Header
232 memcpy(&tmpHead, buf + 12, sizeof(EXE_HEADER));
234 psxRegs.pc = SWAP32(tmpHead.pc0);
235 psxRegs.GPR.n.gp = SWAP32(tmpHead.gp0);
236 psxRegs.GPR.n.sp = SWAP32(tmpHead.s_addr);
237 if (psxRegs.GPR.n.sp == 0) psxRegs.GPR.n.sp = 0x801fff00;
239 tmpHead.t_size = SWAP32(tmpHead.t_size);
240 tmpHead.t_addr = SWAP32(tmpHead.t_addr);
242 psxCpu->Clear(tmpHead.t_addr, tmpHead.t_size / 4);
244 // Read the rest of the main executable
245 while (tmpHead.t_size & ~2047) {
246 void *ptr = (void *)PSXM(tmpHead.t_addr);
251 if (ptr != NULL) memcpy(ptr, buf+12, 2048);
253 tmpHead.t_size -= 2048;
254 tmpHead.t_addr += 2048;
260 int LoadCdromFile(const char *filename, EXE_HEADER *head) {
261 struct iso_directory_record *dir;
268 sscanf(filename, "cdrom:\\%256s", exename);
270 time[0] = itob(0); time[1] = itob(2); time[2] = itob(0x10);
274 // skip head and sub, and go to the root directory record
275 dir = (struct iso_directory_record *)&buf[12 + 156];
277 mmssdd(dir->extent, (char*)time);
281 if (GetCdromFile(mdir, time, exename) == -1) return -1;
285 memcpy(head, buf + 12, sizeof(EXE_HEADER));
289 psxCpu->Clear(addr, size / 4);
291 while (size & ~2047) {
297 memcpy(mem, buf + 12, 2048);
307 struct iso_directory_record *dir;
308 unsigned char time[4];
310 unsigned char mdir[4096];
318 time[2] = itob(0x10);
322 CdromLabel[0] = '\0';
325 strncpy(CdromLabel, buf + 52, 32);
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, "SYSTEM.CNF;1") != -1) {
337 sscanf(buf + 12, "BOOT = cdrom:\\%256s", exename);
338 if (GetCdromFile(mdir, time, exename) == -1) {
339 sscanf(buf + 12, "BOOT = cdrom:%256s", exename);
340 if (GetCdromFile(mdir, time, exename) == -1) {
341 char *ptr = strstr(buf + 12, "cdrom:"); // possibly the executable is in some subdir
344 while (*ptr == '\\' || *ptr == '/') ptr++;
345 strncpy(exename, ptr, 255);
348 while (*ptr != '\0' && *ptr != '\r' && *ptr != '\n') ptr++;
350 if (GetCdromFile(mdir, time, exename) == -1)
351 return -1; // main executable not found
356 } else if (GetCdromFile(mdir, time, "PSX.EXE;1") != -1) {
357 strcpy(exename, "PSX.EXE;1");
358 strcpy(CdromId, "SLUS99999");
360 return -1; // SYSTEM.CNF and PSX.EXE not found
362 if (CdromId[0] == '\0') {
365 if (exename[i - 2] == ';') i-= 2;
367 while (i >= 0 && c >= 0) {
368 if (isalnum(exename[i])) CdromId[c--] = exename[i];
374 if (CdromId[0] == '\0')
375 strcpy(CdromId, "SLUS99999");
377 if (Config.PsxAuto) { // autodetect system (pal or ntsc)
378 if (CdromId[2] == 'e' || CdromId[2] == 'E')
379 Config.PsxType = PSX_TYPE_PAL; // pal
380 else Config.PsxType = PSX_TYPE_NTSC; // ntsc
383 if (CdromLabel[0] == ' ') {
384 strncpy(CdromLabel, CdromId, 9);
386 SysPrintf(_("CD-ROM Label: %.32s\n"), CdromLabel);
387 SysPrintf(_("CD-ROM ID: %.9s\n"), CdromId);
394 static int PSXGetFileType(FILE *f) {
395 unsigned long current;
401 fseek(f, 0L, SEEK_SET);
402 fread(mybuf, 2048, 1, f);
403 fseek(f, current, SEEK_SET);
405 exe_hdr = (EXE_HEADER *)mybuf;
406 if (memcmp(exe_hdr->id, "PS-X EXE", 8) == 0)
409 if (mybuf[0] == 'C' && mybuf[1] == 'P' && mybuf[2] == 'E')
412 coff_hdr = (FILHDR *)mybuf;
413 if (SWAPu16(coff_hdr->f_magic) == 0x0162)
419 // temporary pandora workaround..
421 size_t fread_to_ram(void *ptr, size_t size, size_t nmemb, FILE *stream)
426 tmp = malloc(size * nmemb);
428 ret = fread(tmp, size, nmemb, stream);
429 memcpy(ptr, tmp, size * nmemb);
435 int Load(const char *ExePath) {
441 u32 section_address, section_size;
444 strncpy(CdromId, "SLUS99999", 9);
445 strncpy(CdromLabel, "SLUS_999.99", 11);
447 tmpFile = fopen(ExePath, "rb");
448 if (tmpFile == NULL) {
449 SysPrintf(_("Error opening file: %s.\n"), ExePath);
452 type = PSXGetFileType(tmpFile);
455 fread(&tmpHead,sizeof(EXE_HEADER),1,tmpFile);
456 section_address = SWAP32(tmpHead.t_addr);
457 section_size = SWAP32(tmpHead.t_size);
458 mem = PSXM(section_address);
460 fseek(tmpFile, 0x800, SEEK_SET);
461 fread_to_ram(mem, section_size, 1, tmpFile);
462 psxCpu->Clear(section_address, section_size / 4);
465 psxRegs.pc = SWAP32(tmpHead.pc0);
466 psxRegs.GPR.n.gp = SWAP32(tmpHead.gp0);
467 psxRegs.GPR.n.sp = SWAP32(tmpHead.s_addr);
468 if (psxRegs.GPR.n.sp == 0)
469 psxRegs.GPR.n.sp = 0x801fff00;
473 fseek(tmpFile, 6, SEEK_SET); /* Something tells me we should go to 4 and read the "08 00" here... */
475 fread(&opcode, 1, 1, tmpFile);
477 case 1: /* Section loading */
478 fread(§ion_address, 4, 1, tmpFile);
479 fread(§ion_size, 4, 1, tmpFile);
480 section_address = SWAPu32(section_address);
481 section_size = SWAPu32(section_size);
483 EMU_LOG("Loading %08X bytes from %08X to %08X\n", section_size, ftell(tmpFile), section_address);
485 mem = PSXM(section_address);
487 fread_to_ram(mem, section_size, 1, tmpFile);
488 psxCpu->Clear(section_address, section_size / 4);
491 case 3: /* register loading (PC only?) */
492 fseek(tmpFile, 2, SEEK_CUR); /* unknown field */
493 fread(&psxRegs.pc, 4, 1, tmpFile);
494 psxRegs.pc = SWAPu32(psxRegs.pc);
496 case 0: /* End of file */
499 SysPrintf(_("Unknown CPE opcode %02x at position %08x.\n"), opcode, ftell(tmpFile) - 1);
503 } while (opcode != 0 && retval == 0);
506 SysPrintf(_("COFF files not supported.\n"));
510 SysPrintf(_("This file does not appear to be a valid PSX EXE file.\n"));
511 SysPrintf(_("(did you forget -cdfile ?)\n"));
519 CdromLabel[0] = '\0';
527 static void *zlib_open(const char *name, const char *mode)
529 return gzopen(name, mode);
532 static int zlib_read(void *file, void *buf, u32 len)
534 return gzread(file, buf, len);
537 static int zlib_write(void *file, const void *buf, u32 len)
539 return gzwrite(file, buf, len);
542 static long zlib_seek(void *file, long offs, int whence)
544 return gzseek(file, offs, whence);
547 static void zlib_close(void *file)
552 struct PcsxSaveFuncs SaveFuncs = {
553 zlib_open, zlib_read, zlib_write, zlib_seek, zlib_close
556 static const char PcsxHeader[32] = "STv4 PCSX v" PACKAGE_VERSION;
558 // Savestate Versioning!
559 // If you make changes to the savestate version, please increment the value below.
560 static const u32 SaveVersion = 0x8b410006;
562 int SaveState(const char *file) {
569 f = SaveFuncs.open(file, "wb");
570 if (f == NULL) return -1;
574 SaveFuncs.write(f, (void *)PcsxHeader, 32);
575 SaveFuncs.write(f, (void *)&SaveVersion, sizeof(u32));
576 SaveFuncs.write(f, (void *)&Config.HLE, sizeof(boolean));
578 pMem = (unsigned char *)malloc(128 * 96 * 3);
579 if (pMem == NULL) return -1;
580 GPU_getScreenPic(pMem);
581 SaveFuncs.write(f, pMem, 128 * 96 * 3);
587 SaveFuncs.write(f, psxM, 0x00200000);
588 SaveFuncs.write(f, psxR, 0x00080000);
589 SaveFuncs.write(f, psxH, 0x00010000);
590 SaveFuncs.write(f, (void *)&psxRegs, sizeof(psxRegs));
593 gpufP = (GPUFreeze_t *)malloc(sizeof(GPUFreeze_t));
594 gpufP->ulFreezeVersion = 1;
595 GPU_freeze(1, gpufP);
596 SaveFuncs.write(f, gpufP, sizeof(GPUFreeze_t));
600 spufP = (SPUFreeze_t *) malloc(16);
601 SPU_freeze(2, spufP);
602 Size = spufP->Size; SaveFuncs.write(f, &Size, 4);
604 spufP = (SPUFreeze_t *) malloc(Size);
605 SPU_freeze(1, spufP);
606 SaveFuncs.write(f, spufP, Size);
617 new_dyna_after_save();
622 int LoadState(const char *file) {
631 f = SaveFuncs.open(file, "rb");
632 if (f == NULL) return -1;
634 SaveFuncs.read(f, header, sizeof(header));
635 SaveFuncs.read(f, &version, sizeof(u32));
636 SaveFuncs.read(f, &hle, sizeof(boolean));
638 if (strncmp("STv4 PCSX", header, 9) != 0 || version != SaveVersion) {
648 SaveFuncs.seek(f, 128 * 96 * 3, SEEK_CUR);
650 SaveFuncs.read(f, psxM, 0x00200000);
651 SaveFuncs.read(f, psxR, 0x00080000);
652 SaveFuncs.read(f, psxH, 0x00010000);
653 SaveFuncs.read(f, (void *)&psxRegs, sizeof(psxRegs));
659 gpufP = (GPUFreeze_t *)malloc(sizeof(GPUFreeze_t));
660 SaveFuncs.read(f, gpufP, sizeof(GPUFreeze_t));
661 GPU_freeze(0, gpufP);
663 if (HW_GPU_STATUS == 0)
664 HW_GPU_STATUS = GPU_readStatus();
667 SaveFuncs.read(f, &Size, 4);
668 spufP = (SPUFreeze_t *)malloc(Size);
669 SaveFuncs.read(f, spufP, Size);
670 SPU_freeze(0, spufP);
685 int CheckState(const char *file) {
691 f = SaveFuncs.open(file, "rb");
692 if (f == NULL) return -1;
694 SaveFuncs.read(f, header, sizeof(header));
695 SaveFuncs.read(f, &version, sizeof(u32));
696 SaveFuncs.read(f, &hle, sizeof(boolean));
700 if (strncmp("STv4 PCSX", header, 9) != 0 || version != SaveVersion)
706 // NET Function Helpers
709 if (NET_recvData == NULL || NET_sendData == NULL)
712 NET_sendData(&Config.Xa, sizeof(Config.Xa), PSE_NET_BLOCKING);
713 NET_sendData(&Config.Sio, sizeof(Config.Sio), PSE_NET_BLOCKING);
714 NET_sendData(&Config.SpuIrq, sizeof(Config.SpuIrq), PSE_NET_BLOCKING);
715 NET_sendData(&Config.RCntFix, sizeof(Config.RCntFix), PSE_NET_BLOCKING);
716 NET_sendData(&Config.PsxType, sizeof(Config.PsxType), PSE_NET_BLOCKING);
717 NET_sendData(&Config.Cpu, sizeof(Config.Cpu), PSE_NET_BLOCKING);
725 if (NET_recvData == NULL || NET_sendData == NULL)
728 NET_recvData(&Config.Xa, sizeof(Config.Xa), PSE_NET_BLOCKING);
729 NET_recvData(&Config.Sio, sizeof(Config.Sio), PSE_NET_BLOCKING);
730 NET_recvData(&Config.SpuIrq, sizeof(Config.SpuIrq), PSE_NET_BLOCKING);
731 NET_recvData(&Config.RCntFix, sizeof(Config.RCntFix), PSE_NET_BLOCKING);
732 NET_recvData(&Config.PsxType, sizeof(Config.PsxType), PSE_NET_BLOCKING);
737 NET_recvData(&Config.Cpu, sizeof(Config.Cpu), PSE_NET_BLOCKING);
738 if (tmp != Config.Cpu) {
741 if (Config.Cpu == CPU_INTERPRETER) psxCpu = &psxInt;
742 else psxCpu = &psxRec;
746 if (psxCpu->Init() == -1) {
747 SysClose(); return -1;
755 // remove the leading and trailing spaces in a string
756 void trim(char *str) {
760 // skip leading blanks
761 while (str[pos] <= ' ' && str[pos] > 0)
765 *(dest++) = str[pos];
769 *(dest--) = '\0'; // store the null
771 // remove trailing blanks
772 while (dest >= str && *dest <= ' ' && *dest > 0)
776 // lookup table for crc calculation
777 static unsigned short crctab[256] = {
778 0x0000, 0x1021, 0x2042, 0x3063, 0x4084, 0x50A5, 0x60C6, 0x70E7, 0x8108,
779 0x9129, 0xA14A, 0xB16B, 0xC18C, 0xD1AD, 0xE1CE, 0xF1EF, 0x1231, 0x0210,
780 0x3273, 0x2252, 0x52B5, 0x4294, 0x72F7, 0x62D6, 0x9339, 0x8318, 0xB37B,
781 0xA35A, 0xD3BD, 0xC39C, 0xF3FF, 0xE3DE, 0x2462, 0x3443, 0x0420, 0x1401,
782 0x64E6, 0x74C7, 0x44A4, 0x5485, 0xA56A, 0xB54B, 0x8528, 0x9509, 0xE5EE,
783 0xF5CF, 0xC5AC, 0xD58D, 0x3653, 0x2672, 0x1611, 0x0630, 0x76D7, 0x66F6,
784 0x5695, 0x46B4, 0xB75B, 0xA77A, 0x9719, 0x8738, 0xF7DF, 0xE7FE, 0xD79D,
785 0xC7BC, 0x48C4, 0x58E5, 0x6886, 0x78A7, 0x0840, 0x1861, 0x2802, 0x3823,
786 0xC9CC, 0xD9ED, 0xE98E, 0xF9AF, 0x8948, 0x9969, 0xA90A, 0xB92B, 0x5AF5,
787 0x4AD4, 0x7AB7, 0x6A96, 0x1A71, 0x0A50, 0x3A33, 0x2A12, 0xDBFD, 0xCBDC,
788 0xFBBF, 0xEB9E, 0x9B79, 0x8B58, 0xBB3B, 0xAB1A, 0x6CA6, 0x7C87, 0x4CE4,
789 0x5CC5, 0x2C22, 0x3C03, 0x0C60, 0x1C41, 0xEDAE, 0xFD8F, 0xCDEC, 0xDDCD,
790 0xAD2A, 0xBD0B, 0x8D68, 0x9D49, 0x7E97, 0x6EB6, 0x5ED5, 0x4EF4, 0x3E13,
791 0x2E32, 0x1E51, 0x0E70, 0xFF9F, 0xEFBE, 0xDFDD, 0xCFFC, 0xBF1B, 0xAF3A,
792 0x9F59, 0x8F78, 0x9188, 0x81A9, 0xB1CA, 0xA1EB, 0xD10C, 0xC12D, 0xF14E,
793 0xE16F, 0x1080, 0x00A1, 0x30C2, 0x20E3, 0x5004, 0x4025, 0x7046, 0x6067,
794 0x83B9, 0x9398, 0xA3FB, 0xB3DA, 0xC33D, 0xD31C, 0xE37F, 0xF35E, 0x02B1,
795 0x1290, 0x22F3, 0x32D2, 0x4235, 0x5214, 0x6277, 0x7256, 0xB5EA, 0xA5CB,
796 0x95A8, 0x8589, 0xF56E, 0xE54F, 0xD52C, 0xC50D, 0x34E2, 0x24C3, 0x14A0,
797 0x0481, 0x7466, 0x6447, 0x5424, 0x4405, 0xA7DB, 0xB7FA, 0x8799, 0x97B8,
798 0xE75F, 0xF77E, 0xC71D, 0xD73C, 0x26D3, 0x36F2, 0x0691, 0x16B0, 0x6657,
799 0x7676, 0x4615, 0x5634, 0xD94C, 0xC96D, 0xF90E, 0xE92F, 0x99C8, 0x89E9,
800 0xB98A, 0xA9AB, 0x5844, 0x4865, 0x7806, 0x6827, 0x18C0, 0x08E1, 0x3882,
801 0x28A3, 0xCB7D, 0xDB5C, 0xEB3F, 0xFB1E, 0x8BF9, 0x9BD8, 0xABBB, 0xBB9A,
802 0x4A75, 0x5A54, 0x6A37, 0x7A16, 0x0AF1, 0x1AD0, 0x2AB3, 0x3A92, 0xFD2E,
803 0xED0F, 0xDD6C, 0xCD4D, 0xBDAA, 0xAD8B, 0x9DE8, 0x8DC9, 0x7C26, 0x6C07,
804 0x5C64, 0x4C45, 0x3CA2, 0x2C83, 0x1CE0, 0x0CC1, 0xEF1F, 0xFF3E, 0xCF5D,
805 0xDF7C, 0xAF9B, 0xBFBA, 0x8FD9, 0x9FF8, 0x6E17, 0x7E36, 0x4E55, 0x5E74,
806 0x2E93, 0x3EB2, 0x0ED1, 0x1EF0
809 u16 calcCrc(u8 *d, int len) {
813 for (i = 0; i < len; i++) {
814 crc = crctab[(crc >> 8) ^ d[i]] ^ (crc << 8);