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 (CdromId[0] == '\0')
368 strcpy(CdromId, "SLUS99999");
370 if (Config.PsxAuto) { // autodetect system (pal or ntsc)
371 if (CdromId[2] == 'e' || CdromId[2] == 'E')
372 Config.PsxType = PSX_TYPE_PAL; // pal
373 else Config.PsxType = PSX_TYPE_NTSC; // ntsc
376 if (CdromLabel[0] == ' ') {
377 strncpy(CdromLabel, CdromId, 9);
379 SysPrintf(_("CD-ROM Label: %.32s\n"), CdromLabel);
380 SysPrintf(_("CD-ROM ID: %.9s\n"), CdromId);
387 static int PSXGetFileType(FILE *f) {
388 unsigned long current;
394 fseek(f, 0L, SEEK_SET);
395 fread(mybuf, 2048, 1, f);
396 fseek(f, current, SEEK_SET);
398 exe_hdr = (EXE_HEADER *)mybuf;
399 if (memcmp(exe_hdr->id, "PS-X EXE", 8) == 0)
402 if (mybuf[0] == 'C' && mybuf[1] == 'P' && mybuf[2] == 'E')
405 coff_hdr = (FILHDR *)mybuf;
406 if (SWAPu16(coff_hdr->f_magic) == 0x0162)
412 int Load(const char *ExePath) {
418 u32 section_address, section_size;
421 strncpy(CdromId, "SLUS99999", 9);
422 strncpy(CdromLabel, "SLUS_999.99", 11);
424 tmpFile = fopen(ExePath, "rb");
425 if (tmpFile == NULL) {
426 SysPrintf(_("Error opening file: %s.\n"), ExePath);
429 type = PSXGetFileType(tmpFile);
432 fread(&tmpHead,sizeof(EXE_HEADER),1,tmpFile);
433 section_address = SWAP32(tmpHead.t_addr);
434 section_size = SWAP32(tmpHead.t_size);
435 mem = PSXM(section_address);
437 fseek(tmpFile, 0x800, SEEK_SET);
438 fread(mem, section_size, 1, tmpFile);
439 psxCpu->Clear(section_address, section_size / 4);
442 psxRegs.pc = SWAP32(tmpHead.pc0);
443 psxRegs.GPR.n.gp = SWAP32(tmpHead.gp0);
444 psxRegs.GPR.n.sp = SWAP32(tmpHead.s_addr);
445 if (psxRegs.GPR.n.sp == 0)
446 psxRegs.GPR.n.sp = 0x801fff00;
450 fseek(tmpFile, 6, SEEK_SET); /* Something tells me we should go to 4 and read the "08 00" here... */
452 fread(&opcode, 1, 1, tmpFile);
454 case 1: /* Section loading */
455 fread(§ion_address, 4, 1, tmpFile);
456 fread(§ion_size, 4, 1, tmpFile);
457 section_address = SWAPu32(section_address);
458 section_size = SWAPu32(section_size);
460 EMU_LOG("Loading %08X bytes from %08X to %08X\n", section_size, ftell(tmpFile), section_address);
462 mem = PSXM(section_address);
464 fread(mem, section_size, 1, tmpFile);
465 psxCpu->Clear(section_address, section_size / 4);
468 case 3: /* register loading (PC only?) */
469 fseek(tmpFile, 2, SEEK_CUR); /* unknown field */
470 fread(&psxRegs.pc, 4, 1, tmpFile);
471 psxRegs.pc = SWAPu32(psxRegs.pc);
473 case 0: /* End of file */
476 SysPrintf(_("Unknown CPE opcode %02x at position %08x.\n"), opcode, ftell(tmpFile) - 1);
480 } while (opcode != 0 && retval == 0);
483 SysPrintf(_("COFF files not supported.\n"));
487 SysPrintf(_("This file does not appear to be a valid PSX file.\n"));
495 CdromLabel[0] = '\0';
503 static const char PcsxHeader[32] = "STv4 PCSX v" PACKAGE_VERSION;
505 // Savestate Versioning!
506 // If you make changes to the savestate version, please increment the value below.
507 static const u32 SaveVersion = 0x8b410006;
509 int SaveState(const char *file) {
516 f = gzopen(file, "wb");
517 if (f == NULL) return -1;
521 gzwrite(f, (void *)PcsxHeader, 32);
522 gzwrite(f, (void *)&SaveVersion, sizeof(u32));
523 gzwrite(f, (void *)&Config.HLE, sizeof(boolean));
525 pMem = (unsigned char *)malloc(128 * 96 * 3);
526 if (pMem == NULL) return -1;
527 GPU_getScreenPic(pMem);
528 gzwrite(f, pMem, 128 * 96 * 3);
534 gzwrite(f, psxM, 0x00200000);
535 gzwrite(f, psxR, 0x00080000);
536 gzwrite(f, psxH, 0x00010000);
537 gzwrite(f, (void *)&psxRegs, sizeof(psxRegs));
540 gpufP = (GPUFreeze_t *)malloc(sizeof(GPUFreeze_t));
541 gpufP->ulFreezeVersion = 1;
542 GPU_freeze(1, gpufP);
543 gzwrite(f, gpufP, sizeof(GPUFreeze_t));
547 spufP = (SPUFreeze_t *) malloc(16);
548 SPU_freeze(2, spufP);
549 Size = spufP->Size; gzwrite(f, &Size, 4);
551 spufP = (SPUFreeze_t *) malloc(Size);
552 SPU_freeze(1, spufP);
553 gzwrite(f, spufP, Size);
564 new_dyna_after_save();
569 int LoadState(const char *file) {
578 f = gzopen(file, "rb");
579 if (f == NULL) return -1;
581 gzread(f, header, sizeof(header));
582 gzread(f, &version, sizeof(u32));
583 gzread(f, &hle, sizeof(boolean));
585 if (strncmp("STv4 PCSX", header, 9) != 0 || version != SaveVersion) {
595 gzseek(f, 128 * 96 * 3, SEEK_CUR);
597 gzread(f, psxM, 0x00200000);
598 gzread(f, psxR, 0x00080000);
599 gzread(f, psxH, 0x00010000);
600 gzread(f, (void *)&psxRegs, sizeof(psxRegs));
606 gpufP = (GPUFreeze_t *)malloc(sizeof(GPUFreeze_t));
607 gzread(f, gpufP, sizeof(GPUFreeze_t));
608 GPU_freeze(0, gpufP);
610 if (HW_GPU_STATUS == 0)
611 HW_GPU_STATUS = GPU_readStatus();
615 spufP = (SPUFreeze_t *)malloc(Size);
616 gzread(f, spufP, Size);
617 SPU_freeze(0, spufP);
632 int CheckState(const char *file) {
638 f = gzopen(file, "rb");
639 if (f == NULL) return -1;
641 gzread(f, header, sizeof(header));
642 gzread(f, &version, sizeof(u32));
643 gzread(f, &hle, sizeof(boolean));
647 if (strncmp("STv4 PCSX", header, 9) != 0 || version != SaveVersion)
653 // NET Function Helpers
656 if (NET_recvData == NULL || NET_sendData == NULL)
659 NET_sendData(&Config.Xa, sizeof(Config.Xa), PSE_NET_BLOCKING);
660 NET_sendData(&Config.Sio, sizeof(Config.Sio), PSE_NET_BLOCKING);
661 NET_sendData(&Config.SpuIrq, sizeof(Config.SpuIrq), PSE_NET_BLOCKING);
662 NET_sendData(&Config.RCntFix, sizeof(Config.RCntFix), PSE_NET_BLOCKING);
663 NET_sendData(&Config.PsxType, sizeof(Config.PsxType), PSE_NET_BLOCKING);
664 NET_sendData(&Config.Cpu, sizeof(Config.Cpu), PSE_NET_BLOCKING);
672 if (NET_recvData == NULL || NET_sendData == NULL)
675 NET_recvData(&Config.Xa, sizeof(Config.Xa), PSE_NET_BLOCKING);
676 NET_recvData(&Config.Sio, sizeof(Config.Sio), PSE_NET_BLOCKING);
677 NET_recvData(&Config.SpuIrq, sizeof(Config.SpuIrq), PSE_NET_BLOCKING);
678 NET_recvData(&Config.RCntFix, sizeof(Config.RCntFix), PSE_NET_BLOCKING);
679 NET_recvData(&Config.PsxType, sizeof(Config.PsxType), PSE_NET_BLOCKING);
684 NET_recvData(&Config.Cpu, sizeof(Config.Cpu), PSE_NET_BLOCKING);
685 if (tmp != Config.Cpu) {
688 if (Config.Cpu == CPU_INTERPRETER) psxCpu = &psxInt;
689 else psxCpu = &psxRec;
693 if (psxCpu->Init() == -1) {
694 SysClose(); return -1;
702 // remove the leading and trailing spaces in a string
703 void trim(char *str) {
707 // skip leading blanks
708 while (str[pos] <= ' ' && str[pos] > 0)
712 *(dest++) = str[pos];
716 *(dest--) = '\0'; // store the null
718 // remove trailing blanks
719 while (dest >= str && *dest <= ' ' && *dest > 0)
723 // lookup table for crc calculation
724 static unsigned short crctab[256] = {
725 0x0000, 0x1021, 0x2042, 0x3063, 0x4084, 0x50A5, 0x60C6, 0x70E7, 0x8108,
726 0x9129, 0xA14A, 0xB16B, 0xC18C, 0xD1AD, 0xE1CE, 0xF1EF, 0x1231, 0x0210,
727 0x3273, 0x2252, 0x52B5, 0x4294, 0x72F7, 0x62D6, 0x9339, 0x8318, 0xB37B,
728 0xA35A, 0xD3BD, 0xC39C, 0xF3FF, 0xE3DE, 0x2462, 0x3443, 0x0420, 0x1401,
729 0x64E6, 0x74C7, 0x44A4, 0x5485, 0xA56A, 0xB54B, 0x8528, 0x9509, 0xE5EE,
730 0xF5CF, 0xC5AC, 0xD58D, 0x3653, 0x2672, 0x1611, 0x0630, 0x76D7, 0x66F6,
731 0x5695, 0x46B4, 0xB75B, 0xA77A, 0x9719, 0x8738, 0xF7DF, 0xE7FE, 0xD79D,
732 0xC7BC, 0x48C4, 0x58E5, 0x6886, 0x78A7, 0x0840, 0x1861, 0x2802, 0x3823,
733 0xC9CC, 0xD9ED, 0xE98E, 0xF9AF, 0x8948, 0x9969, 0xA90A, 0xB92B, 0x5AF5,
734 0x4AD4, 0x7AB7, 0x6A96, 0x1A71, 0x0A50, 0x3A33, 0x2A12, 0xDBFD, 0xCBDC,
735 0xFBBF, 0xEB9E, 0x9B79, 0x8B58, 0xBB3B, 0xAB1A, 0x6CA6, 0x7C87, 0x4CE4,
736 0x5CC5, 0x2C22, 0x3C03, 0x0C60, 0x1C41, 0xEDAE, 0xFD8F, 0xCDEC, 0xDDCD,
737 0xAD2A, 0xBD0B, 0x8D68, 0x9D49, 0x7E97, 0x6EB6, 0x5ED5, 0x4EF4, 0x3E13,
738 0x2E32, 0x1E51, 0x0E70, 0xFF9F, 0xEFBE, 0xDFDD, 0xCFFC, 0xBF1B, 0xAF3A,
739 0x9F59, 0x8F78, 0x9188, 0x81A9, 0xB1CA, 0xA1EB, 0xD10C, 0xC12D, 0xF14E,
740 0xE16F, 0x1080, 0x00A1, 0x30C2, 0x20E3, 0x5004, 0x4025, 0x7046, 0x6067,
741 0x83B9, 0x9398, 0xA3FB, 0xB3DA, 0xC33D, 0xD31C, 0xE37F, 0xF35E, 0x02B1,
742 0x1290, 0x22F3, 0x32D2, 0x4235, 0x5214, 0x6277, 0x7256, 0xB5EA, 0xA5CB,
743 0x95A8, 0x8589, 0xF56E, 0xE54F, 0xD52C, 0xC50D, 0x34E2, 0x24C3, 0x14A0,
744 0x0481, 0x7466, 0x6447, 0x5424, 0x4405, 0xA7DB, 0xB7FA, 0x8799, 0x97B8,
745 0xE75F, 0xF77E, 0xC71D, 0xD73C, 0x26D3, 0x36F2, 0x0691, 0x16B0, 0x6657,
746 0x7676, 0x4615, 0x5634, 0xD94C, 0xC96D, 0xF90E, 0xE92F, 0x99C8, 0x89E9,
747 0xB98A, 0xA9AB, 0x5844, 0x4865, 0x7806, 0x6827, 0x18C0, 0x08E1, 0x3882,
748 0x28A3, 0xCB7D, 0xDB5C, 0xEB3F, 0xFB1E, 0x8BF9, 0x9BD8, 0xABBB, 0xBB9A,
749 0x4A75, 0x5A54, 0x6A37, 0x7A16, 0x0AF1, 0x1AD0, 0x2AB3, 0x3A92, 0xFD2E,
750 0xED0F, 0xDD6C, 0xCD4D, 0xBDAA, 0xAD8B, 0x9DE8, 0x8DC9, 0x7C26, 0x6C07,
751 0x5C64, 0x4C45, 0x3CA2, 0x2C83, 0x1CE0, 0x0CC1, 0xEF1F, 0xFF3E, 0xCF5D,
752 0xDF7C, 0xAF9B, 0xBFBA, 0x8FD9, 0x9FF8, 0x6E17, 0x7E36, 0x4E55, 0x5E74,
753 0x2E93, 0x3EB2, 0x0ED1, 0x1EF0
756 u16 calcCrc(u8 *d, int len) {
760 for (i = 0; i < len; i++) {
761 crc = crctab[(crc >> 8) ^ d[i]] ^ (crc << 8);