quickerNES/core/Nes_State.cpp

285 lines
6.4 KiB
C++

// Nes_Emu 0.7.0. http://www.slack.net/~ant/
#include "Nes_State.h"
#include <stdlib.h>
#include <string.h>
#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<unsigned>(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
const char * Nes_State_Writer::end( Nes_Emu const& emu )
{
Nes_State* state = new Nes_State;
CHECK_ALLOC( state );
emu.save_state( state );
const char * err = end( *state );
delete state;
return err;
}
const char * Nes_State_Writer::end( Nes_State const& ss )
{
RETURN_ERR( ss.write_blocks( *this ) );
return Nes_File_Writer::end();
}
const char * Nes_State::write( Auto_File_Writer out ) const
{
Nes_State_Writer writer;
RETURN_ERR( writer.begin( out ) );
return writer.end( *this );
}
const char * 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 )
{
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 ) );
if ( sram_size )
RETURN_ERR( out.write_block( FOUR_CHAR('SRAM'), sram, sram_size ) );
return 0;
}
// read
Nes_State_Reader::Nes_State_Reader() { state_ = 0; owned = 0; }
Nes_State_Reader::~Nes_State_Reader() { delete owned; }
const char * Nes_State_Reader::begin( Auto_File_Reader dr, Nes_State* out )
{
state_ = out;
if ( !out )
CHECK_ALLOC( state_ = owned = new Nes_State );
RETURN_ERR( Nes_File_Reader::begin( dr ) );
if ( block_tag() != state_file_tag )
return "Not a state snapshot file";
return 0;
}
const char * 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;
}
const char * 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;
}
const char * 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();
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;
}
}
}