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);
536 int LoadState(const char *file) {
545 f = gzopen(file, "rb");
546 if (f == NULL) return -1;
548 gzread(f, header, sizeof(header));
549 gzread(f, &version, sizeof(u32));
550 gzread(f, &hle, sizeof(boolean));
552 if (strncmp("STv4 PCSX", header, 9) != 0 || version != SaveVersion) {
562 gzseek(f, 128 * 96 * 3, SEEK_CUR);
564 gzread(f, psxM, 0x00200000);
565 gzread(f, psxR, 0x00080000);
566 gzread(f, psxH, 0x00010000);
567 gzread(f, (void *)&psxRegs, sizeof(psxRegs));
573 gpufP = (GPUFreeze_t *)malloc(sizeof(GPUFreeze_t));
574 gzread(f, gpufP, sizeof(GPUFreeze_t));
575 GPU_freeze(0, gpufP);
580 spufP = (SPUFreeze_t *)malloc(Size);
581 gzread(f, spufP, Size);
582 SPU_freeze(0, spufP);
597 int CheckState(const char *file) {
603 f = gzopen(file, "rb");
604 if (f == NULL) return -1;
606 gzread(f, header, sizeof(header));
607 gzread(f, &version, sizeof(u32));
608 gzread(f, &hle, sizeof(boolean));
612 if (strncmp("STv4 PCSX", header, 9) != 0 || version != SaveVersion)
618 // NET Function Helpers
621 if (NET_recvData == NULL || NET_sendData == NULL)
624 NET_sendData(&Config.Xa, sizeof(Config.Xa), PSE_NET_BLOCKING);
625 NET_sendData(&Config.Sio, sizeof(Config.Sio), PSE_NET_BLOCKING);
626 NET_sendData(&Config.SpuIrq, sizeof(Config.SpuIrq), PSE_NET_BLOCKING);
627 NET_sendData(&Config.RCntFix, sizeof(Config.RCntFix), PSE_NET_BLOCKING);
628 NET_sendData(&Config.PsxType, sizeof(Config.PsxType), PSE_NET_BLOCKING);
629 NET_sendData(&Config.Cpu, sizeof(Config.Cpu), PSE_NET_BLOCKING);
637 if (NET_recvData == NULL || NET_sendData == NULL)
640 NET_recvData(&Config.Xa, sizeof(Config.Xa), PSE_NET_BLOCKING);
641 NET_recvData(&Config.Sio, sizeof(Config.Sio), PSE_NET_BLOCKING);
642 NET_recvData(&Config.SpuIrq, sizeof(Config.SpuIrq), PSE_NET_BLOCKING);
643 NET_recvData(&Config.RCntFix, sizeof(Config.RCntFix), PSE_NET_BLOCKING);
644 NET_recvData(&Config.PsxType, sizeof(Config.PsxType), PSE_NET_BLOCKING);
649 NET_recvData(&Config.Cpu, sizeof(Config.Cpu), PSE_NET_BLOCKING);
650 if (tmp != Config.Cpu) {
653 if (Config.Cpu == CPU_INTERPRETER) psxCpu = &psxInt;
654 else psxCpu = &psxRec;
658 if (psxCpu->Init() == -1) {
659 SysClose(); return -1;
667 // remove the leading and trailing spaces in a string
668 void trim(char *str) {
672 // skip leading blanks
673 while (str[pos] <= ' ' && str[pos] > 0)
677 *(dest++) = str[pos];
681 *(dest--) = '\0'; // store the null
683 // remove trailing blanks
684 while (dest >= str && *dest <= ' ' && *dest > 0)
688 // lookup table for crc calculation
689 static unsigned short crctab[256] = {
690 0x0000, 0x1021, 0x2042, 0x3063, 0x4084, 0x50A5, 0x60C6, 0x70E7, 0x8108,
691 0x9129, 0xA14A, 0xB16B, 0xC18C, 0xD1AD, 0xE1CE, 0xF1EF, 0x1231, 0x0210,
692 0x3273, 0x2252, 0x52B5, 0x4294, 0x72F7, 0x62D6, 0x9339, 0x8318, 0xB37B,
693 0xA35A, 0xD3BD, 0xC39C, 0xF3FF, 0xE3DE, 0x2462, 0x3443, 0x0420, 0x1401,
694 0x64E6, 0x74C7, 0x44A4, 0x5485, 0xA56A, 0xB54B, 0x8528, 0x9509, 0xE5EE,
695 0xF5CF, 0xC5AC, 0xD58D, 0x3653, 0x2672, 0x1611, 0x0630, 0x76D7, 0x66F6,
696 0x5695, 0x46B4, 0xB75B, 0xA77A, 0x9719, 0x8738, 0xF7DF, 0xE7FE, 0xD79D,
697 0xC7BC, 0x48C4, 0x58E5, 0x6886, 0x78A7, 0x0840, 0x1861, 0x2802, 0x3823,
698 0xC9CC, 0xD9ED, 0xE98E, 0xF9AF, 0x8948, 0x9969, 0xA90A, 0xB92B, 0x5AF5,
699 0x4AD4, 0x7AB7, 0x6A96, 0x1A71, 0x0A50, 0x3A33, 0x2A12, 0xDBFD, 0xCBDC,
700 0xFBBF, 0xEB9E, 0x9B79, 0x8B58, 0xBB3B, 0xAB1A, 0x6CA6, 0x7C87, 0x4CE4,
701 0x5CC5, 0x2C22, 0x3C03, 0x0C60, 0x1C41, 0xEDAE, 0xFD8F, 0xCDEC, 0xDDCD,
702 0xAD2A, 0xBD0B, 0x8D68, 0x9D49, 0x7E97, 0x6EB6, 0x5ED5, 0x4EF4, 0x3E13,
703 0x2E32, 0x1E51, 0x0E70, 0xFF9F, 0xEFBE, 0xDFDD, 0xCFFC, 0xBF1B, 0xAF3A,
704 0x9F59, 0x8F78, 0x9188, 0x81A9, 0xB1CA, 0xA1EB, 0xD10C, 0xC12D, 0xF14E,
705 0xE16F, 0x1080, 0x00A1, 0x30C2, 0x20E3, 0x5004, 0x4025, 0x7046, 0x6067,
706 0x83B9, 0x9398, 0xA3FB, 0xB3DA, 0xC33D, 0xD31C, 0xE37F, 0xF35E, 0x02B1,
707 0x1290, 0x22F3, 0x32D2, 0x4235, 0x5214, 0x6277, 0x7256, 0xB5EA, 0xA5CB,
708 0x95A8, 0x8589, 0xF56E, 0xE54F, 0xD52C, 0xC50D, 0x34E2, 0x24C3, 0x14A0,
709 0x0481, 0x7466, 0x6447, 0x5424, 0x4405, 0xA7DB, 0xB7FA, 0x8799, 0x97B8,
710 0xE75F, 0xF77E, 0xC71D, 0xD73C, 0x26D3, 0x36F2, 0x0691, 0x16B0, 0x6657,
711 0x7676, 0x4615, 0x5634, 0xD94C, 0xC96D, 0xF90E, 0xE92F, 0x99C8, 0x89E9,
712 0xB98A, 0xA9AB, 0x5844, 0x4865, 0x7806, 0x6827, 0x18C0, 0x08E1, 0x3882,
713 0x28A3, 0xCB7D, 0xDB5C, 0xEB3F, 0xFB1E, 0x8BF9, 0x9BD8, 0xABBB, 0xBB9A,
714 0x4A75, 0x5A54, 0x6A37, 0x7A16, 0x0AF1, 0x1AD0, 0x2AB3, 0x3A92, 0xFD2E,
715 0xED0F, 0xDD6C, 0xCD4D, 0xBDAA, 0xAD8B, 0x9DE8, 0x8DC9, 0x7C26, 0x6C07,
716 0x5C64, 0x4C45, 0x3CA2, 0x2C83, 0x1CE0, 0x0CC1, 0xEF1F, 0xFF3E, 0xCF5D,
717 0xDF7C, 0xAF9B, 0xBFBA, 0x8FD9, 0x9FF8, 0x6E17, 0x7E36, 0x4E55, 0x5E74,
718 0x2E93, 0x3EB2, 0x0ED1, 0x1EF0
721 u16 calcCrc(u8 *d, int len) {
725 for (i = 0; i < len; i++) {
726 crc = crctab[(crc >> 8) ^ d[i]] ^ (crc << 8);