blargg_err_t Nes_Snapshot::read( Data_Reader& in ) { Nes_Snapshot_Reader reader; BLARGG_RETURN_ERR( reader.begin( &in, this ) ); while ( !reader.done() ) BLARGG_RETURN_ERR( reader.next_block() ); return blargg_success; }
static blargg_err_t apply_ips_patch( Data_Reader& patch, byte** file, long* file_size ) { byte signature [5]; BLARGG_RETURN_ERR( patch.read( signature, sizeof signature ) ); if ( memcmp( signature, "PATCH", sizeof signature ) ) return "Not an IPS patch file"; while ( patch.remain() ) { // read offset byte buf [6]; BLARGG_RETURN_ERR( patch.read( buf, 3 ) ); long offset = buf [0] * 0x10000 + buf [1] * 0x100 + buf [2]; if ( offset == 'EOF' ) break; // read size BLARGG_RETURN_ERR( patch.read( buf, 2 ) ); long size = buf [0] * 0x100 + buf [1]; // size = 0 signals a run of identical bytes int fill = -1; if ( size == 0 ) { BLARGG_RETURN_ERR( patch.read( buf, 3 ) ); size = buf [0] * 0x100 + buf [1]; fill = buf [2]; } // expand file if new data is at exact end of file if ( offset == *file_size ) { *file_size = offset + size; void* p = realloc( *file, *file_size ); BLARGG_CHECK_ALLOC( p ); *file = (byte*) p; } //dprintf( "Patch offset: 0x%04X, size: 0x%04X\n", (int) offset, (int) size ); if ( offset < 0 || *file_size < offset + size ) return "IPS tried to patch past end of file"; // read/fill data if ( fill < 0 ) BLARGG_RETURN_ERR( patch.read( *file + offset, size ) ); else memset( *file + offset, fill, size ); } return blargg_success; }
blargg_err_t Nes_Snapshot_Reader::begin( Data_Reader* dr, Nes_Snapshot* out ) { snapshot_ = out; if ( !out ) { BLARGG_RETURN_ERR( snapshots.resize( 1 ) ); snapshot_ = &snapshots [0]; } BLARGG_RETURN_ERR( Nes_File_Reader::begin( dr ) ); if ( block_tag() != snapshot_file_tag ) return "Not a snapshot file"; return blargg_success; }
// Read big-endian 32-bit int static blargg_err_t read_be32( Emu_Reader& in, long* out ) { unsigned char tag [4]; BLARGG_RETURN_ERR( in.read( tag, sizeof tag ) ); *out = tag [0] * 0x1000000L + tag [1] * 0x10000L + tag [2] * 0x100L + tag [3]; return blargg_success; }
blargg_err_t Nes_Snapshot_Writer::end( Nes_Emu const& emu ) { Nes_Snapshot_Array snapshots; BLARGG_RETURN_ERR( snapshots.resize( 1 ) ); emu.save_snapshot( &snapshots [0] ); return end( snapshots [0] ); }
blargg_err_t Nes_Rewinder::load_ines_rom( Data_Reader& in, Data_Reader* ips ) { if ( !frames ) BLARGG_RETURN_ERR( init() ); return recorder::load_ines_rom( in, ips ); }
blargg_err_t Nes_Rewinder::init() { if ( !frames ) { BLARGG_RETURN_ERR( recorder::init() ); frames = BLARGG_NEW frame_t [frames_size]; BLARGG_CHECK_ALLOC( frames ); } return blargg_success; }
blargg_err_t Nes_Snapshot::read_sta_file( Data_Reader& in ) { sram_size = 0x2000; BLARGG_RETURN_ERR( in.read( sram, sram_size ) ); ram_valid = true; BLARGG_RETURN_ERR( in.read( ram, 0x800 ) ); sta_regs_t r; BLARGG_RETURN_ERR( in.read( &r, sizeof r ) ); this->cpu.pc = r.pc [1] * 0x100 + r.pc [0]; this->cpu.a = r.a; this->cpu.status = r.p; this->cpu.x = r.x; this->cpu.y = r.y; this->cpu.sp = r.s; cpu_valid = true; BLARGG_RETURN_ERR( in.read( spr_ram, 0x100 ) ); spr_ram_valid = true; chr_size = 0x2000; BLARGG_RETURN_ERR( in.read( chr, chr_size ) ); nametable_size = 0x1000; BLARGG_RETURN_ERR( in.read( nametable, nametable_size ) ); return blargg_success; }
// Read multiple strings and separate into individual strings static blargg_err_t read_strs( Emu_Reader& in, long size, std::vector<char>& chars, std::vector<const char*>& strs ) { chars.resize( size + 1 ); chars [size] = 0; // in case last string doesn't have terminator BLARGG_RETURN_ERR( in.read( &(*chars.begin()), size ) ); for ( int i = 0; i < size; i++ ) { //TODO WARNING Not quite sure how this memory is being used, or why we are holding char pointers in a vector // as opposed to making a copy... seems a little dicey and need to come back to it later assert(0); strs.push_back( &(*chars.begin()) + i ); while ( i < size && chars [i] ) i++; } return blargg_success; }
blargg_err_t Nes_Rewinder::next_frame( int joypad, int joypad2 ) { if ( reverse_enabled ) { if ( !get_film().empty() ) // if empty then we can't seek recorder::seek_( reversed_time ); clear_reverse(); } current_frame = 0; if ( quick_reverse ) { current_frame = recorder::tell() % frames_size; if ( buffer_scrambled ) buffer_scrambled--; } set_output( current_frame ); BLARGG_RETURN_ERR( recorder::next_frame( joypad, joypad2 ) ); frame_rendered( current_frame, quick_reverse ); return blargg_success; }
blargg_err_t Nes_Rom::load_ines_rom( Data_Reader& in ) { ines_header_t h; BLARGG_RETURN_ERR( in.read( &h, sizeof h ) ); if ( 0 != memcmp( h.signature, "NES\x1A", 4 ) ) return "Not a iNES ROM file"; if ( h.zero [7] ) // handle header defaced by a f*****g idiot's handle h.flags2 = 0; set_mapper( h.flags, h.flags2 ); if ( h.flags & 0x04 ) // skip trainer BLARGG_RETURN_ERR( in.skip( 512 ) ); BLARGG_RETURN_ERR( resize_prg( h.prg_count * 16 * 1024L ) ); BLARGG_RETURN_ERR( resize_chr( h.chr_count * 8 * 1024L ) ); BLARGG_RETURN_ERR( in.read( prg(), prg_size() ) ); BLARGG_RETURN_ERR( in.read( chr(), chr_size() ) ); return blargg_success; }
blargg_err_t Nes_Snapshot_Writer::end( Nes_Snapshot const& ss ) { BLARGG_RETURN_ERR( ss.write_blocks( *this ) ); return Nes_File_Writer::end(); }
blargg_err_t Nsfe_Emu::load( Emu_Reader& in ) { header_t h; BLARGG_RETURN_ERR( in.read( &h, sizeof h ) ); return load( h, in ); }
blargg_err_t Nsfe_Emu::load( const header_t& nsfe_tag, Emu_Reader& in ) { // check header if ( memcmp( nsfe_tag.tag, "NSFE", 4 ) ) return "Not an NSFE file"; // free previous info track_name_data.clear(); track_names.clear(); playlist.clear(); track_times.clear(); // default nsf header static const Nsf_Emu::header_t base_header = { 'N','E','S','M','\x1A', // tag 1, // version 1, 1, // track count, first track 0,0,0,0,0,0, // addresses "","","", // strings { 0x1A, 0x41 }, // NTSC rate { 0 }, // banks { 0x20, 0x4E }, // PAL rate 0,0, // flags 0,0,0,0 // unused }; Nsf_Emu::header_t& header = info_; header = base_header; // parse tags int phase = 0; while ( phase != 3 ) { // read size and tag long size = 0; long tag = 0; BLARGG_RETURN_ERR( read_le32( in, &size ) ); BLARGG_RETURN_ERR( read_be32( in, &tag ) ); switch ( tag ) { case 'INFO': { check( phase == 0 ); if ( size < 8 ) return "Bad NSFE file"; nsfe_info_t info; info.track_count = 1; info.first_track = 0; int s = size; if ( s > sizeof info ) s = sizeof info; BLARGG_RETURN_ERR( in.read( &info, s ) ); BLARGG_RETURN_ERR( in.skip( size - s ) ); phase = 1; info_.speed_flags = info.speed_flags; info_.chip_flags = info.chip_flags; info_.track_count = info.track_count; info_.first_track = info.first_track; std::memcpy( info_.load_addr, info.load_addr, 2 * 3 ); break; } case 'BANK': if ( size > sizeof info_.banks ) return "Bad NSFE file"; BLARGG_RETURN_ERR( in.read( info_.banks, size ) ); break; case 'auth': { std::vector<char> chars; std::vector<const char*> strs; BLARGG_RETURN_ERR( read_strs( in, size, chars, strs ) ); int n = strs.size(); if ( n > 3 ) copy_str( strs [3], info_.ripper, sizeof info_.ripper ); if ( n > 2 ) copy_str( strs [2], info_.copyright, sizeof info_.copyright ); if ( n > 1 ) copy_str( strs [1], info_.author, sizeof info_.author ); if ( n > 0 ) copy_str( strs [0], info_.game, sizeof info_.game ); break; } case 'time': { track_times.resize( size / 4 ); for ( int i = 0; i < track_times.size(); i++ ) BLARGG_RETURN_ERR( read_le32( in, &track_times [i] ) ); break; } case 'tlbl': BLARGG_RETURN_ERR( read_strs( in, size, track_name_data, track_names ) ); break; case 'plst': playlist.resize( size ); BLARGG_RETURN_ERR( in.read( &(*playlist.begin()), size ) ); break; case 'DATA': { check( phase == 1 ); phase = 2; Subset_Reader sub( &in, size ); // limit emu to nsf data BLARGG_RETURN_ERR( Nsf_Emu::load( info_, sub ) ); check( sub.remain() == 0 ); break; } case 'NEND': check( phase == 2 ); phase = 3; break; default: // tags that can be skipped start with a lowercase character check( islower( (tag >> 24) & 0xff ) ); BLARGG_RETURN_ERR( in.skip( size ) ); break; } } return blargg_success; }
blargg_err_t Nes_Snapshot::read_blocks( Nes_File_Reader& in ) { while ( true ) { BLARGG_RETURN_ERR( in.next_block() ); switch ( in.block_tag() ) { case nes.tag: memset( &nes, 0, sizeof nes ); BLARGG_RETURN_ERR( read_nes_state( in, &nes ) ); set_nes_state( nes ); break; case cpu_state_t::tag: { cpu_state_t s; memset( &s, 0, sizeof s ); BLARGG_RETURN_ERR( read_nes_state( in, &s ) ); cpu.pc = s.pc; cpu.sp = s.s; cpu.a = s.a; cpu.x = s.x; cpu.y = s.y; cpu.status = s.p; cpu_valid = true; break; } case ppu.tag: memset( &ppu, 0, sizeof ppu ); BLARGG_RETURN_ERR( read_nes_state( in, &ppu ) ); ppu_valid = true; break; case apu.tag: memset( &apu, 0, sizeof apu ); BLARGG_RETURN_ERR( read_nes_state( in, &apu ) ); apu_valid = true; break; case joypad.tag: memset( &joypad, 0, sizeof joypad ); BLARGG_RETURN_ERR( read_nes_state( in, &joypad ) ); joypad_valid = true; break; case 'MAPR': mapper.size = in.remain(); BLARGG_RETURN_ERR( in.read_block_data( mapper.data, sizeof mapper.data ) ); mapper_valid = true; break; case 'SPRT': spr_ram_valid = true; BLARGG_RETURN_ERR( in.read_block_data( spr_ram, sizeof spr_ram ) ); break; case 'NTAB': nametable_size = in.remain(); BLARGG_RETURN_ERR( in.read_block_data( nametable, sizeof nametable ) ); break; case 'LRAM': ram_valid = true; BLARGG_RETURN_ERR( in.read_block_data( ram, sizeof ram ) ); break; case 'CHRR': chr_size = in.remain(); BLARGG_RETURN_ERR( in.read_block_data( chr, sizeof chr ) ); break; case 'SRAM': sram_size = in.remain(); BLARGG_RETURN_ERR( in.read_block_data( sram, sizeof sram ) ); break; default: return blargg_success; } } }
blargg_err_t Nes_Snapshot::write( Data_Writer& out ) const { Nes_Snapshot_Writer writer; BLARGG_RETURN_ERR( writer.begin( &out ) ); return writer.end( *this ); }
blargg_err_t Stereo_Buffer::sample_rate( long rate, int msec ) { for ( int i = 0; i < buf_count; i++ ) BLARGG_RETURN_ERR( bufs [i].sample_rate( rate, msec ) ); return Multi_Buffer::sample_rate( bufs [0].sample_rate(), bufs [0].length() ); }
blargg_err_t Nes_Snapshot::write_blocks( Nes_File_Writer& out ) const { if ( nes_valid ) { nes_state_t s = nes; s.pal = false; s.timestamp = nes.timestamp * 15; BLARGG_RETURN_ERR( write_nes_state( out, s ) ); } if ( cpu_valid ) { cpu_state_t s; memset( &s, 0, sizeof s ); s.pc = cpu.pc; s.s = cpu.sp; s.a = cpu.a; s.x = cpu.x; s.y = cpu.y; s.p = cpu.status; BLARGG_RETURN_ERR( write_nes_state( out, s ) ); } if ( ppu_valid ) { ppu_state_t s = ppu; BLARGG_RETURN_ERR( write_nes_state( out, s ) ); } if ( apu_valid ) { apu_snapshot_t s = apu; BLARGG_RETURN_ERR( write_nes_state( out, s ) ); } if ( joypad_valid ) { joypad_state_t s = joypad; BLARGG_RETURN_ERR( write_nes_state( out, s ) ); } if ( mapper_valid ) BLARGG_RETURN_ERR( out.write_block( 'MAPR', mapper.data, mapper.size ) ); if ( ram_valid ) BLARGG_RETURN_ERR( out.write_block( 'LRAM', ram, sizeof ram ) ); if ( spr_ram_valid ) BLARGG_RETURN_ERR( out.write_block( 'SPRT', spr_ram, sizeof spr_ram ) ); if ( nametable_size ) BLARGG_RETURN_ERR( out.write_block( 'NTAB', nametable, nametable_size ) ); if ( chr_size ) BLARGG_RETURN_ERR( out.write_block( 'CHRR', chr, chr_size ) ); if ( sram_size ) BLARGG_RETURN_ERR( out.write_block( 'SRAM', sram, sram_size ) ); return blargg_success; }
blargg_err_t Mono_Buffer::sample_rate( long rate, int msec ) { BLARGG_RETURN_ERR( buf.sample_rate( rate, msec ) ); return Multi_Buffer::sample_rate( buf.sample_rate(), buf.length() ); }