// Nes_Emu 0.7.0. http://www.slack.net/~ant/ #include "Nes_State.h" #include #include #include "blargg_endian.h" #include "Nes_Emu.h" #include "Nes_Mapper.h" /* Copyright (C) 2004-2006 Shay Green. This module is free software; you can redistribute it and/or modify it under the terms of the GNU Lesser General Public License as published by the Free Software Foundation; either version 2.1 of the License, or (at your option) any later version. This module is distributed in the hope that it will be useful, but WITHOUT ANY WARRANTY; without even the implied warranty of MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the GNU Lesser General Public License for more details. You should have received a copy of the GNU Lesser General Public License along with this module; if not, write to the Free Software Foundation, Inc., 59 Temple Place, Suite 330, Boston, MA 02111-1307 USA */ #include "blargg_source.h" int mem_differs( void const* p, int cmp, unsigned long s ) { unsigned char const* cp = (unsigned char*) p; while ( s-- ) { if ( *cp++ != cmp ) return 1; } return 0; } Nes_State::Nes_State() { Nes_State_::cpu = &this->cpu; Nes_State_::joypad = &this->joypad; Nes_State_::apu = &this->apu; Nes_State_::ppu = &this->ppu; Nes_State_::mapper = &this->mapper; Nes_State_::ram = this->ram; Nes_State_::sram = this->sram; Nes_State_::spr_ram = this->spr_ram; Nes_State_::nametable = this->nametable; Nes_State_::chr = this->chr; } void Nes_State_::clear() { memset( &nes, 0, sizeof nes ); nes.frame_count = static_cast(invalid_frame_count); nes_valid = false; cpu_valid = false; joypad_valid = false; apu_valid = false; ppu_valid = false; mapper_valid = false; ram_valid = false; sram_size = 0; spr_ram_valid = false; nametable_size = 0; chr_size = 0; } // write blargg_err_t Nes_State_Writer::end( Nes_Emu const& emu ) { Nes_State* state = BLARGG_NEW Nes_State; CHECK_ALLOC( state ); emu.save_state( state ); blargg_err_t err = end( *state ); delete state; return err; } blargg_err_t Nes_State_Writer::end( Nes_State const& ss ) { RETURN_ERR( ss.write_blocks( *this ) ); return Nes_File_Writer::end(); } blargg_err_t Nes_State::write( Auto_File_Writer out ) const { Nes_State_Writer writer; RETURN_ERR( writer.begin( out ) ); return writer.end( *this ); } blargg_err_t Nes_State_::write_blocks( Nes_File_Writer& out ) const { if ( nes_valid ) { nes_state_t s = nes; s.timestamp *= 5; 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; RETURN_ERR( write_nes_state( out, s ) ); } if ( ppu_valid ) { ppu_state_t s = *ppu; RETURN_ERR( write_nes_state( out, s ) ); } if ( apu_valid ) { apu_state_t s = *apu; RETURN_ERR( write_nes_state( out, s ) ); } if ( joypad_valid ) { joypad_state_t s = *joypad; RETURN_ERR( write_nes_state( out, s ) ); } if ( mapper_valid ) RETURN_ERR( out.write_block( FOUR_CHAR('MAPR'), mapper->data, mapper->size ) ); if ( ram_valid ) RETURN_ERR( out.write_block( FOUR_CHAR('LRAM'), ram, ram_size ) ); if ( spr_ram_valid ) RETURN_ERR( out.write_block( FOUR_CHAR('SPRT'), spr_ram, spr_ram_size ) ); if ( nametable_size ) { check( nametable_size == 0x800 || nametable_size == 0x1000 ); RETURN_ERR( out.write_block_header( FOUR_CHAR('NTAB'), nametable_size ) ); RETURN_ERR( out.write( nametable, 0x800 ) ); if ( nametable_size > 0x800 ) RETURN_ERR( out.write( chr, 0x800 ) ); } if ( chr_size ) RETURN_ERR( out.write_block( FOUR_CHAR('CHRR'), chr, chr_size ) ); #ifdef __LIBRETRO__ // Maintain constant save state size. if ( sram_size ) RETURN_ERR( out.write_block( FOUR_CHAR('SRAM'), sram, sram_size ) ); #else // only save sram if it's been modified if ( sram_size && mem_differs( sram, 0xff, sram_size ) ) RETURN_ERR( out.write_block( FOUR_CHAR('SRAM'), sram, sram_size ) ); #endif return 0; } // read Nes_State_Reader::Nes_State_Reader() { state_ = 0; owned = 0; } Nes_State_Reader::~Nes_State_Reader() { delete owned; } blargg_err_t Nes_State_Reader::begin( Auto_File_Reader dr, Nes_State* out ) { state_ = out; if ( !out ) CHECK_ALLOC( state_ = owned = BLARGG_NEW Nes_State ); RETURN_ERR( Nes_File_Reader::begin( dr ) ); if ( block_tag() != state_file_tag ) return "Not a state snapshot file"; return 0; } blargg_err_t Nes_State::read( Auto_File_Reader in ) { Nes_State_Reader reader; RETURN_ERR( reader.begin( in, this ) ); while ( !reader.done() ) RETURN_ERR( reader.next_block() ); return 0; } blargg_err_t Nes_State_Reader::next_block() { if ( depth() != 0 ) return Nes_File_Reader::next_block(); return state_->read_blocks( *this ); } void Nes_State_::set_nes_state( nes_state_t const& s ) { nes = s; nes.timestamp /= 5; nes_valid = true; } blargg_err_t Nes_State_::read_blocks( Nes_File_Reader& in ) { while ( true ) { RETURN_ERR( in.next_block() ); switch ( in.block_tag() ) { case nes_state_t::tag: memset( &nes, 0, sizeof nes ); 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 ); 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_state_t::tag: memset( ppu, 0, sizeof *ppu ); RETURN_ERR( read_nes_state( in, ppu ) ); ppu_valid = true; break; case apu_state_t::tag: memset( apu, 0, sizeof *apu ); RETURN_ERR( read_nes_state( in, apu ) ); apu_valid = true; break; case joypad_state_t::tag: memset( joypad, 0, sizeof *joypad ); RETURN_ERR( read_nes_state( in, joypad ) ); joypad_valid = true; break; case FOUR_CHAR('MAPR'): mapper->size = in.remain(); RETURN_ERR( in.read_block_data( mapper->data, sizeof mapper->data ) ); mapper_valid = true; break; case FOUR_CHAR('SPRT'): spr_ram_valid = true; RETURN_ERR( in.read_block_data( spr_ram, spr_ram_size ) ); break; case FOUR_CHAR('NTAB'): nametable_size = in.remain(); check( nametable_size == 0x800 || nametable_size == 0x1000 ); RETURN_ERR( in.read( nametable, 0x800 ) ); if ( nametable_size > 0x800 ) RETURN_ERR( in.read( chr, 0x800 ) ); break; case FOUR_CHAR('LRAM'): ram_valid = true; RETURN_ERR( in.read_block_data( ram, ram_size ) ); break; case FOUR_CHAR('CHRR'): chr_size = in.remain(); RETURN_ERR( in.read_block_data( chr, chr_max ) ); break; case FOUR_CHAR('SRAM'): sram_size = in.remain(); RETURN_ERR( in.read_block_data( sram, sram_max ) ); break; default: return 0; } } }