static int ReadStateChunk(MEMFILE *st, SFORMAT *sf, int size) { //if(scan_chunks) // return mem_fseek(st,size,SEEK_CUR) == 0; SFORMAT *tmp; int temp; temp=mem_ftell(st); while(mem_ftell(st)<temp+size) { uint32 tsize; char toa[4]; if(mem_fread(toa,1,4,st)<=0) return 0; mem_read32le(&tsize,st); if((tmp=CheckS(sf,tsize,toa))) { mem_fread((uint8 *)tmp->v,1,tmp->s&(~RLSB),st); #ifndef LSB_FIRST if(tmp->s&RLSB) FlipByteOrder(tmp->v,tmp->s&(~RLSB)); #endif } else { mem_fseek(st,tsize,SEEK_CUR); printf("ReadStateChunk: sect \"%c%c%c%c\" not handled\n", toa[0], toa[1], toa[2], toa[3]); } } // while(...) return 1; }
// // I_ReadDataBlock() // // Returns a handle to the internal storage of a MEMFILE and moves the read // position of the MEMFILE forward to the end of the block. // static byte* I_ReadDataBlock(MEMFILE *mf, size_t length) { if (!mf) return NULL; size_t memfileoffset = mem_ftell(mf); if (mem_fsize(mf) < memfileoffset + length) return NULL; byte* data = (byte*)mem_fgetbuf(mf) + memfileoffset; mem_fseek(mf, length, MEM_SEEK_CUR); return data; }
int FCEUSS_LoadFP(MEMFILE *st, int make_backup) { int x; if(st!=NULL) { uint8 header[16]; mem_fread(&header,1,16,st); if(memcmp(header,"FCS",3)) { mem_fseek(st,0,SEEK_SET); if(!LoadStateOld(st)) goto lerror; goto okload; } stateversion=header[3]; if(stateversion<53) FixOldSaveStateSFreq(); x=ReadStateChunks(st); if(GameStateRestore) GameStateRestore(header[3]); if(x) { okload: TempAddr=TempAddrT; RefreshAddr=RefreshAddrT; SaveStateStatus[CurrentState]=1; } else { SaveStateStatus[CurrentState]=1; FCEU_DispMessage("Error(s) reading state %d!",CurrentState); } } else { lerror: FCEU_DispMessage("State %d load error.",CurrentState); SaveStateStatus[CurrentState]=0; return 0; } return 1; }
void SaveState(const char *fname) { MEMFILE *st=NULL; TempAddrT=TempAddr; RefreshAddrT=RefreshAddr; if(geniestage==1) { FCEU_DispMessage("Cannot save FCS in GG screen."); return; } st=mem_fopen_write(fname); if(st!=NULL) { static uint32 totalsize; static uint8 header[16]="FCS"; memset(header+4,0,13); header[3]=VERSION_NUMERIC; mem_fwrite(header,1,16,st); #ifdef ASM_6502 asmcpu_pack(); #endif totalsize=WriteStateChunk(st,1,SFCPU); totalsize+=WriteStateChunk(st,2,SFCPUC); totalsize+=WriteStateChunk(st,3,FCEUPPU_STATEINFO); totalsize+=WriteStateChunk(st,4,FCEUCTRL_STATEINFO); totalsize+=WriteStateChunk(st,5,SFSND); if(SPreSave) SPreSave(); totalsize+=WriteStateChunk(st,0x10,SFMDATA); if(SPostSave) SPostSave(); mem_fseek(st,4,SEEK_SET); mem_write32(totalsize,st); SaveStateStatus[CurrentState]=1; mem_fclose(st); } }
static int ReadStateChunks(MEMFILE *st) { int t; uint32 size; int ret=1; for(;;) { t=mem_fgetc(st); if(t==EOF) break; if(!mem_read32(&size,st)) break; // printf("ReadStateChunks: chunk %i\n", t); switch(t) { case 1:if(!ReadStateChunk(st,SFCPU,size)) ret=0; #ifdef ASM_6502 asmcpu_unpack(); #endif break; case 2:if(!ReadStateChunk(st,SFCPUC,size)) ret=0; else { X.mooPI=X.P; // Quick and dirty hack. } break; case 3:if(!ReadStateChunk(st,FCEUPPU_STATEINFO,size)) ret=0;break; case 4:if(!ReadStateChunk(st,FCEUCTRL_STATEINFO,size)) ret=0;break; case 5:if(!ReadStateChunk(st,SFSND,size)) ret=0;break; case 0x10:if(!ReadStateChunk(st,SFMDATA,size)) ret=0;break; default:printf("ReadStateChunks: unknown chunk: %i\n", t); if(mem_fseek(st,size,SEEK_CUR)<0) goto endo;break; } } endo: return ret; }
// // MidiSong::_ParseSong() // // Loads a midi song stored in the MEMFILE, parses the header, and reads // all of the midi events from the song. If the midi song has multiple // tracks, they are merged into a single stream of events. // // Based partially on an implementation from Chocolate Doom by Simon Howard. // void MidiSong::_ParseSong(MEMFILE *mf) { if (!mf) return; I_ClearMidiEventList(&mEvents); mem_fseek(mf, 0, MEM_SEEK_SET); midi_chunk_header_t chunkheader; if (!mem_fread(&chunkheader, cTrackHeaderSize, 1, mf)) return; chunkheader.chunk_id = ntohl(chunkheader.chunk_id); chunkheader.chunk_size = ntohl(chunkheader.chunk_size); if (chunkheader.chunk_id != cHeaderChunkId) { Printf(PRINT_HIGH, "MidiSong::_ParseSong: Unexpected file header ID\n"); return; } midi_header_t fileheader; if (!mem_fread(&fileheader, cHeaderSize, 1, mf)) return; fileheader.format_type = ntohs(fileheader.format_type); fileheader.num_tracks = ntohs(fileheader.num_tracks); fileheader.time_division = ntohs(fileheader.time_division); mTimeDivision = fileheader.time_division; if (fileheader.format_type != 0 && fileheader.format_type != 1) { Printf(PRINT_HIGH, "MidiSong::_ParseSong: Only type 0 or type 1 MIDI files are supported.\n"); return; } // Read all the tracks and merge them into one stream of events for (size_t i = 0; i < fileheader.num_tracks; i++) { std::list<MidiEvent*> *eventlist = I_ReadMidiTrack(mf); if (!eventlist) { Printf(PRINT_HIGH, "MidiSong::_ParseSong: Error reading track %d.\n", i + 1); return; } // add this track's list of events to the song's list while (!eventlist->empty()) { mEvents.push_back(eventlist->front()); eventlist->pop_front(); } delete eventlist; } // sort the stream of events by start time // (only needed if we're merging multiple tracks) if (fileheader.num_tracks > 1) mEvents.sort(I_CompareMidiEventTimes); }
// // I_ReadMidiEvent() // // Parses a MEMFILE and will create and return a MidiEvent object of the // next midi event stored in the MEMFILE. It will return NULL if there is // an error. // static MidiEvent* I_ReadMidiEvent(MEMFILE *mf, unsigned int start_time) { if (!mf) return NULL; int delta_time = I_ReadVariableSizeInt(mf); if (delta_time == -1) return NULL; unsigned int event_time = start_time + delta_time; // Read event type byte val; if (!mem_fread(&val, sizeof(val), 1, mf)) return NULL; midi_event_type_t eventtype = static_cast<midi_event_type_t>(val); static midi_event_type_t prev_eventtype = eventtype; // All event types have their top bit set. Therefore, if // the top bit is not set, it is because we are using the "same // as previous event type" shortcut to save a byte. Skip back // a byte so that we read this byte again. if ((eventtype & 0x80) == 0) { eventtype = prev_eventtype; mem_fseek(mf, -1, MEM_SEEK_CUR); // put the byte back in the memfile } else prev_eventtype = eventtype; if (I_IsMidiSysexEvent(eventtype)) { int length = I_ReadVariableSizeInt(mf); if (length == -1) return NULL; byte* data = I_ReadDataBlock(mf, length); if (!data) return NULL; return new MidiSysexEvent(event_time, data, length); } if (I_IsMidiMetaEvent(eventtype)) { byte val; if (!mem_fread(&val, sizeof(val), 1, mf)) return NULL; midi_meta_event_type_t metatype = static_cast<midi_meta_event_type_t>(val); int length = I_ReadVariableSizeInt(mf); if (length == -1) return NULL; byte* data = I_ReadDataBlock(mf, length); if (!data) return NULL; return new MidiMetaEvent(event_time, metatype, data, length); } // Channel Events only use the highest 4 bits to denote the type // Lower four bits denote the channel int channel = eventtype & 0x0F; eventtype = static_cast<midi_event_type_t>(int(eventtype) & 0xF0); if (I_IsMidiControllerEvent(eventtype)) { byte val, param1 = 0; if (!mem_fread(&val, sizeof(val), 1, mf)) return NULL; midi_controller_t controllertype = static_cast<midi_controller_t>(val); if (!mem_fread(¶m1, sizeof(param1), 1, mf)) return NULL; return new MidiControllerEvent(event_time, controllertype, channel, param1); } if (I_IsMidiChannelEvent(eventtype)) { byte param1 = 0, param2 = 0; if (!mem_fread(¶m1, sizeof(param1), 1, mf)) return NULL; if (eventtype != MIDI_EVENT_PROGRAM_CHANGE && eventtype != MIDI_EVENT_CHAN_AFTERTOUCH) { // this is an event that uses two parameters if (!mem_fread(¶m2, sizeof(param2), 1, mf)) return NULL; } return new MidiChannelEvent(event_time, eventtype, channel, param1, param2); } // none of the above? return NULL; }