You can not select more than 25 topics
Topics must start with a letter or number, can include dashes ('-') and can be up to 35 characters long.
251 lines
8.7 KiB
251 lines
8.7 KiB
14 years ago
|
/*==LICENSE==*
|
||
|
|
||
|
CyanWorlds.com Engine - MMOG client, server and tools
|
||
|
Copyright (C) 2011 Cyan Worlds, Inc.
|
||
|
|
||
|
This program 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 3 of the License, or
|
||
|
(at your option) any later version.
|
||
|
|
||
|
This program 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 this program. If not, see <http://www.gnu.org/licenses/>.
|
||
|
|
||
|
You can contact Cyan Worlds, Inc. by email legal@cyan.com
|
||
|
or by snail mail at:
|
||
|
Cyan Worlds, Inc.
|
||
|
14617 N Newport Hwy
|
||
|
Mead, WA 99021
|
||
|
|
||
|
*==LICENSE==*/
|
||
|
//////////////////////////////////////////////////////////////////////////////
|
||
|
//
|
||
|
// plDiffBuffer - A utility class for writing and applying a difference
|
||
|
// buffer--i.e. a buffer containing a series of modifications
|
||
|
// (specifically, adds and copys) that will modify an old
|
||
|
// data buffer to match a new one. It's a useful utility
|
||
|
// class when doing binary file patching, for example, as you
|
||
|
// can write out the changes to this class, get back a data
|
||
|
// buffer suitable for writing, then use this class again
|
||
|
// later to reconstruct the new buffer.
|
||
|
//
|
||
|
//// History /////////////////////////////////////////////////////////////////
|
||
|
//
|
||
|
// 7.24.2002 mcn - Created (Happy late b-day to me!)
|
||
|
//
|
||
|
//////////////////////////////////////////////////////////////////////////////
|
||
|
|
||
|
#include "hsTypes.h"
|
||
|
#include "plDiffBuffer.h"
|
||
|
#include "plBSDiffBuffer.h"
|
||
|
#include "hsUtils.h"
|
||
|
#include "hsStream.h"
|
||
|
|
||
|
|
||
|
//////////////////////////////////////////////////////////////////////////////
|
||
|
//// Constructor/Destructors /////////////////////////////////////////////////
|
||
|
//////////////////////////////////////////////////////////////////////////////
|
||
|
|
||
|
//// Creation Constructor ////////////////////////////////////////////////////
|
||
|
// Use this constructor when creating a new diff buffer. Pass in the length
|
||
|
// of the final new buffer. You don't need to pass in the length of the old
|
||
|
// buffer, but if you do, it'll help this class do some internal space
|
||
|
// optimization.
|
||
|
|
||
|
plDiffBuffer::plDiffBuffer( UInt32 newLength, UInt32 oldLength )
|
||
|
: fBSDiffBuffer( nil )
|
||
|
, fIsBSDiff( false )
|
||
|
{
|
||
|
// Basically, if the new and old lengths can both fit into 16 bits
|
||
|
// (not including a potential negation), then we can store all the
|
||
|
// segment info as 16-bit values instead of 32.
|
||
|
if( oldLength > 0 && oldLength < 32767 && newLength < 32767 )
|
||
|
f16BitMode = true;
|
||
|
else
|
||
|
f16BitMode = false;
|
||
|
|
||
|
fNewLength = newLength;
|
||
|
fStream = TRACKED_NEW hsRAMStream();
|
||
|
fStream->WriteSwap32( fNewLength );
|
||
|
fStream->WriteBool( f16BitMode );
|
||
|
fWriting = true;
|
||
|
}
|
||
|
|
||
|
//// Application Constructor /////////////////////////////////////////////////
|
||
|
// Use this constructor when taking a diff buffer and applying it to an old
|
||
|
// buffer. Pass in a pointer to the diff buffer and its length. The buffer
|
||
|
// will be copied, so you don't need to keep it around after you construct
|
||
|
// this object.
|
||
|
|
||
|
plDiffBuffer::plDiffBuffer( void *buffer, UInt32 length )
|
||
|
: fBSDiffBuffer( nil )
|
||
|
, fStream( nil )
|
||
|
, fIsBSDiff( false )
|
||
|
, fWriting( false )
|
||
|
{
|
||
|
// Check to see if this uses the newer BSDiff format
|
||
|
if ( buffer && length > 32 &&
|
||
|
memcmp(buffer,"BSDIFF40",8)==0 )
|
||
|
{
|
||
|
// This is a bsdiff buffer. Use plBSDiffBuffer to handle it.
|
||
|
fBSDiffBuffer = TRACKED_NEW plBSDiffBuffer(buffer, length);
|
||
|
fIsBSDiff = true;
|
||
|
}
|
||
|
else
|
||
|
{
|
||
|
fStream = TRACKED_NEW hsRAMStream();
|
||
|
fStream->Write( length, buffer );
|
||
|
fStream->Rewind();
|
||
|
|
||
|
fNewLength = fStream->ReadSwap32();
|
||
|
f16BitMode = fStream->ReadBool();
|
||
|
}
|
||
|
}
|
||
|
|
||
|
plDiffBuffer::~plDiffBuffer()
|
||
|
{
|
||
|
if (fStream)
|
||
|
delete fStream;
|
||
|
if (fBSDiffBuffer)
|
||
|
delete fBSDiffBuffer;
|
||
|
}
|
||
|
|
||
|
|
||
|
//////////////////////////////////////////////////////////////////////////////
|
||
|
//// Creation/Write Functions ////////////////////////////////////////////////
|
||
|
//////////////////////////////////////////////////////////////////////////////
|
||
|
|
||
|
//// Add /////////////////////////////////////////////////////////////////////
|
||
|
// Add() appends an Add-New-Data operation to the diff buffer. The data
|
||
|
// supplied will be copied internally, so you can discard it after you call
|
||
|
// this function.
|
||
|
|
||
|
void plDiffBuffer::Add( Int32 length, void *newData )
|
||
|
{
|
||
|
hsAssert( fWriting, "Trying to Add() to a difference buffer that's reading" );
|
||
|
|
||
|
// We flag our two different op types by the sign of the length. Negative
|
||
|
// lengths are an add operation, positive ones are copy ops.
|
||
|
if( f16BitMode )
|
||
|
fStream->WriteSwap16( -( (Int16)length ) );
|
||
|
else
|
||
|
fStream->WriteSwap32( -length );
|
||
|
fStream->Write( length, newData );
|
||
|
}
|
||
|
|
||
|
//// Copy ////////////////////////////////////////////////////////////////////
|
||
|
// Copy() appends a Copy-Data-From-Old operation to the diff buffer.
|
||
|
|
||
|
void plDiffBuffer::Copy( Int32 length, UInt32 oldOffset )
|
||
|
{
|
||
|
hsAssert( fWriting, "Trying to Copy() to a difference buffer that's reading" );
|
||
|
|
||
|
// We flag our two different op types by the sign of the length. Negative
|
||
|
// lengths are an add operation, positive ones are copy ops.
|
||
|
if( f16BitMode )
|
||
|
{
|
||
|
fStream->WriteSwap16( (Int16)length );
|
||
|
fStream->WriteSwap16( (UInt16)oldOffset );
|
||
|
}
|
||
|
else
|
||
|
{
|
||
|
fStream->WriteSwap32( length );
|
||
|
fStream->WriteSwap32( oldOffset );
|
||
|
}
|
||
|
}
|
||
|
|
||
|
//// GetBuffer ///////////////////////////////////////////////////////////////
|
||
|
// GetBuffer() will copy the diff stream into a new buffer and return it.
|
||
|
// You are responsible for freeing the buffer. Call this once you're done
|
||
|
// adding ops and want the raw data to write out somewhere. Note: this
|
||
|
// function will rewind the diff stream, so once you call it, you can't do
|
||
|
// anything else on the object.
|
||
|
|
||
|
void plDiffBuffer::GetBuffer( UInt32 &length, void *&bufferPtr )
|
||
|
{
|
||
|
hsAssert( fWriting, "Trying to GetBuffer() on a difference buffer that's reading" );
|
||
|
|
||
|
length = fStream->GetPosition();
|
||
|
bufferPtr = (void *)TRACKED_NEW UInt8[ length ];
|
||
|
|
||
|
fStream->Rewind();
|
||
|
fStream->Read( length, bufferPtr );
|
||
|
}
|
||
|
|
||
|
|
||
|
//////////////////////////////////////////////////////////////////////////////
|
||
|
//// Application Functions ///////////////////////////////////////////////////
|
||
|
//////////////////////////////////////////////////////////////////////////////
|
||
|
|
||
|
//// Apply ///////////////////////////////////////////////////////////////////
|
||
|
// Apply() will take this diff buffer and apply it to the given old buffer,
|
||
|
// allocating and producing a new buffer. You are responsible for freeing
|
||
|
// the new buffer.
|
||
|
|
||
|
#define hsAssertAndBreak( cond, msg ) { if( cond ) { hsAssert( false, msg ); break; } }
|
||
|
|
||
|
void plDiffBuffer::Apply( UInt32 oldLength, void *oldBuffer, UInt32 &newLength, void *&newBuffer )
|
||
|
{
|
||
|
hsAssert( !fWriting, "Trying to Apply() a difference buffer that's writing" );
|
||
|
|
||
|
// Is this is a BSDiff patch, use plBSDiffBuffer and return.
|
||
|
if (fIsBSDiff)
|
||
|
{
|
||
|
fBSDiffBuffer->Apply(oldLength, oldBuffer, newLength, newBuffer);
|
||
|
return;
|
||
|
}
|
||
|
|
||
|
/// Step 1: Allocate the new buffer
|
||
|
newLength = fNewLength;
|
||
|
UInt8 *new8Buffer = TRACKED_NEW UInt8[ newLength ];
|
||
|
UInt8 *old8Buffer = (UInt8 *)oldBuffer;
|
||
|
newBuffer = (void *)new8Buffer;
|
||
|
|
||
|
|
||
|
/// Step 2: Loop through the difference stream
|
||
|
Int32 opLength;
|
||
|
UInt32 newBufferPos = 0;
|
||
|
while( newBufferPos < newLength )
|
||
|
{
|
||
|
// Read in the op length
|
||
|
if( f16BitMode )
|
||
|
{
|
||
|
Int16 opLen16 = fStream->ReadSwap16();
|
||
|
if( opLen16 < 0 )
|
||
|
opLength = -( (Int32)( -opLen16 ) );
|
||
|
else
|
||
|
opLength = (UInt32)opLen16;
|
||
|
}
|
||
|
else
|
||
|
opLength = fStream->ReadSwap32();
|
||
|
|
||
|
// As defined, negative ops are add ops, positive ones are copys
|
||
|
if( opLength < 0 )
|
||
|
{
|
||
|
hsAssertAndBreak( newBufferPos - opLength > newLength, "Destination buffer offset in plDiffBuffer() is out of range!" );
|
||
|
|
||
|
// Add op, read in the added data
|
||
|
fStream->Read( -opLength, &new8Buffer[ newBufferPos ] );
|
||
|
newBufferPos += -opLength;
|
||
|
}
|
||
|
else
|
||
|
{
|
||
|
// Copy op, so get the old offset and copy from there
|
||
|
UInt32 oldOffset = f16BitMode ? fStream->ReadSwap16() : fStream->ReadSwap32();
|
||
|
|
||
|
hsAssertAndBreak( newBufferPos + opLength > newLength, "Destination buffer offset in plDiffBuffer() is out of range!" );
|
||
|
hsAssertAndBreak( oldOffset + opLength > oldLength, "Difference buffer offset in plDiffBuffer() is out of range of the old buffer!" );
|
||
|
|
||
|
memcpy( &new8Buffer[ newBufferPos ], old8Buffer + oldOffset, opLength );
|
||
|
newBufferPos += opLength;
|
||
|
}
|
||
|
}
|
||
|
|
||
|
hsAssert( newBufferPos == newLength, "Invalid sequence of difference ops in plDiffBuffer::Apply()" );
|
||
|
}
|