mirror of
https://github.com/0ldsk00l/nestopia.git
synced 2025-04-02 10:31:51 -04:00
287 lines
6 KiB
C++
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;
|
|
}
|
|
}
|
|
}
|
|
}
|
|
}
|