/*==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==*/
//////////////////////////////////////////////////////////////////////////////
//
// plBSDiffBuffer - A utility class for writing and applying a difference
// buffer--i.e. a buffer containing a series of modifications
// 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.
//
//////////////////////////////////////////////////////////////////////////////
#include "hsTypes.h"
#include "plBSDiffBuffer.h"
#include "hsUtils.h"
#include "hsStream.h"
#define plBSDiffBuffer_MIN(x,y) (((x)<(y)) ? (x) : (y))
//////////////////////////////////////////////////////////////////////////////
//// 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.
plBSDiffBuffer::plBSDiffBuffer( UInt32 newLength, UInt32 oldLength )
: fNewLength( newLength )
, fPatchLength( 0 )
{
fPatchBuffer = nil;
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.
plBSDiffBuffer::plBSDiffBuffer( void *buffer, UInt32 length )
: fNewLength( 0 )
, fPatchLength( 0 )
, fPatchBuffer( nil )
, fWriting( false )
{
if (!buffer || length < 32)
hsAssert(false, "Corrupt Patch Buffer!");
if((fPatchBuffer=TRACKED_NEW unsigned char[length+1])!=nil)
{
fPatchLength = length;
memcpy(fPatchBuffer, buffer, fPatchLength);
}
}
plBSDiffBuffer::~plBSDiffBuffer()
{
if ( fPatchBuffer )
delete[] fPatchBuffer;
}
//////////////////////////////////////////////////////////////////////////////
//// Creation/Write Functions ////////////////////////////////////////////////
//////////////////////////////////////////////////////////////////////////////
//// Diff /////////////////////////////////////////////////////////////////////
// Diff() creates the diff buffer from the new and old.
UInt32 plBSDiffBuffer::Diff( UInt32 oldLength, void *oldBuffer, UInt32 newLength, void *newBuffer )
{
hsAssert( fWriting, "Trying to Add() to a difference buffer that's reading" );
Int32 *I,*V;
Int32 scan,pos,len;
Int32 lastscan,lastpos,lastoffset;
Int32 oldscore,scsc;
Int32 s,Sf,lenf,Sb,lenb;
Int32 overlap,Ss,lens;
Int32 i;
UInt32 cblen,dblen,eblen;
unsigned char *oldbuf, *newbuf;
unsigned char *cb,*db,*eb;
unsigned char header[32];
if (oldBuffer == nil ||
oldLength < 0 ||
newBuffer == nil ||
newLength < 0)
{
return (-1);
}
oldbuf = (unsigned char *)oldBuffer;
newbuf = (unsigned char *)newBuffer;
if(((I=TRACKED_NEW Int32[oldLength+1])==nil) ||
((V=TRACKED_NEW Int32[oldLength+1])==nil))
{
delete[] I;
delete[] V;
return fPatchLength;
}
IQSuffixSort(I,V,oldbuf,oldLength);
delete[] V;
/*
* These could probably be smaller, especially cb.
*/
if(((cb=TRACKED_NEW unsigned char[newLength+1])==nil) ||
((db=TRACKED_NEW unsigned char[newLength+1])==nil) ||
((eb=TRACKED_NEW unsigned char[newLength+1])==nil))
{
delete[] I;
delete[] cb;
delete[] db;
delete[] eb;
return fPatchLength;
}
cblen=0;
dblen=0;
eblen=0;
/* Header is
0 8 "BSDIFF40"
8 8 length of bzip2ed ctrl block
16 8 length of bzip2ed diff block
24 8 length of new file */
/* File is
0 32 Header
32 ?? Bzip2ed ctrl block
?? ?? Bzip2ed diff block
?? ?? Bzip2ed extra block */
memcpy(header,"BSDIFF40",8);
memset(header+8,0,24);
scan=0;len=0;
lastscan=0;lastpos=0;lastoffset=0;
while(scanoldscore+8)) break;
if((scan+lastoffsetSf*2-lenf) { Sf=s; lenf=i; };
};
lenb=0;
if(scan=lastscan+i)&&(pos>=i);i++) {
if(oldbuf[pos-i]==newbuf[scan-i]) s++;
if(s*2-i>Sb*2-lenb) { Sb=s; lenb=i; };
};
};
if(lastscan+lenf>scan-lenb) {
overlap=(lastscan+lenf)-(scan-lenb);
s=0;Ss=0;lens=0;
for(i=0;iSs) { Ss=s; lens=i+1; };
};
lenf+=lens-overlap;
lenb-=lens;
};
for(i=0;istart) ISplit(I,V,start,jj-start,h);
for(i=0;ikk) ISplit(I,V,kk,start+len-kk,h);
}
void plBSDiffBuffer::IQSuffixSort(Int32 *I,Int32 *V,unsigned char *old,UInt32 oldsize)
{
UInt32 buckets[256];
UInt32 i,h,len;
for(i=0;i<256;i++) buckets[i]=0;
for(i=0;i0;i--) buckets[i]=buckets[i-1];
buckets[0]=0;
for(i=0;iy) {
*pos=I[st];
return x;
} else {
*pos=I[en];
return y;
}
};
x=st+(en-st)/2;
if(memcmp(oldBuffer+I[x],newBuffer,plBSDiffBuffer_MIN(oldLength-I[x],newLength))<0) {
return ISearch(I,oldBuffer,oldLength,newBuffer,newLength,x,en,pos);
} else {
return ISearch(I,oldBuffer,oldLength,newBuffer,newLength,st,x,pos);
};
}