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.
31 char CdromId[10] = "";
32 char CdromLabel[33] = "";
34 // PSX Executable types
40 #define ISODCL(from, to) (to - from + 1)
42 struct iso_directory_record {
43 char length [ISODCL (1, 1)]; /* 711 */
44 char ext_attr_length [ISODCL (2, 2)]; /* 711 */
45 char extent [ISODCL (3, 10)]; /* 733 */
46 char size [ISODCL (11, 18)]; /* 733 */
47 char date [ISODCL (19, 25)]; /* 7 by 711 */
48 char flags [ISODCL (26, 26)];
49 char file_unit_size [ISODCL (27, 27)]; /* 711 */
50 char interleave [ISODCL (28, 28)]; /* 711 */
51 char volume_sequence_number [ISODCL (29, 32)]; /* 723 */
52 unsigned char name_len [ISODCL (33, 33)]; /* 711 */
56 void mmssdd( char *b, char *p )
60 unsigned char *u = (void *)b;
61 int block = (u[3] << 24) | (u[2] << 16) | (u[1] << 8) | u[0];
62 #elif defined(__BIGENDIAN__)
63 int block = (b[0] & 0xff) | ((b[1] & 0xff) << 8) | ((b[2] & 0xff) << 16) | (b[3] << 24);
65 int block = *((int*)b);
69 m = block / 4500; // minutes
70 block = block - m * 4500; // minutes rest
71 s = block / 75; // seconds
72 d = block - s * 75; // seconds rest
74 m = ((m / 10) << 4) | m % 10;
75 s = ((s / 10) << 4) | s % 10;
76 d = ((d / 10) << 4) | d % 10;
84 time[0] = btoi(time[0]); time[1] = btoi(time[1]); time[2] = btoi(time[2]); \
89 if (time[1] == 60) { \
94 time[0] = itob(time[0]); time[1] = itob(time[1]); time[2] = itob(time[2]);
97 if (CDR_readTrack(time) == -1) return -1; \
98 buf = (void *)CDR_getBuffer(); \
99 if (buf == NULL) return -1; \
100 else CheckPPFCache((u8 *)buf, time[0], time[1], time[2]);
102 #define READDIR(_dir) \
104 memcpy(_dir, buf + 12, 2048); \
108 memcpy(_dir + 2048, buf + 12, 2048);
110 int GetCdromFile(u8 *mdir, u8 *time, char *filename) {
111 struct iso_directory_record *dir;
116 // only try to scan if a filename is given
117 if (!strlen(filename)) return -1;
121 dir = (struct iso_directory_record*) &mdir[i];
122 if (dir->length[0] == 0) {
127 if (dir->flags[0] & 0x2) { // it's a dir
128 if (!strnicmp((char *)&dir->name[0], filename, dir->name_len[0])) {
129 if (filename[dir->name_len[0]] != '\\') continue;
131 filename += dir->name_len[0] + 1;
133 mmssdd(dir->extent, (char *)time);
139 if (!strnicmp((char *)&dir->name[0], filename, strlen(filename))) {
140 mmssdd(dir->extent, (char *)time);
148 static const unsigned int gpu_ctl_def[] = {
149 0x00000000, 0x01000000, 0x03000000, 0x04000000,
150 0x05000800, 0x06c60260, 0x0703fc10, 0x08000027,
153 static const unsigned int gpu_data_def[] = {
154 0xe100360b, 0xe2000000, 0xe3000800, 0xe4077e7f,
155 0xe5001000, 0xe6000000,
156 0x02000000, 0x00000000, 0x01ff03ff,
159 static void fake_bios_gpu_setup(void)
163 for (i = 0; i < sizeof(gpu_ctl_def) / sizeof(gpu_ctl_def[0]); i++)
164 GPU_writeStatus(gpu_ctl_def[i]);
166 for (i = 0; i < sizeof(gpu_data_def) / sizeof(gpu_data_def[0]); i++)
167 GPU_writeData(gpu_data_def[i]);
172 struct iso_directory_record *dir;
177 // not the best place to do it, but since BIOS boot logo killer
178 // is just below, do it here
179 fake_bios_gpu_setup();
183 psxRegs.pc = psxRegs.GPR.n.ra;
187 time[0] = itob(0); time[1] = itob(2); time[2] = itob(0x10);
191 // skip head and sub, and go to the root directory record
192 dir = (struct iso_directory_record*) &buf[12+156];
194 mmssdd(dir->extent, (char*)time);
198 // Load SYSTEM.CNF and scan for the main executable
199 if (GetCdromFile(mdir, time, "SYSTEM.CNF;1") == -1) {
200 // if SYSTEM.CNF is missing, start an existing PSX.EXE
201 if (GetCdromFile(mdir, time, "PSX.EXE;1") == -1) return -1;
206 // read the SYSTEM.CNF
209 sscanf((char *)buf + 12, "BOOT = cdrom:\\%256s", exename);
210 if (GetCdromFile(mdir, time, exename) == -1) {
211 sscanf((char *)buf + 12, "BOOT = cdrom:%256s", exename);
212 if (GetCdromFile(mdir, time, exename) == -1) {
213 char *ptr = strstr((char *)buf + 12, "cdrom:");
216 while (*ptr == '\\' || *ptr == '/') ptr++;
217 strncpy(exename, ptr, 255);
220 while (*ptr != '\0' && *ptr != '\r' && *ptr != '\n') ptr++;
222 if (GetCdromFile(mdir, time, exename) == -1)
229 // Read the EXE-Header
233 memcpy(&tmpHead, buf + 12, sizeof(EXE_HEADER));
235 psxRegs.pc = SWAP32(tmpHead.pc0);
236 psxRegs.GPR.n.gp = SWAP32(tmpHead.gp0);
237 psxRegs.GPR.n.sp = SWAP32(tmpHead.s_addr);
238 if (psxRegs.GPR.n.sp == 0) psxRegs.GPR.n.sp = 0x801fff00;
240 tmpHead.t_size = SWAP32(tmpHead.t_size);
241 tmpHead.t_addr = SWAP32(tmpHead.t_addr);
243 psxCpu->Clear(tmpHead.t_addr, tmpHead.t_size / 4);
245 // Read the rest of the main executable
246 while (tmpHead.t_size & ~2047) {
247 void *ptr = (void *)PSXM(tmpHead.t_addr);
252 if (ptr != NULL) memcpy(ptr, buf+12, 2048);
254 tmpHead.t_size -= 2048;
255 tmpHead.t_addr += 2048;
261 int LoadCdromFile(const char *filename, EXE_HEADER *head) {
262 struct iso_directory_record *dir;
269 sscanf(filename, "cdrom:\\%256s", exename);
271 time[0] = itob(0); time[1] = itob(2); time[2] = itob(0x10);
275 // skip head and sub, and go to the root directory record
276 dir = (struct iso_directory_record *)&buf[12 + 156];
278 mmssdd(dir->extent, (char*)time);
282 if (GetCdromFile(mdir, time, exename) == -1) return -1;
286 memcpy(head, buf + 12, sizeof(EXE_HEADER));
290 psxCpu->Clear(addr, size / 4);
292 while (size & ~2047) {
298 memcpy(mem, buf + 12, 2048);
308 struct iso_directory_record *dir;
309 unsigned char time[4];
311 unsigned char mdir[4096];
319 time[2] = itob(0x10);
323 CdromLabel[0] = '\0';
326 strncpy(CdromLabel, buf + 52, 32);
328 // skip head and sub, and go to the root directory record
329 dir = (struct iso_directory_record *)&buf[12 + 156];
331 mmssdd(dir->extent, (char *)time);
335 if (GetCdromFile(mdir, time, "SYSTEM.CNF;1") != -1) {
338 sscanf(buf + 12, "BOOT = cdrom:\\%256s", exename);
339 if (GetCdromFile(mdir, time, exename) == -1) {
340 sscanf(buf + 12, "BOOT = cdrom:%256s", exename);
341 if (GetCdromFile(mdir, time, exename) == -1) {
342 char *ptr = strstr(buf + 12, "cdrom:"); // possibly the executable is in some subdir
345 while (*ptr == '\\' || *ptr == '/') ptr++;
346 strncpy(exename, ptr, 255);
349 while (*ptr != '\0' && *ptr != '\r' && *ptr != '\n') ptr++;
351 if (GetCdromFile(mdir, time, exename) == -1)
352 return -1; // main executable not found
357 } else if (GetCdromFile(mdir, time, "PSX.EXE;1") != -1) {
358 strcpy(exename, "PSX.EXE;1");
359 strcpy(CdromId, "SLUS99999");
361 return -1; // SYSTEM.CNF and PSX.EXE not found
363 if (CdromId[0] == '\0') {
366 if (exename[i - 2] == ';') i-= 2;
368 while (i >= 0 && c >= 0) {
369 if (isalnum(exename[i])) CdromId[c--] = exename[i];
375 if (CdromId[0] == '\0')
376 strcpy(CdromId, "SLUS99999");
378 if (Config.PsxAuto) { // autodetect system (pal or ntsc)
379 if (CdromId[2] == 'e' || CdromId[2] == 'E')
380 Config.PsxType = PSX_TYPE_PAL; // pal
381 else Config.PsxType = PSX_TYPE_NTSC; // ntsc
384 if (CdromLabel[0] == ' ') {
385 strncpy(CdromLabel, CdromId, 9);
387 SysPrintf(_("CD-ROM Label: %.32s\n"), CdromLabel);
388 SysPrintf(_("CD-ROM ID: %.9s\n"), CdromId);
395 static int PSXGetFileType(FILE *f) {
396 unsigned long current;
402 fseek(f, 0L, SEEK_SET);
403 fread(mybuf, 2048, 1, f);
404 fseek(f, current, SEEK_SET);
406 exe_hdr = (EXE_HEADER *)mybuf;
407 if (memcmp(exe_hdr->id, "PS-X EXE", 8) == 0)
410 if (mybuf[0] == 'C' && mybuf[1] == 'P' && mybuf[2] == 'E')
413 coff_hdr = (FILHDR *)mybuf;
414 if (SWAPu16(coff_hdr->f_magic) == 0x0162)
420 // temporary pandora workaround..
422 size_t fread_to_ram(void *ptr, size_t size, size_t nmemb, FILE *stream)
427 tmp = malloc(size * nmemb);
429 ret = fread(tmp, size, nmemb, stream);
430 memcpy(ptr, tmp, size * nmemb);
436 int Load(const char *ExePath) {
442 u32 section_address, section_size;
445 strncpy(CdromId, "SLUS99999", 9);
446 strncpy(CdromLabel, "SLUS_999.99", 11);
448 tmpFile = fopen(ExePath, "rb");
449 if (tmpFile == NULL) {
450 SysPrintf(_("Error opening file: %s.\n"), ExePath);
453 type = PSXGetFileType(tmpFile);
456 fread(&tmpHead,sizeof(EXE_HEADER),1,tmpFile);
457 section_address = SWAP32(tmpHead.t_addr);
458 section_size = SWAP32(tmpHead.t_size);
459 mem = PSXM(section_address);
461 fseek(tmpFile, 0x800, SEEK_SET);
462 fread_to_ram(mem, section_size, 1, tmpFile);
463 psxCpu->Clear(section_address, section_size / 4);
466 psxRegs.pc = SWAP32(tmpHead.pc0);
467 psxRegs.GPR.n.gp = SWAP32(tmpHead.gp0);
468 psxRegs.GPR.n.sp = SWAP32(tmpHead.s_addr);
469 if (psxRegs.GPR.n.sp == 0)
470 psxRegs.GPR.n.sp = 0x801fff00;
474 fseek(tmpFile, 6, SEEK_SET); /* Something tells me we should go to 4 and read the "08 00" here... */
476 fread(&opcode, 1, 1, tmpFile);
478 case 1: /* Section loading */
479 fread(§ion_address, 4, 1, tmpFile);
480 fread(§ion_size, 4, 1, tmpFile);
481 section_address = SWAPu32(section_address);
482 section_size = SWAPu32(section_size);
484 EMU_LOG("Loading %08X bytes from %08X to %08X\n", section_size, ftell(tmpFile), section_address);
486 mem = PSXM(section_address);
488 fread_to_ram(mem, section_size, 1, tmpFile);
489 psxCpu->Clear(section_address, section_size / 4);
492 case 3: /* register loading (PC only?) */
493 fseek(tmpFile, 2, SEEK_CUR); /* unknown field */
494 fread(&psxRegs.pc, 4, 1, tmpFile);
495 psxRegs.pc = SWAPu32(psxRegs.pc);
497 case 0: /* End of file */
500 SysPrintf(_("Unknown CPE opcode %02x at position %08x.\n"), opcode, ftell(tmpFile) - 1);
504 } while (opcode != 0 && retval == 0);
507 SysPrintf(_("COFF files not supported.\n"));
511 SysPrintf(_("This file does not appear to be a valid PSX EXE file.\n"));
512 SysPrintf(_("(did you forget -cdfile ?)\n"));
520 CdromLabel[0] = '\0';
528 static void *zlib_open(const char *name, const char *mode)
530 return gzopen(name, mode);
533 static int zlib_read(void *file, void *buf, u32 len)
535 return gzread(file, buf, len);
538 static int zlib_write(void *file, const void *buf, u32 len)
540 return gzwrite(file, buf, len);
543 static long zlib_seek(void *file, long offs, int whence)
545 return gzseek(file, offs, whence);
548 static void zlib_close(void *file)
553 struct PcsxSaveFuncs SaveFuncs = {
554 zlib_open, zlib_read, zlib_write, zlib_seek, zlib_close
557 static const char PcsxHeader[32] = "STv4 PCSX v" PACKAGE_VERSION;
559 // Savestate Versioning!
560 // If you make changes to the savestate version, please increment the value below.
561 static const u32 SaveVersion = 0x8b410006;
563 int SaveState(const char *file) {
570 f = SaveFuncs.open(file, "wb");
571 if (f == NULL) return -1;
575 SaveFuncs.write(f, (void *)PcsxHeader, 32);
576 SaveFuncs.write(f, (void *)&SaveVersion, sizeof(u32));
577 SaveFuncs.write(f, (void *)&Config.HLE, sizeof(boolean));
579 pMem = (unsigned char *)malloc(128 * 96 * 3);
580 if (pMem == NULL) return -1;
581 GPU_getScreenPic(pMem);
582 SaveFuncs.write(f, pMem, 128 * 96 * 3);
588 SaveFuncs.write(f, psxM, 0x00200000);
589 SaveFuncs.write(f, psxR, 0x00080000);
590 SaveFuncs.write(f, psxH, 0x00010000);
591 SaveFuncs.write(f, (void *)&psxRegs, sizeof(psxRegs));
594 gpufP = (GPUFreeze_t *)malloc(sizeof(GPUFreeze_t));
595 gpufP->ulFreezeVersion = 1;
596 GPU_freeze(1, gpufP);
597 SaveFuncs.write(f, gpufP, sizeof(GPUFreeze_t));
601 spufP = (SPUFreeze_t *) malloc(16);
602 SPU_freeze(2, spufP);
603 Size = spufP->Size; SaveFuncs.write(f, &Size, 4);
605 spufP = (SPUFreeze_t *) malloc(Size);
606 SPU_freeze(1, spufP);
607 SaveFuncs.write(f, spufP, Size);
618 new_dyna_after_save();
623 int LoadState(const char *file) {
632 f = SaveFuncs.open(file, "rb");
633 if (f == NULL) return -1;
635 SaveFuncs.read(f, header, sizeof(header));
636 SaveFuncs.read(f, &version, sizeof(u32));
637 SaveFuncs.read(f, &hle, sizeof(boolean));
639 if (strncmp("STv4 PCSX", header, 9) != 0 || version != SaveVersion) {
649 SaveFuncs.seek(f, 128 * 96 * 3, SEEK_CUR);
651 SaveFuncs.read(f, psxM, 0x00200000);
652 SaveFuncs.read(f, psxR, 0x00080000);
653 SaveFuncs.read(f, psxH, 0x00010000);
654 SaveFuncs.read(f, (void *)&psxRegs, sizeof(psxRegs));
660 gpufP = (GPUFreeze_t *)malloc(sizeof(GPUFreeze_t));
661 SaveFuncs.read(f, gpufP, sizeof(GPUFreeze_t));
662 GPU_freeze(0, gpufP);
664 if (HW_GPU_STATUS == 0)
665 HW_GPU_STATUS = GPU_readStatus();
668 SaveFuncs.read(f, &Size, 4);
669 spufP = (SPUFreeze_t *)malloc(Size);
670 SaveFuncs.read(f, spufP, Size);
671 SPU_freeze(0, spufP);
686 int CheckState(const char *file) {
692 f = SaveFuncs.open(file, "rb");
693 if (f == NULL) return -1;
695 SaveFuncs.read(f, header, sizeof(header));
696 SaveFuncs.read(f, &version, sizeof(u32));
697 SaveFuncs.read(f, &hle, sizeof(boolean));
701 if (strncmp("STv4 PCSX", header, 9) != 0 || version != SaveVersion)
707 // NET Function Helpers
710 if (NET_recvData == NULL || NET_sendData == NULL)
713 NET_sendData(&Config.Xa, sizeof(Config.Xa), PSE_NET_BLOCKING);
714 NET_sendData(&Config.Sio, sizeof(Config.Sio), PSE_NET_BLOCKING);
715 NET_sendData(&Config.SpuIrq, sizeof(Config.SpuIrq), PSE_NET_BLOCKING);
716 NET_sendData(&Config.RCntFix, sizeof(Config.RCntFix), PSE_NET_BLOCKING);
717 NET_sendData(&Config.PsxType, sizeof(Config.PsxType), PSE_NET_BLOCKING);
718 NET_sendData(&Config.Cpu, sizeof(Config.Cpu), PSE_NET_BLOCKING);
726 if (NET_recvData == NULL || NET_sendData == NULL)
729 NET_recvData(&Config.Xa, sizeof(Config.Xa), PSE_NET_BLOCKING);
730 NET_recvData(&Config.Sio, sizeof(Config.Sio), PSE_NET_BLOCKING);
731 NET_recvData(&Config.SpuIrq, sizeof(Config.SpuIrq), PSE_NET_BLOCKING);
732 NET_recvData(&Config.RCntFix, sizeof(Config.RCntFix), PSE_NET_BLOCKING);
733 NET_recvData(&Config.PsxType, sizeof(Config.PsxType), PSE_NET_BLOCKING);
738 NET_recvData(&Config.Cpu, sizeof(Config.Cpu), PSE_NET_BLOCKING);
739 if (tmp != Config.Cpu) {
742 if (Config.Cpu == CPU_INTERPRETER) psxCpu = &psxInt;
743 else psxCpu = &psxRec;
747 if (psxCpu->Init() == -1) {
748 SysClose(); return -1;
756 // remove the leading and trailing spaces in a string
757 void trim(char *str) {
761 // skip leading blanks
762 while (str[pos] <= ' ' && str[pos] > 0)
766 *(dest++) = str[pos];
770 *(dest--) = '\0'; // store the null
772 // remove trailing blanks
773 while (dest >= str && *dest <= ' ' && *dest > 0)
777 // lookup table for crc calculation
778 static unsigned short crctab[256] = {
779 0x0000, 0x1021, 0x2042, 0x3063, 0x4084, 0x50A5, 0x60C6, 0x70E7, 0x8108,
780 0x9129, 0xA14A, 0xB16B, 0xC18C, 0xD1AD, 0xE1CE, 0xF1EF, 0x1231, 0x0210,
781 0x3273, 0x2252, 0x52B5, 0x4294, 0x72F7, 0x62D6, 0x9339, 0x8318, 0xB37B,
782 0xA35A, 0xD3BD, 0xC39C, 0xF3FF, 0xE3DE, 0x2462, 0x3443, 0x0420, 0x1401,
783 0x64E6, 0x74C7, 0x44A4, 0x5485, 0xA56A, 0xB54B, 0x8528, 0x9509, 0xE5EE,
784 0xF5CF, 0xC5AC, 0xD58D, 0x3653, 0x2672, 0x1611, 0x0630, 0x76D7, 0x66F6,
785 0x5695, 0x46B4, 0xB75B, 0xA77A, 0x9719, 0x8738, 0xF7DF, 0xE7FE, 0xD79D,
786 0xC7BC, 0x48C4, 0x58E5, 0x6886, 0x78A7, 0x0840, 0x1861, 0x2802, 0x3823,
787 0xC9CC, 0xD9ED, 0xE98E, 0xF9AF, 0x8948, 0x9969, 0xA90A, 0xB92B, 0x5AF5,
788 0x4AD4, 0x7AB7, 0x6A96, 0x1A71, 0x0A50, 0x3A33, 0x2A12, 0xDBFD, 0xCBDC,
789 0xFBBF, 0xEB9E, 0x9B79, 0x8B58, 0xBB3B, 0xAB1A, 0x6CA6, 0x7C87, 0x4CE4,
790 0x5CC5, 0x2C22, 0x3C03, 0x0C60, 0x1C41, 0xEDAE, 0xFD8F, 0xCDEC, 0xDDCD,
791 0xAD2A, 0xBD0B, 0x8D68, 0x9D49, 0x7E97, 0x6EB6, 0x5ED5, 0x4EF4, 0x3E13,
792 0x2E32, 0x1E51, 0x0E70, 0xFF9F, 0xEFBE, 0xDFDD, 0xCFFC, 0xBF1B, 0xAF3A,
793 0x9F59, 0x8F78, 0x9188, 0x81A9, 0xB1CA, 0xA1EB, 0xD10C, 0xC12D, 0xF14E,
794 0xE16F, 0x1080, 0x00A1, 0x30C2, 0x20E3, 0x5004, 0x4025, 0x7046, 0x6067,
795 0x83B9, 0x9398, 0xA3FB, 0xB3DA, 0xC33D, 0xD31C, 0xE37F, 0xF35E, 0x02B1,
796 0x1290, 0x22F3, 0x32D2, 0x4235, 0x5214, 0x6277, 0x7256, 0xB5EA, 0xA5CB,
797 0x95A8, 0x8589, 0xF56E, 0xE54F, 0xD52C, 0xC50D, 0x34E2, 0x24C3, 0x14A0,
798 0x0481, 0x7466, 0x6447, 0x5424, 0x4405, 0xA7DB, 0xB7FA, 0x8799, 0x97B8,
799 0xE75F, 0xF77E, 0xC71D, 0xD73C, 0x26D3, 0x36F2, 0x0691, 0x16B0, 0x6657,
800 0x7676, 0x4615, 0x5634, 0xD94C, 0xC96D, 0xF90E, 0xE92F, 0x99C8, 0x89E9,
801 0xB98A, 0xA9AB, 0x5844, 0x4865, 0x7806, 0x6827, 0x18C0, 0x08E1, 0x3882,
802 0x28A3, 0xCB7D, 0xDB5C, 0xEB3F, 0xFB1E, 0x8BF9, 0x9BD8, 0xABBB, 0xBB9A,
803 0x4A75, 0x5A54, 0x6A37, 0x7A16, 0x0AF1, 0x1AD0, 0x2AB3, 0x3A92, 0xFD2E,
804 0xED0F, 0xDD6C, 0xCD4D, 0xBDAA, 0xAD8B, 0x9DE8, 0x8DC9, 0x7C26, 0x6C07,
805 0x5C64, 0x4C45, 0x3CA2, 0x2C83, 0x1CE0, 0x0CC1, 0xEF1F, 0xFF3E, 0xCF5D,
806 0xDF7C, 0xAF9B, 0xBFBA, 0x8FD9, 0x9FF8, 0x6E17, 0x7E36, 0x4E55, 0x5E74,
807 0x2E93, 0x3EB2, 0x0ED1, 0x1EF0
810 u16 calcCrc(u8 *d, int len) {
814 for (i = 0; i < len; i++) {
815 crc = crctab[(crc >> 8) ^ d[i]] ^ (crc << 8);