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 // temporary pandora workaround..
414 size_t fread_to_ram(void *ptr, size_t size, size_t nmemb, FILE *stream)
419 tmp = malloc(size * nmemb);
421 ret = fread(tmp, size, nmemb, stream);
422 memcpy(ptr, tmp, size * nmemb);
428 int Load(const char *ExePath) {
434 u32 section_address, section_size;
437 strncpy(CdromId, "SLUS99999", 9);
438 strncpy(CdromLabel, "SLUS_999.99", 11);
440 tmpFile = fopen(ExePath, "rb");
441 if (tmpFile == NULL) {
442 SysPrintf(_("Error opening file: %s.\n"), ExePath);
445 type = PSXGetFileType(tmpFile);
448 fread(&tmpHead,sizeof(EXE_HEADER),1,tmpFile);
449 section_address = SWAP32(tmpHead.t_addr);
450 section_size = SWAP32(tmpHead.t_size);
451 mem = PSXM(section_address);
453 fseek(tmpFile, 0x800, SEEK_SET);
454 fread_to_ram(mem, section_size, 1, tmpFile);
455 psxCpu->Clear(section_address, section_size / 4);
458 psxRegs.pc = SWAP32(tmpHead.pc0);
459 psxRegs.GPR.n.gp = SWAP32(tmpHead.gp0);
460 psxRegs.GPR.n.sp = SWAP32(tmpHead.s_addr);
461 if (psxRegs.GPR.n.sp == 0)
462 psxRegs.GPR.n.sp = 0x801fff00;
466 fseek(tmpFile, 6, SEEK_SET); /* Something tells me we should go to 4 and read the "08 00" here... */
468 fread(&opcode, 1, 1, tmpFile);
470 case 1: /* Section loading */
471 fread(§ion_address, 4, 1, tmpFile);
472 fread(§ion_size, 4, 1, tmpFile);
473 section_address = SWAPu32(section_address);
474 section_size = SWAPu32(section_size);
476 EMU_LOG("Loading %08X bytes from %08X to %08X\n", section_size, ftell(tmpFile), section_address);
478 mem = PSXM(section_address);
480 fread_to_ram(mem, section_size, 1, tmpFile);
481 psxCpu->Clear(section_address, section_size / 4);
484 case 3: /* register loading (PC only?) */
485 fseek(tmpFile, 2, SEEK_CUR); /* unknown field */
486 fread(&psxRegs.pc, 4, 1, tmpFile);
487 psxRegs.pc = SWAPu32(psxRegs.pc);
489 case 0: /* End of file */
492 SysPrintf(_("Unknown CPE opcode %02x at position %08x.\n"), opcode, ftell(tmpFile) - 1);
496 } while (opcode != 0 && retval == 0);
499 SysPrintf(_("COFF files not supported.\n"));
503 SysPrintf(_("This file does not appear to be a valid PSX file.\n"));
511 CdromLabel[0] = '\0';
519 static const char PcsxHeader[32] = "STv4 PCSX v" PACKAGE_VERSION;
521 // Savestate Versioning!
522 // If you make changes to the savestate version, please increment the value below.
523 static const u32 SaveVersion = 0x8b410006;
525 int SaveState(const char *file) {
532 f = gzopen(file, "wb");
533 if (f == NULL) return -1;
537 gzwrite(f, (void *)PcsxHeader, 32);
538 gzwrite(f, (void *)&SaveVersion, sizeof(u32));
539 gzwrite(f, (void *)&Config.HLE, sizeof(boolean));
541 pMem = (unsigned char *)malloc(128 * 96 * 3);
542 if (pMem == NULL) return -1;
543 GPU_getScreenPic(pMem);
544 gzwrite(f, pMem, 128 * 96 * 3);
550 gzwrite(f, psxM, 0x00200000);
551 gzwrite(f, psxR, 0x00080000);
552 gzwrite(f, psxH, 0x00010000);
553 gzwrite(f, (void *)&psxRegs, sizeof(psxRegs));
556 gpufP = (GPUFreeze_t *)malloc(sizeof(GPUFreeze_t));
557 gpufP->ulFreezeVersion = 1;
558 GPU_freeze(1, gpufP);
559 gzwrite(f, gpufP, sizeof(GPUFreeze_t));
563 spufP = (SPUFreeze_t *) malloc(16);
564 SPU_freeze(2, spufP);
565 Size = spufP->Size; gzwrite(f, &Size, 4);
567 spufP = (SPUFreeze_t *) malloc(Size);
568 SPU_freeze(1, spufP);
569 gzwrite(f, spufP, Size);
580 new_dyna_after_save();
585 int LoadState(const char *file) {
594 f = gzopen(file, "rb");
595 if (f == NULL) return -1;
597 gzread(f, header, sizeof(header));
598 gzread(f, &version, sizeof(u32));
599 gzread(f, &hle, sizeof(boolean));
601 if (strncmp("STv4 PCSX", header, 9) != 0 || version != SaveVersion) {
611 gzseek(f, 128 * 96 * 3, SEEK_CUR);
613 gzread(f, psxM, 0x00200000);
614 gzread(f, psxR, 0x00080000);
615 gzread(f, psxH, 0x00010000);
616 gzread(f, (void *)&psxRegs, sizeof(psxRegs));
622 gpufP = (GPUFreeze_t *)malloc(sizeof(GPUFreeze_t));
623 gzread(f, gpufP, sizeof(GPUFreeze_t));
624 GPU_freeze(0, gpufP);
626 if (HW_GPU_STATUS == 0)
627 HW_GPU_STATUS = GPU_readStatus();
631 spufP = (SPUFreeze_t *)malloc(Size);
632 gzread(f, spufP, Size);
633 SPU_freeze(0, spufP);
648 int CheckState(const char *file) {
654 f = gzopen(file, "rb");
655 if (f == NULL) return -1;
657 gzread(f, header, sizeof(header));
658 gzread(f, &version, sizeof(u32));
659 gzread(f, &hle, sizeof(boolean));
663 if (strncmp("STv4 PCSX", header, 9) != 0 || version != SaveVersion)
669 // NET Function Helpers
672 if (NET_recvData == NULL || NET_sendData == NULL)
675 NET_sendData(&Config.Xa, sizeof(Config.Xa), PSE_NET_BLOCKING);
676 NET_sendData(&Config.Sio, sizeof(Config.Sio), PSE_NET_BLOCKING);
677 NET_sendData(&Config.SpuIrq, sizeof(Config.SpuIrq), PSE_NET_BLOCKING);
678 NET_sendData(&Config.RCntFix, sizeof(Config.RCntFix), PSE_NET_BLOCKING);
679 NET_sendData(&Config.PsxType, sizeof(Config.PsxType), PSE_NET_BLOCKING);
680 NET_sendData(&Config.Cpu, sizeof(Config.Cpu), PSE_NET_BLOCKING);
688 if (NET_recvData == NULL || NET_sendData == NULL)
691 NET_recvData(&Config.Xa, sizeof(Config.Xa), PSE_NET_BLOCKING);
692 NET_recvData(&Config.Sio, sizeof(Config.Sio), PSE_NET_BLOCKING);
693 NET_recvData(&Config.SpuIrq, sizeof(Config.SpuIrq), PSE_NET_BLOCKING);
694 NET_recvData(&Config.RCntFix, sizeof(Config.RCntFix), PSE_NET_BLOCKING);
695 NET_recvData(&Config.PsxType, sizeof(Config.PsxType), PSE_NET_BLOCKING);
700 NET_recvData(&Config.Cpu, sizeof(Config.Cpu), PSE_NET_BLOCKING);
701 if (tmp != Config.Cpu) {
704 if (Config.Cpu == CPU_INTERPRETER) psxCpu = &psxInt;
705 else psxCpu = &psxRec;
709 if (psxCpu->Init() == -1) {
710 SysClose(); return -1;
718 // remove the leading and trailing spaces in a string
719 void trim(char *str) {
723 // skip leading blanks
724 while (str[pos] <= ' ' && str[pos] > 0)
728 *(dest++) = str[pos];
732 *(dest--) = '\0'; // store the null
734 // remove trailing blanks
735 while (dest >= str && *dest <= ' ' && *dest > 0)
739 // lookup table for crc calculation
740 static unsigned short crctab[256] = {
741 0x0000, 0x1021, 0x2042, 0x3063, 0x4084, 0x50A5, 0x60C6, 0x70E7, 0x8108,
742 0x9129, 0xA14A, 0xB16B, 0xC18C, 0xD1AD, 0xE1CE, 0xF1EF, 0x1231, 0x0210,
743 0x3273, 0x2252, 0x52B5, 0x4294, 0x72F7, 0x62D6, 0x9339, 0x8318, 0xB37B,
744 0xA35A, 0xD3BD, 0xC39C, 0xF3FF, 0xE3DE, 0x2462, 0x3443, 0x0420, 0x1401,
745 0x64E6, 0x74C7, 0x44A4, 0x5485, 0xA56A, 0xB54B, 0x8528, 0x9509, 0xE5EE,
746 0xF5CF, 0xC5AC, 0xD58D, 0x3653, 0x2672, 0x1611, 0x0630, 0x76D7, 0x66F6,
747 0x5695, 0x46B4, 0xB75B, 0xA77A, 0x9719, 0x8738, 0xF7DF, 0xE7FE, 0xD79D,
748 0xC7BC, 0x48C4, 0x58E5, 0x6886, 0x78A7, 0x0840, 0x1861, 0x2802, 0x3823,
749 0xC9CC, 0xD9ED, 0xE98E, 0xF9AF, 0x8948, 0x9969, 0xA90A, 0xB92B, 0x5AF5,
750 0x4AD4, 0x7AB7, 0x6A96, 0x1A71, 0x0A50, 0x3A33, 0x2A12, 0xDBFD, 0xCBDC,
751 0xFBBF, 0xEB9E, 0x9B79, 0x8B58, 0xBB3B, 0xAB1A, 0x6CA6, 0x7C87, 0x4CE4,
752 0x5CC5, 0x2C22, 0x3C03, 0x0C60, 0x1C41, 0xEDAE, 0xFD8F, 0xCDEC, 0xDDCD,
753 0xAD2A, 0xBD0B, 0x8D68, 0x9D49, 0x7E97, 0x6EB6, 0x5ED5, 0x4EF4, 0x3E13,
754 0x2E32, 0x1E51, 0x0E70, 0xFF9F, 0xEFBE, 0xDFDD, 0xCFFC, 0xBF1B, 0xAF3A,
755 0x9F59, 0x8F78, 0x9188, 0x81A9, 0xB1CA, 0xA1EB, 0xD10C, 0xC12D, 0xF14E,
756 0xE16F, 0x1080, 0x00A1, 0x30C2, 0x20E3, 0x5004, 0x4025, 0x7046, 0x6067,
757 0x83B9, 0x9398, 0xA3FB, 0xB3DA, 0xC33D, 0xD31C, 0xE37F, 0xF35E, 0x02B1,
758 0x1290, 0x22F3, 0x32D2, 0x4235, 0x5214, 0x6277, 0x7256, 0xB5EA, 0xA5CB,
759 0x95A8, 0x8589, 0xF56E, 0xE54F, 0xD52C, 0xC50D, 0x34E2, 0x24C3, 0x14A0,
760 0x0481, 0x7466, 0x6447, 0x5424, 0x4405, 0xA7DB, 0xB7FA, 0x8799, 0x97B8,
761 0xE75F, 0xF77E, 0xC71D, 0xD73C, 0x26D3, 0x36F2, 0x0691, 0x16B0, 0x6657,
762 0x7676, 0x4615, 0x5634, 0xD94C, 0xC96D, 0xF90E, 0xE92F, 0x99C8, 0x89E9,
763 0xB98A, 0xA9AB, 0x5844, 0x4865, 0x7806, 0x6827, 0x18C0, 0x08E1, 0x3882,
764 0x28A3, 0xCB7D, 0xDB5C, 0xEB3F, 0xFB1E, 0x8BF9, 0x9BD8, 0xABBB, 0xBB9A,
765 0x4A75, 0x5A54, 0x6A37, 0x7A16, 0x0AF1, 0x1AD0, 0x2AB3, 0x3A92, 0xFD2E,
766 0xED0F, 0xDD6C, 0xCD4D, 0xBDAA, 0xAD8B, 0x9DE8, 0x8DC9, 0x7C26, 0x6C07,
767 0x5C64, 0x4C45, 0x3CA2, 0x2C83, 0x1CE0, 0x0CC1, 0xEF1F, 0xFF3E, 0xCF5D,
768 0xDF7C, 0xAF9B, 0xBFBA, 0x8FD9, 0x9FF8, 0x6E17, 0x7E36, 0x4E55, 0x5E74,
769 0x2E93, 0x3EB2, 0x0ED1, 0x1EF0
772 u16 calcCrc(u8 *d, int len) {
776 for (i = 0; i < len; i++) {
777 crc = crctab[(crc >> 8) ^ d[i]] ^ (crc << 8);