nestopia/source/core/NstState.cpp
2013-01-14 16:40:40 -05:00

287 lines
6 KiB
C++

////////////////////////////////////////////////////////////////////////////////////////
//
// Nestopia - NES/Famicom emulator written in C++
//
// Copyright (C) 2003-2008 Martin Freij
//
// This file is part of Nestopia.
//
// Nestopia is free software; you can redistribute it and/or modify
// it under the terms of the GNU General Public License as published by
// the Free Software Foundation; either version 2 of the License, or
// (at your option) any later version.
//
// Nestopia 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 General Public License for more details.
//
// You should have received a copy of the GNU General Public License
// along with Nestopia; if not, write to the Free Software
// Foundation, Inc., 59 Temple Place, Suite 330, Boston, MA 02111-1307 USA
//
////////////////////////////////////////////////////////////////////////////////////////
#include "NstState.hpp"
#include "NstZlib.hpp"
namespace Nes
{
namespace Core
{
namespace State
{
enum Compression
{
NO_COMPRESSION,
ZLIB_COMPRESSION
};
#ifdef NST_MSVC_OPTIMIZE
#pragma optimize("s", on)
#endif
Saver::Saver(StdStream p,bool c,bool i,dword append)
: stream(p), chunks(CHUNK_RESERVE), useCompression(c), internal(i)
{
NST_COMPILE_ASSERT( CHUNK_RESERVE >= 2 );
chunks.SetTo(1);
chunks.Front() = 0;
if (append)
{
chunks.SetTo(2);
chunks[1] = append;
stream.Seek( 4 + 4 + append );
}
}
Saver::~Saver()
{
NST_VERIFY( chunks.Size() == 1 );
}
#ifdef NST_MSVC_OPTIMIZE
#pragma optimize("", on)
#endif
Saver& Saver::Begin(dword chunk)
{
stream.Write32( chunk );
stream.Write32( 0 );
chunks.Append( 0 );
return *this;
}
Saver& Saver::End()
{
NST_ASSERT( chunks.Size() > 1 );
const dword written = chunks.Pop();
chunks.Back() += 4 + 4 + written;
stream.Seek( -idword(written + 4) );
stream.Write32( written );
stream.Seek( written );
return *this;
}
Saver& Saver::Write8(uint data)
{
chunks.Back() += 1;
stream.Write8( data );
return *this;
}
Saver& Saver::Write16(uint data)
{
chunks.Back() += 2;
stream.Write16( data );
return *this;
}
Saver& Saver::Write32(dword data)
{
chunks.Back() += 4;
stream.Write32( data );
return *this;
}
Saver& Saver::Write64(qaword data)
{
chunks.Back() += 8;
stream.Write64( data );
return *this;
}
Saver& Saver::Write(const byte* data,dword length)
{
chunks.Back() += length;
stream.Write( data, length );
return *this;
}
Saver& Saver::Compress(const byte* const data,const dword length)
{
NST_VERIFY( length );
if (Zlib::AVAILABLE && useCompression && length > 1)
{
Vector<byte> buffer( length - 1 );
if (const dword compressed = Zlib::Compress( data, length, buffer.Begin(), buffer.Size(), Zlib::BEST_COMPRESSION ))
{
chunks.Back() += 1 + compressed;
stream.Write8( ZLIB_COMPRESSION );
stream.Write( buffer.Begin(), compressed );
return *this;
}
}
chunks.Back() += 1 + length;
stream.Write8( NO_COMPRESSION );
stream.Write( data, length );
return *this;
}
#ifdef NST_MSVC_OPTIMIZE
#pragma optimize("s", on)
#endif
Loader::Loader(StdStream p,bool c)
: stream(p), chunks(CHUNK_RESERVE), checkCrc(c)
{
chunks.SetTo(0);
}
Loader::~Loader()
{
NST_VERIFY( chunks.Size() <= 1 );
}
#ifdef NST_MSVC_OPTIMIZE
#pragma optimize("", on)
#endif
dword Loader::Begin()
{
if (chunks.Size() && !chunks.Back())
return 0;
const dword chunk = stream.Read32();
const dword length = stream.Read32();
if (chunks.Size())
{
if (chunks.Back() >= 4+4+length)
chunks.Back() -= 4+4+length;
else
throw RESULT_ERR_CORRUPT_FILE;
}
chunks.Append( length );
return chunk;
}
dword Loader::Length() const
{
return chunks.Size() ? chunks.Back() : 0;
}
dword Loader::Check()
{
return chunks.Size() && !chunks.Back() ? 0 : stream.Peek32();
}
void Loader::End()
{
if (const dword remaining = chunks.Pop())
{
NST_DEBUG_MSG("unreferenced state chunk data!");
stream.Seek( remaining );
}
}
void Loader::End(dword rollBack)
{
if (const idword back = -idword(rollBack+4+4) + idword(chunks.Pop()))
stream.Seek( back );
}
void Loader::CheckRead(dword length)
{
if (chunks.Back() >= length)
chunks.Back() -= length;
else
throw RESULT_ERR_CORRUPT_FILE;
}
uint Loader::Read8()
{
CheckRead( 1 );
return stream.Read8();
}
uint Loader::Read16()
{
CheckRead( 2 );
return stream.Read16();
}
dword Loader::Read32()
{
CheckRead( 4 );
return stream.Read32();
}
qaword Loader::Read64()
{
CheckRead( 8 );
return stream.Read64();
}
void Loader::Read(byte* const data,const dword length)
{
CheckRead( length );
stream.Read( data, length );
}
void Loader::Uncompress(byte* const data,const dword length)
{
NST_VERIFY( length );
switch (Read8())
{
case NO_COMPRESSION:
Read( data, length );
break;
case ZLIB_COMPRESSION:
if (!Zlib::AVAILABLE)
{
throw RESULT_ERR_UNSUPPORTED;
}
else if (chunks.Back())
{
Vector<byte> buffer( chunks.Back() );
Read( buffer.Begin(), buffer.Size() );
if (Zlib::Uncompress( buffer.Begin(), buffer.Size(), data, length ))
break;
}
default:
throw RESULT_ERR_CORRUPT_FILE;
}
}
}
}
}