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