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.
30 char CdromId[10] = "";
31 char CdromLabel[33] = "";
33 // PSX Executable types
39 #define ISODCL(from, to) (to - from + 1)
41 struct iso_directory_record {
42 char length [ISODCL (1, 1)]; /* 711 */
43 char ext_attr_length [ISODCL (2, 2)]; /* 711 */
44 char extent [ISODCL (3, 10)]; /* 733 */
45 char size [ISODCL (11, 18)]; /* 733 */
46 char date [ISODCL (19, 25)]; /* 7 by 711 */
47 char flags [ISODCL (26, 26)];
48 char file_unit_size [ISODCL (27, 27)]; /* 711 */
49 char interleave [ISODCL (28, 28)]; /* 711 */
50 char volume_sequence_number [ISODCL (29, 32)]; /* 723 */
51 unsigned char name_len [ISODCL (33, 33)]; /* 711 */
55 void mmssdd( char *b, char *p )
59 int block = (b[3] << 24) | (b[2] << 16) | (b[1] << 8) | b[0];
60 #elif defined(__BIGENDIAN__)
61 int block = (b[0] & 0xff) | ((b[1] & 0xff) << 8) | ((b[2] & 0xff) << 16) | (b[3] << 24);
63 int block = *((int*)b);
67 m = block / 4500; // minutes
68 block = block - m * 4500; // minutes rest
69 s = block / 75; // seconds
70 d = block - s * 75; // seconds rest
72 m = ((m / 10) << 4) | m % 10;
73 s = ((s / 10) << 4) | s % 10;
74 d = ((d / 10) << 4) | d % 10;
82 time[0] = btoi(time[0]); time[1] = btoi(time[1]); time[2] = btoi(time[2]); \
87 if (time[1] == 60) { \
92 time[0] = itob(time[0]); time[1] = itob(time[1]); time[2] = itob(time[2]);
95 if (CDR_readTrack(time) == -1) return -1; \
96 buf = CDR_getBuffer(); \
97 if (buf == NULL) return -1; else CheckPPFCache(buf, time[0], time[1], time[2]);
99 #define READDIR(_dir) \
101 memcpy(_dir, buf + 12, 2048); \
105 memcpy(_dir + 2048, buf + 12, 2048);
107 int GetCdromFile(u8 *mdir, u8 *time, s8 *filename) {
108 struct iso_directory_record *dir;
113 // only try to scan if a filename is given
114 if (!strlen(filename)) return -1;
118 dir = (struct iso_directory_record*) &mdir[i];
119 if (dir->length[0] == 0) {
124 if (dir->flags[0] & 0x2) { // it's a dir
125 if (!strnicmp((char *)&dir->name[0], filename, dir->name_len[0])) {
126 if (filename[dir->name_len[0]] != '\\') continue;
128 filename += dir->name_len[0] + 1;
130 mmssdd(dir->extent, (char *)time);
136 if (!strnicmp((char *)&dir->name[0], filename, strlen(filename))) {
137 mmssdd(dir->extent, (char *)time);
145 static const unsigned int gpu_ctl_def[] = {
146 0x00000000, 0x01000000, 0x03000000, 0x04000000,
147 0x05000800, 0x06c60260, 0x0703fc10, 0x08000027,
150 static const unsigned int gpu_data_def[] = {
151 0xe100360b, 0xe2000000, 0xe3000800, 0xe4077e7f,
152 0xe5001000, 0xe6000000,
153 0x02000000, 0x00000000, 0x01ff03ff,
156 static void fake_bios_gpu_setup(void)
160 for (i = 0; i < sizeof(gpu_ctl_def) / sizeof(gpu_ctl_def[0]); i++)
161 GPU_writeStatus(gpu_ctl_def[i]);
163 for (i = 0; i < sizeof(gpu_data_def) / sizeof(gpu_data_def[0]); i++)
164 GPU_writeData(gpu_data_def[i]);
169 struct iso_directory_record *dir;
174 // not the best place to do it, but since BIOS boot logo killer
175 // is just below, do it here
176 fake_bios_gpu_setup();
180 psxRegs.pc = psxRegs.GPR.n.ra;
184 time[0] = itob(0); time[1] = itob(2); time[2] = itob(0x10);
188 // skip head and sub, and go to the root directory record
189 dir = (struct iso_directory_record*) &buf[12+156];
191 mmssdd(dir->extent, (char*)time);
195 // Load SYSTEM.CNF and scan for the main executable
196 if (GetCdromFile(mdir, time, "SYSTEM.CNF;1") == -1) {
197 // if SYSTEM.CNF is missing, start an existing PSX.EXE
198 if (GetCdromFile(mdir, time, "PSX.EXE;1") == -1) return -1;
203 // read the SYSTEM.CNF
206 sscanf((char *)buf + 12, "BOOT = cdrom:\\%256s", exename);
207 if (GetCdromFile(mdir, time, exename) == -1) {
208 sscanf((char *)buf + 12, "BOOT = cdrom:%256s", exename);
209 if (GetCdromFile(mdir, time, exename) == -1) {
210 char *ptr = strstr(buf + 12, "cdrom:");
213 while (*ptr == '\\' || *ptr == '/') ptr++;
214 strncpy(exename, ptr, 255);
217 while (*ptr != '\0' && *ptr != '\r' && *ptr != '\n') ptr++;
219 if (GetCdromFile(mdir, time, exename) == -1)
226 // Read the EXE-Header
230 memcpy(&tmpHead, buf + 12, sizeof(EXE_HEADER));
232 psxRegs.pc = SWAP32(tmpHead.pc0);
233 psxRegs.GPR.n.gp = SWAP32(tmpHead.gp0);
234 psxRegs.GPR.n.sp = SWAP32(tmpHead.s_addr);
235 if (psxRegs.GPR.n.sp == 0) psxRegs.GPR.n.sp = 0x801fff00;
237 tmpHead.t_size = SWAP32(tmpHead.t_size);
238 tmpHead.t_addr = SWAP32(tmpHead.t_addr);
240 psxCpu->Clear(tmpHead.t_addr, tmpHead.t_size / 4);
242 // Read the rest of the main executable
243 while (tmpHead.t_size & ~2047) {
244 void *ptr = (void *)PSXM(tmpHead.t_addr);
249 if (ptr != NULL) memcpy(ptr, buf+12, 2048);
251 tmpHead.t_size -= 2048;
252 tmpHead.t_addr += 2048;
258 int LoadCdromFile(const char *filename, EXE_HEADER *head) {
259 struct iso_directory_record *dir;
261 u8 mdir[4096], exename[256];
264 sscanf(filename, "cdrom:\\%256s", exename);
266 time[0] = itob(0); time[1] = itob(2); time[2] = itob(0x10);
270 // skip head and sub, and go to the root directory record
271 dir = (struct iso_directory_record *)&buf[12 + 156];
273 mmssdd(dir->extent, (char*)time);
277 if (GetCdromFile(mdir, time, exename) == -1) return -1;
281 memcpy(head, buf + 12, sizeof(EXE_HEADER));
285 psxCpu->Clear(addr, size / 4);
287 while (size & ~2047) {
291 memcpy((void *)PSXM(addr), buf + 12, 2048);
301 struct iso_directory_record *dir;
302 unsigned char time[4], *buf;
303 unsigned char mdir[4096];
311 time[2] = itob(0x10);
315 CdromLabel[0] = '\0';
318 strncpy(CdromLabel, buf + 52, 32);
320 // skip head and sub, and go to the root directory record
321 dir = (struct iso_directory_record *)&buf[12 + 156];
323 mmssdd(dir->extent, (char *)time);
327 if (GetCdromFile(mdir, time, "SYSTEM.CNF;1") != -1) {
330 sscanf((char *)buf + 12, "BOOT = cdrom:\\%256s", exename);
331 if (GetCdromFile(mdir, time, exename) == -1) {
332 sscanf((char *)buf + 12, "BOOT = cdrom:%256s", exename);
333 if (GetCdromFile(mdir, time, exename) == -1) {
334 char *ptr = strstr(buf + 12, "cdrom:"); // possibly the executable is in some subdir
337 while (*ptr == '\\' || *ptr == '/') ptr++;
338 strncpy(exename, ptr, 255);
341 while (*ptr != '\0' && *ptr != '\r' && *ptr != '\n') ptr++;
343 if (GetCdromFile(mdir, time, exename) == -1)
344 return -1; // main executable not found
349 } else if (GetCdromFile(mdir, time, "PSX.EXE;1") != -1) {
350 strcpy(exename, "PSX.EXE;1");
351 strcpy(CdromId, "SLUS99999");
353 return -1; // SYSTEM.CNF and PSX.EXE not found
355 if (CdromId[0] == '\0') {
358 if (exename[i - 2] == ';') i-= 2;
360 while (i >= 0 && c >= 0) {
361 if (isalnum(exename[i])) CdromId[c--] = exename[i];
367 if (Config.PsxAuto) { // autodetect system (pal or ntsc)
368 if (CdromId[2] == 'e' || CdromId[2] == 'E')
369 Config.PsxType = PSX_TYPE_PAL; // pal
370 else Config.PsxType = PSX_TYPE_NTSC; // ntsc
373 if (CdromLabel[0] == ' ') {
374 strncpy(CdromLabel, CdromId, 9);
376 SysPrintf(_("CD-ROM Label: %.32s\n"), CdromLabel);
377 SysPrintf(_("CD-ROM ID: %.9s\n"), CdromId);
384 static int PSXGetFileType(FILE *f) {
385 unsigned long current;
391 fseek(f, 0L, SEEK_SET);
392 fread(mybuf, 2048, 1, f);
393 fseek(f, current, SEEK_SET);
395 exe_hdr = (EXE_HEADER *)mybuf;
396 if (memcmp(exe_hdr->id, "PS-X EXE", 8) == 0)
399 if (mybuf[0] == 'C' && mybuf[1] == 'P' && mybuf[2] == 'E')
402 coff_hdr = (FILHDR *)mybuf;
403 if (SWAPu16(coff_hdr->f_magic) == 0x0162)
409 int Load(const char *ExePath) {
415 u32 section_address, section_size;
418 strncpy(CdromId, "SLUS99999", 9);
419 strncpy(CdromLabel, "SLUS_999.99", 11);
421 tmpFile = fopen(ExePath, "rb");
422 if (tmpFile == NULL) {
423 SysPrintf(_("Error opening file: %s.\n"), ExePath);
426 type = PSXGetFileType(tmpFile);
429 fread(&tmpHead,sizeof(EXE_HEADER),1,tmpFile);
430 section_address = SWAP32(tmpHead.t_addr);
431 section_size = SWAP32(tmpHead.t_size);
432 mem = PSXM(section_address);
434 fseek(tmpFile, 0x800, SEEK_SET);
435 fread(mem, section_size, 1, tmpFile);
436 psxCpu->Clear(section_address, section_size / 4);
439 psxRegs.pc = SWAP32(tmpHead.pc0);
440 psxRegs.GPR.n.gp = SWAP32(tmpHead.gp0);
441 psxRegs.GPR.n.sp = SWAP32(tmpHead.s_addr);
442 if (psxRegs.GPR.n.sp == 0)
443 psxRegs.GPR.n.sp = 0x801fff00;
447 fseek(tmpFile, 6, SEEK_SET); /* Something tells me we should go to 4 and read the "08 00" here... */
449 fread(&opcode, 1, 1, tmpFile);
451 case 1: /* Section loading */
452 fread(§ion_address, 4, 1, tmpFile);
453 fread(§ion_size, 4, 1, tmpFile);
454 section_address = SWAPu32(section_address);
455 section_size = SWAPu32(section_size);
457 EMU_LOG("Loading %08X bytes from %08X to %08X\n", section_size, ftell(tmpFile), section_address);
459 mem = PSXM(section_address);
461 fread(mem, section_size, 1, tmpFile);
462 psxCpu->Clear(section_address, section_size / 4);
465 case 3: /* register loading (PC only?) */
466 fseek(tmpFile, 2, SEEK_CUR); /* unknown field */
467 fread(&psxRegs.pc, 4, 1, tmpFile);
468 psxRegs.pc = SWAPu32(psxRegs.pc);
470 case 0: /* End of file */
473 SysPrintf(_("Unknown CPE opcode %02x at position %08x.\n"), opcode, ftell(tmpFile) - 1);
477 } while (opcode != 0 && retval == 0);
480 SysPrintf(_("COFF files not supported.\n"));
484 SysPrintf(_("This file does not appear to be a valid PSX file.\n"));
492 CdromLabel[0] = '\0';
500 static const char PcsxHeader[32] = "STv4 PCSX v" PACKAGE_VERSION;
502 // Savestate Versioning!
503 // If you make changes to the savestate version, please increment the value below.
504 static const u32 SaveVersion = 0x8b410006;
506 int SaveState(const char *file) {
513 f = gzopen(file, "wb");
514 if (f == NULL) return -1;
518 gzwrite(f, (void *)PcsxHeader, 32);
519 gzwrite(f, (void *)&SaveVersion, sizeof(u32));
520 gzwrite(f, (void *)&Config.HLE, sizeof(boolean));
522 pMem = (unsigned char *)malloc(128 * 96 * 3);
523 if (pMem == NULL) return -1;
524 GPU_getScreenPic(pMem);
525 gzwrite(f, pMem, 128 * 96 * 3);
531 gzwrite(f, psxM, 0x00200000);
532 gzwrite(f, psxR, 0x00080000);
533 gzwrite(f, psxH, 0x00010000);
534 gzwrite(f, (void *)&psxRegs, sizeof(psxRegs));
537 gpufP = (GPUFreeze_t *)malloc(sizeof(GPUFreeze_t));
538 gpufP->ulFreezeVersion = 1;
539 GPU_freeze(1, gpufP);
540 gzwrite(f, gpufP, sizeof(GPUFreeze_t));
544 spufP = (SPUFreeze_t *) malloc(16);
545 SPU_freeze(2, spufP);
546 Size = spufP->Size; gzwrite(f, &Size, 4);
548 spufP = (SPUFreeze_t *) malloc(Size);
549 SPU_freeze(1, spufP);
550 gzwrite(f, spufP, Size);
561 new_dyna_after_save();
566 int LoadState(const char *file) {
575 f = gzopen(file, "rb");
576 if (f == NULL) return -1;
578 gzread(f, header, sizeof(header));
579 gzread(f, &version, sizeof(u32));
580 gzread(f, &hle, sizeof(boolean));
582 if (strncmp("STv4 PCSX", header, 9) != 0 || version != SaveVersion) {
592 gzseek(f, 128 * 96 * 3, SEEK_CUR);
594 gzread(f, psxM, 0x00200000);
595 gzread(f, psxR, 0x00080000);
596 gzread(f, psxH, 0x00010000);
597 gzread(f, (void *)&psxRegs, sizeof(psxRegs));
603 gpufP = (GPUFreeze_t *)malloc(sizeof(GPUFreeze_t));
604 gzread(f, gpufP, sizeof(GPUFreeze_t));
605 GPU_freeze(0, gpufP);
607 if (HW_GPU_STATUS == 0)
608 HW_GPU_STATUS = GPU_readStatus();
612 spufP = (SPUFreeze_t *)malloc(Size);
613 gzread(f, spufP, Size);
614 SPU_freeze(0, spufP);
629 int CheckState(const char *file) {
635 f = gzopen(file, "rb");
636 if (f == NULL) return -1;
638 gzread(f, header, sizeof(header));
639 gzread(f, &version, sizeof(u32));
640 gzread(f, &hle, sizeof(boolean));
644 if (strncmp("STv4 PCSX", header, 9) != 0 || version != SaveVersion)
650 // NET Function Helpers
653 if (NET_recvData == NULL || NET_sendData == NULL)
656 NET_sendData(&Config.Xa, sizeof(Config.Xa), PSE_NET_BLOCKING);
657 NET_sendData(&Config.Sio, sizeof(Config.Sio), PSE_NET_BLOCKING);
658 NET_sendData(&Config.SpuIrq, sizeof(Config.SpuIrq), PSE_NET_BLOCKING);
659 NET_sendData(&Config.RCntFix, sizeof(Config.RCntFix), PSE_NET_BLOCKING);
660 NET_sendData(&Config.PsxType, sizeof(Config.PsxType), PSE_NET_BLOCKING);
661 NET_sendData(&Config.Cpu, sizeof(Config.Cpu), PSE_NET_BLOCKING);
669 if (NET_recvData == NULL || NET_sendData == NULL)
672 NET_recvData(&Config.Xa, sizeof(Config.Xa), PSE_NET_BLOCKING);
673 NET_recvData(&Config.Sio, sizeof(Config.Sio), PSE_NET_BLOCKING);
674 NET_recvData(&Config.SpuIrq, sizeof(Config.SpuIrq), PSE_NET_BLOCKING);
675 NET_recvData(&Config.RCntFix, sizeof(Config.RCntFix), PSE_NET_BLOCKING);
676 NET_recvData(&Config.PsxType, sizeof(Config.PsxType), PSE_NET_BLOCKING);
681 NET_recvData(&Config.Cpu, sizeof(Config.Cpu), PSE_NET_BLOCKING);
682 if (tmp != Config.Cpu) {
685 if (Config.Cpu == CPU_INTERPRETER) psxCpu = &psxInt;
686 else psxCpu = &psxRec;
690 if (psxCpu->Init() == -1) {
691 SysClose(); return -1;
699 // remove the leading and trailing spaces in a string
700 void trim(char *str) {
704 // skip leading blanks
705 while (str[pos] <= ' ' && str[pos] > 0)
709 *(dest++) = str[pos];
713 *(dest--) = '\0'; // store the null
715 // remove trailing blanks
716 while (dest >= str && *dest <= ' ' && *dest > 0)
720 // lookup table for crc calculation
721 static unsigned short crctab[256] = {
722 0x0000, 0x1021, 0x2042, 0x3063, 0x4084, 0x50A5, 0x60C6, 0x70E7, 0x8108,
723 0x9129, 0xA14A, 0xB16B, 0xC18C, 0xD1AD, 0xE1CE, 0xF1EF, 0x1231, 0x0210,
724 0x3273, 0x2252, 0x52B5, 0x4294, 0x72F7, 0x62D6, 0x9339, 0x8318, 0xB37B,
725 0xA35A, 0xD3BD, 0xC39C, 0xF3FF, 0xE3DE, 0x2462, 0x3443, 0x0420, 0x1401,
726 0x64E6, 0x74C7, 0x44A4, 0x5485, 0xA56A, 0xB54B, 0x8528, 0x9509, 0xE5EE,
727 0xF5CF, 0xC5AC, 0xD58D, 0x3653, 0x2672, 0x1611, 0x0630, 0x76D7, 0x66F6,
728 0x5695, 0x46B4, 0xB75B, 0xA77A, 0x9719, 0x8738, 0xF7DF, 0xE7FE, 0xD79D,
729 0xC7BC, 0x48C4, 0x58E5, 0x6886, 0x78A7, 0x0840, 0x1861, 0x2802, 0x3823,
730 0xC9CC, 0xD9ED, 0xE98E, 0xF9AF, 0x8948, 0x9969, 0xA90A, 0xB92B, 0x5AF5,
731 0x4AD4, 0x7AB7, 0x6A96, 0x1A71, 0x0A50, 0x3A33, 0x2A12, 0xDBFD, 0xCBDC,
732 0xFBBF, 0xEB9E, 0x9B79, 0x8B58, 0xBB3B, 0xAB1A, 0x6CA6, 0x7C87, 0x4CE4,
733 0x5CC5, 0x2C22, 0x3C03, 0x0C60, 0x1C41, 0xEDAE, 0xFD8F, 0xCDEC, 0xDDCD,
734 0xAD2A, 0xBD0B, 0x8D68, 0x9D49, 0x7E97, 0x6EB6, 0x5ED5, 0x4EF4, 0x3E13,
735 0x2E32, 0x1E51, 0x0E70, 0xFF9F, 0xEFBE, 0xDFDD, 0xCFFC, 0xBF1B, 0xAF3A,
736 0x9F59, 0x8F78, 0x9188, 0x81A9, 0xB1CA, 0xA1EB, 0xD10C, 0xC12D, 0xF14E,
737 0xE16F, 0x1080, 0x00A1, 0x30C2, 0x20E3, 0x5004, 0x4025, 0x7046, 0x6067,
738 0x83B9, 0x9398, 0xA3FB, 0xB3DA, 0xC33D, 0xD31C, 0xE37F, 0xF35E, 0x02B1,
739 0x1290, 0x22F3, 0x32D2, 0x4235, 0x5214, 0x6277, 0x7256, 0xB5EA, 0xA5CB,
740 0x95A8, 0x8589, 0xF56E, 0xE54F, 0xD52C, 0xC50D, 0x34E2, 0x24C3, 0x14A0,
741 0x0481, 0x7466, 0x6447, 0x5424, 0x4405, 0xA7DB, 0xB7FA, 0x8799, 0x97B8,
742 0xE75F, 0xF77E, 0xC71D, 0xD73C, 0x26D3, 0x36F2, 0x0691, 0x16B0, 0x6657,
743 0x7676, 0x4615, 0x5634, 0xD94C, 0xC96D, 0xF90E, 0xE92F, 0x99C8, 0x89E9,
744 0xB98A, 0xA9AB, 0x5844, 0x4865, 0x7806, 0x6827, 0x18C0, 0x08E1, 0x3882,
745 0x28A3, 0xCB7D, 0xDB5C, 0xEB3F, 0xFB1E, 0x8BF9, 0x9BD8, 0xABBB, 0xBB9A,
746 0x4A75, 0x5A54, 0x6A37, 0x7A16, 0x0AF1, 0x1AD0, 0x2AB3, 0x3A92, 0xFD2E,
747 0xED0F, 0xDD6C, 0xCD4D, 0xBDAA, 0xAD8B, 0x9DE8, 0x8DC9, 0x7C26, 0x6C07,
748 0x5C64, 0x4C45, 0x3CA2, 0x2C83, 0x1CE0, 0x0CC1, 0xEF1F, 0xFF3E, 0xCF5D,
749 0xDF7C, 0xAF9B, 0xBFBA, 0x8FD9, 0x9FF8, 0x6E17, 0x7E36, 0x4E55, 0x5E74,
750 0x2E93, 0x3EB2, 0x0ED1, 0x1EF0
753 u16 calcCrc(u8 *d, int len) {
757 for (i = 0; i < len; i++) {
758 crc = crctab[(crc >> 8) ^ d[i]] ^ (crc << 8);