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);
147 struct iso_directory_record *dir;
153 psxRegs.pc = psxRegs.GPR.n.ra;
157 time[0] = itob(0); time[1] = itob(2); time[2] = itob(0x10);
161 // skip head and sub, and go to the root directory record
162 dir = (struct iso_directory_record*) &buf[12+156];
164 mmssdd(dir->extent, (char*)time);
168 // Load SYSTEM.CNF and scan for the main executable
169 if (GetCdromFile(mdir, time, "SYSTEM.CNF;1") == -1) {
170 // if SYSTEM.CNF is missing, start an existing PSX.EXE
171 if (GetCdromFile(mdir, time, "PSX.EXE;1") == -1) return -1;
176 // read the SYSTEM.CNF
179 sscanf((char *)buf + 12, "BOOT = cdrom:\\%256s", exename);
180 if (GetCdromFile(mdir, time, exename) == -1) {
181 sscanf((char *)buf + 12, "BOOT = cdrom:%256s", exename);
182 if (GetCdromFile(mdir, time, exename) == -1) {
183 char *ptr = strstr(buf + 12, "cdrom:");
186 while (*ptr == '\\' || *ptr == '/') ptr++;
187 strncpy(exename, ptr, 255);
190 while (*ptr != '\0' && *ptr != '\r' && *ptr != '\n') ptr++;
192 if (GetCdromFile(mdir, time, exename) == -1)
199 // Read the EXE-Header
203 memcpy(&tmpHead, buf + 12, sizeof(EXE_HEADER));
205 psxRegs.pc = SWAP32(tmpHead.pc0);
206 psxRegs.GPR.n.gp = SWAP32(tmpHead.gp0);
207 psxRegs.GPR.n.sp = SWAP32(tmpHead.s_addr);
208 if (psxRegs.GPR.n.sp == 0) psxRegs.GPR.n.sp = 0x801fff00;
210 tmpHead.t_size = SWAP32(tmpHead.t_size);
211 tmpHead.t_addr = SWAP32(tmpHead.t_addr);
213 psxCpu->Clear(tmpHead.t_addr, tmpHead.t_size / 4);
215 // Read the rest of the main executable
216 while (tmpHead.t_size & ~2047) {
217 void *ptr = (void *)PSXM(tmpHead.t_addr);
222 if (ptr != NULL) memcpy(ptr, buf+12, 2048);
224 tmpHead.t_size -= 2048;
225 tmpHead.t_addr += 2048;
231 int LoadCdromFile(const char *filename, EXE_HEADER *head) {
232 struct iso_directory_record *dir;
234 u8 mdir[4096], exename[256];
237 sscanf(filename, "cdrom:\\%256s", exename);
239 time[0] = itob(0); time[1] = itob(2); time[2] = itob(0x10);
243 // skip head and sub, and go to the root directory record
244 dir = (struct iso_directory_record *)&buf[12 + 156];
246 mmssdd(dir->extent, (char*)time);
250 if (GetCdromFile(mdir, time, exename) == -1) return -1;
254 memcpy(head, buf + 12, sizeof(EXE_HEADER));
258 psxCpu->Clear(addr, size / 4);
260 while (size & ~2047) {
264 memcpy((void *)PSXM(addr), buf + 12, 2048);
274 struct iso_directory_record *dir;
275 unsigned char time[4], *buf;
276 unsigned char mdir[4096];
284 time[2] = itob(0x10);
288 CdromLabel[0] = '\0';
291 strncpy(CdromLabel, buf + 52, 32);
293 // skip head and sub, and go to the root directory record
294 dir = (struct iso_directory_record *)&buf[12 + 156];
296 mmssdd(dir->extent, (char *)time);
300 if (GetCdromFile(mdir, time, "SYSTEM.CNF;1") != -1) {
303 sscanf((char *)buf + 12, "BOOT = cdrom:\\%256s", exename);
304 if (GetCdromFile(mdir, time, exename) == -1) {
305 sscanf((char *)buf + 12, "BOOT = cdrom:%256s", exename);
306 if (GetCdromFile(mdir, time, exename) == -1) {
307 char *ptr = strstr(buf + 12, "cdrom:"); // possibly the executable is in some subdir
310 while (*ptr == '\\' || *ptr == '/') ptr++;
311 strncpy(exename, ptr, 255);
314 while (*ptr != '\0' && *ptr != '\r' && *ptr != '\n') ptr++;
316 if (GetCdromFile(mdir, time, exename) == -1)
317 return -1; // main executable not found
322 } else if (GetCdromFile(mdir, time, "PSX.EXE;1") != -1) {
323 strcpy(exename, "PSX.EXE;1");
324 strcpy(CdromId, "SLUS99999");
326 return -1; // SYSTEM.CNF and PSX.EXE not found
328 if (CdromId[0] == '\0') {
331 if (exename[i - 2] == ';') i-= 2;
333 while (i >= 0 && c >= 0) {
334 if (isalnum(exename[i])) CdromId[c--] = exename[i];
340 if (Config.PsxAuto) { // autodetect system (pal or ntsc)
341 if (CdromId[2] == 'e' || CdromId[2] == 'E')
342 Config.PsxType = PSX_TYPE_PAL; // pal
343 else Config.PsxType = PSX_TYPE_NTSC; // ntsc
346 if (CdromLabel[0] == ' ') {
347 strncpy(CdromLabel, CdromId, 9);
349 SysPrintf(_("CD-ROM Label: %.32s\n"), CdromLabel);
350 SysPrintf(_("CD-ROM ID: %.9s\n"), CdromId);
357 static int PSXGetFileType(FILE *f) {
358 unsigned long current;
364 fseek(f, 0L, SEEK_SET);
365 fread(mybuf, 2048, 1, f);
366 fseek(f, current, SEEK_SET);
368 exe_hdr = (EXE_HEADER *)mybuf;
369 if (memcmp(exe_hdr->id, "PS-X EXE", 8) == 0)
372 if (mybuf[0] == 'C' && mybuf[1] == 'P' && mybuf[2] == 'E')
375 coff_hdr = (FILHDR *)mybuf;
376 if (SWAPu16(coff_hdr->f_magic) == 0x0162)
382 int Load(const char *ExePath) {
388 u32 section_address, section_size;
391 strncpy(CdromId, "SLUS99999", 9);
392 strncpy(CdromLabel, "SLUS_999.99", 11);
394 tmpFile = fopen(ExePath, "rb");
395 if (tmpFile == NULL) {
396 SysPrintf(_("Error opening file: %s.\n"), ExePath);
399 type = PSXGetFileType(tmpFile);
402 fread(&tmpHead,sizeof(EXE_HEADER),1,tmpFile);
403 section_address = SWAP32(tmpHead.t_addr);
404 section_size = SWAP32(tmpHead.t_size);
405 mem = PSXM(section_address);
407 fseek(tmpFile, 0x800, SEEK_SET);
408 fread(mem, section_size, 1, tmpFile);
409 psxCpu->Clear(section_address, section_size / 4);
412 psxRegs.pc = SWAP32(tmpHead.pc0);
413 psxRegs.GPR.n.gp = SWAP32(tmpHead.gp0);
414 psxRegs.GPR.n.sp = SWAP32(tmpHead.s_addr);
415 if (psxRegs.GPR.n.sp == 0)
416 psxRegs.GPR.n.sp = 0x801fff00;
420 fseek(tmpFile, 6, SEEK_SET); /* Something tells me we should go to 4 and read the "08 00" here... */
422 fread(&opcode, 1, 1, tmpFile);
424 case 1: /* Section loading */
425 fread(§ion_address, 4, 1, tmpFile);
426 fread(§ion_size, 4, 1, tmpFile);
427 section_address = SWAPu32(section_address);
428 section_size = SWAPu32(section_size);
430 EMU_LOG("Loading %08X bytes from %08X to %08X\n", section_size, ftell(tmpFile), section_address);
432 mem = PSXM(section_address);
434 fread(mem, section_size, 1, tmpFile);
435 psxCpu->Clear(section_address, section_size / 4);
438 case 3: /* register loading (PC only?) */
439 fseek(tmpFile, 2, SEEK_CUR); /* unknown field */
440 fread(&psxRegs.pc, 4, 1, tmpFile);
441 psxRegs.pc = SWAPu32(psxRegs.pc);
443 case 0: /* End of file */
446 SysPrintf(_("Unknown CPE opcode %02x at position %08x.\n"), opcode, ftell(tmpFile) - 1);
450 } while (opcode != 0 && retval == 0);
453 SysPrintf(_("COFF files not supported.\n"));
457 SysPrintf(_("This file does not appear to be a valid PSX file.\n"));
465 CdromLabel[0] = '\0';
473 static const char PcsxHeader[32] = "STv4 PCSX v" PACKAGE_VERSION;
475 // Savestate Versioning!
476 // If you make changes to the savestate version, please increment the value below.
477 static const u32 SaveVersion = 0x8b410006;
479 int SaveState(const char *file) {
486 f = gzopen(file, "wb");
487 if (f == NULL) return -1;
491 gzwrite(f, (void *)PcsxHeader, 32);
492 gzwrite(f, (void *)&SaveVersion, sizeof(u32));
493 gzwrite(f, (void *)&Config.HLE, sizeof(boolean));
495 pMem = (unsigned char *)malloc(128 * 96 * 3);
496 if (pMem == NULL) return -1;
497 GPU_getScreenPic(pMem);
498 gzwrite(f, pMem, 128 * 96 * 3);
504 gzwrite(f, psxM, 0x00200000);
505 gzwrite(f, psxR, 0x00080000);
506 gzwrite(f, psxH, 0x00010000);
507 gzwrite(f, (void *)&psxRegs, sizeof(psxRegs));
510 gpufP = (GPUFreeze_t *)malloc(sizeof(GPUFreeze_t));
511 gpufP->ulFreezeVersion = 1;
512 GPU_freeze(1, gpufP);
513 gzwrite(f, gpufP, sizeof(GPUFreeze_t));
517 spufP = (SPUFreeze_t *) malloc(16);
518 SPU_freeze(2, spufP);
519 Size = spufP->Size; gzwrite(f, &Size, 4);
521 spufP = (SPUFreeze_t *) malloc(Size);
522 SPU_freeze(1, spufP);
523 gzwrite(f, spufP, Size);
534 new_dyna_after_save();
539 int LoadState(const char *file) {
548 f = gzopen(file, "rb");
549 if (f == NULL) return -1;
551 gzread(f, header, sizeof(header));
552 gzread(f, &version, sizeof(u32));
553 gzread(f, &hle, sizeof(boolean));
555 if (strncmp("STv4 PCSX", header, 9) != 0 || version != SaveVersion) {
565 gzseek(f, 128 * 96 * 3, SEEK_CUR);
567 gzread(f, psxM, 0x00200000);
568 gzread(f, psxR, 0x00080000);
569 gzread(f, psxH, 0x00010000);
570 gzread(f, (void *)&psxRegs, sizeof(psxRegs));
576 gpufP = (GPUFreeze_t *)malloc(sizeof(GPUFreeze_t));
577 gzread(f, gpufP, sizeof(GPUFreeze_t));
578 GPU_freeze(0, gpufP);
580 if (HW_GPU_STATUS == 0)
581 HW_GPU_STATUS = GPU_readStatus();
585 spufP = (SPUFreeze_t *)malloc(Size);
586 gzread(f, spufP, Size);
587 SPU_freeze(0, spufP);
602 int CheckState(const char *file) {
608 f = gzopen(file, "rb");
609 if (f == NULL) return -1;
611 gzread(f, header, sizeof(header));
612 gzread(f, &version, sizeof(u32));
613 gzread(f, &hle, sizeof(boolean));
617 if (strncmp("STv4 PCSX", header, 9) != 0 || version != SaveVersion)
623 // NET Function Helpers
626 if (NET_recvData == NULL || NET_sendData == NULL)
629 NET_sendData(&Config.Xa, sizeof(Config.Xa), PSE_NET_BLOCKING);
630 NET_sendData(&Config.Sio, sizeof(Config.Sio), PSE_NET_BLOCKING);
631 NET_sendData(&Config.SpuIrq, sizeof(Config.SpuIrq), PSE_NET_BLOCKING);
632 NET_sendData(&Config.RCntFix, sizeof(Config.RCntFix), PSE_NET_BLOCKING);
633 NET_sendData(&Config.PsxType, sizeof(Config.PsxType), PSE_NET_BLOCKING);
634 NET_sendData(&Config.Cpu, sizeof(Config.Cpu), PSE_NET_BLOCKING);
642 if (NET_recvData == NULL || NET_sendData == NULL)
645 NET_recvData(&Config.Xa, sizeof(Config.Xa), PSE_NET_BLOCKING);
646 NET_recvData(&Config.Sio, sizeof(Config.Sio), PSE_NET_BLOCKING);
647 NET_recvData(&Config.SpuIrq, sizeof(Config.SpuIrq), PSE_NET_BLOCKING);
648 NET_recvData(&Config.RCntFix, sizeof(Config.RCntFix), PSE_NET_BLOCKING);
649 NET_recvData(&Config.PsxType, sizeof(Config.PsxType), PSE_NET_BLOCKING);
654 NET_recvData(&Config.Cpu, sizeof(Config.Cpu), PSE_NET_BLOCKING);
655 if (tmp != Config.Cpu) {
658 if (Config.Cpu == CPU_INTERPRETER) psxCpu = &psxInt;
659 else psxCpu = &psxRec;
663 if (psxCpu->Init() == -1) {
664 SysClose(); return -1;
672 // remove the leading and trailing spaces in a string
673 void trim(char *str) {
677 // skip leading blanks
678 while (str[pos] <= ' ' && str[pos] > 0)
682 *(dest++) = str[pos];
686 *(dest--) = '\0'; // store the null
688 // remove trailing blanks
689 while (dest >= str && *dest <= ' ' && *dest > 0)
693 // lookup table for crc calculation
694 static unsigned short crctab[256] = {
695 0x0000, 0x1021, 0x2042, 0x3063, 0x4084, 0x50A5, 0x60C6, 0x70E7, 0x8108,
696 0x9129, 0xA14A, 0xB16B, 0xC18C, 0xD1AD, 0xE1CE, 0xF1EF, 0x1231, 0x0210,
697 0x3273, 0x2252, 0x52B5, 0x4294, 0x72F7, 0x62D6, 0x9339, 0x8318, 0xB37B,
698 0xA35A, 0xD3BD, 0xC39C, 0xF3FF, 0xE3DE, 0x2462, 0x3443, 0x0420, 0x1401,
699 0x64E6, 0x74C7, 0x44A4, 0x5485, 0xA56A, 0xB54B, 0x8528, 0x9509, 0xE5EE,
700 0xF5CF, 0xC5AC, 0xD58D, 0x3653, 0x2672, 0x1611, 0x0630, 0x76D7, 0x66F6,
701 0x5695, 0x46B4, 0xB75B, 0xA77A, 0x9719, 0x8738, 0xF7DF, 0xE7FE, 0xD79D,
702 0xC7BC, 0x48C4, 0x58E5, 0x6886, 0x78A7, 0x0840, 0x1861, 0x2802, 0x3823,
703 0xC9CC, 0xD9ED, 0xE98E, 0xF9AF, 0x8948, 0x9969, 0xA90A, 0xB92B, 0x5AF5,
704 0x4AD4, 0x7AB7, 0x6A96, 0x1A71, 0x0A50, 0x3A33, 0x2A12, 0xDBFD, 0xCBDC,
705 0xFBBF, 0xEB9E, 0x9B79, 0x8B58, 0xBB3B, 0xAB1A, 0x6CA6, 0x7C87, 0x4CE4,
706 0x5CC5, 0x2C22, 0x3C03, 0x0C60, 0x1C41, 0xEDAE, 0xFD8F, 0xCDEC, 0xDDCD,
707 0xAD2A, 0xBD0B, 0x8D68, 0x9D49, 0x7E97, 0x6EB6, 0x5ED5, 0x4EF4, 0x3E13,
708 0x2E32, 0x1E51, 0x0E70, 0xFF9F, 0xEFBE, 0xDFDD, 0xCFFC, 0xBF1B, 0xAF3A,
709 0x9F59, 0x8F78, 0x9188, 0x81A9, 0xB1CA, 0xA1EB, 0xD10C, 0xC12D, 0xF14E,
710 0xE16F, 0x1080, 0x00A1, 0x30C2, 0x20E3, 0x5004, 0x4025, 0x7046, 0x6067,
711 0x83B9, 0x9398, 0xA3FB, 0xB3DA, 0xC33D, 0xD31C, 0xE37F, 0xF35E, 0x02B1,
712 0x1290, 0x22F3, 0x32D2, 0x4235, 0x5214, 0x6277, 0x7256, 0xB5EA, 0xA5CB,
713 0x95A8, 0x8589, 0xF56E, 0xE54F, 0xD52C, 0xC50D, 0x34E2, 0x24C3, 0x14A0,
714 0x0481, 0x7466, 0x6447, 0x5424, 0x4405, 0xA7DB, 0xB7FA, 0x8799, 0x97B8,
715 0xE75F, 0xF77E, 0xC71D, 0xD73C, 0x26D3, 0x36F2, 0x0691, 0x16B0, 0x6657,
716 0x7676, 0x4615, 0x5634, 0xD94C, 0xC96D, 0xF90E, 0xE92F, 0x99C8, 0x89E9,
717 0xB98A, 0xA9AB, 0x5844, 0x4865, 0x7806, 0x6827, 0x18C0, 0x08E1, 0x3882,
718 0x28A3, 0xCB7D, 0xDB5C, 0xEB3F, 0xFB1E, 0x8BF9, 0x9BD8, 0xABBB, 0xBB9A,
719 0x4A75, 0x5A54, 0x6A37, 0x7A16, 0x0AF1, 0x1AD0, 0x2AB3, 0x3A92, 0xFD2E,
720 0xED0F, 0xDD6C, 0xCD4D, 0xBDAA, 0xAD8B, 0x9DE8, 0x8DC9, 0x7C26, 0x6C07,
721 0x5C64, 0x4C45, 0x3CA2, 0x2C83, 0x1CE0, 0x0CC1, 0xEF1F, 0xFF3E, 0xCF5D,
722 0xDF7C, 0xAF9B, 0xBFBA, 0x8FD9, 0x9FF8, 0x6E17, 0x7E36, 0x4E55, 0x5E74,
723 0x2E93, 0x3EB2, 0x0ED1, 0x1EF0
726 u16 calcCrc(u8 *d, int len) {
730 for (i = 0; i < len; i++) {
731 crc = crctab[(crc >> 8) ^ d[i]] ^ (crc << 8);