mirror of
https://github.com/0ldsk00l/nestopia.git
synced 2025-04-02 10:31:51 -04:00
457 lines
8.4 KiB
C++
457 lines
8.4 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 <cstring>
|
|
#include <new>
|
|
#include <iosfwd>
|
|
#include "NstVector.hpp"
|
|
#include "NstCrc32.hpp"
|
|
#include "NstStream.hpp"
|
|
#include "NstPatcherUps.hpp"
|
|
|
|
namespace Nes
|
|
{
|
|
namespace Core
|
|
{
|
|
class Ups::Reader
|
|
{
|
|
public:
|
|
|
|
uint Read();
|
|
dword ReadInt();
|
|
dword ReadCrc();
|
|
|
|
private:
|
|
|
|
Stream::In stream;
|
|
dword remaining;
|
|
dword crc;
|
|
|
|
public:
|
|
|
|
explicit Reader(std::istream& stdStream)
|
|
:
|
|
stream (&stdStream),
|
|
remaining (stream.Length()),
|
|
crc (0)
|
|
{}
|
|
|
|
void ReadSignature()
|
|
{
|
|
if
|
|
(
|
|
Read() != Ascii<'U'>::V ||
|
|
Read() != Ascii<'P'>::V ||
|
|
Read() != Ascii<'S'>::V ||
|
|
Read() != Ascii<'1'>::V
|
|
)
|
|
throw RESULT_ERR_INVALID_FILE;
|
|
}
|
|
|
|
dword Remaining() const
|
|
{
|
|
return remaining;
|
|
}
|
|
|
|
dword Crc() const
|
|
{
|
|
return crc;
|
|
}
|
|
};
|
|
|
|
class Ups::Writer
|
|
{
|
|
public:
|
|
|
|
void Write(uint);
|
|
void Write(const byte* NST_RESTRICT,dword);
|
|
void WriteInt(dword);
|
|
void WriteCrc(dword);
|
|
|
|
private:
|
|
|
|
Stream::Out stream;
|
|
dword crc;
|
|
|
|
public:
|
|
|
|
explicit Writer(std::ostream& stdStream)
|
|
:
|
|
stream (&stdStream),
|
|
crc (0)
|
|
{}
|
|
|
|
void WriteSignature()
|
|
{
|
|
const byte data[] =
|
|
{
|
|
Ascii<'U'>::V,
|
|
Ascii<'P'>::V,
|
|
Ascii<'S'>::V,
|
|
Ascii<'1'>::V
|
|
};
|
|
|
|
Write( data, sizeof(data) );
|
|
}
|
|
|
|
dword Crc() const
|
|
{
|
|
return crc;
|
|
}
|
|
};
|
|
|
|
#ifdef NST_MSVC_OPTIMIZE
|
|
#pragma optimize("s", on)
|
|
#endif
|
|
|
|
Ups::Ups()
|
|
:
|
|
srcSize (0),
|
|
srcCrc (0),
|
|
dstSize (0),
|
|
dstCrc (0),
|
|
patch (NULL)
|
|
{
|
|
}
|
|
|
|
Ups::~Ups()
|
|
{
|
|
Destroy();
|
|
}
|
|
|
|
void Ups::Destroy()
|
|
{
|
|
srcSize = 0;
|
|
srcCrc = 0;
|
|
dstSize = 0;
|
|
dstCrc = 0;
|
|
|
|
delete [] patch;
|
|
patch = NULL;
|
|
}
|
|
|
|
#ifdef NST_MSVC_OPTIMIZE
|
|
#pragma optimize("", on)
|
|
#endif
|
|
|
|
bool Ups::IsUps(std::istream& stream)
|
|
{
|
|
try
|
|
{
|
|
return Stream::In(&stream).Peek32() == AsciiId<'U','P','S','1'>::V;
|
|
}
|
|
catch (...)
|
|
{
|
|
return false;
|
|
}
|
|
}
|
|
|
|
Result Ups::Load(std::istream& stdStream,const bool bypassChecksum)
|
|
{
|
|
Destroy();
|
|
|
|
try
|
|
{
|
|
Reader reader( stdStream );
|
|
|
|
reader.ReadSignature();
|
|
|
|
srcSize = reader.ReadInt();
|
|
dstSize = reader.ReadInt();
|
|
|
|
const dword size = NST_MAX(srcSize,dstSize) + 1;
|
|
|
|
patch = new byte [size];
|
|
std::memset( patch, 0, size );
|
|
|
|
for (dword i=0; reader.Remaining() > 4+4+4; ++i)
|
|
{
|
|
i += reader.ReadInt();
|
|
|
|
if (i > MAX_OFFSET)
|
|
throw RESULT_ERR_OUT_OF_MEMORY;
|
|
|
|
while (const uint data = reader.Read())
|
|
{
|
|
if (i < dstSize)
|
|
patch[i++] = data;
|
|
else
|
|
throw RESULT_ERR_CORRUPT_FILE;
|
|
}
|
|
}
|
|
|
|
srcCrc = reader.ReadCrc();
|
|
dstCrc = reader.ReadCrc();
|
|
|
|
const dword crc = reader.Crc();
|
|
const dword fileCrc = reader.ReadCrc();
|
|
|
|
if (!bypassChecksum && crc != fileCrc)
|
|
throw RESULT_ERR_INVALID_CRC;
|
|
}
|
|
catch (Result result)
|
|
{
|
|
Destroy();
|
|
return result;
|
|
}
|
|
catch (const std::bad_alloc&)
|
|
{
|
|
Destroy();
|
|
return RESULT_ERR_OUT_OF_MEMORY;
|
|
}
|
|
catch (...)
|
|
{
|
|
Destroy();
|
|
return RESULT_ERR_CORRUPT_FILE;
|
|
}
|
|
|
|
return RESULT_OK;
|
|
}
|
|
|
|
Result Ups::Save(std::ostream& stdStream) const
|
|
{
|
|
try
|
|
{
|
|
Writer writer( stdStream );
|
|
|
|
writer.WriteSignature();
|
|
|
|
writer.WriteInt( srcSize );
|
|
writer.WriteInt( dstSize );
|
|
|
|
for (dword i=0, offset=0, n=dstSize; i < n; ++i)
|
|
{
|
|
if (patch[i])
|
|
{
|
|
writer.WriteInt( i - offset );
|
|
offset = i;
|
|
|
|
while (patch[++i]);
|
|
|
|
NST_ASSERT( i <= n );
|
|
|
|
writer.Write( patch + offset, i - offset );
|
|
writer.Write( 0 );
|
|
|
|
offset = i + 1;
|
|
}
|
|
}
|
|
|
|
writer.WriteCrc( srcCrc );
|
|
writer.WriteCrc( dstCrc );
|
|
writer.WriteCrc( writer.Crc() );
|
|
}
|
|
catch (Result result)
|
|
{
|
|
return result;
|
|
}
|
|
catch (const std::bad_alloc&)
|
|
{
|
|
return RESULT_ERR_OUT_OF_MEMORY;
|
|
}
|
|
catch (...)
|
|
{
|
|
return RESULT_ERR_GENERIC;
|
|
}
|
|
|
|
return RESULT_OK;
|
|
}
|
|
|
|
Result Ups::Test(std::istream& stdStream,const bool bypassChecksum) const
|
|
{
|
|
Vector<byte> buffer( srcSize );
|
|
|
|
try
|
|
{
|
|
Stream::In stream( &stdStream );
|
|
|
|
if (stream.Length() < buffer.Size())
|
|
return RESULT_ERR_CORRUPT_FILE;
|
|
|
|
if (buffer.Size())
|
|
stream.Peek( buffer.Begin(), buffer.Size() );
|
|
}
|
|
catch (Result result)
|
|
{
|
|
return result;
|
|
}
|
|
catch (const std::bad_alloc&)
|
|
{
|
|
return RESULT_ERR_OUT_OF_MEMORY;
|
|
}
|
|
catch (...)
|
|
{
|
|
return RESULT_ERR_CORRUPT_FILE;
|
|
}
|
|
|
|
return Test( buffer.Begin(), buffer.Size(), bypassChecksum );
|
|
}
|
|
|
|
Result Ups::Test(const byte* const NST_RESTRICT src,const dword size,const bool bypassChecksum) const
|
|
{
|
|
NST_ASSERT( !size || src );
|
|
|
|
if (size < srcSize)
|
|
return RESULT_ERR_CORRUPT_FILE;
|
|
|
|
if (!bypassChecksum)
|
|
{
|
|
if (Crc32::Compute( src, srcSize ) != srcCrc)
|
|
return RESULT_ERR_INVALID_CRC;
|
|
|
|
dword crc = 0;
|
|
|
|
for (dword i=0, n=dstSize; i < n; ++i)
|
|
crc = Crc32::Compute( (i < size ? src[i] : 0U) ^ patch[i], crc );
|
|
|
|
if (crc != dstCrc)
|
|
return RESULT_ERR_INVALID_CRC;
|
|
}
|
|
|
|
return RESULT_OK;
|
|
}
|
|
|
|
bool Ups::Patch(const byte* const src,byte* const dst,const dword size,dword offset) const
|
|
{
|
|
NST_ASSERT( !size || (src && dst) );
|
|
|
|
uint patched = 0;
|
|
|
|
if (dstSize || src != dst)
|
|
{
|
|
for (dword i=0, end=dstSize; i < size; ++i)
|
|
dst[i] = src[i] ^ (offset < end ? patched |= patch[offset], patch[offset++] : 0U);
|
|
}
|
|
|
|
return patched;
|
|
}
|
|
|
|
Result Ups::Create(const byte* const src,const byte* const dst,const dword size)
|
|
{
|
|
NST_ASSERT( !size || (src && dst) );
|
|
|
|
Destroy();
|
|
|
|
if (size)
|
|
{
|
|
patch = new (std::nothrow) byte [size];
|
|
|
|
if (!patch)
|
|
return RESULT_ERR_OUT_OF_MEMORY;
|
|
|
|
srcSize = size;
|
|
dstSize = size;
|
|
|
|
srcCrc = Crc32::Compute( src, size );
|
|
dstCrc = Crc32::Compute( dst, size );
|
|
|
|
for (dword i=0; i < size; ++i)
|
|
patch[i] = uint(src[i]) ^ uint(dst[i]);
|
|
}
|
|
|
|
return RESULT_OK;
|
|
}
|
|
|
|
uint Ups::Reader::Read()
|
|
{
|
|
if (remaining)
|
|
remaining--;
|
|
else
|
|
throw 1;
|
|
|
|
const uint data = stream.Read8();
|
|
crc = Crc32::Compute( data, crc );
|
|
|
|
return data;
|
|
}
|
|
|
|
dword Ups::Reader::ReadInt()
|
|
{
|
|
dword v = 0, s = 0;
|
|
|
|
for (;;)
|
|
{
|
|
uint b = Read();
|
|
v += (b & 0x7F) << s;
|
|
|
|
if (v > MAX_SIZE)
|
|
throw 1;
|
|
|
|
if (b & 0x80)
|
|
break;
|
|
|
|
s += 7;
|
|
v += 0x1UL << s;
|
|
}
|
|
|
|
return v;
|
|
}
|
|
|
|
dword Ups::Reader::ReadCrc()
|
|
{
|
|
dword crc = 0;
|
|
|
|
for (uint i=0; i < 32; i += 8)
|
|
crc |= dword(Read()) << i;
|
|
|
|
return crc;
|
|
}
|
|
|
|
void Ups::Writer::Write(const uint data)
|
|
{
|
|
crc = Crc32::Compute( data, crc );
|
|
stream.Write8( data );
|
|
}
|
|
|
|
void Ups::Writer::Write(const byte* const NST_RESTRICT data,const dword size)
|
|
{
|
|
crc = Crc32::Compute( data, size, crc );
|
|
stream.Write( data, size );
|
|
}
|
|
|
|
void Ups::Writer::WriteCrc(const dword crc)
|
|
{
|
|
for (uint i=0; i < 32; i += 8)
|
|
Write( crc >> i & 0xFF );
|
|
}
|
|
|
|
void Ups::Writer::WriteInt(dword v)
|
|
{
|
|
for (;;)
|
|
{
|
|
uint x = v & 0x7F;
|
|
v >>= 7;
|
|
|
|
if (!v)
|
|
{
|
|
Write( 0x80 | x );
|
|
break;
|
|
}
|
|
|
|
Write( x );
|
|
v--;
|
|
}
|
|
}
|
|
}
|
|
}
|