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.
250 lines
8.7 KiB
250 lines
8.7 KiB
/*==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()" ); |
|
}
|
|
|