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.
29 char CdromId[10] = "";
30 char CdromLabel[33] = "";
32 // PSX Executable types
38 #define ISODCL(from, to) (to - from + 1)
40 struct iso_directory_record {
41 char length [ISODCL (1, 1)]; /* 711 */
42 char ext_attr_length [ISODCL (2, 2)]; /* 711 */
43 char extent [ISODCL (3, 10)]; /* 733 */
44 char size [ISODCL (11, 18)]; /* 733 */
45 char date [ISODCL (19, 25)]; /* 7 by 711 */
46 char flags [ISODCL (26, 26)];
47 char file_unit_size [ISODCL (27, 27)]; /* 711 */
48 char interleave [ISODCL (28, 28)]; /* 711 */
49 char volume_sequence_number [ISODCL (29, 32)]; /* 723 */
50 unsigned char name_len [ISODCL (33, 33)]; /* 711 */
54 void mmssdd( char *b, char *p )
58 int block = (b[3] << 24) | (b[2] << 16) | (b[1] << 8) | b[0];
59 #elif defined(__BIGENDIAN__)
60 int block = (b[0] & 0xff) | ((b[1] & 0xff) << 8) | ((b[2] & 0xff) << 16) | (b[3] << 24);
62 int block = *((int*)b);
66 m = block / 4500; // minutes
67 block = block - m * 4500; // minutes rest
68 s = block / 75; // seconds
69 d = block - s * 75; // seconds rest
71 m = ((m / 10) << 4) | m % 10;
72 s = ((s / 10) << 4) | s % 10;
73 d = ((d / 10) << 4) | d % 10;
81 time[0] = btoi(time[0]); time[1] = btoi(time[1]); time[2] = btoi(time[2]); \
86 if (time[1] == 60) { \
91 time[0] = itob(time[0]); time[1] = itob(time[1]); time[2] = itob(time[2]);
94 if (CDR_readTrack(time) == -1) return -1; \
95 buf = CDR_getBuffer(); \
96 if (buf == NULL) return -1; else CheckPPFCache(buf, time[0], time[1], time[2]);
98 #define READDIR(_dir) \
100 memcpy(_dir, buf + 12, 2048); \
104 memcpy(_dir + 2048, buf + 12, 2048);
106 int GetCdromFile(u8 *mdir, u8 *time, s8 *filename) {
107 struct iso_directory_record *dir;
112 // only try to scan if a filename is given
113 if (!strlen(filename)) return -1;
117 dir = (struct iso_directory_record*) &mdir[i];
118 if (dir->length[0] == 0) {
123 if (dir->flags[0] & 0x2) { // it's a dir
124 if (!strnicmp((char *)&dir->name[0], filename, dir->name_len[0])) {
125 if (filename[dir->name_len[0]] != '\\') continue;
127 filename += dir->name_len[0] + 1;
129 mmssdd(dir->extent, (char *)time);
135 if (!strnicmp((char *)&dir->name[0], filename, strlen(filename))) {
136 mmssdd(dir->extent, (char *)time);
146 struct iso_directory_record *dir;
152 psxRegs.pc = psxRegs.GPR.n.ra;
156 time[0] = itob(0); time[1] = itob(2); time[2] = itob(0x10);
160 // skip head and sub, and go to the root directory record
161 dir = (struct iso_directory_record*) &buf[12+156];
163 mmssdd(dir->extent, (char*)time);
167 // Load SYSTEM.CNF and scan for the main executable
168 if (GetCdromFile(mdir, time, "SYSTEM.CNF;1") == -1) {
169 // if SYSTEM.CNF is missing, start an existing PSX.EXE
170 if (GetCdromFile(mdir, time, "PSX.EXE;1") == -1) return -1;
175 // read the SYSTEM.CNF
178 sscanf((char *)buf + 12, "BOOT = cdrom:\\%256s", exename);
179 if (GetCdromFile(mdir, time, exename) == -1) {
180 sscanf((char *)buf + 12, "BOOT = cdrom:%256s", exename);
181 if (GetCdromFile(mdir, time, exename) == -1) {
182 char *ptr = strstr(buf + 12, "cdrom:");
185 while (*ptr == '\\' || *ptr == '/') ptr++;
186 strncpy(exename, ptr, 255);
189 while (*ptr != '\0' && *ptr != '\r' && *ptr != '\n') ptr++;
191 if (GetCdromFile(mdir, time, exename) == -1)
198 // Read the EXE-Header
202 memcpy(&tmpHead, buf + 12, sizeof(EXE_HEADER));
204 psxRegs.pc = SWAP32(tmpHead.pc0);
205 psxRegs.GPR.n.gp = SWAP32(tmpHead.gp0);
206 psxRegs.GPR.n.sp = SWAP32(tmpHead.s_addr);
207 if (psxRegs.GPR.n.sp == 0) psxRegs.GPR.n.sp = 0x801fff00;
209 tmpHead.t_size = SWAP32(tmpHead.t_size);
210 tmpHead.t_addr = SWAP32(tmpHead.t_addr);
212 psxCpu->Clear(tmpHead.t_addr, tmpHead.t_size / 4);
214 // Read the rest of the main executable
215 while (tmpHead.t_size & ~2047) {
216 void *ptr = (void *)PSXM(tmpHead.t_addr);
221 if (ptr != NULL) memcpy(ptr, buf+12, 2048);
223 tmpHead.t_size -= 2048;
224 tmpHead.t_addr += 2048;
230 int LoadCdromFile(const char *filename, EXE_HEADER *head) {
231 struct iso_directory_record *dir;
233 u8 mdir[4096], exename[256];
236 sscanf(filename, "cdrom:\\%256s", exename);
238 time[0] = itob(0); time[1] = itob(2); time[2] = itob(0x10);
242 // skip head and sub, and go to the root directory record
243 dir = (struct iso_directory_record *)&buf[12 + 156];
245 mmssdd(dir->extent, (char*)time);
249 if (GetCdromFile(mdir, time, exename) == -1) return -1;
253 memcpy(head, buf + 12, sizeof(EXE_HEADER));
257 psxCpu->Clear(addr, size / 4);
259 while (size & ~2047) {
263 memcpy((void *)PSXM(addr), buf + 12, 2048);
273 struct iso_directory_record *dir;
274 unsigned char time[4], *buf;
275 unsigned char mdir[4096];
283 time[2] = itob(0x10);
287 CdromLabel[0] = '\0';
290 strncpy(CdromLabel, buf + 52, 32);
292 // skip head and sub, and go to the root directory record
293 dir = (struct iso_directory_record *)&buf[12 + 156];
295 mmssdd(dir->extent, (char *)time);
299 if (GetCdromFile(mdir, time, "SYSTEM.CNF;1") != -1) {
302 sscanf((char *)buf + 12, "BOOT = cdrom:\\%256s", exename);
303 if (GetCdromFile(mdir, time, exename) == -1) {
304 sscanf((char *)buf + 12, "BOOT = cdrom:%256s", exename);
305 if (GetCdromFile(mdir, time, exename) == -1) {
306 char *ptr = strstr(buf + 12, "cdrom:"); // possibly the executable is in some subdir
309 while (*ptr == '\\' || *ptr == '/') ptr++;
310 strncpy(exename, ptr, 255);
313 while (*ptr != '\0' && *ptr != '\r' && *ptr != '\n') ptr++;
315 if (GetCdromFile(mdir, time, exename) == -1)
316 return -1; // main executable not found
321 } else if (GetCdromFile(mdir, time, "PSX.EXE;1") != -1) {
322 strcpy(exename, "PSX.EXE;1");
323 strcpy(CdromId, "SLUS99999");
325 return -1; // SYSTEM.CNF and PSX.EXE not found
327 if (CdromId[0] == '\0') {
330 if (exename[i - 2] == ';') i-= 2;
332 while (i >= 0 && c >= 0) {
333 if (isalnum(exename[i])) CdromId[c--] = exename[i];
339 if (Config.PsxAuto) { // autodetect system (pal or ntsc)
340 if (CdromId[2] == 'e' || CdromId[2] == 'E')
341 Config.PsxType = PSX_TYPE_PAL; // pal
342 else Config.PsxType = PSX_TYPE_NTSC; // ntsc
345 if (CdromLabel[0] == ' ') {
346 strncpy(CdromLabel, CdromId, 9);
348 SysPrintf(_("CD-ROM Label: %.32s\n"), CdromLabel);
349 SysPrintf(_("CD-ROM ID: %.9s\n"), CdromId);
356 static int PSXGetFileType(FILE *f) {
357 unsigned long current;
363 fseek(f, 0L, SEEK_SET);
364 fread(mybuf, 2048, 1, f);
365 fseek(f, current, SEEK_SET);
367 exe_hdr = (EXE_HEADER *)mybuf;
368 if (memcmp(exe_hdr->id, "PS-X EXE", 8) == 0)
371 if (mybuf[0] == 'C' && mybuf[1] == 'P' && mybuf[2] == 'E')
374 coff_hdr = (FILHDR *)mybuf;
375 if (SWAPu16(coff_hdr->f_magic) == 0x0162)
381 int Load(const char *ExePath) {
387 u32 section_address, section_size;
390 strncpy(CdromId, "SLUS99999", 9);
391 strncpy(CdromLabel, "SLUS_999.99", 11);
393 tmpFile = fopen(ExePath, "rb");
394 if (tmpFile == NULL) {
395 SysPrintf(_("Error opening file: %s.\n"), ExePath);
398 type = PSXGetFileType(tmpFile);
401 fread(&tmpHead,sizeof(EXE_HEADER),1,tmpFile);
402 section_address = SWAP32(tmpHead.t_addr);
403 section_size = SWAP32(tmpHead.t_size);
404 mem = PSXM(section_address);
406 fseek(tmpFile, 0x800, SEEK_SET);
407 fread(mem, section_size, 1, tmpFile);
408 psxCpu->Clear(section_address, section_size / 4);
411 psxRegs.pc = SWAP32(tmpHead.pc0);
412 psxRegs.GPR.n.gp = SWAP32(tmpHead.gp0);
413 psxRegs.GPR.n.sp = SWAP32(tmpHead.s_addr);
414 if (psxRegs.GPR.n.sp == 0)
415 psxRegs.GPR.n.sp = 0x801fff00;
419 fseek(tmpFile, 6, SEEK_SET); /* Something tells me we should go to 4 and read the "08 00" here... */
421 fread(&opcode, 1, 1, tmpFile);
423 case 1: /* Section loading */
424 fread(§ion_address, 4, 1, tmpFile);
425 fread(§ion_size, 4, 1, tmpFile);
426 section_address = SWAPu32(section_address);
427 section_size = SWAPu32(section_size);
429 EMU_LOG("Loading %08X bytes from %08X to %08X\n", section_size, ftell(tmpFile), section_address);
431 mem = PSXM(section_address);
433 fread(mem, section_size, 1, tmpFile);
434 psxCpu->Clear(section_address, section_size / 4);
437 case 3: /* register loading (PC only?) */
438 fseek(tmpFile, 2, SEEK_CUR); /* unknown field */
439 fread(&psxRegs.pc, 4, 1, tmpFile);
440 psxRegs.pc = SWAPu32(psxRegs.pc);
442 case 0: /* End of file */
445 SysPrintf(_("Unknown CPE opcode %02x at position %08x.\n"), opcode, ftell(tmpFile) - 1);
449 } while (opcode != 0 && retval == 0);
452 SysPrintf(_("COFF files not supported.\n"));
456 SysPrintf(_("This file does not appear to be a valid PSX file.\n"));
464 CdromLabel[0] = '\0';
472 static const char PcsxHeader[32] = "STv4 PCSX v" PACKAGE_VERSION;
474 // Savestate Versioning!
475 // If you make changes to the savestate version, please increment the value below.
476 static const u32 SaveVersion = 0x8b410006;
478 int SaveState(const char *file) {
485 f = gzopen(file, "wb");
486 if (f == NULL) return -1;
490 gzwrite(f, (void *)PcsxHeader, 32);
491 gzwrite(f, (void *)&SaveVersion, sizeof(u32));
492 gzwrite(f, (void *)&Config.HLE, sizeof(boolean));
494 pMem = (unsigned char *)malloc(128 * 96 * 3);
495 if (pMem == NULL) return -1;
496 GPU_getScreenPic(pMem);
497 gzwrite(f, pMem, 128 * 96 * 3);
503 gzwrite(f, psxM, 0x00200000);
504 gzwrite(f, psxR, 0x00080000);
505 gzwrite(f, psxH, 0x00010000);
506 gzwrite(f, (void *)&psxRegs, sizeof(psxRegs));
509 gpufP = (GPUFreeze_t *)malloc(sizeof(GPUFreeze_t));
510 gpufP->ulFreezeVersion = 1;
511 GPU_freeze(1, gpufP);
512 gzwrite(f, gpufP, sizeof(GPUFreeze_t));
516 spufP = (SPUFreeze_t *) malloc(16);
517 SPU_freeze(2, spufP);
518 Size = spufP->Size; gzwrite(f, &Size, 4);
520 spufP = (SPUFreeze_t *) malloc(Size);
521 SPU_freeze(1, spufP);
522 gzwrite(f, spufP, Size);
533 new_dyna_after_save();
538 int LoadState(const char *file) {
547 f = gzopen(file, "rb");
548 if (f == NULL) return -1;
550 gzread(f, header, sizeof(header));
551 gzread(f, &version, sizeof(u32));
552 gzread(f, &hle, sizeof(boolean));
554 if (strncmp("STv4 PCSX", header, 9) != 0 || version != SaveVersion) {
564 gzseek(f, 128 * 96 * 3, SEEK_CUR);
566 gzread(f, psxM, 0x00200000);
567 gzread(f, psxR, 0x00080000);
568 gzread(f, psxH, 0x00010000);
569 gzread(f, (void *)&psxRegs, sizeof(psxRegs));
575 gpufP = (GPUFreeze_t *)malloc(sizeof(GPUFreeze_t));
576 gzread(f, gpufP, sizeof(GPUFreeze_t));
577 GPU_freeze(0, gpufP);
582 spufP = (SPUFreeze_t *)malloc(Size);
583 gzread(f, spufP, Size);
584 SPU_freeze(0, spufP);
599 int CheckState(const char *file) {
605 f = gzopen(file, "rb");
606 if (f == NULL) return -1;
608 gzread(f, header, sizeof(header));
609 gzread(f, &version, sizeof(u32));
610 gzread(f, &hle, sizeof(boolean));
614 if (strncmp("STv4 PCSX", header, 9) != 0 || version != SaveVersion)
620 // NET Function Helpers
623 if (NET_recvData == NULL || NET_sendData == NULL)
626 NET_sendData(&Config.Xa, sizeof(Config.Xa), PSE_NET_BLOCKING);
627 NET_sendData(&Config.Sio, sizeof(Config.Sio), PSE_NET_BLOCKING);
628 NET_sendData(&Config.SpuIrq, sizeof(Config.SpuIrq), PSE_NET_BLOCKING);
629 NET_sendData(&Config.RCntFix, sizeof(Config.RCntFix), PSE_NET_BLOCKING);
630 NET_sendData(&Config.PsxType, sizeof(Config.PsxType), PSE_NET_BLOCKING);
631 NET_sendData(&Config.Cpu, sizeof(Config.Cpu), PSE_NET_BLOCKING);
639 if (NET_recvData == NULL || NET_sendData == NULL)
642 NET_recvData(&Config.Xa, sizeof(Config.Xa), PSE_NET_BLOCKING);
643 NET_recvData(&Config.Sio, sizeof(Config.Sio), PSE_NET_BLOCKING);
644 NET_recvData(&Config.SpuIrq, sizeof(Config.SpuIrq), PSE_NET_BLOCKING);
645 NET_recvData(&Config.RCntFix, sizeof(Config.RCntFix), PSE_NET_BLOCKING);
646 NET_recvData(&Config.PsxType, sizeof(Config.PsxType), PSE_NET_BLOCKING);
651 NET_recvData(&Config.Cpu, sizeof(Config.Cpu), PSE_NET_BLOCKING);
652 if (tmp != Config.Cpu) {
655 if (Config.Cpu == CPU_INTERPRETER) psxCpu = &psxInt;
656 else psxCpu = &psxRec;
660 if (psxCpu->Init() == -1) {
661 SysClose(); return -1;
669 // remove the leading and trailing spaces in a string
670 void trim(char *str) {
674 // skip leading blanks
675 while (str[pos] <= ' ' && str[pos] > 0)
679 *(dest++) = str[pos];
683 *(dest--) = '\0'; // store the null
685 // remove trailing blanks
686 while (dest >= str && *dest <= ' ' && *dest > 0)
690 // lookup table for crc calculation
691 static unsigned short crctab[256] = {
692 0x0000, 0x1021, 0x2042, 0x3063, 0x4084, 0x50A5, 0x60C6, 0x70E7, 0x8108,
693 0x9129, 0xA14A, 0xB16B, 0xC18C, 0xD1AD, 0xE1CE, 0xF1EF, 0x1231, 0x0210,
694 0x3273, 0x2252, 0x52B5, 0x4294, 0x72F7, 0x62D6, 0x9339, 0x8318, 0xB37B,
695 0xA35A, 0xD3BD, 0xC39C, 0xF3FF, 0xE3DE, 0x2462, 0x3443, 0x0420, 0x1401,
696 0x64E6, 0x74C7, 0x44A4, 0x5485, 0xA56A, 0xB54B, 0x8528, 0x9509, 0xE5EE,
697 0xF5CF, 0xC5AC, 0xD58D, 0x3653, 0x2672, 0x1611, 0x0630, 0x76D7, 0x66F6,
698 0x5695, 0x46B4, 0xB75B, 0xA77A, 0x9719, 0x8738, 0xF7DF, 0xE7FE, 0xD79D,
699 0xC7BC, 0x48C4, 0x58E5, 0x6886, 0x78A7, 0x0840, 0x1861, 0x2802, 0x3823,
700 0xC9CC, 0xD9ED, 0xE98E, 0xF9AF, 0x8948, 0x9969, 0xA90A, 0xB92B, 0x5AF5,
701 0x4AD4, 0x7AB7, 0x6A96, 0x1A71, 0x0A50, 0x3A33, 0x2A12, 0xDBFD, 0xCBDC,
702 0xFBBF, 0xEB9E, 0x9B79, 0x8B58, 0xBB3B, 0xAB1A, 0x6CA6, 0x7C87, 0x4CE4,
703 0x5CC5, 0x2C22, 0x3C03, 0x0C60, 0x1C41, 0xEDAE, 0xFD8F, 0xCDEC, 0xDDCD,
704 0xAD2A, 0xBD0B, 0x8D68, 0x9D49, 0x7E97, 0x6EB6, 0x5ED5, 0x4EF4, 0x3E13,
705 0x2E32, 0x1E51, 0x0E70, 0xFF9F, 0xEFBE, 0xDFDD, 0xCFFC, 0xBF1B, 0xAF3A,
706 0x9F59, 0x8F78, 0x9188, 0x81A9, 0xB1CA, 0xA1EB, 0xD10C, 0xC12D, 0xF14E,
707 0xE16F, 0x1080, 0x00A1, 0x30C2, 0x20E3, 0x5004, 0x4025, 0x7046, 0x6067,
708 0x83B9, 0x9398, 0xA3FB, 0xB3DA, 0xC33D, 0xD31C, 0xE37F, 0xF35E, 0x02B1,
709 0x1290, 0x22F3, 0x32D2, 0x4235, 0x5214, 0x6277, 0x7256, 0xB5EA, 0xA5CB,
710 0x95A8, 0x8589, 0xF56E, 0xE54F, 0xD52C, 0xC50D, 0x34E2, 0x24C3, 0x14A0,
711 0x0481, 0x7466, 0x6447, 0x5424, 0x4405, 0xA7DB, 0xB7FA, 0x8799, 0x97B8,
712 0xE75F, 0xF77E, 0xC71D, 0xD73C, 0x26D3, 0x36F2, 0x0691, 0x16B0, 0x6657,
713 0x7676, 0x4615, 0x5634, 0xD94C, 0xC96D, 0xF90E, 0xE92F, 0x99C8, 0x89E9,
714 0xB98A, 0xA9AB, 0x5844, 0x4865, 0x7806, 0x6827, 0x18C0, 0x08E1, 0x3882,
715 0x28A3, 0xCB7D, 0xDB5C, 0xEB3F, 0xFB1E, 0x8BF9, 0x9BD8, 0xABBB, 0xBB9A,
716 0x4A75, 0x5A54, 0x6A37, 0x7A16, 0x0AF1, 0x1AD0, 0x2AB3, 0x3A92, 0xFD2E,
717 0xED0F, 0xDD6C, 0xCD4D, 0xBDAA, 0xAD8B, 0x9DE8, 0x8DC9, 0x7C26, 0x6C07,
718 0x5C64, 0x4C45, 0x3CA2, 0x2C83, 0x1CE0, 0x0CC1, 0xEF1F, 0xFF3E, 0xCF5D,
719 0xDF7C, 0xAF9B, 0xBFBA, 0x8FD9, 0x9FF8, 0x6E17, 0x7E36, 0x4E55, 0x5E74,
720 0x2E93, 0x3EB2, 0x0ED1, 0x1EF0
723 u16 calcCrc(u8 *d, int len) {
727 for (i = 0; i < len; i++) {
728 crc = crctab[(crc >> 8) ^ d[i]] ^ (crc << 8);