Exemplo n.º 1
0
EMUFILE* EMUFILE_FILE::memwrap()
{
	EMUFILE_MEMORY* mem = new EMUFILE_MEMORY(size());
	if(size()==0) return mem;
	fread(mem->buf(),size());
	return mem;
}
Exemplo n.º 2
0
bool VFAT::build(const char* path, int extra_MB)
{
	dataSectors = 0;
	currVirtPath = "";
	currPath = path;
	list_files(path, count_ListCallback);

	dataSectors += 8; //a few for reserved sectors, etc.

	dataSectors += extra_MB*1024*1024/512; //add extra write space
	//dataSectors += 16*1024*1024/512; //add 16MB worth of write space. this is probably enough for anyone, but maybe it should be configurable.
	//we could always suggest to users to add a big file to their directory to overwrite (that would cause the image to get padded)

	//this seems to be the minimum size that will turn into a solid fat32
	if(dataSectors<36*1024*1024/512)
		dataSectors = 36*1024*1024/512;

	if(dataSectors>=(0x80000000>>9))
	{
		printf("error allocating memory for fat (%d KBytes)\n",(dataSectors*512)/1024);
		printf("total fat sizes > 2GB are never going to work\n");
	}

	delete file;
	try
	{
		file = new EMUFILE_MEMORY(dataSectors*512);
	}
	catch(std::bad_alloc)
	{
		printf("error allocating memory for fat (%d KBytes)\n",(dataSectors*512)/1024);
		printf("(out of memory)\n");
		return false;
	}

	//debug..
	//file = new EMUFILE_FILE("c:\\temp.ima","rb+");

	//format the disk
	{
		EmuFat fat(file);
		EmuFatVolume vol;
		u8 ok = vol.init(&fat);
		vol.formatNew(dataSectors);

		//ensure we are working in memory, just in case we were testing with a disk file.
		//libfat will need to go straight to memory (for now; we could easily change it to work with the disk)
		file = file->memwrap();
	}
	EMUFILE_MEMORY* memf = (EMUFILE_MEMORY*)file;

	//setup libfat and write all the files through it
	LIBFAT::Init(memf->buf(),memf->size());
	list_files(path, build_ListCallback);
	LIBFAT::Shutdown();

	return true;
}
Exemplo n.º 3
0
bool retro_serialize(void *data, size_t size)
{
    EMUFILE_MEMORY state;
    savestate_save(&state);

    if(state.size() <= size)
    {
        memcpy(data, state.buf(), state.size());
        return true;
    }

    return false;
}
Exemplo n.º 4
0
bool LoadSample(const char *name)
{
	SampleLoaded = 0;
	if (!name) return true;
	 
	EMUFILE_FILE inf(name,"rb");
	if (inf.fail()) return false;

	//wav reading code adapted from AUDIERE (LGPL)

    // read the RIFF header
    u8 riff_id[4];
    u32 riff_length;
    u8 riff_datatype[4];

    inf.fread(riff_id, 4);
    inf.read_32LE(riff_length);
    inf.fread(riff_datatype, 4);

	if (inf.size() < 12 ||
        memcmp(riff_id, "RIFF", 4) != 0 ||
        riff_length == 0 ||
        memcmp(riff_datatype, "WAVE", 4) != 0) {
			MessageBox(0,"not a valid RIFF WAVE file",0,0);
			return false;
	}
   
	 if (!formatChunk(inf))
      return false;
    
	 if (!dataChunk(inf))
	 {
		 MessageBox(0,"not a valid WAVE file. some unknown problem.",0,0);
		 return false;
	 }

	 delete[] samplebuffer;
	 samplebuffersize = (int)newWavData.size();
	 samplebuffer = new char[samplebuffersize];
	 memcpy(samplebuffer,newWavData.buf(),samplebuffersize);
 	new(&newWavData) EMUFILE_MEMORY();

	SampleLoaded=1;

	return true;
}
Exemplo n.º 5
0
static bool s_slot2_loadstate(EMUFILE* is, int size)
{
	u32 version = is->read32le();

	//version 0:
	if(version >= 0)
	{
		slot2Type = NDS_SLOT2_AUTO;
		u8 slotID = is->read32le();
		if (version == 0)
			slot2_getTypeByID(slotID, slot2Type);
		slot2_Change(slot2Type);

		EMUFILE_MEMORY temp;
		is->readMemoryStream(&temp);
		temp.fseek(0,SEEK_SET);
		slot2_Loadstate(&temp);
	}

	return true;
}
Exemplo n.º 6
0
static bool dataChunk(EMUFILE &inf)
{
	bool found = false;

	// seek to just after the RIFF header
	inf.fseek(12,SEEK_SET);

	// search for a format chunk
	for (;;)
	{
		char chunk_id[4];
		u32  chunk_length;

		if (inf.eof()) return found;
		if (inf.fread(chunk_id, 4) != 4) return found;
		if (!inf.read_32LE(chunk_length)) return found;

		// if we found a data chunk, excellent!
      if (memcmp(chunk_id, "data", 4) == 0)
	  {
		  found = true;
		  u8 *temp = new u8[chunk_length];
		  if (inf.fread(temp,chunk_length) != chunk_length)
		  {
			  delete[] temp;
			  return false;
		  }
		  newWavData.fwrite(temp,chunk_length);
		  delete[] temp;
		  chunk_length = 0;
	  }

	  inf.fseek(chunk_length,SEEK_CUR);
	}

	return found;
}
Exemplo n.º 7
0
bool FCEUSS_SaveMS(EMUFILE* outstream, int compressionLevel)
{
	// reinit memory_savestate
	// memory_savestate is global variable which already has its vector of bytes, so no need to allocate memory every time we use save/loadstate
	memory_savestate.set_len(0);	// this also seeks to the beginning
	memory_savestate.unfail();

	EMUFILE* os = &memory_savestate;

	uint32 totalsize = 0;

	FCEUPPU_SaveState();
	FCEUSND_SaveState();
	totalsize=WriteStateChunk(os,1,SFCPU);
	totalsize+=WriteStateChunk(os,2,SFCPUC);
	totalsize+=WriteStateChunk(os,3,FCEUPPU_STATEINFO);
	totalsize+=WriteStateChunk(os,31,FCEU_NEWPPU_STATEINFO);
	totalsize+=WriteStateChunk(os,4,FCEUCTRL_STATEINFO);
	totalsize+=WriteStateChunk(os,5,FCEUSND_STATEINFO);
	if(FCEUMOV_Mode(MOVIEMODE_PLAY|MOVIEMODE_RECORD|MOVIEMODE_FINISHED))
	{
		totalsize+=WriteStateChunk(os,6,FCEUMOV_STATEINFO);

		//MBG TAS Editor HACK HACK HACK!
		//do not save the movie state if we are in Taseditor! That would be a huge waste of time and space!
		if(!FCEUMOV_Mode(MOVIEMODE_TASEDITOR))
		{
			os->fseek(5,SEEK_CUR);
			int size = FCEUMOV_WriteState(os);
			os->fseek(-(size+5),SEEK_CUR);
			os->fputc(7);
			write32le(size, os);
			os->fseek(size,SEEK_CUR);

			totalsize += 5 + size;
		}
	}
	// save back buffer
	{
		extern uint8 *XBackBuf;
		uint32 size = 256 * 256 + 8;
		os->fputc(8);
		write32le(size, os);
		os->fwrite((char*)XBackBuf,size);
		totalsize += 5 + size;
	}

	if(SPreSave) SPreSave();
	totalsize+=WriteStateChunk(os,0x10,SFMDATA);
	if(SPreSave) SPostSave();

	//save the length of the file
	int len = memory_savestate.size();

	//sanity check: len and totalsize should be the same
	if(len != totalsize)
	{
		FCEUD_PrintError("sanity violation: len != totalsize");
		return false;
	}

	int error = Z_OK;
	uint8* cbuf = (uint8*)memory_savestate.buf();
	uLongf comprlen = -1;
	if(compressionLevel != Z_NO_COMPRESSION && (compressSavestates || FCEUMOV_Mode(MOVIEMODE_TASEDITOR)))
	{
		// worst case compression: zlib says "0.1% larger than sourceLen plus 12 bytes"
		comprlen = (len>>9)+12 + len;
		if (compressed_buf.size() < comprlen) compressed_buf.resize(comprlen);
		cbuf = &compressed_buf[0];
		// do compression
		error = compress2(cbuf, &comprlen, (uint8*)memory_savestate.buf(), len, compressionLevel);
	}
Exemplo n.º 8
0
FCEUFILE * FCEU_fopen(const char *path, const char *ipsfn, char *mode, char *ext, int index, const char** extensions)
{
	FILE *ipsfile=0;
	FCEUFILE *fceufp=0;

	bool read = (std::string)mode == "rb";
	bool write = (std::string)mode == "wb";
	if((read&&write) || (!read&&!write))
	{
		FCEU_PrintError("invalid file open mode specified (only wb and rb are supported)");
		return 0;
	}

	std::string archive,fname,fileToOpen;
	FCEU_SplitArchiveFilename(path,archive,fname,fileToOpen);
	

	//try to setup the ips file
	if(ipsfn && read)
		ipsfile=FCEUD_UTF8fopen(ipsfn,"rb");
	if(read)
	{
		ArchiveScanRecord asr = FCEUD_ScanArchive(fileToOpen);
		asr.files.FilterByExtension(extensions);
		if(!asr.isArchive())
		{
			//if the archive contained no files, try to open it the old fashioned way
			EMUFILE_FILE* fp = FCEUD_UTF8_fstream(fileToOpen,mode);
			if(!fp || (fp->get_fp() == NULL))
			{
				return 0;
			}

			//try to read a zip file
			{
				fceufp = TryUnzip(fileToOpen);
				if(fceufp) {
					delete fp;
					fceufp->filename = fileToOpen;
					fceufp->logicalPath = fileToOpen;
					fceufp->fullFilename = fileToOpen;
					fceufp->archiveIndex = -1;
					goto applyips;
				}
			}

			//try to read a gzipped file
			{
				uint32 magic;
				
				magic = fp->fgetc();
				magic|=fp->fgetc()<<8;
				magic|=fp->fgetc()<<16;
				fp->fseek(0,SEEK_SET);

				if(magic==0x088b1f) {
					 // maybe gzip... 

					void* gzfile = gzopen(fileToOpen.c_str(),"rb");
					if(gzfile) {
						delete fp;

						int size;
						for(size=0; gzgetc(gzfile) != EOF; size++) {}
						EMUFILE_MEMORY* ms = new EMUFILE_MEMORY(size);
						gzseek(gzfile,0,SEEK_SET);
						gzread(gzfile,ms->buf(),size);
						gzclose(gzfile);

						fceufp = new FCEUFILE();
						fceufp->filename = fileToOpen;
						fceufp->logicalPath = fileToOpen;
						fceufp->fullFilename = fileToOpen;
						fceufp->archiveIndex = -1;
						fceufp->stream = ms;
						fceufp->size = size;
						goto applyips;
					}
				}
			}

		
			//open a plain old file
			fceufp = new FCEUFILE();
			fceufp->filename = fileToOpen;
			fceufp->logicalPath = fileToOpen;
			fceufp->fullFilename = fileToOpen;
			fceufp->archiveIndex = -1;
			fceufp->stream = fp;
			FCEU_fseek(fceufp,0,SEEK_END);
			fceufp->size = FCEU_ftell(fceufp);
			FCEU_fseek(fceufp,0,SEEK_SET);
			goto applyips;
		}
		else
		{
			//open an archive file
			if(archive == "")
				if(index != -1)
					fceufp = FCEUD_OpenArchiveIndex(asr, fileToOpen, index);
				else 
					fceufp = FCEUD_OpenArchive(asr, fileToOpen, 0);
			else
				fceufp = FCEUD_OpenArchive(asr, archive, &fname);

			if(!fceufp) return 0;

			FileBaseInfo fbi = DetermineFileBase(fileToOpen);
			fceufp->logicalPath = fbi.filebasedirectory + fceufp->filename;
			goto applyips;
		}

	applyips:
		//try to open the ips file
		if(!ipsfile && !ipsfn)
			ipsfile=FCEUD_UTF8fopen(FCEU_MakeIpsFilename(DetermineFileBase(fceufp->logicalPath.c_str())),"rb");
		ApplyIPS(ipsfile,fceufp);
		return fceufp;
	}
	return 0;
}
Exemplo n.º 9
0
static FCEUFILE * TryUnzip(const std::string& path) {
	unzFile tz;
	if((tz=unzOpen(path.c_str())))  // If it's not a zip file, use regular file handlers.
		// Assuming file type by extension usually works,
		// but I don't like it. :)
	{
		if(unzGoToFirstFile(tz)==UNZ_OK)
		{
			for(;;)
			{
				char tempu[512];	// Longer filenames might be possible, but I don't
				// think people would name files that long in zip files...
				unzGetCurrentFileInfo(tz,0,tempu,512,0,0,0,0);
				tempu[511]=0;
				if(strlen(tempu)>=4)
				{
					char *za=tempu+strlen(tempu)-4;

					//if(!ext)
					{
						if(!strcasecmp(za,".nes") || !strcasecmp(za,".fds") ||
							!strcasecmp(za,".nsf") || !strcasecmp(za,".unf") ||
							!strcasecmp(za,".nez"))
							break;
					}
					//else if(!strcasecmp(za,ext))
					//	break;
				}
				if(strlen(tempu)>=5)
				{
					if(!strcasecmp(tempu+strlen(tempu)-5,".unif"))
						break;
				}
				if(unzGoToNextFile(tz)!=UNZ_OK)
				{
					if(unzGoToFirstFile(tz)!=UNZ_OK) goto zpfail;
					unzCloseCurrentFile(tz);
					unzClose(tz);
					return 0;
				}
			}
			if(unzOpenCurrentFile(tz)!=UNZ_OK)
				goto zpfail;
		}
		else
		{
zpfail:
			unzClose(tz);
			return 0;
		}

		unz_file_info ufo;
		unzGetCurrentFileInfo(tz,&ufo,0,0,0,0,0,0);
		
		int size = ufo.uncompressed_size;
		EMUFILE_MEMORY* ms = new EMUFILE_MEMORY(size);
		unzReadCurrentFile(tz,ms->buf(),ufo.uncompressed_size);
		unzCloseCurrentFile(tz);
		unzClose(tz);

		FCEUFILE *fceufp = new FCEUFILE();
		fceufp->stream = ms;
		fceufp->size = size;
		return fceufp;
		
	}

	return 0;
}
Exemplo n.º 10
0
bool FCEUSS_SaveMS(EMUFILE* outstream, int compressionLevel)
{
	//a temp memory stream. we'll dump some data here and then compress
	//TODO - support dumping directly without compressing to save a buffer copy

	EMUFILE_MEMORY ms;
	EMUFILE* os = &ms;

	uint32 totalsize = 0;

	FCEUPPU_SaveState();
	FCEUSND_SaveState();
	totalsize=WriteStateChunk(os,1,SFCPU);
	totalsize+=WriteStateChunk(os,2,SFCPUC);
	totalsize+=WriteStateChunk(os,3,FCEUPPU_STATEINFO);
	totalsize+=WriteStateChunk(os,31,FCEU_NEWPPU_STATEINFO);
	totalsize+=WriteStateChunk(os,4,FCEUCTRL_STATEINFO);
	totalsize+=WriteStateChunk(os,5,FCEUSND_STATEINFO);
	if(FCEUMOV_Mode(MOVIEMODE_PLAY|MOVIEMODE_RECORD|MOVIEMODE_FINISHED))
	{
		totalsize+=WriteStateChunk(os,6,FCEUMOV_STATEINFO);

		//MBG tasedit HACK HACK HACK!
		//do not save the movie state if we are in tasedit! that is a huge waste of time and space!
		if(!FCEUMOV_Mode(MOVIEMODE_TASEDIT))
		{
			os->fseek(5,SEEK_CUR);
			int size = FCEUMOV_WriteState(os);
			os->fseek(-(size+5),SEEK_CUR);
			os->fputc(7);
			write32le(size, os);
			os->fseek(size,SEEK_CUR);

			totalsize += 5 + size;
		}
	}
	// save back buffer
	{
		extern uint8 *XBackBuf;
		uint32 size = 256 * 256 + 8;
		os->fputc(8);
		write32le(size, os);
		os->fwrite((char*)XBackBuf,size);
		totalsize += 5 + size;
	}

	if(SPreSave) SPreSave();
	totalsize+=WriteStateChunk(os,0x10,SFMDATA);
	if(SPreSave) SPostSave();

	//save the length of the file
	int len = ms.size();

	//sanity check: len and totalsize should be the same
	if(len != totalsize)
	{
		FCEUD_PrintError("sanity violation: len != totalsize");
		return false;
	}

	int error = Z_OK;
	uint8* cbuf = (uint8*)ms.buf();
	uLongf comprlen = -1;
	if(compressionLevel != Z_NO_COMPRESSION && compressSavestates)
	{
		//worst case compression.
		//zlib says "0.1% larger than sourceLen plus 12 bytes"
		comprlen = (len>>9)+12 + len;
		cbuf = new uint8[comprlen];
		error = compress2(cbuf,&comprlen,(uint8*)ms.buf(),len,compressionLevel);
	}
Exemplo n.º 11
0
int LoadStateEmbed(char *file) {
	GPUFreeze_t *gpufP;
	int Size;
	char header[32];
	FILE* fp;
	FILE* fp2;
	uint8 * embSaveTmp;
	size_t blockSize = Movie.memoryCard1Offset-Movie.saveStateOffset;

	embSaveTmp = (uint8*)malloc(blockSize);
	fp = fopen(file,"rb");
	fp2 = fopen("embsave.tmp","wb");
	fseek(fp, Movie.saveStateOffset, SEEK_SET);
	fread(embSaveTmp, 1, blockSize, fp);
	fwrite(embSaveTmp, 1, blockSize, fp2);
	fclose(fp);
	fclose(fp2);

	EMUFILE_FILE ef("embsave.tmp", "rb");
	if (ef.fail()) return -1;
	EMUFILE *f = &ef;

	psxCpu->Reset();

	gzread(f, header, 32);

	if (strncmp("STv3 PSXjin", header, 9)) { return -1; }

	exceptionPatches.clear();
	int tag;
	gzread(f, &tag, 4);
	if (tag == 'ExPs') {
		gzread(f, &Size, 4);
		while (Size--) {
			u32 addr, val;
			gzread(f, &addr, 4);
			gzread(f, &val, 4);
			exceptionPatches.push_back(std::make_pair(addr, val));
		}
		gzseek(f, 128*96*3-4-4-exceptionPatches.size()*4*2, SEEK_CUR);
	}
	else
		gzseek(f, 128*96*3-4, SEEK_CUR);

	gzread(f, psxM, 0x00200000);
	gzread(f, psxP, 0x00010000);
	gzread(f, psxR, 0x00080000);
	gzread(f, psxH, 0x00010000);
	gzread(f, (void*)&psxRegs, sizeof(psxRegs));

	if (Config.HLE)	//adelikat: TODO: remove all references to Config.HLE, we will not be using that BIOS, ever
		psxBiosFreeze(0);

	// gpu
	gpufP = (GPUFreeze_t *) malloc (sizeof(GPUFreeze_t));
	gzread(f, gpufP, sizeof(GPUFreeze_t));
	gpufP->extraData = malloc(gpufP->extraDataSize);
	gzread(f, gpufP->extraData, gpufP->extraDataSize);
	GPUfreeze(0, gpufP);
	free(gpufP->extraData);
	free(gpufP);

	sioFreeze(f, 0);
	cdrFreeze(f, 0);
	psxHwFreeze(f, 0);
	CDRisoFreeze(f,0);
	psxRcntFreeze(f, 0);
	mdecFreeze(f, 0);
	//TODO - no movie state? are you sure?

	// spu
	gzread(f, &Size, 4);
	EMUFILE_MEMORY memfile;
	memfile.truncate(Size);
	gzread(f, memfile.buf(), Size);
	bool ok = SPUunfreeze_new(&memfile);
	if(!ok) return 1;

	remove("embsave.tmp");

	return 0;
}
Exemplo n.º 12
0
int SaveStateEmbed(char *file) {
	GPUFreeze_t *gpufP;
	int Size;
	unsigned char *pMem;

	EMUFILE_FILE ef(file, "ab");
	if (ef.fail()) return -1;
	EMUFILE *f = &ef;

	gzwrite(f, (void*)PSXjinHeader, 32);

	pMem = (unsigned char *) malloc(128*96*3);
	if (pMem == NULL) return -1;
	//GPU_getScreenPic(pMem);
	memset(pMem,0,128*96*3);

	int tag = 'ExPs';
	gzwrite(f, &tag, 4);
	Size = exceptionPatches.size();
	gzwrite(f, &Size, 4);
	for (int i = 0; i < Size; i++) {
		gzwrite(f, &exceptionPatches[i].first, 4);
		gzwrite(f, &exceptionPatches[i].second, 4);
	}

	gzwrite(f, pMem, 128*96*3-4-4-Size*4*2);
	free(pMem);

	gzwrite(f, psxM, 0x00200000);
	gzwrite(f, psxP, 0x00010000);
	gzwrite(f, psxR, 0x00080000);
	gzwrite(f, psxH, 0x00010000);
	gzwrite(f, (void*)&psxRegs, sizeof(psxRegs));

	if (Config.HLE)
		psxBiosFreeze(1);

	// gpu
	gpufP = (GPUFreeze_t *) malloc(sizeof(GPUFreeze_t));
	gpufP->ulFreezeVersion = 1;
	GPUfreeze(1, gpufP);
	void* temp = gpufP->extraData;
	gpufP->extraData = 0;
	gzwrite(f, gpufP, sizeof(GPUFreeze_t));
	gzwrite(f, temp, gpufP->extraDataSize);
	GPUfreeze(3, gpufP);
	free(gpufP);

	sioFreeze(f, 1);
	cdrFreeze(f, 1);
	psxHwFreeze(f, 1);
	CDRisoFreeze(f,1);
	psxRcntFreeze(f, 1);
	mdecFreeze(f, 1);
	//TODO - no movie state? are you sure?

	// spu
	EMUFILE_MEMORY memfile;
	SPUfreeze_new(&memfile);
	Size = memfile.size();
	gzwrite(f, &Size, 4);
	gzwrite(f, memfile.buf(),Size);

	return 0;
}
Exemplo n.º 13
0
int LoadStateEmufile(EMUFILE *f) {
	GPUFreeze_t *gpufP;
	int Size;
	char header[32];

	printf("loadstate---\n");

	psxCpu->Reset();

	gzread(f, header, 32);
	if (strncmp("STv3 PSXjin", header, 9)) { return -1; }

	exceptionPatches.clear();
	int tag;
	gzread(f, &tag, 4);
	if (tag == 'ExPs') {
		gzread(f, &Size, 4);
		while (Size--) {
			u32 addr, val;
			gzread(f, &addr, 4);
			gzread(f, &val, 4);
			exceptionPatches.push_back(std::make_pair(addr, val));
		}
		gzseek(f, 128*96*3-4-4-exceptionPatches.size()*4*2, SEEK_CUR);
	}
	else
		gzseek(f, 128*96*3-4, SEEK_CUR);

	gzread(f, psxM, 0x00200000);
	gzread(f, psxP, 0x00010000);
	gzread(f, psxR, 0x00080000);
	gzread(f, psxH, 0x00010000);
	gzread(f, (void*)&psxRegs, sizeof(psxRegs));

	if (Config.HLE)
		psxBiosFreeze(0);

	// gpu
	gpufP = (GPUFreeze_t *) malloc (sizeof(GPUFreeze_t));
	gzread(f, gpufP, sizeof(GPUFreeze_t));
	gpufP->extraData = malloc(gpufP->extraDataSize);
	gzread(f, gpufP->extraData, gpufP->extraDataSize);
	GPUfreeze(0, gpufP);
	free(gpufP->extraData);
	free(gpufP);

	sioFreeze(f, 0);
	cdrFreeze(f, 0);
	psxHwFreeze(f, 0);
	CDRisoFreeze(f,0);
	psxRcntFreeze(f, 0);
	mdecFreeze(f, 0);
	PadFreeze(f, 0);
	MovieFreeze(f, 0);

	// spu
	gzread(f, &Size, 4);
	EMUFILE_MEMORY memfile;
	memfile.truncate(Size);
	gzread(f, memfile.buf(), Size);
	bool ok = SPUunfreeze_new(&memfile);
	if(!ok) return 1;

	return 0;
}
Exemplo n.º 14
0
int SaveStateEmufile(EMUFILE *f) {
	GPUFreeze_t *gpufP;
	int Size;
	unsigned char *pMem;

	gzwrite(f, (void*)PSXjinHeader, 32);

	pMem = (unsigned char *) malloc(128*96*3);
	if (pMem == NULL) return -1;
	memset(pMem,0,128*96*3);

	// Ugh. We need to store this information, but in a backwards-compatible fashion. Do this
	// by (ab)using the gap previously occupied by GPU_getScreenPic. The data is tagged so it
	// can be detected on savestate load
	int tag = 'ExPs';
	gzwrite(f, &tag, 4);
	Size = exceptionPatches.size();
	gzwrite(f, &Size, 4);
	for (int i = 0; i < Size; i++) {
		gzwrite(f, &exceptionPatches[i].first, 4);
		gzwrite(f, &exceptionPatches[i].second, 4);
	}

	gzwrite(f, pMem, 128*96*3-4-4-Size*4*2);
	free(pMem);

	gzwrite(f, psxM, 0x00200000);
	gzwrite(f, psxP, 0x00010000);
	gzwrite(f, psxR, 0x00080000);
	gzwrite(f, psxH, 0x00010000);
	gzwrite(f, (void*)&psxRegs, sizeof(psxRegs));

	if (Config.HLE)
		psxBiosFreeze(1);

	// gpu
	gpufP = (GPUFreeze_t *) malloc(sizeof(GPUFreeze_t));
	gpufP->ulFreezeVersion = 1;
	GPUfreeze(1, gpufP);
	void* temp = gpufP->extraData;
	gpufP->extraData = 0;
	gzwrite(f, gpufP, sizeof(GPUFreeze_t));
	gzwrite(f, temp, gpufP->extraDataSize);
	GPUfreeze(3, gpufP);
	free(gpufP);

	sioFreeze(f, 1);
	cdrFreeze(f, 1);
	psxHwFreeze(f, 1);
	CDRisoFreeze(f,1);
	psxRcntFreeze(f, 1);
	mdecFreeze(f, 1);
	PadFreeze(f, 1);
	MovieFreeze(f, 1);

	EMUFILE_MEMORY memfile;
	SPUfreeze_new(&memfile);
	Size = memfile.size();
	gzwrite(f, &Size, 4);
	gzwrite(f, memfile.buf(),Size);

	return 0;
}