frontend: update libpicofe, fix missed callbacks
[pcsx_rearmed.git] / libpcsxcore / misc.c
index cd16c41..e0445e7 100644 (file)
 * Miscellaneous functions, including savestates and CD-ROM loading.
 */
 
+#include <stddef.h>
+#include <errno.h>
+#include <assert.h>
 #include "misc.h"
 #include "cdrom.h"
+#include "cdrom-async.h"
 #include "mdec.h"
 #include "gpu.h"
 #include "ppf.h"
+#include "psxbios.h"
 #include "database.h"
 #include <zlib.h>
+#include "revision.h"
 
 char CdromId[10] = "";
 char CdromLabel[33] = "";
+int  CdromFrontendId; // for frontend use
+
+static u32 save_counter;
 
 // PSX Executable types
 #define PSX_EXE     1
@@ -54,17 +63,11 @@ struct iso_directory_record {
        char name                       [1];
 };
 
-void mmssdd( char *b, char *p )
+static void mmssdd(const char *b, char *p)
 {
+       const unsigned char *ub = (void *)b;
+       int block = (ub[3] << 24) | (ub[2] << 16) | (ub[1] << 8) | ub[0];
        int m, s, d;
-#if defined(__arm__)
-       unsigned char *u = (void *)b;
-       int block = (u[3] << 24) | (u[2] << 16) | (u[1] << 8) | u[0];
-#elif defined(__BIGENDIAN__)
-       int block = (b[0] & 0xff) | ((b[1] & 0xff) << 8) | ((b[2] & 0xff) << 16) | (b[3] << 24);
-#else
-       int block = *((int*)b);
-#endif
 
        block += 150;
        m = block / 4500;                       // minutes
@@ -72,17 +75,12 @@ void mmssdd( char *b, char *p )
        s = block / 75;                         // seconds
        d = block - s * 75;                     // seconds rest
 
-       m = ((m / 10) << 4) | m % 10;
-       s = ((s / 10) << 4) | s % 10;
-       d = ((d / 10) << 4) | d % 10;   
-
        p[0] = m;
        p[1] = s;
        p[2] = d;
 }
 
 #define incTime() \
-       time[0] = btoi(time[0]); time[1] = btoi(time[1]); time[2] = btoi(time[2]); \
        time[2]++; \
        if(time[2] == 75) { \
                time[2] = 0; \
@@ -92,11 +90,10 @@ void mmssdd( char *b, char *p )
                        time[0]++; \
                } \
        } \
-       time[0] = itob(time[0]); time[1] = itob(time[1]); time[2] = itob(time[2]);
 
 #define READTRACK() \
-       if (CDR_readTrack(time) == -1) return -1; \
-       buf = (void *)CDR_getBuffer(); \
+       if (cdra_readTrack(time)) return -1; \
+       buf = cdra_getBuffer(); \
        if (buf == NULL) return -1; \
        else CheckPPFCache((u8 *)buf, time[0], time[1], time[2]);
 
@@ -116,7 +113,7 @@ int GetCdromFile(u8 *mdir, u8 *time, char *filename) {
        int i;
 
        // only try to scan if a filename is given
-       if (!strlen(filename)) return -1;
+       if (filename == INVALID_PTR || !strlen(filename)) return -1;
 
        i = 0;
        while (i < 4096) {
@@ -148,51 +145,83 @@ int GetCdromFile(u8 *mdir, u8 *time, char *filename) {
        return retval;
 }
 
-static const unsigned int gpu_ctl_def[] = {
-       0x00000000, 0x01000000, 0x03000000, 0x04000000,
-       0x05000800, 0x06c60260, 0x0703fc10, 0x08000027,
-};
+static void SetBootRegs(u32 pc, u32 gp, u32 sp)
+{
+       //printf("%s %08x %08x %08x\n", __func__, pc, gp, sp);
+       psxCpu->Notify(R3000ACPU_NOTIFY_BEFORE_SAVE, NULL);
 
-static const unsigned int gpu_data_def[] = {
-       0xe100360b, 0xe2000000, 0xe3000800, 0xe4077e7f,
-       0xe5001000, 0xe6000000,
-       0x02000000, 0x00000000, 0x01ff03ff,
-};
+       psxRegs.pc = pc;
+       psxRegs.GPR.n.gp = gp;
+       psxRegs.GPR.n.sp = sp ? sp : 0x801fff00;
+       psxRegs.GPR.n.fp = psxRegs.GPR.n.sp;
 
-static void fake_bios_gpu_setup(void)
-{
-       int i;
+       psxRegs.GPR.n.t0 = psxRegs.GPR.n.sp; // mimic A(43)
+       psxRegs.GPR.n.t3 = pc;
+
+       psxCpu->Notify(R3000ACPU_NOTIFY_AFTER_LOAD, NULL);
+}
+
+int BiosBootBypass() {
+       struct CdrStat stat = { 0, 0, };
+       assert(psxRegs.pc == 0x80030000);
 
-       for (i = 0; i < sizeof(gpu_ctl_def) / sizeof(gpu_ctl_def[0]); i++)
-               GPU_writeStatus(gpu_ctl_def[i]);
+       // no bypass if the lid is open
+       CDR__getStatus(&stat);
+       if (stat.Status & 0x10)
+               return 0;
+
+       // skip BIOS logos and region check
+       psxCpu->Notify(R3000ACPU_NOTIFY_BEFORE_SAVE, NULL);
+       psxRegs.pc = psxRegs.GPR.n.ra;
+       return 1;
+}
 
-       for (i = 0; i < sizeof(gpu_data_def) / sizeof(gpu_data_def[0]); i++)
-               GPU_writeData(gpu_data_def[i]);
+static void getFromCnf(char *buf, const char *key, u32 *val)
+{
+       buf = strstr(buf, key);
+       if (buf)
+               buf = strchr(buf, '=');
+       if (buf) {
+               unsigned long v;
+               errno = 0;
+               v = strtoul(buf + 1, NULL, 16);
+               if (errno == 0)
+                       *val = v;
+       }
 }
 
 int LoadCdrom() {
-       EXE_HEADER tmpHead;
+       union {
+               EXE_HEADER h;
+               u32 d[sizeof(EXE_HEADER) / sizeof(u32)];
+       } tmpHead;
        struct iso_directory_record *dir;
        u8 time[4], *buf;
        u8 mdir[4096];
        char exename[256];
+       u32 cnf_tcb = 4;
+       u32 cnf_event = 16;
+       u32 cnf_stack = 0;
+       u32 t_addr;
+       u32 t_size;
+       u32 sp = 0;
+       int i, ret;
 
-       // not the best place to do it, but since BIOS boot logo killer
-       // is just below, do it here
-       fake_bios_gpu_setup();
+       save_counter = 0;
 
        if (!Config.HLE) {
-               // skip BIOS logos
-               psxRegs.pc = psxRegs.GPR.n.ra;
-               return 0;
+               if (psxRegs.pc != 0x80030000) // BiosBootBypass'ed or custom BIOS?
+                       return 0;
+               if (Config.SlowBoot)
+                       return 0;
        }
 
-       time[0] = itob(0); time[1] = itob(2); time[2] = itob(0x10);
+       time[0] = 0; time[1] = 2; time[2] = 0x10;
 
        READTRACK();
 
        // skip head and sub, and go to the root directory record
-       dir = (struct iso_directory_record*) &buf[12+156]; 
+       dir = (struct iso_directory_record*) &buf[12+156];
 
        mmssdd(dir->extent, (char*)time);
 
@@ -202,17 +231,19 @@ int LoadCdrom() {
        if (GetCdromFile(mdir, time, "SYSTEM.CNF;1") == -1) {
                // if SYSTEM.CNF is missing, start an existing PSX.EXE
                if (GetCdromFile(mdir, time, "PSX.EXE;1") == -1) return -1;
+               strcpy(exename, "PSX.EXE;1");
 
                READTRACK();
        }
        else {
                // read the SYSTEM.CNF
                READTRACK();
+               buf[1023] = 0;
 
-               sscanf((char *)buf + 12, "BOOT = cdrom:\\%255s", exename);
-               if (GetCdromFile(mdir, time, exename) == -1) {
-                       sscanf((char *)buf + 12, "BOOT = cdrom:%255s", exename);
-                       if (GetCdromFile(mdir, time, exename) == -1) {
+               ret = sscanf((char *)buf + 12, "BOOT = cdrom:\\%255s", exename);
+               if (ret < 1 || GetCdromFile(mdir, time, exename) == -1) {
+                       ret = sscanf((char *)buf + 12, "BOOT = cdrom:%255s", exename);
+                       if (ret < 1 || GetCdromFile(mdir, time, exename) == -1) {
                                char *ptr = strstr((char *)buf + 12, "cdrom:");
                                if (ptr != NULL) {
                                        ptr += 6;
@@ -228,56 +259,73 @@ int LoadCdrom() {
                                        return -1;
                        }
                }
+               getFromCnf((char *)buf + 12, "TCB", &cnf_tcb);
+               getFromCnf((char *)buf + 12, "EVENT", &cnf_event);
+               getFromCnf((char *)buf + 12, "STACK", &cnf_stack);
+               if (Config.HLE)
+                       psxBiosCnfLoaded(cnf_tcb, cnf_event, cnf_stack);
 
                // Read the EXE-Header
                READTRACK();
        }
 
        memcpy(&tmpHead, buf + 12, sizeof(EXE_HEADER));
+       for (i = 2; i < sizeof(tmpHead.d) / sizeof(tmpHead.d[0]); i++)
+               tmpHead.d[i] = SWAP32(tmpHead.d[i]);
 
-       psxRegs.pc = SWAP32(tmpHead.pc0);
-       psxRegs.GPR.n.gp = SWAP32(tmpHead.gp0);
-       psxRegs.GPR.n.sp = SWAP32(tmpHead.s_addr); 
-       if (psxRegs.GPR.n.sp == 0) psxRegs.GPR.n.sp = 0x801fff00;
-
-       tmpHead.t_size = SWAP32(tmpHead.t_size);
-       tmpHead.t_addr = SWAP32(tmpHead.t_addr);
-
-       psxCpu->Clear(tmpHead.t_addr, tmpHead.t_size / 4);
-       psxCpu->Reset();
+       SysPrintf("manual booting '%s' pc=%x\n", exename, tmpHead.h.pc0);
+       sp = tmpHead.h.s_addr;
+       if (cnf_stack)
+               sp = cnf_stack;
+       SetBootRegs(tmpHead.h.pc0, tmpHead.h.gp0, sp);
 
        // Read the rest of the main executable
-       while (tmpHead.t_size & ~2047) {
-               void *ptr = (void *)PSXM(tmpHead.t_addr);
+       for (t_addr = tmpHead.h.t_addr, t_size = tmpHead.h.t_size; t_size & ~2047; ) {
+               void *ptr = (void *)PSXM(t_addr);
 
                incTime();
                READTRACK();
 
-               if (ptr != NULL) memcpy(ptr, buf+12, 2048);
+               if (ptr != INVALID_PTR) memcpy(ptr, buf+12, 2048);
 
-               tmpHead.t_size -= 2048;
-               tmpHead.t_addr += 2048;
+               t_addr += 2048;
+               t_size -= 2048;
        }
 
+       psxCpu->Clear(tmpHead.h.t_addr, tmpHead.h.t_size / 4);
+       //psxCpu->Reset();
+
+       if (Config.HLE)
+               psxBiosCheckExe(tmpHead.h.t_addr, tmpHead.h.t_size, 0);
+
        return 0;
 }
 
-int LoadCdromFile(const char *filename, EXE_HEADER *head) {
+int LoadCdromFile(const char *filename, EXE_HEADER *head, u8 *time_bcd_out) {
        struct iso_directory_record *dir;
        u8 time[4],*buf;
        u8 mdir[4096];
        char exename[256];
+       const char *p1, *p2;
        u32 size, addr;
        void *mem;
 
-       sscanf(filename, "cdrom:\\%255s", exename);
+       if (filename == INVALID_PTR)
+               return -1;
+
+       p1 = filename;
+       if ((p2 = strchr(p1, ':')))
+               p1 = p2 + 1;
+       while (*p1 == '\\')
+               p1++;
+       snprintf(exename, sizeof(exename), "%s", p1);
 
-       time[0] = itob(0); time[1] = itob(2); time[2] = itob(0x10);
+       time[0] = 0; time[1] = 2; time[2] = 0x10;
 
        READTRACK();
 
        // skip head and sub, and go to the root directory record
-       dir = (struct iso_directory_record *)&buf[12 + 156]; 
+       dir = (struct iso_directory_record *)&buf[12 + 156];
 
        mmssdd(dir->extent, (char*)time);
 
@@ -286,53 +334,75 @@ int LoadCdromFile(const char *filename, EXE_HEADER *head) {
        if (GetCdromFile(mdir, time, exename) == -1) return -1;
 
        READTRACK();
+       incTime();
 
        memcpy(head, buf + 12, sizeof(EXE_HEADER));
-       size = head->t_size;
-       addr = head->t_addr;
+       size = SWAP32(head->t_size);
+       addr = SWAP32(head->t_addr);
 
        psxCpu->Clear(addr, size / 4);
-       psxCpu->Reset();
+       //psxCpu->Reset();
 
        while (size & ~2047) {
-               incTime();
                READTRACK();
+               incTime();
 
                mem = PSXM(addr);
-               if (mem)
+               if (mem != INVALID_PTR)
                        memcpy(mem, buf + 12, 2048);
 
                size -= 2048;
                addr += 2048;
        }
+       if (time_bcd_out) {
+               time_bcd_out[0] = itob(time[0]);
+               time_bcd_out[1] = itob(time[1]);
+               time_bcd_out[2] = itob(time[2]);
+       }
 
        return 0;
 }
 
 int CheckCdrom() {
        struct iso_directory_record *dir;
-       unsigned char time[4];
+       struct CdrStat stat = { 0, 0, };
+       unsigned char time[4] = { 0, 2, 4 };
        char *buf;
        unsigned char mdir[4096];
        char exename[256];
+       int lic_region_detected = -1;
        int i, len, c;
 
        FreePPFCache();
-
-       time[0] = itob(0);
-       time[1] = itob(2);
-       time[2] = itob(0x10);
-
-       READTRACK();
-
        memset(CdromLabel, 0, sizeof(CdromLabel));
        memset(CdromId, 0, sizeof(CdromId));
        memset(exename, 0, sizeof(exename));
 
+       if (!Config.HLE && Config.SlowBoot) {
+               // boot to BIOS in case of CDDA or lid is open
+               cdra_getStatus(&stat);
+               if ((stat.Status & 0x10) || stat.Type == 2 || cdra_readTrack(time))
+                       return 0;
+       }
+       if (Config.PsxAuto) {
+               time[0] = 0;
+               time[1] = 2;
+               time[2] = 4;
+               READTRACK();
+               if (strcmp((char *)buf + 12 + 46, "Entertainment Euro pe   ") == 0)
+                       lic_region_detected = PSX_TYPE_PAL;
+               // else it'll default to NTSC anyway
+       }
+
+       time[0] = 0;
+       time[1] = 2;
+       time[2] = 0x10;
+       READTRACK();
+
        strncpy(CdromLabel, buf + 52, 32);
 
        // skip head and sub, and go to the root directory record
-       dir = (struct iso_directory_record *)&buf[12 + 156]; 
+       dir = (struct iso_directory_record *)&buf[12 + 156];
 
        mmssdd(dir->extent, (char *)time);
 
@@ -360,6 +430,14 @@ int CheckCdrom() {
                                        return -1;
                        }
                }
+               /* Workaround for Wild Arms EU/US which has non-standard string causing incorrect region detection */
+               if (exename[0] == 'E' && exename[1] == 'X' && exename[2] == 'E' && exename[3] == '\\') {
+                       size_t offset = 4;
+                       size_t i, len = strlen(exename) - offset;
+                       for (i = 0; i < len; i++)
+                               exename[i] = exename[i + offset];
+                       exename[i] = '\0';
+               }
        } else if (GetCdromFile(mdir, time, "PSX.EXE;1") != -1) {
                strcpy(exename, "PSX.EXE;1");
                strcpy(CdromId, "SLUS99999");
@@ -372,7 +450,7 @@ int CheckCdrom() {
                for (i = 0; i < len; ++i) {
                        if (exename[i] == ';' || c >= sizeof(CdromId) - 1)
                                break;
-                       if (isalnum(exename[i]))
+                       if (isalnum((int)exename[i]))
                                CdromId[c++] = exename[i];
                }
        }
@@ -381,7 +459,9 @@ int CheckCdrom() {
                strcpy(CdromId, "SLUS99999");
 
        if (Config.PsxAuto) { // autodetect system (pal or ntsc)
-               if (
+               if (lic_region_detected >= 0)
+                       Config.PsxType = lic_region_detected;
+               else if (
                        /* Make sure Wild Arms SCUS-94608 is not detected as a PAL game. */
                        ((CdromId[0] == 's' || CdromId[0] == 'S') && (CdromId[2] == 'e' || CdromId[2] == 'E')) ||
                        !strncmp(CdromId, "DTLS3035", 8) ||
@@ -401,7 +481,7 @@ int CheckCdrom() {
        
        Apply_Hacks_Cdrom();
 
-       BuildPPFCache();
+       BuildPPFCache(NULL);
 
        return 0;
 }
@@ -414,7 +494,9 @@ static int PSXGetFileType(FILE *f) {
 
        current = ftell(f);
        fseek(f, 0L, SEEK_SET);
-       fread(mybuf, 2048, 1, f);
+       if (fread(&mybuf, 1, sizeof(mybuf), f) != sizeof(mybuf))
+               goto io_fail;
+       
        fseek(f, current, SEEK_SET);
 
        exe_hdr = (EXE_HEADER *)mybuf;
@@ -429,6 +511,12 @@ static int PSXGetFileType(FILE *f) {
                return COFF_EXE;
 
        return INVALID_EXE;
+
+io_fail:
+#ifndef NDEBUG
+       SysPrintf(_("File IO error in <%s:%s>.\n"), __FILE__, __func__);
+#endif
+       return INVALID_EXE;
 }
 
 // temporary pandora workaround..
@@ -437,13 +525,15 @@ size_t fread_to_ram(void *ptr, size_t size, size_t nmemb, FILE *stream)
 {
        void *tmp;
        size_t ret = 0;
-       
+
        tmp = malloc(size * nmemb);
        if (tmp) {
                ret = fread(tmp, size, nmemb, stream);
                memcpy(ptr, tmp, size * nmemb);
                free(tmp);
        }
+       else
+               ret = fread(ptr, size, nmemb, stream);
        return ret;
 }
 
@@ -456,8 +546,8 @@ int Load(const char *ExePath) {
        u32 section_address, section_size;
        void *mem;
 
-       strncpy(CdromId, "SLUS99999", 9);
-       strncpy(CdromLabel, "SLUS_999.99", 11);
+       strcpy(CdromId, "SLUS99999");
+       strcpy(CdromLabel, "SLUS_999.99");
 
        tmpFile = fopen(ExePath, "rb");
        if (tmpFile == NULL) {
@@ -467,51 +557,52 @@ int Load(const char *ExePath) {
                type = PSXGetFileType(tmpFile);
                switch (type) {
                        case PSX_EXE:
-                               fread(&tmpHead,sizeof(EXE_HEADER),1,tmpFile);
+                               if (fread(&tmpHead, 1, sizeof(EXE_HEADER), tmpFile) != sizeof(EXE_HEADER))
+                                       goto fail_io;
                                section_address = SWAP32(tmpHead.t_addr);
                                section_size = SWAP32(tmpHead.t_size);
                                mem = PSXM(section_address);
-                               if (mem != NULL) {
-                                       fseek(tmpFile, 0x800, SEEK_SET);                
+                               if (mem != INVALID_PTR) {
+                                       fseek(tmpFile, 0x800, SEEK_SET);
                                        fread_to_ram(mem, section_size, 1, tmpFile);
                                        psxCpu->Clear(section_address, section_size / 4);
                                }
-                               fclose(tmpFile);
-                               psxRegs.pc = SWAP32(tmpHead.pc0);
-                               psxRegs.GPR.n.gp = SWAP32(tmpHead.gp0);
-                               psxRegs.GPR.n.sp = SWAP32(tmpHead.s_addr); 
-                               if (psxRegs.GPR.n.sp == 0)
-                                       psxRegs.GPR.n.sp = 0x801fff00;
+                               SetBootRegs(SWAP32(tmpHead.pc0), SWAP32(tmpHead.gp0),
+                                       SWAP32(tmpHead.s_addr));
                                retval = 0;
                                break;
                        case CPE_EXE:
                                fseek(tmpFile, 6, SEEK_SET); /* Something tells me we should go to 4 and read the "08 00" here... */
                                do {
-                                       fread(&opcode, 1, 1, tmpFile);
+                                       if (fread(&opcode, 1, sizeof(opcode), tmpFile) != sizeof(opcode))
+                                               goto fail_io;
                                        switch (opcode) {
                                                case 1: /* Section loading */
-                                                       fread(&section_address, 4, 1, tmpFile);
-                                                       fread(&section_size, 4, 1, tmpFile);
+                                                       if (fread(&section_address, 1, sizeof(section_address), tmpFile) != sizeof(section_address))
+                                                               goto fail_io;
+                                                       if (fread(&section_size, 1, sizeof(section_size), tmpFile) != sizeof(section_size))
+                                                               goto fail_io;
                                                        section_address = SWAPu32(section_address);
                                                        section_size = SWAPu32(section_size);
 #ifdef EMU_LOG
                                                        EMU_LOG("Loading %08X bytes from %08X to %08X\n", section_size, ftell(tmpFile), section_address);
 #endif
                                                        mem = PSXM(section_address);
-                                                       if (mem != NULL) {
+                                                       if (mem != INVALID_PTR) {
                                                                fread_to_ram(mem, section_size, 1, tmpFile);
                                                                psxCpu->Clear(section_address, section_size / 4);
                                                        }
                                                        break;
                                                case 3: /* register loading (PC only?) */
                                                        fseek(tmpFile, 2, SEEK_CUR); /* unknown field */
-                                                       fread(&psxRegs.pc, 4, 1, tmpFile);
+                                                       if (fread(&psxRegs.pc, 1, sizeof(psxRegs.pc), tmpFile) != sizeof(psxRegs.pc))
+                                                               goto fail_io;
                                                        psxRegs.pc = SWAPu32(psxRegs.pc);
                                                        break;
                                                case 0: /* End of file */
                                                        break;
                                                default:
-                                                       SysPrintf(_("Unknown CPE opcode %02x at position %08x.\n"), opcode, ftell(tmpFile) - 1);
+                                                       SysPrintf(_("Unknown CPE opcode %02x at position %08lx.\n"), opcode, ftell(tmpFile) - 1);
                                                        retval = -1;
                                                        break;
                                        }
@@ -534,7 +625,16 @@ int Load(const char *ExePath) {
                CdromLabel[0] = '\0';
        }
 
+       if (tmpFile)
+               fclose(tmpFile);
        return retval;
+
+fail_io:
+#ifndef NDEBUG
+       SysPrintf(_("File IO error in <%s:%s>.\n"), __FILE__, __func__);
+#endif
+       fclose(tmpFile);
+       return -1;
 }
 
 // STATES
@@ -568,33 +668,91 @@ struct PcsxSaveFuncs SaveFuncs = {
        zlib_open, zlib_read, zlib_write, zlib_seek, zlib_close
 };
 
-static const char PcsxHeader[32] = "STv4 PCSX v" PACKAGE_VERSION;
+static const char PcsxHeader[32] = "STv4 PCSXra " REV;
 
 // Savestate Versioning!
 // If you make changes to the savestate version, please increment the value below.
 static const u32 SaveVersion = 0x8b410006;
 
+struct origin_info {
+       boolean icache_emulation;
+       boolean DisableStalls;
+       boolean PreciseExceptions;
+       boolean TurboCD;
+       s8 GpuListWalking;
+       s8 FractionalFramerate;
+       u8 Cpu;
+       u8 PsxType;
+       char build_info[64];
+};
+
+#define MISC_MAGIC 0x4353494d
+struct misc_save_data {
+       u32 magic;
+       u32 gteBusyCycle;
+       u32 muldivBusyCycle;
+       u32 biuReg;
+       u32 biosBranchCheck;
+       u32 gpuIdleAfter;
+       u32 gpuSr;
+       u32 frame_counter;
+       int CdromFrontendId;
+       u32 save_counter;
+};
+
+#define EX_SCREENPIC_SIZE (128 * 96 * 3)
+
 int SaveState(const char *file) {
-       void *f;
-       GPUFreeze_t *gpufP;
-       SPUFreeze_t *spufP;
+       struct misc_save_data *misc = (void *)(psxH + 0xf000);
+       struct origin_info oi = { 0, };
+       GPUFreeze_t *gpufP = NULL;
+       SPUFreezeHdr_t spufH;
+       SPUFreeze_t *spufP = NULL;
+       u8 buf[EX_SCREENPIC_SIZE];
+       int result = -1;
        int Size;
-       unsigned char *pMem;
+       void *f;
+
+       assert(!psxRegs.branching);
+       assert(!psxRegs.cpuInRecursion);
+       assert(!misc->magic);
 
        f = SaveFuncs.open(file, "wb");
        if (f == NULL) return -1;
 
-       new_dyna_before_save();
+       misc->magic = MISC_MAGIC;
+       misc->gteBusyCycle = psxRegs.gteBusyCycle;
+       misc->muldivBusyCycle = psxRegs.muldivBusyCycle;
+       misc->biuReg = psxRegs.biuReg;
+       misc->biosBranchCheck = psxRegs.biosBranchCheck;
+       misc->gpuIdleAfter = psxRegs.gpuIdleAfter;
+       misc->gpuSr = HW_GPU_STATUS;
+       misc->frame_counter = frame_counter;
+       misc->CdromFrontendId = CdromFrontendId;
+       misc->save_counter = ++save_counter;
+
+       psxCpu->Notify(R3000ACPU_NOTIFY_BEFORE_SAVE, NULL);
 
        SaveFuncs.write(f, (void *)PcsxHeader, 32);
        SaveFuncs.write(f, (void *)&SaveVersion, sizeof(u32));
        SaveFuncs.write(f, (void *)&Config.HLE, sizeof(boolean));
 
-       pMem = (unsigned char *)malloc(128 * 96 * 3);
-       if (pMem == NULL) return -1;
-       GPU_getScreenPic(pMem);
-       SaveFuncs.write(f, pMem, 128 * 96 * 3);
-       free(pMem);
+       oi.icache_emulation = Config.icache_emulation;
+       oi.DisableStalls = Config.DisableStalls;
+       oi.PreciseExceptions = Config.PreciseExceptions;
+       oi.TurboCD = Config.TurboCD;
+       oi.GpuListWalking = Config.GpuListWalking;
+       oi.FractionalFramerate = Config.FractionalFramerate;
+       oi.Cpu = Config.Cpu;
+       oi.PsxType = Config.PsxType;
+       snprintf(oi.build_info, sizeof(oi.build_info), "%s", get_build_info());
+
+       // this was space for ScreenPic
+       assert(sizeof(buf) >= EX_SCREENPIC_SIZE);
+       assert(sizeof(oi) - 3 <= EX_SCREENPIC_SIZE);
+       memset(buf, 0, sizeof(buf));
+       memcpy(buf + 3, &oi, sizeof(oi));
+       SaveFuncs.write(f, buf, EX_SCREENPIC_SIZE);
 
        if (Config.HLE)
                psxBiosFreeze(1);
@@ -602,86 +760,124 @@ int SaveState(const char *file) {
        SaveFuncs.write(f, psxM, 0x00200000);
        SaveFuncs.write(f, psxR, 0x00080000);
        SaveFuncs.write(f, psxH, 0x00010000);
-       SaveFuncs.write(f, (void *)&psxRegs, sizeof(psxRegs));
+       // only partial save of psxRegisters to maintain savestate compat
+       SaveFuncs.write(f, &psxRegs, offsetof(psxRegisters, gteBusyCycle));
 
        // gpu
        gpufP = (GPUFreeze_t *)malloc(sizeof(GPUFreeze_t));
+       if (gpufP == NULL) goto cleanup;
        gpufP->ulFreezeVersion = 1;
        GPU_freeze(1, gpufP);
        SaveFuncs.write(f, gpufP, sizeof(GPUFreeze_t));
-       free(gpufP);
+       free(gpufP); gpufP = NULL;
 
        // spu
-       spufP = (SPUFreeze_t *) malloc(16);
-       SPU_freeze(2, spufP, psxRegs.cycle);
-       Size = spufP->Size; SaveFuncs.write(f, &Size, 4);
-       free(spufP);
+       SPU_freeze(2, (SPUFreeze_t *)&spufH, psxRegs.cycle);
+       Size = spufH.Size; SaveFuncs.write(f, &Size, 4);
        spufP = (SPUFreeze_t *) malloc(Size);
+       if (spufP == NULL) goto cleanup;
        SPU_freeze(1, spufP, psxRegs.cycle);
        SaveFuncs.write(f, spufP, Size);
-       free(spufP);
+       free(spufP); spufP = NULL;
 
        sioFreeze(f, 1);
        cdrFreeze(f, 1);
        psxHwFreeze(f, 1);
        psxRcntFreeze(f, 1);
        mdecFreeze(f, 1);
-       new_dyna_freeze(f, 1);
+       ndrc_freeze(f, 1);
+       padFreeze(f, 1);
 
+       result = 0;
+cleanup:
+       memset(misc, 0, sizeof(*misc));
        SaveFuncs.close(f);
-
-       new_dyna_after_save();
-
-       return 0;
+       return result;
 }
 
 int LoadState(const char *file) {
+       struct misc_save_data *misc = (void *)(psxH + 0xf000);
+       u32 biosBranchCheckOld = psxRegs.biosBranchCheck;
        void *f;
-       GPUFreeze_t *gpufP;
-       SPUFreeze_t *spufP;
+       GPUFreeze_t *gpufP = NULL;
+       SPUFreeze_t *spufP = NULL;
+       boolean hle, oldhle;
        int Size;
        char header[32];
        u32 version;
-       boolean hle;
+       int result = -1;
 
        f = SaveFuncs.open(file, "rb");
        if (f == NULL) return -1;
 
-       SaveFuncs.read(f, header, sizeof(header));
+       if (!file)
+               file = "(stream)";
+       memset(header, 0, sizeof(header));
+       SaveFuncs.read(f, header, 16);
+       if (strncmp("RASTATE", header, 7) == 0) {
+               // looks like RA header, normal savestate should follow
+               SysPrintf("%s: trying to skip RASTATE header\n", file);
+               SaveFuncs.read(f, header, 16);
+       }
+       SaveFuncs.read(f, header + 16, 16);
        SaveFuncs.read(f, &version, sizeof(u32));
        SaveFuncs.read(f, &hle, sizeof(boolean));
 
-       if (strncmp("STv4 PCSX", header, 9) != 0 || version != SaveVersion) {
-               SaveFuncs.close(f);
-               return -1;
+       if (strncmp("STv4 PCSX", header, 9) != 0) {
+               SysPrintf("%s: is not a savestate?\n", file);
+               goto cleanup;
+       }
+       if (version != SaveVersion) {
+               SysPrintf("%s: incompatible savestate version %x\n", file, version);
+               goto cleanup;
        }
+       oldhle = Config.HLE;
        Config.HLE = hle;
 
        if (Config.HLE)
                psxBiosInit();
 
-       psxCpu->Reset();
-       SaveFuncs.seek(f, 128 * 96 * 3, SEEK_CUR);
+       // ex-ScreenPic space
+       SaveFuncs.seek(f, EX_SCREENPIC_SIZE, SEEK_CUR);
 
        SaveFuncs.read(f, psxM, 0x00200000);
        SaveFuncs.read(f, psxR, 0x00080000);
        SaveFuncs.read(f, psxH, 0x00010000);
-       SaveFuncs.read(f, (void *)&psxRegs, sizeof(psxRegs));
+       SaveFuncs.read(f, &psxRegs, offsetof(psxRegisters, gteBusyCycle));
+       psxRegs.gteBusyCycle = psxRegs.cycle;
+       psxRegs.branching = 0;
+       psxRegs.biosBranchCheck = ~0;
+       psxRegs.cpuInRecursion = 0;
+       psxRegs.gpuIdleAfter = psxRegs.cycle - 1;
+       HW_GPU_STATUS &= SWAP32(~PSXGPU_nBUSY);
+       if (misc->magic == MISC_MAGIC) {
+               psxRegs.gteBusyCycle = misc->gteBusyCycle;
+               psxRegs.muldivBusyCycle = misc->muldivBusyCycle;
+               psxRegs.biuReg = misc->biuReg;
+               psxRegs.biosBranchCheck = misc->biosBranchCheck;
+               psxRegs.gpuIdleAfter = misc->gpuIdleAfter;
+               HW_GPU_STATUS = misc->gpuSr;
+               frame_counter = misc->frame_counter;
+               CdromFrontendId = misc->CdromFrontendId;
+               if (misc->save_counter)
+                       save_counter = misc->save_counter;
+       }
 
        if (Config.HLE)
                psxBiosFreeze(0);
 
        // gpu
        gpufP = (GPUFreeze_t *)malloc(sizeof(GPUFreeze_t));
+       if (gpufP == NULL) goto cleanup;
        SaveFuncs.read(f, gpufP, sizeof(GPUFreeze_t));
        GPU_freeze(0, gpufP);
        free(gpufP);
-       if (HW_GPU_STATUS == 0)
-               HW_GPU_STATUS = GPU_readStatus();
+       gpuSyncPluginSR();
 
        // spu
        SaveFuncs.read(f, &Size, 4);
        spufP = (SPUFreeze_t *)malloc(Size);
+       if (spufP == NULL) goto cleanup;
        SaveFuncs.read(f, spufP, Size);
        SPU_freeze(0, spufP, psxRegs.cycle);
        free(spufP);
@@ -691,11 +887,26 @@ int LoadState(const char *file) {
        psxHwFreeze(f, 0);
        psxRcntFreeze(f, 0);
        mdecFreeze(f, 0);
-       new_dyna_freeze(f, 0);
 
-       SaveFuncs.close(f);
+       if (Config.HLE != oldhle) {
+               // at least ari64 drc compiles differently so hard reset
+               psxCpu->Shutdown();
+               psxCpu->Init();
+       }
+       ndrc_freeze(f, 0);
+       padFreeze(f, 0);
 
-       return 0;
+       events_restore();
+       if (Config.HLE)
+               psxBiosCheckExe(biosBranchCheckOld, 0x60, 1);
+
+       psxCpu->Notify(R3000ACPU_NOTIFY_AFTER_LOAD, NULL);
+
+       result = 0;
+cleanup:
+       memset(misc, 0, sizeof(*misc));
+       SaveFuncs.close(f);
+       return result;
 }
 
 int CheckState(const char *file) {
@@ -707,7 +918,11 @@ int CheckState(const char *file) {
        f = SaveFuncs.open(file, "rb");
        if (f == NULL) return -1;
 
-       SaveFuncs.read(f, header, sizeof(header));
+       memset(header, 0, sizeof(header));
+       SaveFuncs.read(f, header, 16);
+       if (strncmp("RASTATE", header, 7) == 0)
+               SaveFuncs.read(f, header, 16);
+       SaveFuncs.read(f, header + 16, 16);
        SaveFuncs.read(f, &version, sizeof(u32));
        SaveFuncs.read(f, &hle, sizeof(boolean));
 
@@ -719,55 +934,6 @@ int CheckState(const char *file) {
        return 0;
 }
 
-// NET Function Helpers
-
-int SendPcsxInfo() {
-       if (NET_recvData == NULL || NET_sendData == NULL)
-               return 0;
-
-       NET_sendData(&Config.Xa, sizeof(Config.Xa), PSE_NET_BLOCKING);
-       NET_sendData(&Config.Sio, sizeof(Config.Sio), PSE_NET_BLOCKING);
-       NET_sendData(&Config.SpuIrq, sizeof(Config.SpuIrq), PSE_NET_BLOCKING);
-       NET_sendData(&Config.RCntFix, sizeof(Config.RCntFix), PSE_NET_BLOCKING);
-       NET_sendData(&Config.PsxType, sizeof(Config.PsxType), PSE_NET_BLOCKING);
-       NET_sendData(&Config.Cpu, sizeof(Config.Cpu), PSE_NET_BLOCKING);
-
-       return 0;
-}
-
-int RecvPcsxInfo() {
-       int tmp;
-
-       if (NET_recvData == NULL || NET_sendData == NULL)
-               return 0;
-
-       NET_recvData(&Config.Xa, sizeof(Config.Xa), PSE_NET_BLOCKING);
-       NET_recvData(&Config.Sio, sizeof(Config.Sio), PSE_NET_BLOCKING);
-       NET_recvData(&Config.SpuIrq, sizeof(Config.SpuIrq), PSE_NET_BLOCKING);
-       NET_recvData(&Config.RCntFix, sizeof(Config.RCntFix), PSE_NET_BLOCKING);
-       NET_recvData(&Config.PsxType, sizeof(Config.PsxType), PSE_NET_BLOCKING);
-
-       SysUpdate();
-
-       tmp = Config.Cpu;
-       NET_recvData(&Config.Cpu, sizeof(Config.Cpu), PSE_NET_BLOCKING);
-       if (tmp != Config.Cpu) {
-               psxCpu->Shutdown();
-#ifndef DRC_DISABLE
-               if (Config.Cpu == CPU_INTERPRETER) psxCpu = &psxInt;
-               else psxCpu = &psxRec;
-#else
-               psxCpu = &psxInt;
-#endif
-               if (psxCpu->Init() == -1) {
-                       SysClose(); return -1;
-               }
-               psxCpu->Reset();
-       }
-
-       return 0;
-}
-
 // remove the leading and trailing spaces in a string
 void trim(char *str) {
        int pos = 0;
@@ -822,7 +988,7 @@ static unsigned short crctab[256] = {
        0x2E93, 0x3EB2, 0x0ED1, 0x1EF0
 };
 
-u16 calcCrc(u8 *d, int len) {
+u16 calcCrc(const u8 *d, int len) {
        u16 crc = 0;
        int i;
 
@@ -832,3 +998,54 @@ u16 calcCrc(u8 *d, int len) {
 
        return ~crc;
 }
+
+#define MKSTR2(x) #x
+#define MKSTR(x) MKSTR2(x)
+const char *get_build_info(void)
+{
+       return ""
+#ifdef __VERSION__
+               "cc " __VERSION__ " "
+#endif
+#if defined(__SIZEOF_POINTER__) && __SIZEOF_POINTER__ == 8
+               "64bit "
+#elif defined(__SIZEOF_POINTER__) && __SIZEOF_POINTER__ == 4
+               "32bit "
+#endif
+#if defined(__BYTE_ORDER__) && __BYTE_ORDER__ == __ORDER_BIG_ENDIAN__
+               "be "
+#endif
+#if defined(__PIE__) || defined(__pie__)
+               "pie "
+#endif
+#if defined(__PIC__) || defined(__pic__)
+               "pic "
+#endif
+#if defined(__aarch64__)
+               "arm64"
+#elif defined(__arm__)
+               "arm"
+#endif
+#ifdef __ARM_ARCH
+               "v" MKSTR(__ARM_ARCH) " "
+#endif
+#ifdef __thumb__
+               "thumb "
+#endif
+#if defined(__AVX__)
+               "avx "
+#elif defined(__SSSE3__)
+               "ssse3 "
+#elif defined(__ARM_NEON) || defined(__ARM_NEON__)
+               "neon "
+#endif
+#if defined(__ARM_FEATURE_SVE) && __ARM_FEATURE_SVE
+               "sve "
+#endif
+#if defined(LIGHTREC)
+               "lightrec "
+#elif !defined(DRC_DISABLE)
+               "ari64 "
+#endif
+               "gpu=" MKSTR(BUILTIN_GPU);
+}