//////////////////////////////////////////////////////////////////////////////////////// // // 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 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 buffer( chunks.Back() ); Read( buffer.Begin(), buffer.Size() ); if (Zlib::Uncompress( buffer.Begin(), buffer.Size(), data, length )) break; } default: throw RESULT_ERR_CORRUPT_FILE; } } } } }