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_ReadVariableSizeInt() // // Will read an int from a MEMFILE of unknown length. For each byte we read, // the highest bit will idicate if the next byte is to be read. Returns the // value or -1 if there was a read error. // static int I_ReadVariableSizeInt(MEMFILE *mf) { if (!mf) return -1; // In midi files the variable can use between 1 and 5 bytes // if the high bit is set, the next byte is used int var = 0; for (size_t i = 0; i < 5; i++) { byte curbyte = 0; size_t res = mem_fread(&curbyte, sizeof(curbyte), 1, mf); if (!res) return -1; // Insert the bottom seven bits from this byte. var = (var << 7) | (curbyte & 0x7f); // If the top bit is not set, this is the end. if ((curbyte & 0x80) == 0) break; } return var; }
// // I_ReadMidiTrack() // // Reads an entire midi track from memory and creates a list of MidiEvents // pointers where the event's start time is the time since the beginning of the // track. It is the caller's responsibility to delete the list returned // by this function. // static std::list<MidiEvent*> *I_ReadMidiTrack(MEMFILE *mf) { if (!mf) return NULL; midi_chunk_header_t chunkheader; unsigned int track_time = 0; size_t res = mem_fread(&chunkheader, cTrackHeaderSize, 1, mf); if (!res) return NULL; chunkheader.chunk_id = ntohl(chunkheader.chunk_id); chunkheader.chunk_size = ntohl(chunkheader.chunk_size); if (chunkheader.chunk_id != cTrackChunkId) { Printf(PRINT_HIGH, "I_ReadMidiTrack: Unexpected chunk header ID\n"); return NULL; } std::list<MidiEvent*> *eventlist = new std::list<MidiEvent*>; size_t trackend = mem_ftell(mf) + chunkheader.chunk_size; while (mem_ftell(mf) < int(trackend)) { MidiEvent *newevent = I_ReadMidiEvent(mf, track_time); if (!newevent) { Printf(PRINT_HIGH, "I_ReadMidiTrack: Unable to read MIDI event\n"); I_ClearMidiEventList(eventlist); delete eventlist; return NULL; } eventlist->push_back(newevent); track_time = newevent->getMidiClockTime(); } return eventlist; }
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; }
static int LoadStateOld(MEMFILE *st) { int x; int32 nada; uint8 version; nada=0; printf("LoadStateOld\n"); StateBuffer=FCEU_malloc(59999); if(StateBuffer==NULL) return 0; if(!mem_fread(StateBuffer,59999,1,st)) { mem_fclose(st); free(StateBuffer); return 0; } intostate=0; { uint8 a[2]; afread(&a[0],1,1); afread(&a[1],1,1); X.PC=a[0]|(a[1]<<8); } afread(&X.A,1,1); afread(&X.P,1,1); afread(&X.X,1,1); afread(&X.Y,1,1); afread(&X.S,1,1); afread(&version,1,1); afread(&nada,1,1); afread(&nada,1,1); afread(&nada,1,1); afread(&nada,1,1); aread32((int8 *)&X.count); afread(&nada,1,1); afread(&nada,1,1); afread(&nada,1,1); afread(&nada,1,1); aread32((int8 *)&nada); afread(&nada,1,1); afread(&nada,1,1); afread(&nada,1,1); afread(&nada,1,1); for(x=0;x<8;x++) areadupper8of16((int8 *)&CHRBankList[x]); afread(PRGBankList,4,1); for(x=0;x<8;x++) areadlower8of16((int8 *)&CHRBankList[x]); afread(CHRRAM,1,0x2000); afread(NTARAM,1,0x400); afread(ExtraNTARAM,1,0x400); afread(NTARAM+0x400,1,0x400); afread(ExtraNTARAM+0x400,1,0x400); for(x=0;x<0xF00;x++) afread(&nada,1,1); afread(PALRAM,1,0x20); for(x=0;x<256-32;x++) afread(&nada,1,1); for(x=0x00;x<0x20;x++) PALRAM[x]&=0x3f; afread(PPU,1,4); afread(SPRAM,1,0x100); afread(WRAM,1,8192); afread(RAM,1,0x800); aread16((int8 *)&scanline); aread16((int8 *)&RefreshAddr); afread(&VRAMBuffer,1,1); afread(&IRQa,1,1); aread32((int8 *)&IRQCount); aread32((int8 *)&IRQLatch); afread(&Mirroring,1,1); afread(PSG,1,0x17); PSG[0x11]&=0x7F; afread(MapperExRAM,1,193); if(version>=31) PSG[0x17]=MapperExRAM[115]; else PSG[0x17]|=0x40; PSG[0x15]&=0xF; sqnon=PSG[0x15]; X.IRQlow=0; afread(&nada,1,1); afread(&nada,1,1); afread(&nada,1,1); afread(&nada,1,1); afread(&nada,1,1); afread(&nada,1,1); afread(&XOffset,1,1); PPUCHRRAM=0; for(x=0;x<8;x++) { nada=0; afread(&nada,1,1); PPUCHRRAM|=(nada?1:0)<<x; } afread(mapbyte1,1,8); afread(mapbyte2,1,8); afread(mapbyte3,1,8); afread(mapbyte4,1,8); for(x=0;x<4;x++) aread16((int8 *)&nada); PPUNTARAM=0; for(x=0;x<4;x++) { nada=0; aread16((int8 *)&nada); PPUNTARAM|=((nada&0x800)?0:1)<<x; } afread(MapperExRAM,1,32768); afread(&vtoggle,1,1); aread16((int8 *)&TempAddrT); aread16((int8 *)&RefreshAddrT); if(GameStateRestore) GameStateRestore(version); free(StateBuffer); FixOldSaveStateSFreq(); X.mooPI=X.P; return 1; }
// // 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; }