VPatch 3.0

git-svn-id: https://svn.code.sf.net/p/nsis/code/NSIS/trunk@4271 212acab6-be3b-0410-9dea-997c60f758d6
This commit is contained in:
kichik 2005-09-17 09:25:44 +00:00
parent 9929eb7120
commit 4bf6509225
53 changed files with 3918 additions and 1782 deletions

View file

@ -0,0 +1,120 @@
//---------------------------------------------------------------------------
// Checksums.cpp
//---------------------------------------------------------------------------
// -=* VPatch *=-
//---------------------------------------------------------------------------
// Copyright (C) 2001-2005 Koen van de Sande / Van de Sande Productions
//---------------------------------------------------------------------------
// Website: http://www.tibed.net/vpatch
//
// This software is provided 'as-is', without any express or implied
// warranty. In no event will the authors be held liable for any damages
// arising from the use of this software.
//
// Permission is granted to anyone to use this software for any purpose,
// including commercial applications, and to alter it and redistribute it
// freely, subject to the following restrictions:
//
// 1. The origin of this software must not be misrepresented; you must not
// claim that you wrote the original software. If you use this software
// in a product, an acknowledgment in the product documentation would be
// appreciated but is not required.
// 2. Altered source versions must be plainly marked as such, and must not be
// misrepresented as being the original software.
// 3. This notice may not be removed or altered from any source distribution.
#include "Checksums.h"
/* ------------------------ CRC32 checksum calculation ----------------- */
uint32_t CRCTable[256];
bool bInitCRC = false;
void InitCRC() {
int i, j; unsigned long c;
for (c = i = 0; i < 256; c = ++i) {
for (j = 0; j < 8; j++) {
if (c & 1) c = (c>>1) ^ 0xEDB88320;
else c >>= 1;
}
CRCTable[i] = c;
}
bInitCRC = true;
}
crc32_t streamCRC32(bistream& data) {
if(!bInitCRC) InitCRC();
const int CRCBLOCKSIZE = 16384;
uint8_t block[CRCBLOCKSIZE];
unsigned int read;
uint8_t *p;
crc32_t crc = 0xFFFFFFFF;
while(data.good()) {
data.read(reinterpret_cast<char*>(block), CRCBLOCKSIZE);
read = data.gcount();
for (p = block; p < block + read; p++)
crc = CRCTable[(crc & 0xFF) ^ *p] ^ (crc >> 8);
}
crc = (crc ^ 0xFFFFFFFF);
return crc;
}
/* ------------------------ MD5 checksum calculation ----------------- */
void streamMD5(bistream& data, md5_byte_t digest[16]) {
const int MD5BLOCKSIZE = 16384;
uint8_t md5block[MD5BLOCKSIZE];
unsigned int read;
md5_state_t state;
md5_init(&state);
while(data.good()) {
data.read(reinterpret_cast<char*>(md5block), MD5BLOCKSIZE);
read = data.gcount();
md5_append(&state, md5block, read);
}
md5_finish(&state, digest);
}
TChecksum::TChecksum(std::string& fileName) : mode(MD5) {
bifstream data;
data.open(fileName.c_str(), ios::binary | ios::in);
data.seekg(0, ios::beg);
crc = streamCRC32(data);
data.close();
bifstream data2;
data2.open(fileName.c_str(), ios::binary | ios::in);
data2.seekg(0, ios::beg);
streamMD5(data2, digest);
data2.close();
}
void TChecksum::loadMD5(md5_byte_t newdigest[16]) {
mode = MD5;
for(int i = 0; i < 16; i++) {
digest[i] = newdigest[i];
}
}
void TChecksum::loadCRC32(crc32_t newcrc) {
mode = CRC32;
crc = newcrc;
}
bool TChecksum::operator==(const TChecksum& b) {
if(mode != b.mode) throw "Checksums in different mode: MD5/CRC32";
if(mode == MD5) {
for(int md5index = 0; md5index < 16; md5index++) {
if(digest[md5index] != b.digest[md5index]) break;
if(md5index == 15) return true;
}
return false;
} else {
return (crc == b.crc);
}
}

View file

@ -0,0 +1,50 @@
//---------------------------------------------------------------------------
// Checksums.h
//---------------------------------------------------------------------------
// -=* VPatch *=-
//---------------------------------------------------------------------------
// Copyright (C) 2001-2005 Koen van de Sande / Van de Sande Productions
//---------------------------------------------------------------------------
// Website: http://www.tibed.net/vpatch
//
// This software is provided 'as-is', without any express or implied
// warranty. In no event will the authors be held liable for any damages
// arising from the use of this software.
//
// Permission is granted to anyone to use this software for any purpose,
// including commercial applications, and to alter it and redistribute it
// freely, subject to the following restrictions:
//
// 1. The origin of this software must not be misrepresented; you must not
// claim that you wrote the original software. If you use this software
// in a product, an acknowledgment in the product documentation would be
// appreciated but is not required.
// 2. Altered source versions must be plainly marked as such, and must not be
// misrepresented as being the original software.
// 3. This notice may not be removed or altered from any source distribution.
#if !defined(Checksums_H)
#define Checksums_H
#include "md5.h"
#include <string>
#include "GlobalTypes.h"
typedef uint32_t crc32_t;
class TChecksum {
public:
md5_byte_t digest[16];
crc32_t crc;
enum { CRC32, MD5 } mode;
TChecksum() : mode(MD5) { }
TChecksum(std::string& fileName);
void loadMD5(md5_byte_t newdigest[16]);
void loadCRC32(crc32_t newcrc);
bool operator==(const TChecksum& b);
};
#endif // Checksums_H

View file

@ -0,0 +1,95 @@
//---------------------------------------------------------------------------
// ChunkedFile.cpp
//---------------------------------------------------------------------------
// -=* VPatch *=-
//---------------------------------------------------------------------------
// Copyright (C) 2001-2005 Koen van de Sande / Van de Sande Productions
//---------------------------------------------------------------------------
// Website: http://www.tibed.net/vpatch
//
// This software is provided 'as-is', without any express or implied
// warranty. In no event will the authors be held liable for any damages
// arising from the use of this software.
//
// Permission is granted to anyone to use this software for any purpose,
// including commercial applications, and to alter it and redistribute it
// freely, subject to the following restrictions:
//
// 1. The origin of this software must not be misrepresented; you must not
// claim that you wrote the original software. If you use this software
// in a product, an acknowledgment in the product documentation would be
// appreciated but is not required.
// 2. Altered source versions must be plainly marked as such, and must not be
// misrepresented as being the original software.
// 3. This notice may not be removed or altered from any source distribution.
#include "ChunkedFile.h"
using namespace std;
ChunkedFile::ChunkedFile(bistream& f, TFileOffset fSize, TFileOffset chunkSize) :
chunks(NULL) {
chunkCount = fSize / chunkSize;
cout << "[ChunkedFile] Filesize of " << static_cast<unsigned int>(fSize) << " gives " << static_cast<unsigned int>(chunkCount) << " chunks.\n";
cout << "[ChunkedFile] Memory to be used by those chunks: " << sizeof(FileChunk) * chunkCount << " bytes...";
if(chunkCount == 0) {
chunks = NULL;
return;
}
chunks = new FileChunk[chunkCount];
cout << " allocated.\n";
unsigned char* data = new unsigned char[chunkSize];
for(TFileOffset i = 0; i < chunkCount; i++) {
f.read(reinterpret_cast<char*>(data),chunkSize);
chunks[i].offset = i * chunkSize;
calculateChecksum(data,chunkSize,chunks[i].checksum);
}
delete[] data;
cout << "[ChunkedFile] Sorting chunks... ";
std::sort(chunks,chunks + chunkCount);
cout << "done.\n";
}
bool ChunkedFile::search(TChunkChecksum key, TFileOffset* start) {
// function:
// Searches sortedArray[first]..sortedArray[last] for key.
// returns: index of the matching element if it finds key,
// otherwise -(index where it could be inserted)-1.
// parameters:
// sortedArray in array of sorted (ascending) values.
// first, last in lower and upper subscript bounds
// key in value to search for.
// returns:
// index of key, or -insertion_position -1 if key is not
// in the array. This value can easily be
// transformed into the position to insert it.
if(chunkCount == 0) return false;
int first = 0;
int last = chunkCount - 1;
while (first <= last) {
int mid = (first + last) / 2; // compute mid point.
if(key == chunks[mid].checksum) {
while(true) {
if(mid == 0) break;
mid--;
if(!(key == chunks[mid].checksum)) {
mid++;
break;
}
}
*start = mid;
return true; // found it. return position
}
if (key < chunks[mid].checksum)
last = mid - 1; // repeat search in bottom half.
else
first = mid + 1; // repeat search in top half.
}
return false; // failed to find key
}

View file

@ -0,0 +1,77 @@
//---------------------------------------------------------------------------
// ChunkedFile.h
//---------------------------------------------------------------------------
// -=* VPatch *=-
//---------------------------------------------------------------------------
// Copyright (C) 2001-2005 Koen van de Sande / Van de Sande Productions
//---------------------------------------------------------------------------
// Website: http://www.tibed.net/vpatch
//
// This software is provided 'as-is', without any express or implied
// warranty. In no event will the authors be held liable for any damages
// arising from the use of this software.
//
// Permission is granted to anyone to use this software for any purpose,
// including commercial applications, and to alter it and redistribute it
// freely, subject to the following restrictions:
//
// 1. The origin of this software must not be misrepresented; you must not
// claim that you wrote the original software. If you use this software
// in a product, an acknowledgment in the product documentation would be
// appreciated but is not required.
// 2. Altered source versions must be plainly marked as such, and must not be
// misrepresented as being the original software.
// 3. This notice may not be removed or altered from any source distribution.
#if !defined(ChunkedFile_H)
#define ChunkedFile_H
#include "GlobalTypes.h"
#include "adler32.h"
#include <iostream>
#include <algorithm>
// private data type: the data tree information
typedef struct TChunkChecksum {
Checksum::uLong adler32;
CHECKSUM_BLOCK v;
} TChunkChecksum;
inline bool operator<(const TChunkChecksum& a, const TChunkChecksum& b) {
return (a.adler32 < b.adler32) ? true : (
(a.adler32 == b.adler32) ? (a.v < b.v) : false
);
}
inline bool operator==(const TChunkChecksum& a, const TChunkChecksum& b) {
return (a.v == b.v) && (a.adler32 == b.adler32);
}
typedef struct FileChunk {
TFileOffset offset;
TChunkChecksum checksum;
} FileChunk;
inline bool operator<(const FileChunk& a, const FileChunk& b) {
return a.checksum < b.checksum;
}
class ChunkedFile {
public:
TFileOffset chunkCount;
FileChunk* chunks;
ChunkedFile(bistream& f, TFileOffset fSize, TFileOffset chunkSize);
~ChunkedFile() {
if(chunks != NULL) delete[] chunks;
}
bool search(TChunkChecksum key, TFileOffset* start);
inline void calculateChecksum(unsigned char* data, TFileOffset size, TChunkChecksum& K) {
K.v = *reinterpret_cast<CHECKSUM_BLOCK*>(data);
K.adler32 = Checksum::adler32(1L,data,size);
}
};
#endif // ChunkedFile_H

View file

@ -0,0 +1,243 @@
//---------------------------------------------------------------------------
// FileFormat1.cpp
//---------------------------------------------------------------------------
// -=* VPatch *=-
//---------------------------------------------------------------------------
// Copyright (C) 2001-2005 Koen van de Sande / Van de Sande Productions
//---------------------------------------------------------------------------
// Website: http://www.tibed.net/vpatch
//
// This software is provided 'as-is', without any express or implied
// warranty. In no event will the authors be held liable for any damages
// arising from the use of this software.
//
// Permission is granted to anyone to use this software for any purpose,
// including commercial applications, and to alter it and redistribute it
// freely, subject to the following restrictions:
//
// 1. The origin of this software must not be misrepresented; you must not
// claim that you wrote the original software. If you use this software
// in a product, an acknowledgment in the product documentation would be
// appreciated but is not required.
// 2. Altered source versions must be plainly marked as such, and must not be
// misrepresented as being the original software.
// 3. This notice may not be removed or altered from any source distribution.
#include "FileFormat1.h"
#include "GlobalTypes.h"
#define MAGIC_VPAT 0x54415056
namespace FileFormat1 {
void writeByte(bostream& patch, TFileOffset dw) {
unsigned char b = dw & 0xFF;
patch.write(reinterpret_cast<char*>(&b),sizeof(b));
}
void writeWord(bostream& patch, TFileOffset dw) {
unsigned char b = dw & 0xFF;
patch.write(reinterpret_cast<char*>(&b),sizeof(b));
b = (dw & 0xFF00) >> 8;
patch.write(reinterpret_cast<char*>(&b),sizeof(b));
}
void writeDword(bostream& patch, TFileOffset dw) {
unsigned char b = dw & 0xFF;
patch.write(reinterpret_cast<char*>(&b),sizeof(b));
b = (dw & 0xFF00) >> 8;
patch.write(reinterpret_cast<char*>(&b),sizeof(b));
b = (dw & 0xFF0000) >> 16;
patch.write(reinterpret_cast<char*>(&b),sizeof(b));
b = (dw & 0xFF000000) >> 24;
patch.write(reinterpret_cast<char*>(&b),sizeof(b));
}
void writeMD5(bostream& patch, md5_byte_t digest[16]) {
for(int i = 0; i < 16; i++) {
writeByte(patch, digest[i]);
}
}
TFileOffset readDword(bistream& patch) {
unsigned char b;
patch.read(reinterpret_cast<char*>(&b),sizeof(b));
TFileOffset dw = b;
patch.read(reinterpret_cast<char*>(&b),sizeof(b));
dw = dw | (b << 8);
patch.read(reinterpret_cast<char*>(&b),sizeof(b));
dw = dw | (b << 16);
patch.read(reinterpret_cast<char*>(&b),sizeof(b));
dw = dw | (b << 24);
return dw;
}
void readMD5(bistream& patch, md5_byte_t digest[16]) {
unsigned char b;
for(int i = 0; i < 16; i++) {
patch.read(reinterpret_cast<char*>(&b),sizeof(b));
digest[i] = b;
}
}
void writeFileCount(bostream& f, TFileOffset currentCount) {
f.seekp(4,ios::beg);
writeDword(f,currentCount);
}
TFileOffset removeExistingPatch(bistream& in, TFileOffset inSize, bostream& out, TChecksum* removeCRC, bool existanceIsError) {
TFileOffset fileCount = 0x80000000; // MD5 mode
if(in.bad() || in.eof() || (inSize == 0)) { // empty file/does not yet exist
writeDword(out,MAGIC_VPAT);
writeDword(out,fileCount); // noFiles
return fileCount;
}
// copy and do stuff
if(readDword(in) != MAGIC_VPAT) {
writeDword(out,MAGIC_VPAT);
writeDword(out,fileCount); // noFiles
return fileCount;
}
fileCount = readDword(in);
writeDword(out,MAGIC_VPAT);
writeDword(out,fileCount); // noFiles
bool MD5Mode = (fileCount & 0x80000000) != 0;
if(MD5Mode) removeCRC->mode = TChecksum::MD5;
if(!MD5Mode) removeCRC->mode = TChecksum::CRC32;
// top byte is reserved for extensions
fileCount = fileCount & 0x00FFFFFF;
TFileOffset tempCount = fileCount;
for(TFileOffset i = 0; i < tempCount; i++) {
TFileOffset startOffset = in.tellg();
readDword(in); // noBlocks
TChecksum sourceChecksum;
if(!MD5Mode) {
crc32_t sourceCRC = readDword(in); // SourceCRC
readDword(in); // TargetCRC
sourceChecksum.loadCRC32(sourceCRC);
} else {
md5_byte_t digest[16];
readMD5(in, digest); // SourceCRC
sourceChecksum.loadMD5(digest);
readMD5(in, digest); // TargetCRC
}
TFileOffset bodySize = readDword(in); // bodySize
in.seekg(bodySize,ios::cur);
TFileOffset endOffset = in.tellg();
if(sourceChecksum == *removeCRC) {
if(existanceIsError) {
throw "Source file with the exact same contents already exists in patch!\nUse /R option (replace) to replace it with this patch!";
}
fileCount--;
} else {
// copy this patch to out
in.seekg(startOffset,ios::beg);
TFileOffset size = endOffset-startOffset;
char* buffer = new char[size];
in.read(buffer,size);
out.write(buffer,size);
delete[] buffer;
}
}
TFileOffset curPos = out.tellp();
if(MD5Mode) fileCount = fileCount | 0x80000000;
writeFileCount(out,fileCount);
out.seekp(curPos,ios::beg);
return fileCount;
}
void writePatch(bostream& patch, bistream& target, vector<SameBlock*>& sameBlocks, TChecksum* sourceCRC, TChecksum* targetCRC, TFileOffset currentFileCount, POSIX::ALT_FILETIME targetTime) {
TFileOffset bodySize = 0;
TFileOffset noBlocks = 0;
TFileOffset noBlocksOffset = patch.tellp();
writeDword(patch,noBlocks);
if(sourceCRC->mode == TChecksum::MD5) {
writeMD5(patch,sourceCRC->digest); // sourceCRC
writeMD5(patch,targetCRC->digest); // targetCRC
} else {
writeDword(patch,sourceCRC->crc); // sourceCRC
writeDword(patch,targetCRC->crc); // targetCRC
}
TFileOffset bodySizeOffset = patch.tellp();
writeDword(patch,bodySize);
for(vector<SameBlock*>::iterator iter = sameBlocks.begin(); iter != sameBlocks.end(); iter++) {
SameBlock* current = *iter;
// store current block
if(current->size > 0) {
// copy block from sourceFile
if(current->size < 256) {
writeByte(patch,1);
writeByte(patch,current->size);
bodySize += 2;
} else if(current->size < 65536) {
writeByte(patch,2);
writeWord(patch,current->size);
bodySize += 3;
} else {
writeByte(patch,3);
writeDword(patch,current->size);
bodySize += 5;
}
writeDword(patch,current->sourceOffset);
bodySize += 4;
noBlocks++;
}
iter++;
if(iter == sameBlocks.end()) break;
SameBlock* next = *iter;
iter--;
// calculate area inbetween this block and the next
TFileOffset notFoundStart = current->targetOffset+current->size;
if(notFoundStart > next->targetOffset) {
throw "makeBinaryPatch input problem: there was overlap";
}
TFileOffset notFoundSize = next->targetOffset - notFoundStart;
if(notFoundSize > 0) {
// we need to include this area in the patch directly
if(notFoundSize < 256) {
writeByte(patch,5);
writeByte(patch,notFoundSize);
bodySize += 2;
} else if(notFoundSize < 65536) {
writeByte(patch,6);
writeWord(patch,notFoundSize);
bodySize += 3;
} else {
writeByte(patch,7);
writeDword(patch,notFoundSize);
bodySize += 5;
}
// copy from target...
target.seekg(notFoundStart,ios::beg);
#define COPY_BUF_SIZE 4096
char copyBuffer[COPY_BUF_SIZE];
for(TFileOffset i = 0; i < notFoundSize; i += COPY_BUF_SIZE) {
TFileOffset j = notFoundSize - i;
if(j > COPY_BUF_SIZE) j = COPY_BUF_SIZE;
target.read(copyBuffer,j);
patch.write(copyBuffer,j);
}
bodySize += notFoundSize;
noBlocks++;
}
}
// we are done, now add just one extra block with the target file time
writeByte(patch,255);
writeDword(patch,targetTime.dwLowDateTime);
writeDword(patch,targetTime.dwHighDateTime);
noBlocks++;
bodySize += 9;
TFileOffset curPos = patch.tellp();
patch.seekp(noBlocksOffset,ios::beg);
writeDword(patch,noBlocks);
patch.seekp(bodySizeOffset,ios::beg);
writeDword(patch,bodySize);
// do this at the end because it messes up file position
writeFileCount(patch,++currentFileCount);
patch.seekp(curPos,ios::beg);
}
}

View file

@ -0,0 +1,41 @@
//---------------------------------------------------------------------------
// FileFormat1
//---------------------------------------------------------------------------
// -=* VPatch *=-
//---------------------------------------------------------------------------
// Copyright (C) 2001-2005 Koen van de Sande / Van de Sande Productions
//---------------------------------------------------------------------------
// Website: http://www.tibed.net/vpatch
//
// This software is provided 'as-is', without any express or implied
// warranty. In no event will the authors be held liable for any damages
// arising from the use of this software.
//
// Permission is granted to anyone to use this software for any purpose,
// including commercial applications, and to alter it and redistribute it
// freely, subject to the following restrictions:
//
// 1. The origin of this software must not be misrepresented; you must not
// claim that you wrote the original software. If you use this software
// in a product, an acknowledgment in the product documentation would be
// appreciated but is not required.
// 2. Altered source versions must be plainly marked as such, and must not be
// misrepresented as being the original software.
// 3. This notice may not be removed or altered from any source distribution.
#if !defined(FileFormat1_H)
#define FileFormat1_H
#include <iostream>
#include <vector>
#include "Checksums.h"
#include "PatchGenerator.h"
#include "POSIXUtil.h"
using namespace std;
namespace FileFormat1 {
TFileOffset removeExistingPatch(bistream& in, TFileOffset inSize, bostream& out, TChecksum* removeCRC, bool existanceIsError);
void writePatch(bostream& patch, bistream& target, vector<SameBlock*>& sameBlocks, TChecksum* sourceCRC, TChecksum* targetCRC, TFileOffset currentFileCount, POSIX::ALT_FILETIME targetTime);
}
#endif // FileFormat1_H

View file

@ -1,226 +0,0 @@
program GenPat2;
{
VPatch 2 - Patch Generator
===============================
(c) 2001-2003 Van de Sande Productions
This is the main program unit for the commandline version. It implements
commandline options (like /b=) and displays help if no options are given.
What's new
----------
2.1 20031219 Koen Added error checking, handling, shouldn't
crash when invalid arguments, returns
exit codes now.
2.0 20030811 Koen Initial documentation
}
{$APPTYPE CONSOLE}
uses
PatchGenerator in 'PatchGenerator.pas',
VDSP_CRC in 'VDSP_CRC.pas',
Sysutils,
TreeCode in 'TreeCode.pas';
type
TEventHandler = class
procedure PrintDebug(S: String);
end;
procedure TEventhandler.PrintDebug(S: String);
begin
WriteLn(S);
end;
{$DEFINE READCONFIG} //try to read genpat.ini?
{.$DEFINE AUTOWAIT} //have /wait command line switch on by default?
//useful when debugging
var
Config: TextFile;
T1,T2: TDateTime;
d, i: Integer;
S,Key: String;
SourceFile, TargetFile, PatchFile: String;
ShowDebug, ShowHelp: Boolean;
PG: TPatchGenerator;
EV: TEventHandler;
begin
EV:=TEventHandler.Create;
PG:=TPatchGenerator.Create;
PG.StartBlockSize:=64;
WriteLn('GenPat v2.1');
WriteLn('===========');
WriteLn;
WriteLn('(c) 2001-2003 Van de Sande Productions');
WriteLn('Website: http://www.tibed.net/vpatch');
WriteLn('E-mail: koen@tibed.net');
WriteLn;
ShowDebug:=FindCmdLineSwitch('debug',['/'],True);
if ShowDebug then
DebugEvent:=EV.PrintDebug;
{$IFDEF READCONFIG}
if FileExists('genpat.ini') then begin
AssignFile(Config,'genpat.ini');
Reset(Config);
while not eof(Config) do begin
ReadLn(Config,S);
d:=Pos('=',S);
if not (d=0) then begin
Key:=LowerCase(Copy(S,1,d-1));
S:=Copy(S,d+1,Length(S));
if CompareStr(Key,'startblocksize')=0 then PG.StartBlockSize:=StrToInt(S);
end;
end;
CloseFile(Config);
end;
{$ENDIF}
i:=0;
for d:=1 to ParamCount do begin
if CompareStr(LowerCase(Copy(ParamStr(d),1,3)),'/b=')=0 then begin
PG.StartBlockSize:=StrToInt(Copy(ParamStr(d),4,10));
end else begin
// not a parameter?
if not (ParamStr(d)[1] = '/') then begin
if i = 2 then begin
PatchFile:=ParamStr(d);
Inc(i);
end;
if i = 1 then begin
TargetFile:=ParamStr(d);
Inc(i);
end;
if i = 0 then begin
SourceFile:=ParamStr(d);
Inc(i);
end;
end;
end;
end;
ShowHelp:=False;
if(CompareStr(PatchFile,'')=0) then ShowHelp:=True;
if SourceFile = '' then ShowHelp:=True;
if TargetFile = '' then ShowHelp:=True;
if ShowHelp then begin
WriteLn('This program will take (sourcefile) as input and create a (patchfile).');
WriteLn('With this patchfile, you can convert a (sourcefile) into (targetfile).');
WriteLn;
WriteLn('Command line info:');
WriteLn(' GENPAT (sourcefile) (targetfile) (patchfile)');
WriteLn;
WriteLn('Command line options (you do not need them):');
WriteLn('/B=(BlockSize) Set blocksize (def=64), multiple of 2');
WriteLn('/NOEQUALERROR Exit code becomes 0 instead of 10 when');
WriteLn(' two files with equal CRC are encountered');
WriteLn(' (patch file will remain unchanged)');
WriteLn('/DEBUG Show runtime debug information');
WriteLn;
WriteLn('Note: filenames should never start with / character!');
WriteLn;
WriteLn('Possible exit codes:');
WriteLn(' 0 Success');
WriteLn(' 1 Arguments missing');
WriteLn(' 2 Source file not found');
WriteLn(' 3 Target file not found');
WriteLn(' 4 Unknown error while reading existing patch file');
WriteLn(' 5 Unknown error while generating patch');
WriteLn(' 6 Unknown error while writing patch file to disk');
WriteLn(' 10 CRC of source and target file are equal (impossible with /NOEQUALERROR)');
WriteLn(' 11 Not enough memory for source file');
WriteLn(' 12 Not enough memory for target file');
PG.Free;
ExitCode:=1;
Exit;
end;
// stop if file error, result shown above
if not FileExists(SourceFile) then begin
WriteLn('Error: Source file not found');
PG.Free;
ExitCode:=2;
Exit;
end;
if not FileExists(TargetFile) then begin
WriteLn('Error: Target file not found');
PG.Free;
ExitCode:=3;
Exit;
end;
if FileExists(PatchFile) then begin
WriteLn('Using existing file to include patches in: '+PatchFile);
try
PG.LoadFromFile(PatchFile);
except
on E: Exception do begin
WriteLn('Error: Reading existing patch file failed');
WriteLn('Error message: ', E.ClassName, ': ', E.Message);
PG.Free;
ExitCode:=4;
Exit;
end;
end;
end;
WriteLn('Source (original) file: ', SourceFile);
WriteLn('Target (newer) file: ', TargetFile);
T1:=Now;
// create patch file, with error handling
try
i:=PG.CreatePatch(SourceFile,TargetFile);
except
on E: Exception do begin
WriteLn('Error: Generating patch failed');
WriteLn('Error message: ', E.ClassName, ': ', E.Message);
PG.Free;
ExitCode:=5;
Exit;
end;
end;
if(i < 0) then begin
if(i = -1) then begin
if not FindCmdLineSwitch('noequalerror',['/'],True) then
WriteLn('Error: CRC of source and target file are equal');
end;
if(i = -2) then WriteLn('Error: Not enough memory for source file');
if(i = -3) then WriteLn('Error: Not enough memory for target file');
ExitCode:=9 - i;
if(i = -1) and (FindCmdLineSwitch('noequalerror',['/'],True)) then begin
WriteLn('Equal CRCs ignored (no patch will be written and exit code is 0)');
ExitCode:=0;
end;
end else begin
WriteLn('Patch body size: '+IntToStr(i));
try
PG.WriteToFile(PatchFile);
except
on E: Exception do begin
WriteLn('Error: Writing patch to file ' + PatchFile + ' failed');
WriteLn('Error message: ', E.ClassName, ': ', E.Message);
PG.Free;
ExitCode:=6;
Exit;
end;
end;
T2:=Now;
Write('Time taken for generation: ');
WriteLn(FloatToStr((T2-T1)*24*60*60),'s');
WriteLn;
ExitCode:=0;
end;
PG.Free;
end.

View file

@ -0,0 +1,28 @@
//---------------------------------------------------------------------------
// GlobalTypes.cpp
//---------------------------------------------------------------------------
// -=* VPatch *=-
//---------------------------------------------------------------------------
// Copyright (C) 2001-2005 Koen van de Sande / Van de Sande Productions
//---------------------------------------------------------------------------
// Website: http://www.tibed.net/vpatch
//
// This software is provided 'as-is', without any express or implied
// warranty. In no event will the authors be held liable for any damages
// arising from the use of this software.
//
// Permission is granted to anyone to use this software for any purpose,
// including commercial applications, and to alter it and redistribute it
// freely, subject to the following restrictions:
//
// 1. The origin of this software must not be misrepresented; you must not
// claim that you wrote the original software. If you use this software
// in a product, an acknowledgment in the product documentation would be
// appreciated but is not required.
// 2. Altered source versions must be plainly marked as such, and must not be
// misrepresented as being the original software.
// 3. This notice may not be removed or altered from any source distribution.
#include "GlobalTypes.h"

View file

@ -0,0 +1,54 @@
//---------------------------------------------------------------------------
// GlobalTypes.h
//---------------------------------------------------------------------------
// -=* VPatch *=-
//---------------------------------------------------------------------------
// Copyright (C) 2001-2005 Koen van de Sande / Van de Sande Productions
//---------------------------------------------------------------------------
// Website: http://www.tibed.net/vpatch
//
// This software is provided 'as-is', without any express or implied
// warranty. In no event will the authors be held liable for any damages
// arising from the use of this software.
//
// Permission is granted to anyone to use this software for any purpose,
// including commercial applications, and to alter it and redistribute it
// freely, subject to the following restrictions:
//
// 1. The origin of this software must not be misrepresented; you must not
// claim that you wrote the original software. If you use this software
// in a product, an acknowledgment in the product documentation would be
// appreciated but is not required.
// 2. Altered source versions must be plainly marked as such, and must not be
// misrepresented as being the original software.
// 3. This notice may not be removed or altered from any source distribution.
#if !defined(GlobalTypes_H)
#define GlobalTypes_H
#ifndef _MSC_VER
#include <stdint.h>
#endif
#include <iostream>
#include <fstream>
#include <ios>
#include <string>
using namespace std;
#ifdef _MSC_VER
typedef unsigned char uint8_t;
typedef unsigned __int32 uint32_t;
typedef unsigned __int64 uint64_t;
#define CHECKSUM_BLOCK unsigned __int64
#define __WIN32__
#else
#define CHECKSUM_BLOCK unsigned long long
#endif
typedef uint32_t TFileOffset;
typedef ifstream bifstream;
typedef istream bistream;
typedef ofstream bofstream;
typedef ostream bostream;
#endif // GlobalTypes_H

View file

@ -0,0 +1,128 @@
//---------------------------------------------------------------------------
// POSIXUtil.cpp
//---------------------------------------------------------------------------
// -=* VPatch *=-
//---------------------------------------------------------------------------
// Copyright (C) 2001-2005 Koen van de Sande / Van de Sande Productions
//---------------------------------------------------------------------------
// Website: http://www.tibed.net/vpatch
//
// This software is provided 'as-is', without any express or implied
// warranty. In no event will the authors be held liable for any damages
// arising from the use of this software.
//
// Permission is granted to anyone to use this software for any purpose,
// including commercial applications, and to alter it and redistribute it
// freely, subject to the following restrictions:
//
// 1. The origin of this software must not be misrepresented; you must not
// claim that you wrote the original software. If you use this software
// in a product, an acknowledgment in the product documentation would be
// appreciated but is not required.
// 2. Altered source versions must be plainly marked as such, and must not be
// misrepresented as being the original software.
// 3. This notice may not be removed or altered from any source distribution.
#include "POSIXUtil.h"
#include <iostream>
#include <fstream>
#include <time.h>
#include <sys/types.h>
#include <sys/stat.h>
/* My Borland/VC++ compiler do not have this file */
#if !defined(__BORLANDC__) && !defined(_MSC_VER)
#include <unistd.h>
#endif
#ifdef __WIN32__ /* The Windows way of getting a temp file needs windows.h*/
#include <windows.h>
#endif
using namespace std;
namespace POSIX {
void TimeT_To_FILETIME(time_t t, ALT_FILETIME *pft ) {
uint64_t ll = (((uint64_t)t) *10000000L) + (((uint64_t)116444736L) * 1000000000L);
pft->dwLowDateTime = (uint32_t)(ll & 0xFFFFFFFF);
pft->dwHighDateTime = (uint32_t)((ll>>32)&0xFFFFFFFF);
}
#ifdef __WIN32__
/* do it the old way on Win32, because POSIX does not get timezone stuff right */
ALT_FILETIME getFileTime(const char* sFileName) {
FILETIME temp;
GetSystemTimeAsFileTime(&temp);
HANDLE h = CreateFile(sFileName, GENERIC_READ, FILE_SHARE_READ, NULL,
OPEN_EXISTING, FILE_ATTRIBUTE_NORMAL, 0);
if (h == INVALID_HANDLE_VALUE) {
cerr << "Cannot read file time of " << sFileName << "\n";
} else {
GetFileTime(h, NULL, NULL, &temp);
CloseHandle(h);
}
ALT_FILETIME result;
result.dwHighDateTime = temp.dwHighDateTime;
result.dwLowDateTime = temp.dwLowDateTime;
return result;
}
#else
ALT_FILETIME getFileTime(const char* sFileName) {
struct stat buf;
/* get current time first as a fall-back */
time_t currentTime = time(NULL);
if(stat(sFileName, &buf)) {
cerr << "Cannot read file time of " << sFileName << "\n";
} else {
/* get the time from the file */
currentTime = buf.st_mtime;
}
ALT_FILETIME result;
TimeT_To_FILETIME(currentTime, &result);
return result;
}
#endif
uint32_t getFileSize(const char* sFileName) {
std::ifstream f;
f.open(sFileName, std::ios_base::binary | std::ios_base::in);
if (!f.good() || f.eof() || !f.is_open()) {
throw "File could not be read (getFileSize)";
}
f.seekg(0, std::ios_base::beg);
std::ifstream::pos_type begin_pos = f.tellg();
f.seekg(0, std::ios_base::end);
return static_cast<int>(f.tellg() - begin_pos);
}
#ifdef __WIN32__
string getTempFile() {
char buffer[MAX_PATH];
if(GetTempFileName(".","vpatch",0,buffer) == 0) {
cerr << "Cannot create temporary filename";
}
return string(buffer);
}
#else
//#ifdef POSIX
// This is a POSIX version of the function, together with #include <stdio.h>
// but I will not add it in a final release version.
string getTempFile() {
char filebuf [L_tmpnam];
// create a temporary filename
const char *fname = tmpnam (filebuf);
if (!fname)
cerr << "Cannot create temporary filename";
return string(fname);
}
//#endif
#endif
}

View file

@ -0,0 +1,44 @@
//---------------------------------------------------------------------------
// POSIXUtil.h
//---------------------------------------------------------------------------
// -=* VPatch *=-
//---------------------------------------------------------------------------
// Copyright (C) 2001-2005 Koen van de Sande / Van de Sande Productions
//---------------------------------------------------------------------------
// Website: http://www.tibed.net/vpatch
//
// This software is provided 'as-is', without any express or implied
// warranty. In no event will the authors be held liable for any damages
// arising from the use of this software.
//
// Permission is granted to anyone to use this software for any purpose,
// including commercial applications, and to alter it and redistribute it
// freely, subject to the following restrictions:
//
// 1. The origin of this software must not be misrepresented; you must not
// claim that you wrote the original software. If you use this software
// in a product, an acknowledgment in the product documentation would be
// appreciated but is not required.
// 2. Altered source versions must be plainly marked as such, and must not be
// misrepresented as being the original software.
// 3. This notice may not be removed or altered from any source distribution.
#if !defined(POSIXUtil_H)
#define POSIXUtil_H
#include "GlobalTypes.h"
#include <string>
using namespace std;
namespace POSIX {
typedef struct ALT_FILETIME {
uint32_t dwLowDateTime;
uint32_t dwHighDateTime;
} ALT_FILETIME;
ALT_FILETIME getFileTime(const char* sFileName);
uint32_t getFileSize(const char* sFileName);
string getTempFile();
}
#endif // POSIXUtil_H

View file

@ -0,0 +1,247 @@
//---------------------------------------------------------------------------
// PatchGenerator.cpp
//---------------------------------------------------------------------------
// -=* VPatch *=-
//---------------------------------------------------------------------------
// Copyright (C) 2001-2005 Koen van de Sande / Van de Sande Productions
//---------------------------------------------------------------------------
// Website: http://www.tibed.net/vpatch
//
// This software is provided 'as-is', without any express or implied
// warranty. In no event will the authors be held liable for any damages
// arising from the use of this software.
//
// Permission is granted to anyone to use this software for any purpose,
// including commercial applications, and to alter it and redistribute it
// freely, subject to the following restrictions:
//
// 1. The origin of this software must not be misrepresented; you must not
// claim that you wrote the original software. If you use this software
// in a product, an acknowledgment in the product documentation would be
// appreciated but is not required.
// 2. Altered source versions must be plainly marked as such, and must not be
// misrepresented as being the original software.
// 3. This notice may not be removed or altered from any source distribution.
#include "PatchGenerator.h"
#include <algorithm>
#define TARGET_BUFFER_SIZE 65536
#define TARGET_LOOKAHEAD_SIZE 4096
#define MAX_BLOCK_SIZE 16384
#define DEFAULT_MAX_MATCHES 500
PatchGenerator::PatchGenerator(bistream& source, TFileOffset sourceSize, bistream& target, TFileOffset targetSize, bostream& patch) :
source(source), sourceSize(sourceSize), target(target), targetSize(targetSize), patch(patch),
targetCData(NULL), targetCDataBaseOffset(0), targetCDataSize(0), blockSize(64), maxMatches(DEFAULT_MAX_MATCHES), beVerbose(false) {
targetCData = new unsigned char[TARGET_BUFFER_SIZE];
}
PatchGenerator::~PatchGenerator() {
if(targetCData != NULL) delete[] targetCData;
}
void PatchGenerator::execute(vector<SameBlock*>& sameBlocks) {
ChunkedFile* sourceTree = new ChunkedFile(source,sourceSize,blockSize);
// sameBlocks: this vector will store blocks that have been found to be the same
// between files
// the vector needs an 'empty' first block so checking for overlap with the
// 'previous' block never fails
SameBlock* firstBlock = new SameBlock;
firstBlock->sourceOffset = 0;
firstBlock->targetOffset = 0;
firstBlock->size = 0;
sameBlocks.push_back(firstBlock);
targetCDataBaseOffset = 0;
targetCDataSize = 0;
bool firstRun = true;
// currentOffset is in the target file
for(TFileOffset currentOffset = 0; currentOffset < targetSize; ) {
bool reloadTargetCData = true;
if((currentOffset >= targetCDataBaseOffset) &&
(currentOffset + TARGET_LOOKAHEAD_SIZE < targetCDataBaseOffset + TARGET_BUFFER_SIZE)) {
if(firstRun) {
firstRun = false;
} else {
reloadTargetCData = false;
}
}
if(reloadTargetCData) {
// at least support looking back blockSize, if possible (findBlock relies on this!)
targetCDataBaseOffset = currentOffset - blockSize;
// handle start of file correctly
if(currentOffset < blockSize) targetCDataBaseOffset = 0;
targetCDataSize = TARGET_BUFFER_SIZE;
// check if this does not extend beyond EOF
if(targetCDataBaseOffset + targetCDataSize > targetSize) {
targetCDataSize = targetSize - targetCDataBaseOffset;
}
// we need to update the memory cache of target
cout << "[CacheReload] File position = " << static_cast<unsigned int>(targetCDataBaseOffset) << "\n";
target.seekg(targetCDataBaseOffset,ios::beg);
target.read(reinterpret_cast<char*>(targetCData),targetCDataSize);
}
//cout << currentOffset << " ";
SameBlock* currentSameBlock = findBlock(sourceTree,currentOffset);
if(currentSameBlock) {
// we have a match
SameBlock* previousBlock = sameBlocks.back();
if(previousBlock->targetOffset + previousBlock->size > currentSameBlock->targetOffset) {
// there is overlap, resolve it
TFileOffset difference = previousBlock->targetOffset + previousBlock->size - currentSameBlock->targetOffset;
currentSameBlock->sourceOffset += difference;
currentSameBlock->targetOffset += difference;
currentSameBlock->size -= difference;
}
sameBlocks.push_back(currentSameBlock);
// debug info
if(beVerbose) {
cout << "Block found: " << static_cast<unsigned int>(currentSameBlock->targetOffset)
<< " " << static_cast<unsigned int>(currentSameBlock->size)
<< " (source offset=" << static_cast<unsigned int>(currentSameBlock->sourceOffset) << ")\n";
}
currentOffset = currentSameBlock->targetOffset + currentSameBlock->size;
} else {
// no match, advance one byte
currentOffset++;
}
}
// add a block at the end, again to prevent bounds checking hassle
SameBlock* lastBlock = new SameBlock;
lastBlock->sourceOffset = 0;
lastBlock->targetOffset = targetSize;
lastBlock->size = 0;
sameBlocks.push_back(lastBlock);
delete sourceTree;
}
SameBlock* PatchGenerator::findBlock(ChunkedFile* sourceTree,
TFileOffset targetFileStartOffset) {
if(targetSize - targetFileStartOffset < blockSize) return NULL;
TFileOffset preDataSize = targetFileStartOffset - targetCDataBaseOffset;
//unsigned char* p = &(targetCData[preDataSize]);
// SameBlock* currentSameBlock = findBlock(sourceTree,p,preDataSize,
// targetCDataSize - preDataSize,
// currentOffset);
//unsigned char* targetData, // target data contains a memory part of target stream
//TFileOffset targetDataPreSize, // this specifies how many bytes we can access in front (required to be at least blockSize)
//TFileOffset targetDataSize, // this specifies how many bytes we can acces in the pointer (must be at least blockSize)
// read the current data part into memory
TChunkChecksum checksum;
sourceTree->calculateChecksum(&(targetCData[preDataSize]),blockSize,checksum);
TFileOffset foundIndex; // location into sourceTree chunks array of found chunk
if(sourceTree->search(checksum,&foundIndex)) {
// we found something
SameBlock* bestMatch = new SameBlock;
bestMatch->sourceOffset = sourceTree->chunks[foundIndex].offset;
bestMatch->targetOffset = targetFileStartOffset;
bestMatch->size = 0; // default to 0, because they can all be mismatches as well
// increase match size if possible, also check if it is a match at all
int matchCount = 0;
while((sourceTree->chunks[foundIndex].checksum == checksum) && ((maxMatches == 0) || (matchCount < maxMatches))) {
// check if this one is better than the current match
SameBlock match;
match.sourceOffset = sourceTree->chunks[foundIndex].offset;
match.targetOffset = targetFileStartOffset;
match.size = 0; // default to 0, could be a mismatch with the same key
improveSameBlockMatch(match,bestMatch->size);
if(match.size > bestMatch->size) {
*bestMatch = match;
}
foundIndex++;
matchCount++;
}
if(beVerbose) {
if(maxMatches != 0) {
if(matchCount == maxMatches) {
cout << "[FindBlock] Abort due to >" << static_cast<unsigned int>(maxMatches)
<< " matches; file position = " << static_cast<unsigned int>(targetFileStartOffset) << "\n";
}
}
}
if(bestMatch->size == 0)
return NULL;
else
return bestMatch;
} else {
return NULL;
}
}
#define COMPARISON_SIZE 2048
void PatchGenerator::improveSameBlockMatch(SameBlock& match, TFileOffset currentBest) {
// we should now try to make the match longer by reading big chunks of the
// files to come
source.seekg(match.sourceOffset + match.size,ios::beg);
target.seekg(match.targetOffset + match.size,ios::beg);
while(true) {
unsigned char sourceData[COMPARISON_SIZE];
unsigned char targetData[COMPARISON_SIZE];
TFileOffset startTarget = match.targetOffset + match.size;
TFileOffset startSource = match.sourceOffset + match.size;
TFileOffset checkSize = COMPARISON_SIZE;
if(checkSize > targetSize - startTarget) checkSize = targetSize - startTarget;
if(checkSize > sourceSize - startSource) checkSize = sourceSize - startSource;
if(checkSize == 0) break;
source.read(reinterpret_cast<char*>(sourceData),checkSize);
target.read(reinterpret_cast<char*>(targetData),checkSize);
TFileOffset i = 0;
while((sourceData[i] == targetData[i]) && (i < checkSize )) {
match.size++;
i++;
}
// check if we stopped because we had a mismatch
if(i < checkSize) break;
}
if(match.size < blockSize) {
match.size = 0;
} else {
// try to improve before match if this is useful
if(match.size + blockSize <= currentBest) return;
// do not do if there is no more data in the target...
if(match.targetOffset == 0) return;
// we know it is stored in the cache... so we just need the source one
unsigned char sourceData[MAX_BLOCK_SIZE];
TFileOffset startSource = match.sourceOffset - blockSize;
TFileOffset checkSize = blockSize;
if(checkSize > match.sourceOffset) {
checkSize = match.sourceOffset;
startSource = 0;
}
if(checkSize == 0) return;
source.seekg(startSource,ios::beg);
source.read(reinterpret_cast<char*>(sourceData),checkSize);
checkSize--;
while(sourceData[checkSize] == targetCData[match.targetOffset - targetCDataBaseOffset - 1]) {
match.targetOffset--;
match.sourceOffset--;
match.size++;
checkSize--;
if(checkSize == 0) break;
if(match.targetOffset == 0) break;
}
}
}

View file

@ -0,0 +1,72 @@
//---------------------------------------------------------------------------
// PatchGenerator.h
//---------------------------------------------------------------------------
// -=* VPatch *=-
//---------------------------------------------------------------------------
// Copyright (C) 2001-2005 Koen van de Sande / Van de Sande Productions
//---------------------------------------------------------------------------
// Website: http://www.tibed.net/vpatch
//
// This software is provided 'as-is', without any express or implied
// warranty. In no event will the authors be held liable for any damages
// arising from the use of this software.
//
// Permission is granted to anyone to use this software for any purpose,
// including commercial applications, and to alter it and redistribute it
// freely, subject to the following restrictions:
//
// 1. The origin of this software must not be misrepresented; you must not
// claim that you wrote the original software. If you use this software
// in a product, an acknowledgment in the product documentation would be
// appreciated but is not required.
// 2. Altered source versions must be plainly marked as such, and must not be
// misrepresented as being the original software.
// 3. This notice may not be removed or altered from any source distribution.
#if !defined(PatchGenerator_H)
#define PatchGenerator_H
#include <iostream>
#include <vector>
#include "GlobalTypes.h"
#include "ChunkedFile.h"
using namespace std;
typedef struct SameBlock {
TFileOffset sourceOffset;
TFileOffset targetOffset;
TFileOffset size;
} SameBlock;
class PatchGenerator {
protected:
bistream& source;
TFileOffset sourceSize;
bistream& target;
TFileOffset targetSize;
bostream& patch;
// cache
unsigned char* targetCData;
TFileOffset targetCDataBaseOffset;
TFileOffset targetCDataSize;
SameBlock* PatchGenerator::findBlock(ChunkedFile* sourceTree,
TFileOffset targetFileStartOffset);
// SameBlock* findBlock(ChunkedFile* sourceTree, unsigned char* targetData, TFileOffset targetStartOffset);
void improveSameBlockMatch(SameBlock& match, TFileOffset currentBest = 0);
public:
TFileOffset blockSize;
int maxMatches;
bool beVerbose;
// load in the source, target streams
PatchGenerator(bistream& source, TFileOffset sourceSize, bistream& target, TFileOffset targetSize, bostream& patch);
~PatchGenerator();
// construct the actual patch
void execute(vector<SameBlock*>& sameBlocks);
};
#endif // PatchGenerator_H

View file

@ -1,615 +0,0 @@
unit PatchGenerator;
{
VPatch 2 - Patch Generator
==========================
(c) 2002-2003 Van de Sande Productions
This unit contains the 'core' functionality of VPatch. TPatchGenerator can
load/create/save .PAT files and supports CreatePatch(Old, New) to generate
new patches. The only configurable parameter is StartBlockSize.
Though I cleaned up the code a little bit, there is very little documentation.
That's why I will briefly explain the general workings of the current VPatch
algoritm.
There is a source file, which is divided into blocks of BlockSize. Block 1
spans bytes 0-15, block 2 16-31, etc if blocksize = 16. For every block, a
checksum is calculated and then the block is inserted into a binary search
tree, which is sorted on this checksum.
Now, the target file (new version) is traversed linearly. For a block at a
certain position, the checksum is calculated. Then, a lookup is performed in
the binary search tree to find all blocks in the source file which match this
checksum. For every occurence, it is checked how many consecutive bytes match
with this block (note: since the checksum is not unique, this can be 0 as well,
but since all occurences are checked, the largest match is selected). Note also
that this match length is not limited to BlockSize but can be larger as well;
everything beyond the block end is checked as well (the block is merely used
as a starting position for checking the match).
For those biggest block matches between source/target files, a copy instruction
will be generated in the patch file. For 'inbetween' or unmatchable blocks, the
data of the new file is placed in the patch file. This involves some
housekeeping, which is what most of the other code does.
What's new
----------
2.1 20031219 Koen Added error checking to CreatePatch, returns
negative numbers when there are errors.
2.0 20030811 Koen Initial documentation
}
interface
uses
Classes,
Sysutils,
TreeCode,
VDSP_CRC;
type
TStatusNotifyEvent = procedure(S: String; Current, Total, Savings: Integer) of object;
TDebugNotifyEvent = procedure(S: String) of object;
PDataBlock = ^TDataBlock;
TDataBlock = record
SourceOffset: Integer;
TargetOffset: Integer;
Size: Integer;
Next: PDataBlock;
end;
//internal structure for FindBlock
TBlock = record
Offset: Integer;
Size: Integer;
end;
TPatchGenerator = class
private
noPat: Integer;
PRay: Array of TDataBlock;
NRay: Array of TDataBlock;
FPatchData: TMemoryStream;
FStartBlockSize: Integer; //initial block size
FBlockDivider: Integer; //... block size is divided by this
FMinimumBlockSize: Integer;//until this minimum is reached
FStepSize: Integer;
//input: ASubBlock, which is a pointer to the start of the block to look
//for in ABlock. The entire ABlock is searched. The function returns the
//offset of the block, when it is found. The ASize parameter contains the
//size of this block
procedure ShowDebug(S: String);
function FindBlock(ASubBlock, ABlock, ABlockTree: Pointer;
var ASubBlockStart: Integer; ASubBlockSize, ABlockSize,
AMatchSize, ABlockTreeNodeCount: Integer; var ASize: Integer): Integer;
procedure FindBlockSize(ASubBlock, ABlock: Pointer; ASubBlockSize,
ABlockSize: Integer; var ASubStart, AStart, AFoundSize: Integer);
function WritePatchToStream(Target: Pointer; SourceCRC,
TargetCRC: Integer): Integer;
procedure RemoveExistingPatch(ACRC: Integer);
public
constructor Create;
destructor Destroy; override;
procedure Clear;
function CreatePatch(SourceFileName, TargetFileName: String): Integer;
property StartBlockSize: Integer read FStartBlockSize write FStartBlockSize;
property BlockDivider: Integer read FBlockDivider write FBlockDivider;
property MinimumBlockSize: Integer read FMinimumBlockSize write FMinimumBlockSize;
property StepSize: Integer read FStepSize write FStepSize;
function Size: Integer;
procedure WriteToFile(AFileName: String);
procedure WriteToStream(AStream: TStream);
procedure LoadFromFile(AFileName: String);
end;
const
BUF_BLOCK_SIZE = 4096;
INIT_BLOCK_COUNT=10000;
var
DebugEvent: TDebugNotifyEvent = nil;
implementation
{ TPatchGenerator }
procedure TPatchGenerator.Clear;
begin
FPatchData.Clear;
end;
constructor TPatchGenerator.Create;
begin
inherited;
FPatchData:=TMemoryStream.Create;
end;
function TPatchGenerator.CreatePatch(SourceFileName,
TargetFileName: String): Integer;
var
fsSource, fsTarget: TFileStream;
fm: TMemoryStream;
Source, Target: Pointer;
SourceSize, TargetSize: Integer;
SourceCRC, TargetCRC: Integer;
SourceTree: Pointer;
SourceTreeNodeCount: Cardinal;
cBlockSize: Integer;
o,i,lastO: Integer;
Start,Siz,BetweenSiz: Integer;
retTO: Integer;
noN: Integer;
begin
fsSource:=TFileStream.Create(SourceFileName,fmOpenRead+fmShareDenyNone);
fsTarget:=TFileStream.Create(TargetFileName,fmOpenRead+fmShareDenyNone);
fm:=TMemoryStream.Create;
SetLength(PRay,INIT_BLOCK_COUNT);
SetLength(NRay,INIT_BLOCK_COUNT);
//Load those files into memory!
SourceSize:=fsSource.Size;
try
GetMem(Source,SourceSize);
except
on EOutOfMemory do begin
Result:=-2; // not enough memory for source file
Exit;
end;
end;
fm.CopyFrom(fsSource,SourceSize);
Move(fm.Memory^,Source^,SourceSize);
SourceCRC:=FileCRC(fsSource);
fsSource.Free;
fm.Clear;
TargetSize:=fsTarget.Size;
try
GetMem(Target,TargetSize);
except
on EOutOfMemory do begin
FreeMem(Source,SourceSize);
Result:=-3; // not enough memory for target file
Exit;
end;
end;
fm.CopyFrom(fsTarget,TargetSize);
Move(fm.Memory^,Target^,TargetSize);
TargetCRC:=FileCRC(fsTarget);
fsTarget.Free;
fm.Free;
if(SourceCRC = TargetCRC) then begin
FreeMem(Source,SourceSize);
FreeMem(Target,TargetSize);
Result:=-1;
Exit;
end;
PRay[0].TargetOffset:=0;
PRay[0].SourceOffset:=0;
PRay[0].Size:=0;
noPat:=1;
//termination block
PRay[noPat].SourceOffset:=0;
PRay[noPat].TargetOffset:=TargetSize;
PRay[noPat].Size:=0;
//we only have one pass in this mode
// StartBlockSize:=16;
MinimumBlockSize:=StartBlockSize;
StepSize:=1;
BlockDivider:=2;
//because we are dividing first inside.
cBlockSize:=StartBlockSize*BlockDivider;
SourceTree:=nil;
SourceTreeNodeCount:=BuildTree(Source,SourceTree,SourceSize,cBlockSize div BlockDivider);
SortTree(SourceTree,SourceTreeNodeCount);
//now, we must do the above again - with a smaller block size
repeat
if cBlockSize<=MinimumBlockSize then break;
cBlockSize:=cBlockSize div BlockDivider;
noN:=0;
for i:=1 to noPat do begin
//calculate location of the inbetween parts
Start:=PRay[i-1].TargetOffset+PRay[i-1].Size;
BetweenSiz:=PRay[i].TargetOffset-Start;
NRay[noN].SourceOffset:=PRay[i-1].SourceOffset;
NRay[noN].TargetOffset:=PRay[i-1].TargetOffset;
NRay[noN].Size:=PRay[i-1].Size;
Inc(noN);
if BetweenSiz>0 then begin
o:=Start;
repeat
//ShowDebug(PChar('DoFind '+IntToStr(o)));
LastO:=o;
retTO:=FindBlock(Target,Source,SourceTree,o,TargetSize,SourceSize,cBlockSize,SourceTreeNodeCount,Siz);
if not (Siz=0) then
ShowDebug(IntToStr(LastO)+' -> Source='+IntToStr(retTO)+' Target='+IntToStr(o)+' Size='+IntToStr(Siz));
if Siz=0 then begin
o:=LastO+StepSize;
end else begin
//we have found a block, let's add it!
NRay[noN].SourceOffset:=retTO;
NRay[noN].TargetOffset:=o;
NRay[noN].Size:=Siz;
Inc(noN);
if noN>=Length(NRay) then begin
SetLength(NRay,Length(NRay)*2);
SetLength(PRay,Length(PRay)*2);
end;
Inc(o,Siz);
end;
//check to see if we're not inside another one.
Siz:=NRay[noN].TargetOffset-NRay[noN-1].TargetOffset-NRay[noN-1].Size;
If Siz<0 then begin //that's impossible! (overlapping should be eliminated)
NRay[noN].TargetOffset:=NRay[noN].TargetOffset-Siz;
NRay[noN].Size:=NRay[noN].Size+Siz;
NRay[noN].SourceOffset:=NRay[noN].SourceOffset-Siz;
end;
until o>Start+BetweenSiz;
end;
end;
//I think the last termination block isn't copied: do so now.
NRay[noN].SourceOffset:=PRay[noPat].SourceOffset;
NRay[noN].TargetOffset:=PRay[noPat].TargetOffset;
NRay[noN].Size:=PRay[noPat].Size;
//copy back into PRay
for i:=0 to noN do begin
PRay[i].SourceOffset:=NRay[i].SourceOffset;
PRay[i].TargetOffset:=NRay[i].TargetOffset;
PRay[i].Size:=NRay[i].Size;
end;
noPat:=noN;
until false;
//writing is next!
ShowDebug('Writing patch');
Result:=WritePatchToStream(Target, SourceCRC, TargetCRC);
ClearTree(SourceTree,SourceTreeNodeCount);
FreeMem(Source,SourceSize);
FreeMem(Target,TargetSize);
ShowDebug('Done');
end;
destructor TPatchGenerator.Destroy;
begin
FPatchData.Free;
inherited;
end;
function TPatchGenerator.FindBlock(ASubBlock, ABlock, ABlockTree: Pointer; var ASubBlockStart: Integer;
ASubBlockSize, ABlockSize, AMatchSize, ABlockTreeNodeCount: Integer; var ASize: Integer): Integer;
//This procedure locates location of a block in the target file
//Then, it calls FindBlockSize to determine size of this block
var
MatchSize, FoundSize: Integer;
q,r,i: Integer;
FoundCache_SubOffset, FoundCache_Size, FoundCache_Offset: Integer;
Checksum: Cardinal;
PFound: PTreeNode;
FoundCount: Integer;
begin
//if we find nothing...
FoundCache_Size:=0;
FoundCache_Offset:=0;
FoundCache_SubOffset:=ASubBlockStart;
FindBlock:=0;
ASize:=0;
MatchSize:=AMatchSize;
//we can only find MatchSize sized blocks in the tree!
if MatchSize > ASubBlockSize - ASubBlockStart then Exit;
if MatchSize = 0 then Exit;
Checksum:=0;
calculateChecksum(ASubBlock,ASubBlockStart,MatchSize,Checksum);
PFound:=TreeFind(Checksum,ABlockTree,ABlockTreeNodeCount,FoundCount);
for i:=0 to Pred(FoundCount) do begin
FoundSize:=MatchSize;
//q = offset in Block
q:=PFound^.Offset;
//r = offset in SubBlock
r:=ASubBlockStart;
FindBlockSize(ASubBlock, ABlock, ASubBlockSize, ABlockSize, r, q, FoundSize);
if FoundSize>FoundCache_Size then begin
FoundCache_SubOffset:=r;
FoundCache_Offset:=q;
FoundCache_Size:=FoundSize;
end;
ShowDebug(' Block Size Start='+IntToStr(r)+' tarStart='+IntToStr(q)+' Size='+IntToStr(FoundSize));
PFound:=PTreeNode(Integer(PFound)+SizeOf(TTreeNode));
end;
FindBlock:=FoundCache_Offset;
ASize:=FoundCache_Size;
ASubBlockStart:=FoundCache_SubOffset;
end;
procedure TPatchGenerator.FindBlockSize(ASubBlock, ABlock: Pointer; ASubBlockSize, ABlockSize: Integer; var ASubStart,AStart,AFoundSize: Integer);
var
FoundSize: Integer;
a,c,d,i: Integer;
f1p,f2p,f1Size,f2Size: Integer;
beforeSize: Integer;
CurBufSize: Integer;
begin
//OK, now let's go...
//Trace after -> how long does this go on?
f1p:=Integer(ASubBlock)+ASubStart;
f2p:=Integer(ABlock)+AStart;
f1Size:=ASubBlockSize-ASubStart;
f2Size:=ABlockSize-AStart;
FoundSize:=0;
CurBufSize := BUF_BLOCK_SIZE; //size of the block we're checking
while not (CurBufSize = 0) do begin
//we need equal bytes from both... so if one of them EOF, it's the end.
if FoundSize+CurBufSize>f1Size then CurBufSize:=f1Size - FoundSize;
if FoundSize+CurBufSize>f2Size then CurBufSize:=f2Size - FoundSize;
if CompareMem(Pointer(f1p),Pointer(f2p),CurBufSize) then begin
Inc(FoundSize,CurBufSize);
Inc(f1p,CurBufSize);
Inc(f2p,CurBufSize);
end
else begin
CurBufSize:=CurBufSize div 2;
end;
end;
if FoundSize = 0 then begin AFoundSize:=0; Exit; end;
//Trace before -> how much bytes are still the same before the block?
//First, read 1 block from source and 1 block from target, start from back to compare how much they differ
//just take BUF_BLOCK_SIZE as maximum size for the block before - that's surely
//big enough!
beforeSize:=BUF_BLOCK_SIZE;
a:=ASubStart-beforeSize;
if a<0 then begin
a:=0;
beforeSize:=ASubStart;
end;
//b is the current before block size
c:=AStart-beforeSize;
if c<0 then begin
c:=0;
beforeSize:=AStart;
a:=ASubStart-beforeSize;
end;
//a=Offset in source
//b=Size of beforeblock
//c=offset in target
d:=0;
for i:=beforeSize-1 downto 0 do begin
//if not (f1^[a+i]=f2^[c+i]) then begin
if not (PByte(Integer(ASubBlock)+a+i)^=PByte(Integer(ABlock)+c+i)^) then begin
//d=how many bytes before are the same?
Break;
end;
Inc(d);
end;
Inc(FoundSize,d);
Dec(ASubStart,d);
Dec(AStart,d);
AFoundSize:=FoundSize;
end;
function TPatchGenerator.Size: Integer;
begin
Result:=FPatchData.Size;
end;
procedure TPatchGenerator.ShowDebug(S: String);
begin
if Assigned(DebugEvent) then DebugEvent(S);
end;
function TPatchGenerator.WritePatchToStream(Target: Pointer; SourceCRC, TargetCRC: Integer): Integer;
var
HeadID: Array[0..3] of Char;
NoBlocks, NoBlocksOffset, BodySize, BodySizeOffset: Integer;
b: Byte;
w: Word;
i, j: Integer;
l: LongWord;
Start, Siz: Integer;
PTarget: Pointer;
begin
RemoveExistingPatch(SourceCRC);
with FPatchData do begin
Seek(0,soFromEnd);
if Size = 0 then begin
HeadID:='VPAT';
Write(HeadID,SizeOf(HeadID));
l:=0;
Write(l,SizeOf(l)); //NoFiles
end;
l:=0;
NoBlocksOffset:=Position;
Write(l,SizeOf(l)); //should become NoBlocks later
Write(SourceCRC,SizeOf(SourceCRC)); //source CRC
Write(TargetCRC,SizeOf(TargetCRC)); //target CRC
BodySizeOffset:=Position;
Write(l,SizeOf(l)); //should become BodySize (of this patch)
NoBlocks:=0;
BodySize:=0;
//Write the patch...
for i:=0 to noPat - 1 do begin
//write char 1 - integer/copysource
//write char 2 - long/copysource
//write char 5 - integer/insidepatch
//write char 6 - long/insidepatch
Start:=PRay[i].TargetOffset+PRay[i].Size;
Siz:= PRay[i+1].TargetOffset-Start;
If Siz<0 then begin //that's impossible! (overlapping should be eliminated)
PRay[i+1].TargetOffset:=PRay[i+1].TargetOffset-Siz;
PRay[i+1].Size:=PRay[i+1].Size+Siz;
PRay[i+1].SourceOffset:=PRay[i+1].SourceOffset-Siz;
Siz:=0;
end;
if not (PRay[i].Size=0) then begin
if (PRay[i].Size<=255) then begin
b:=1;
Write(b,SizeOf(b));
b:=PRay[i].Size;
Write(b,SizeOf(b));
Inc(BodySize,2);
end else if PRay[i].Size<=65535 then begin
b:=2;
Write(b,SizeOf(b));
w:=PRay[i].Size;
Write(w,SizeOf(w));
Inc(BodySize,3);
end else begin
b:=3;
Write(b,SizeOf(b));
Write(PRay[i].Size,SizeOf(Integer));
Inc(BodySize,5);
end;
Write(PRay[i].SourceOffset,SizeOf(Integer));
Inc(BodySize,SizeOf(Integer));
Inc(NoBlocks);
end;
//Now write the writeblock
If Not (Siz = 0) Then begin
if Siz<=255 then begin
b:=5;
Write(b,SizeOf(b));
b:=Siz;
Write(b,SizeOf(b));
Inc(BodySize,2);
end else if Siz<=65535 then begin
b:=6;
Write(b,1);
w:=Siz;
Write(w,2);
Inc(BodySize,3);
end else begin
b:=7;
Write(b,1);
Write(Siz,4);
Inc(BodySize,5);
end;
PTarget:=Pointer(Integer(Target)+Start);
j:=Start;
repeat
//read
if (j+4096>Start+Siz) then begin
Write(PTarget^,Start+Siz-j);
break;
end;
Write(PTarget^,4096);
Inc(j,4096);
PTarget:=Pointer(Integer(PTarget)+4096);
until false;
Inc(BodySize,Siz);
Inc(NoBlocks);
end;
end;
Seek(NoBlocksOffset,soFromBeginning);
Write(NoBlocks,SizeOf(NoBlocks));
Seek(BodySizeOffset,soFromBeginning);
Write(BodySize,SizeOf(BodySize));
ShowDebug('Patch body size: '+IntToStr(BodySize));
ShowDebug('Total patch size:'+IntToStr(Size));
//now increase file count
Seek(4,soFromBeginning);
Read(i,SizeOf(i));
Inc(i);
Seek(4,soFromBeginning);
Write(i,SizeOf(i));
Seek(0,soFromEnd);
Result:=BodySize;
end;
end;
procedure TPatchGenerator.WriteToFile(AFileName: String);
var
fs: TFileStream;
begin
fs:=TFileStream.Create(AFileName,fmCreate);
FPatchData.Seek(0,soFromBeginning);
fs.CopyFrom(FPatchData,FPatchData.Size);
fs.Free;
end;
procedure TPatchGenerator.LoadFromFile(AFileName: String);
var
fs: TFileStream;
begin
fs:=TFileStream.Create(AFileName,fmOpenRead);
FPatchData.Clear;
FPatchData.CopyFrom(fs,fs.Size);
fs.Free;
end;
procedure TPatchGenerator.RemoveExistingPatch(ACRC: Integer);
var
HeadID: Array[0..3] of Char;
NoFiles, i, j, SourceCRC, MSize: Integer;
StartPos: Integer;
ms: TMemoryStream;
begin
with FPatchData do begin
if Size = 0 then Exit;
Seek(0,soFromBeginning);
Read(HeadID,SizeOf(HeadID));
if HeadID = 'VPAT' then begin
Read(NoFiles,SizeOf(NoFiles));
for i:=0 to Pred(NoFiles) do begin
if Position >= Size then Break;
StartPos:=Position;
Read(j,SizeOf(j)); //NoBlocks
Read(SourceCRC,SizeOf(SourceCRC)); //SourceCRC
Read(j,SizeOf(j)); //TargetCRC
Read(j,SizeOf(j)); //BodySize
Seek(j,soFromCurrent);
if SourceCRC = ACRC then begin
ms:=TMemoryStream.Create;
MSize:=Size-Position;
if MSize > 0 then ms.CopyFrom(FPatchData,MSize);
ms.Seek(0, soFromBeginning);
FPatchData.Seek(StartPos,soFromBeginning);
FPatchData.SetSize(Size - j - SizeOf(Integer) * 4);
FPatchData.CopyFrom(ms,ms.Size);
ms.Free;
Dec(NoFiles);
Seek(4,soFromBeginning);
Write(NoFiles,SizeOf(NoFiles));
Break;
end;
end;
end;
end;
end;
procedure TPatchGenerator.WriteToStream(AStream: TStream);
begin
FPatchData.Seek(0,soFromBeginning);
AStream.CopyFrom(FPatchData,FPatchData.Size);
end;
end.

View file

@ -0,0 +1,20 @@
target = "GenPat"
files = Split("""
adler32.cpp
Checksums.cpp
ChunkedFile.cpp
FileFormat1.cpp
GlobalTypes.cpp
main.cpp
md5.c
PatchGenerator.cpp
POSIXUtil.cpp
""")
libs = Split("""
""")
Import('BuildUtil defenv')
BuildUtil(target, files, libs, flags = ['$EXCEPTION_FLAG'], install = 'Bin')

View file

@ -1,251 +0,0 @@
unit TreeCode;
{
VPatch 2 - Binary Checksum Tree
===============================
(c) 2002-2003 Van de Sande Productions
This unit implements a binary search tree, which is constructed from a memory
block by BuildTree. This memory block is divided into equal-sized blocks of
BlockSize, and for every block a checksum is calculated. Then, it is inserted
in the binary tree, which is sorted on the checksum.
The patch generator will search for the checksums using a binary search, which
is O(log n) (much better than the old 1.x algoritm, which was O(n)).
What's new
----------
2.1 20031219 Koen Fixed bug in TreeFind: when tree was a nil
pointer, now returns instead of AVing
2.0 20030811 Koen Initial documentation
}
interface
type
TSortStack=record
lo,hi: Integer;
end;
PTreeNode = ^TTreeNode;
TTreeNode = record
Checksum: Cardinal;
Offset: Cardinal;
end;
const
TREENODE_SIZE = SizeOf(TTreeNode);
procedure calculateChecksum(AData: Pointer; AStart, ASize: Cardinal; var K: Cardinal);
procedure calculateNext(AData: Pointer; AStart, ASize: Cardinal; var K: Cardinal);
function BuildTree(ASource: Pointer; var ATree: Pointer; ASourceSize, ABLOCKSIZE: Cardinal): Cardinal;
procedure SortTree(ATree: Pointer; ANodeCount: Cardinal);
procedure ClearTree(var ATree: Pointer; ANodeCount: Cardinal);
function TreeFind(AChecksum: Cardinal; ABlockTree: Pointer; ABlockTreeNodeCount: Integer; var FoundCount: Integer): PTreeNode;
function GetItem(ATree: Pointer; Index, ANodeCount: Cardinal): TTreeNode;
procedure Test;
implementation
uses SysUtils;
procedure calculateChecksum(AData: Pointer; AStart, ASize: Cardinal; var K: Cardinal);
var
A,B,i,j: Cardinal;
begin
A:=K and $0000FFFF;
B:=(K and $FFFF0000) shr 16;
j:=Cardinal(AData)+AStart;
for i:=1 to ASize do begin
A:=A + PByte(j)^;
B:=B + (ASize-i+1)*PByte(j)^;
Inc(j);
end;
K:=(A and $0000FFFF) or ((B and $0000FFFF) shl 16);
end;
procedure calculateNext(AData: Pointer; AStart, ASize: Cardinal; var K: Cardinal);
var
A,B,j: Cardinal;
begin
j:=Cardinal(AData)+AStart;
A:=(K-PByte(j-1)^+PByte(j+ASize-1)^) and $0000FFFF;
B:=((K shr 16)-ASize*PByte(j-1)^+A) and $0000FFFF;
K:=A or (B shl 16);
end;
function BuildTree(ASource: Pointer; var ATree: Pointer; ASourceSize, ABLOCKSIZE: Cardinal): Cardinal;
var
i, NodeCount: Cardinal;
Node: TTreeNode;
begin
Assert(not Assigned(ATree),'Cannot use initialized tree in BuildTree!');
NodeCount:=ASourceSize div ABLOCKSIZE;
GetMem(ATree,NodeCount*TREENODE_SIZE);
if NodeCount > 0 then begin
for i:=0 to Pred(NodeCount) do begin
Node.Offset:=i*ABLOCKSIZE;
Node.Checksum:=0;
calculateChecksum(ASource,Node.Offset,ABLOCKSIZE,Node.Checksum);
Move(Node,Pointer(Cardinal(ATree)+i*TREENODE_SIZE)^,TREENODE_SIZE);
end;
end;
Result:=NodeCount;
end;
procedure SetItem(ATree: Pointer; Index, ANodeCount: Cardinal; New: TTreeNode);
var
p: PTreeNode;
begin
Assert(Index<ANodeCount,'Tree/GetItem: Index too big');
p:=PTreeNode(Cardinal(ATree)+Index*TREENODE_SIZE);
p^:=New;
end;
function GetItem(ATree: Pointer; Index, ANodeCount: Cardinal): TTreeNode;
var
p: PTreeNode;
begin
Assert(Index<ANodeCount,'Tree/GetItem: Index too big '+IntToStr(Index));
p:=PTreeNode(Cardinal(ATree)+Index*TREENODE_SIZE);
Result:=p^;
end;
procedure SortTree(ATree: Pointer; ANodeCount: Cardinal);
var
compare: Cardinal;
aStack: Array[1..128] of TSortStack;
StackPtr: Integer;
Mid,i,j,low,hi: Integer;
Switcher: TTreeNode;
begin
If ANodeCount = 0 Then Exit;
StackPtr:=1;
aStack[StackPtr].lo:=0;
aStack[StackPtr].hi:=ANodeCount - 1;
Inc(StackPtr);
while not (StackPtr=1) do begin
StackPtr:=StackPtr-1;
low:=aStack[StackPtr].lo;
hi:=aStack[StackPtr].hi;
while true do begin
i:=low;
j:=hi;
Mid:=(low + hi) div 2;
compare:=PTreeNode(Integer(ATree)+Mid*TREENODE_SIZE)^.Checksum;
while true do begin
While PTreeNode(Integer(ATree)+i*TREENODE_SIZE)^.Checksum < compare do begin
Inc(i);
end;
While PTreeNode(Integer(ATree)+j*TREENODE_SIZE)^.Checksum > compare do begin
j:=j-1;
end;
If (i <= j) Then begin
Move(Pointer(Integer(ATree)+j*TREENODE_SIZE)^,Switcher,TREENODE_SIZE);
Move(Pointer(Integer(ATree)+i*TREENODE_SIZE)^,Pointer(Integer(ATree)+j*TREENODE_SIZE)^,TREENODE_SIZE);
Move(Switcher,Pointer(Integer(ATree)+i*TREENODE_SIZE)^,TREENODE_SIZE);
Inc(i);
Dec(j);
End;
if not (i <= j) then break;
end;
If j - low < hi - i Then begin
If i < hi Then begin
aStack[StackPtr].lo:=i;
aStack[StackPtr].hi:=hi;
Inc(StackPtr);
End;
hi:=j;
end Else begin
If low < j Then begin
aStack[StackPtr].lo:=low;
aStack[StackPtr].hi:=j;
Inc(StackPtr);
End;
low:=i;
End;
if not (low<hi) then break;
end;
if StackPtr=1 then break;
end;
end;
procedure ClearTree(var ATree: Pointer; ANodeCount: Cardinal);
begin
FreeMem(ATree,ANodeCount*TREENODE_SIZE);
ATree:=nil;
end;
function TreeFind(AChecksum: Cardinal; ABlockTree: Pointer; ABlockTreeNodeCount: Integer; var FoundCount: Integer): PTreeNode;
var
lo,mid,hi,m: Integer;
tmp: Cardinal;
begin
if not Assigned(ABlockTree) then begin
FoundCount:=0; Result:=nil;
Exit;
end;
lo:=0;
hi:=ABlockTreeNodeCount-1;
while true do begin
mid:=(lo+hi) div 2;
tmp:=PCardinal(Integer(ABlockTree)+mid*TREENODE_SIZE)^;
if tmp = AChecksum then begin
FoundCount:=1;
m:=mid;
Result:=PTreeNode(Integer(ABlockTree)+m*TREENODE_SIZE);
while m > 0 do begin
Dec(m);
if PCardinal(Integer(ABlockTree)+m*TREENODE_SIZE)^ = tmp then begin
Result:=PTreeNode(Integer(ABlockTree)+m*TREENODE_SIZE);
Inc(FoundCount);
end else
Break;
end;
m:=mid;
while m < ABlockTreeNodeCount-1 do begin
Inc(m);
if PCardinal(Integer(ABlockTree)+m*TREENODE_SIZE)^ = tmp then begin
Inc(FoundCount);
end else
Break;
end;
Exit;
end;
if lo>=hi then Break;
if AChecksum < tmp then begin
hi:=mid-1;
end else begin
lo:=mid+1;
end;
end;
FoundCount:=0; Result:=nil;
end;
procedure Test;
var
p: Pointer;
t: TTreeNode;
r: PTreeNode;
i,q: Integer;
NC: Integer;
begin
NC:=100;
GetMem(p,800);
for i:=0 to 99 do begin
t.Offset:=i*100;
t.Checksum:=i div 2;
SetItem(p,i,NC,t);
end;
SortTree(p,NC);
for i:=0 to 99 do begin
t:=GetItem(p,i,NC);
Write(IntToStr(t.Checksum)+' ');
end;
r:=TreeFind(7,p,NC,q);
WriteLn(IntToStr(q));
t:=r^;
WriteLn(IntToStr(t.Checksum)+' ');
end;
end.

View file

@ -1,77 +0,0 @@
program VAppend;
{$APPTYPE CONSOLE}
uses
SysUtils;
var
fs, fo: File;
Patch: String;
OutFile: String = 'VPATCH.EXE';
Runtime: String = 'VPATCH.BIN';
o: LongWord;
Buf: Array[0..4095] of Byte;
Size, BufSize: Integer;
begin
WriteLn('VAppend v2.0');
WriteLn('============');
WriteLn;
WriteLn('(c) 2001-2002 Van de Sande Productions');
WriteLn('Website: http://www.tibed.net/vpatch');
WriteLn('E-mail: koen@tibed.net');
WriteLn;
if ParamCount = 0 then begin
WriteLn('Use this program to append .PAT files to the VPatch runtime.');
WriteLn;
WriteLn(' VAPPEND (patch file) [output file] [runtime]');
WriteLn;
WriteLn('By default, the output file is VPATCH.EXE and the runtime is VPATCH.BIN');
end;
if not FileExists(ParamStr(1)) then begin
WriteLn('ERROR: Patch file not found');
Exit;
end;
Patch := ParamStr(1);
if ParamCount > 1 then OutFile := ParamStr(2);
if ParamCount > 2 then Runtime := ParamStr(3);
WriteLn('Patch: '+Patch);
WriteLn('Runtime: '+Runtime);
WriteLn('Output: '+OutFile);
AssignFile(fo,OutFile);
Rewrite(fo,1);
//copy the runtime
AssignFile(fs,Runtime);
FileMode:=fmOpenRead;
Reset(fs,1);
BufSize:=4096;
o:=FileSize(fs); //patch start offset
Size:=FileSize(fs);
while Size>0 do begin
if Size-BufSize<0 then BufSize:=Size;
BlockRead(fs,Buf,BufSize);
BlockWrite(fo,Buf,BufSize);
Dec(Size,BufSize);
end;
CloseFile(fs);
//do the patch
AssignFile(fs,Patch);
FileMode:=fmOpenRead;
Reset(fs,1);
BufSize:=4096;
Size:=FileSize(fs);
while Size>0 do begin
if Size-BufSize<0 then BufSize:=Size;
BlockRead(fs,Buf,BufSize);
BlockWrite(fo,Buf,BufSize);
Dec(Size,BufSize);
end;
CloseFile(fs);
BlockWrite(fo,o,SizeOf(o));
CloseFile(fo);
WriteLn('Created.');
end.

View file

@ -0,0 +1,92 @@
/* adler32.c -- compute the Adler-32 checksum of a data stream
* Copyright (C) 1995-2003 Mark Adler
THIS IS A MODIFIED VERSION OF THE ORIGINAL ZLIB adler32.c!
The following was copied from zlib.h:
This software is provided 'as-is', without any express or implied
warranty. In no event will the authors be held liable for any damages
arising from the use of this software.
Permission is granted to anyone to use this software for any purpose,
including commercial applications, and to alter it and redistribute it
freely, subject to the following restrictions:
1. The origin of this software must not be misrepresented; you must not
claim that you wrote the original software. If you use this software
in a product, an acknowledgment in the product documentation would be
appreciated but is not required.
2. Altered source versions must be plainly marked as such, and must not be
misrepresented as being the original software.
3. This notice may not be removed or altered from any source distribution.
*/
#include "adler32.h"
#define Z_NULL 0 /* for initializing zalloc, zfree, opaque */
#define BASE 65521UL /* largest prime smaller than 65536 */
#define NMAX 5552
/* NMAX is the largest n such that 255n(n+1)/2 + (n+1)(BASE-1) <= 2^32-1 */
#define DO1(buf,i) {s1 += buf[i]; s2 += s1;}
#define DO2(buf,i) DO1(buf,i); DO1(buf,i+1);
#define DO4(buf,i) DO2(buf,i); DO2(buf,i+2);
#define DO8(buf,i) DO4(buf,i); DO4(buf,i+4);
#define DO16(buf) DO8(buf,0); DO8(buf,8);
#ifdef NO_DIVIDE
# define MOD(a) \
do { \
if (a >= (BASE << 16)) a -= (BASE << 16); \
if (a >= (BASE << 15)) a -= (BASE << 15); \
if (a >= (BASE << 14)) a -= (BASE << 14); \
if (a >= (BASE << 13)) a -= (BASE << 13); \
if (a >= (BASE << 12)) a -= (BASE << 12); \
if (a >= (BASE << 11)) a -= (BASE << 11); \
if (a >= (BASE << 10)) a -= (BASE << 10); \
if (a >= (BASE << 9)) a -= (BASE << 9); \
if (a >= (BASE << 8)) a -= (BASE << 8); \
if (a >= (BASE << 7)) a -= (BASE << 7); \
if (a >= (BASE << 6)) a -= (BASE << 6); \
if (a >= (BASE << 5)) a -= (BASE << 5); \
if (a >= (BASE << 4)) a -= (BASE << 4); \
if (a >= (BASE << 3)) a -= (BASE << 3); \
if (a >= (BASE << 2)) a -= (BASE << 2); \
if (a >= (BASE << 1)) a -= (BASE << 1); \
if (a >= BASE) a -= BASE; \
} while (0)
#else
# define MOD(a) a %= BASE
#endif
namespace Checksum {
/* ========================================================================= */
uLong adler32(uLong adler, const Byte *buf, uInt len) {
unsigned long s1 = adler & 0xffff;
unsigned long s2 = (adler >> 16) & 0xffff;
int k;
if (buf == Z_NULL) return 1L;
while (len > 0) {
k = len < NMAX ? (int)len : NMAX;
len -= k;
while (k >= 16) {
DO16(buf);
buf += 16;
k -= 16;
}
if (k != 0) do {
s1 += *buf++;
s2 += s1;
} while (--k);
MOD(s1);
MOD(s2);
}
return (s2 << 16) | s1;
}
}

View file

@ -0,0 +1,18 @@
//---------------------------------------------------------------------------
// Adler32
//---------------------------------------------------------------------------
#if !defined(Adler32_H)
#define Adler32_H
namespace Checksum {
typedef unsigned int uInt; /* 16 bits or more */
typedef unsigned long uLong; /* 32 bits or more */
typedef unsigned char Byte; /* 8 bits */
uLong adler32(uLong adler, const Byte *buf, uInt len);
}
#endif // Adler32_H

View file

@ -0,0 +1,292 @@
//---------------------------------------------------------------------------
// main.cpp
//---------------------------------------------------------------------------
// -=* VPatch *=-
//---------------------------------------------------------------------------
// Copyright (C) 2001-2005 Koen van de Sande / Van de Sande Productions
//---------------------------------------------------------------------------
// Website: http://www.tibed.net/vpatch
//
// This software is provided 'as-is', without any express or implied
// warranty. In no event will the authors be held liable for any damages
// arising from the use of this software.
//
// Permission is granted to anyone to use this software for any purpose,
// including commercial applications, and to alter it and redistribute it
// freely, subject to the following restrictions:
//
// 1. The origin of this software must not be misrepresented; you must not
// claim that you wrote the original software. If you use this software
// in a product, an acknowledgment in the product documentation would be
// appreciated but is not required.
// 2. Altered source versions must be plainly marked as such, and must not be
// misrepresented as being the original software.
// 3. This notice may not be removed or altered from any source distribution.
#ifdef __BORLANDC__
#pragma argsused
#endif
#if !defined(__BORLANDC__) && !defined(_MSC_VER)
#include <unistd.h>
#endif
#include "GlobalTypes.h"
#include "POSIXUtil.h"
#include "Checksums.h"
#include "PatchGenerator.h"
#include "FileFormat1.h"
#include <fstream>
#include <sstream>
int main( int argc, char * argv[] ) {
cout << "GenPat v3.1\n";
cout << "===========\n\n(c) 2001-2005 Van de Sande Productions\n";
cout << "Website: http://www.tibed.net/vpatch\n\n";
string sourceFileName;
string targetFileName;
string patchFileName;
bool showHelp = true;
int blockSize = 64;
int maxMatches = 500;
bool beVerbose = false;
bool beOptimal = false;
bool existanceIsError = true; // flag error if patch already has source's MD5/CRC32?
int fileNameArgument = 0;
if(argc > 1) {
for(int i = 1; i < argc; i++) {
string s(argv[i]);
if(s.size() > 0) {
if(s[0] == '/') {
if(s.size() > 1) {
if((s[1] == 'v') || (s[1] == 'V')) {
beVerbose = true;
}
if((s[1] == 'o') || (s[1] == 'O')) {
beOptimal = true;
}
if((s[1] == 'r') || (s[1] == 'R')) {
existanceIsError = false;
}
}
if(s.size() > 2) {
if((s[1] == 'b') || (s[1] == 'B')) {
if(s[2] == '=') {
istringstream ss(s.substr(3));
ss >> blockSize;
}
}
if((s[1] == 'a') || (s[1] == 'A')) {
if(s[2] == '=') {
istringstream ss(s.substr(3));
ss >> maxMatches;
}
}
}
} else {
switch (fileNameArgument) {
case 0:
sourceFileName = s;
break;
case 1:
targetFileName = s;
break;
case 2:
patchFileName = s;
showHelp = false;
break;
default:
cerr << "WARNING: extra filename argument not used: " << s << "\n";
}
fileNameArgument++;
}
}
}
}
if(beOptimal) {
maxMatches = 0;
}
if(showHelp) {
cout << "This program will take (sourcefile) as input and create a (patchfile).\n";
cout << "With this patchfile, you can convert a (sourcefile) into (targetfile).\n\n";
cout << "Command line info:\n";
cout << " GENPAT (sourcefile) (targetfile) (patchfile)\n\n";
cout << "Command line option (optional):\n";
cout << "/R Replace a patch with same contents as source silently if it\n already exists.\n";
cout << "/B=64 Set blocksize (default=64), multiple of 2 is required.\n";
cout << "/V More verbose information during patch creation.\n";
cout << "/O Deactivate match limit of the /A switch (sometimes smaller patches).\n";
cout << "/A=500 Maximum number of block matches per block (improves performance).\n";
cout << " Default is 500, larger is slower. Use /V to see the cut-off aborts.\n\n";
cout << "Note: filenames should never start with / character!\n\n";
cout << "Possible exit codes:\n";
cout << " 0 Success\n";
cout << " 1 Arguments missing\n";
cout << " 2 Other error\n";
cout << " 3 Source file already has a patch in specified patch file (=error)\n";
return 1;
}
cout << "[Source] " << sourceFileName.c_str() << "\n";
cout << "[Target] " << targetFileName.c_str() << "\n[PatchFile] " << patchFileName.c_str() << "\n";
// get the file sizes
TFileOffset sourceSize = 0;
try {
sourceSize = POSIX::getFileSize(sourceFileName.c_str());
}
catch(char* s) {
cerr << "Source file size reading failed: " << s << "\n";
return 2;
}
TFileOffset targetSize;
try {
targetSize = POSIX::getFileSize(targetFileName.c_str());
}
catch(const char* s) {
cerr << "Target file size reading failed: " << s << "\n";
return 2;
}
// calculate CRCs
TChecksum* sourceCRC = new TChecksum(sourceFileName);
sourceCRC->mode = TChecksum::MD5; // default
TChecksum* targetCRC = new TChecksum(targetFileName);
targetCRC->mode = TChecksum::MD5; // default
string tempFileName = POSIX::getTempFile();
// open the files
bifstream source;
source.open(sourceFileName.c_str(), std::ios_base::binary | std::ios_base::in);
bifstream target;
target.open(targetFileName.c_str(), std::ios_base::binary | std::ios_base::in);
bofstream patch;
patch.open(tempFileName.c_str(), std::ios_base::binary | std::ios_base::out);
// remove existing patch with sourceCRC
TFileOffset fileCount = 0;
while(true) {
TFileOffset previousPatchSize;
try {
previousPatchSize = POSIX::getFileSize(patchFileName.c_str());
} catch(const char* s) {
cout << "Patch file does not yet exist: " << s << ", it will be created.\n";
std::ofstream newfile;
newfile.open(patchFileName.c_str(), std::ios_base::binary | std::ios_base::out);
newfile.close();
}
bifstream previousPatch;
previousPatch.open(patchFileName.c_str(), std::ios_base::binary | std::ios_base::in);
try {
// this will copy the contents of previousPatch to patch, but without sourceCRC
fileCount = FileFormat1::removeExistingPatch(previousPatch,previousPatchSize,patch,sourceCRC,existanceIsError);
} catch(const char* s) {
cerr << "ERROR: " << s << "\n";
patch.close();
unlink(tempFileName.c_str());
return 3;
}
// set them to the same checksum mode
targetCRC->mode = sourceCRC->mode;
cout << "[Checksum] Kind of checksums used: ";
if(targetCRC->mode == TChecksum::MD5) cout << "MD5\n";
if(targetCRC->mode == TChecksum::CRC32) cout << "CRC32\n";
break;
}
if(source.good() && target.good() && patch.good()) {
PatchGenerator* gen = new PatchGenerator(source,sourceSize,target,targetSize,patch);
try {
// clean up the blocksize to be a multiple of 2
int orgBlockSize = blockSize;
int bs_counter = 0;
while(blockSize != 0) {
bs_counter++;
blockSize >>= 1;
}
blockSize = 1;
while(bs_counter != 0) {
blockSize <<= 1;
bs_counter--;
}
if((blockSize >> 1) == orgBlockSize) blockSize = orgBlockSize;
if(blockSize != orgBlockSize) {
cout << "[BlockSizeFix] Your blocksize had to be fixed since it is not a multiple of 2\n";
}
if(blockSize < 16) {
blockSize = 16;
cout << "[BlockSizeFix] Your blocksize had to be fixed since it is smaller than 16\n";
}
gen->blockSize = blockSize;
cout << "[BlockSize] " << static_cast<unsigned int>(gen->blockSize) << " bytes\n";
gen->maxMatches = maxMatches;
if(gen->maxMatches == 0) {
cout << "[FindBlockMatchLimit] Unlimited matches\n";
} else {
cout << "[FindBlockMatchLimit] " << gen->maxMatches << " matches\n";
}
gen->beVerbose = beVerbose;
if(beVerbose) {
cout << "[Debug] Verbose output during patch generation activated.\n";
}
// create sameBlock storage
vector<SameBlock*> sameBlocks;
// run the patch generator to find similar blocks
gen->execute(sameBlocks);
// construct the actual patch in FileFormat1
FileFormat1::writePatch(patch,target,sameBlocks,sourceCRC,targetCRC,fileCount,POSIX::getFileTime(targetFileName.c_str()));
// cleanup sameblocks
for(vector<SameBlock*>::iterator iter = sameBlocks.begin(); iter != sameBlocks.end(); iter++) {
delete *iter;
*iter = NULL;
}
patch.close();
TFileOffset patchSize = POSIX::getFileSize(tempFileName.c_str());
// finally: copy the temporary file to the actual patch
bifstream tempF;
tempF.open(tempFileName.c_str(), std::ios_base::binary | std::ios_base::in);
bofstream patchF;
patchF.open(patchFileName.c_str(), std::ios_base::binary | std::ios_base::out);
char* buf = new char[patchSize];
tempF.read(buf,patchSize);
patchF.write(buf,patchSize);
delete[] buf;
tempF.close();
// now empty the temporary file
std::ofstream clearF;
clearF.open(tempFileName.c_str(), std::ios_base::binary | std::ios_base::out);
}
catch(string s) {
cerr << "Error thrown: " << s.c_str();
return 2;
}
catch(const char* s) {
cerr << "Error thrown: " << s;
return 2;
}
} else {
cerr << "There was a problem opening the files.\n";
return 2;
}
if(*sourceCRC == *targetCRC)
cerr << "WARNING: source and target file have equal CRCs!";
delete sourceCRC;
delete targetCRC;
unlink(tempFileName.c_str());
return 0;
}

View file

@ -0,0 +1,389 @@
/*
Copyright (C) 1999, 2000, 2002 Aladdin Enterprises. All rights reserved.
This software is provided 'as-is', without any express or implied
warranty. In no event will the authors be held liable for any damages
arising from the use of this software.
Permission is granted to anyone to use this software for any purpose,
including commercial applications, and to alter it and redistribute it
freely, subject to the following restrictions:
1. The origin of this software must not be misrepresented; you must not
claim that you wrote the original software. If you use this software
in a product, an acknowledgment in the product documentation would be
appreciated but is not required.
2. Altered source versions must be plainly marked as such, and must not be
misrepresented as being the original software.
3. This notice may not be removed or altered from any source distribution.
L. Peter Deutsch
ghost@aladdin.com
*/
/* $Id: md5.c,v 1.6 2002/04/13 19:20:28 lpd Exp $ */
/*
Independent implementation of MD5 (RFC 1321).
This code implements the MD5 Algorithm defined in RFC 1321, whose
text is available at
http://www.ietf.org/rfc/rfc1321.txt
The code is derived from the text of the RFC, including the test suite
(section A.5) but excluding the rest of Appendix A. It does not include
any code or documentation that is identified in the RFC as being
copyrighted.
The original and principal author of md5.c is L. Peter Deutsch
<ghost@aladdin.com>. Other authors are noted in the change history
that follows (in reverse chronological order):
2002-04-13 lpd Clarified derivation from RFC 1321; now handles byte order
either statically or dynamically; added missing #include <string.h>
in library.
2002-03-11 lpd Corrected argument list for main(), and added int return
type, in test program and T value program.
2002-02-21 lpd Added missing #include <stdio.h> in test program.
2000-07-03 lpd Patched to eliminate warnings about "constant is
unsigned in ANSI C, signed in traditional"; made test program
self-checking.
1999-11-04 lpd Edited comments slightly for automatic TOC extraction.
1999-10-18 lpd Fixed typo in header comment (ansi2knr rather than md5).
1999-05-03 lpd Original version.
*/
#include "md5.h"
void memcpy2( void *dest, const void *src, int count ) {
md5_byte_t* bDest = (md5_byte_t*)dest;
md5_byte_t* bSrc = (md5_byte_t*)src;
int i = 0;
for(; i < count; i++) {
bDest[i] = bSrc[i];
}
}
#undef BYTE_ORDER /* 1 = big-endian, -1 = little-endian, 0 = unknown */
#ifdef ARCH_IS_BIG_ENDIAN
# define BYTE_ORDER (ARCH_IS_BIG_ENDIAN ? 1 : -1)
#else
# define BYTE_ORDER 0
#endif
#define T_MASK ((md5_word_t)~0)
#define T1 /* 0xd76aa478 */ (T_MASK ^ 0x28955b87)
#define T2 /* 0xe8c7b756 */ (T_MASK ^ 0x173848a9)
#define T3 0x242070db
#define T4 /* 0xc1bdceee */ (T_MASK ^ 0x3e423111)
#define T5 /* 0xf57c0faf */ (T_MASK ^ 0x0a83f050)
#define T6 0x4787c62a
#define T7 /* 0xa8304613 */ (T_MASK ^ 0x57cfb9ec)
#define T8 /* 0xfd469501 */ (T_MASK ^ 0x02b96afe)
#define T9 0x698098d8
#define T10 /* 0x8b44f7af */ (T_MASK ^ 0x74bb0850)
#define T11 /* 0xffff5bb1 */ (T_MASK ^ 0x0000a44e)
#define T12 /* 0x895cd7be */ (T_MASK ^ 0x76a32841)
#define T13 0x6b901122
#define T14 /* 0xfd987193 */ (T_MASK ^ 0x02678e6c)
#define T15 /* 0xa679438e */ (T_MASK ^ 0x5986bc71)
#define T16 0x49b40821
#define T17 /* 0xf61e2562 */ (T_MASK ^ 0x09e1da9d)
#define T18 /* 0xc040b340 */ (T_MASK ^ 0x3fbf4cbf)
#define T19 0x265e5a51
#define T20 /* 0xe9b6c7aa */ (T_MASK ^ 0x16493855)
#define T21 /* 0xd62f105d */ (T_MASK ^ 0x29d0efa2)
#define T22 0x02441453
#define T23 /* 0xd8a1e681 */ (T_MASK ^ 0x275e197e)
#define T24 /* 0xe7d3fbc8 */ (T_MASK ^ 0x182c0437)
#define T25 0x21e1cde6
#define T26 /* 0xc33707d6 */ (T_MASK ^ 0x3cc8f829)
#define T27 /* 0xf4d50d87 */ (T_MASK ^ 0x0b2af278)
#define T28 0x455a14ed
#define T29 /* 0xa9e3e905 */ (T_MASK ^ 0x561c16fa)
#define T30 /* 0xfcefa3f8 */ (T_MASK ^ 0x03105c07)
#define T31 0x676f02d9
#define T32 /* 0x8d2a4c8a */ (T_MASK ^ 0x72d5b375)
#define T33 /* 0xfffa3942 */ (T_MASK ^ 0x0005c6bd)
#define T34 /* 0x8771f681 */ (T_MASK ^ 0x788e097e)
#define T35 0x6d9d6122
#define T36 /* 0xfde5380c */ (T_MASK ^ 0x021ac7f3)
#define T37 /* 0xa4beea44 */ (T_MASK ^ 0x5b4115bb)
#define T38 0x4bdecfa9
#define T39 /* 0xf6bb4b60 */ (T_MASK ^ 0x0944b49f)
#define T40 /* 0xbebfbc70 */ (T_MASK ^ 0x4140438f)
#define T41 0x289b7ec6
#define T42 /* 0xeaa127fa */ (T_MASK ^ 0x155ed805)
#define T43 /* 0xd4ef3085 */ (T_MASK ^ 0x2b10cf7a)
#define T44 0x04881d05
#define T45 /* 0xd9d4d039 */ (T_MASK ^ 0x262b2fc6)
#define T46 /* 0xe6db99e5 */ (T_MASK ^ 0x1924661a)
#define T47 0x1fa27cf8
#define T48 /* 0xc4ac5665 */ (T_MASK ^ 0x3b53a99a)
#define T49 /* 0xf4292244 */ (T_MASK ^ 0x0bd6ddbb)
#define T50 0x432aff97
#define T51 /* 0xab9423a7 */ (T_MASK ^ 0x546bdc58)
#define T52 /* 0xfc93a039 */ (T_MASK ^ 0x036c5fc6)
#define T53 0x655b59c3
#define T54 /* 0x8f0ccc92 */ (T_MASK ^ 0x70f3336d)
#define T55 /* 0xffeff47d */ (T_MASK ^ 0x00100b82)
#define T56 /* 0x85845dd1 */ (T_MASK ^ 0x7a7ba22e)
#define T57 0x6fa87e4f
#define T58 /* 0xfe2ce6e0 */ (T_MASK ^ 0x01d3191f)
#define T59 /* 0xa3014314 */ (T_MASK ^ 0x5cfebceb)
#define T60 0x4e0811a1
#define T61 /* 0xf7537e82 */ (T_MASK ^ 0x08ac817d)
#define T62 /* 0xbd3af235 */ (T_MASK ^ 0x42c50dca)
#define T63 0x2ad7d2bb
#define T64 /* 0xeb86d391 */ (T_MASK ^ 0x14792c6e)
static void
md5_process(md5_state_t *pms, const md5_byte_t *data /*[64]*/)
{
md5_word_t
a = pms->abcd[0], b = pms->abcd[1],
c = pms->abcd[2], d = pms->abcd[3];
md5_word_t t;
#if BYTE_ORDER > 0
/* Define storage only for big-endian CPUs. */
md5_word_t X[16];
#else
/* Define storage for little-endian or both types of CPUs. */
md5_word_t xbuf[16];
const md5_word_t *X;
#endif
{
#if BYTE_ORDER == 0
/*
* Determine dynamically whether this is a big-endian or
* little-endian machine, since we can use a more efficient
* algorithm on the latter.
*/
static const int w = 1;
if (*((const md5_byte_t *)&w)) /* dynamic little-endian */
#endif
#if BYTE_ORDER <= 0 /* little-endian */
{
/*
* On little-endian machines, we can process properly aligned
* data without copying it.
*/
if (!((data - (const md5_byte_t *)0) & 3)) {
/* data are properly aligned */
X = (const md5_word_t *)data;
} else {
/* not aligned */
memcpy2(xbuf, data, 64);
X = xbuf;
}
}
#endif
#if BYTE_ORDER == 0
else /* dynamic big-endian */
#endif
#if BYTE_ORDER >= 0 /* big-endian */
{
/*
* On big-endian machines, we must arrange the bytes in the
* right order.
*/
const md5_byte_t *xp = data;
int i;
# if BYTE_ORDER == 0
X = xbuf; /* (dynamic only) */
# else
# define xbuf X /* (static only) */
# endif
for (i = 0; i < 16; ++i, xp += 4)
xbuf[i] = xp[0] + (xp[1] << 8) + (xp[2] << 16) + (xp[3] << 24);
}
#endif
}
#define ROTATE_LEFT(x, n) (((x) << (n)) | ((x) >> (32 - (n))))
/* Round 1. */
/* Let [abcd k s i] denote the operation
a = b + ((a + F(b,c,d) + X[k] + T[i]) <<< s). */
#define F(x, y, z) (((x) & (y)) | (~(x) & (z)))
#define SET(a, b, c, d, k, s, Ti)\
t = a + F(b,c,d) + X[k] + Ti;\
a = ROTATE_LEFT(t, s) + b
/* Do the following 16 operations. */
SET(a, b, c, d, 0, 7, T1);
SET(d, a, b, c, 1, 12, T2);
SET(c, d, a, b, 2, 17, T3);
SET(b, c, d, a, 3, 22, T4);
SET(a, b, c, d, 4, 7, T5);
SET(d, a, b, c, 5, 12, T6);
SET(c, d, a, b, 6, 17, T7);
SET(b, c, d, a, 7, 22, T8);
SET(a, b, c, d, 8, 7, T9);
SET(d, a, b, c, 9, 12, T10);
SET(c, d, a, b, 10, 17, T11);
SET(b, c, d, a, 11, 22, T12);
SET(a, b, c, d, 12, 7, T13);
SET(d, a, b, c, 13, 12, T14);
SET(c, d, a, b, 14, 17, T15);
SET(b, c, d, a, 15, 22, T16);
#undef SET
/* Round 2. */
/* Let [abcd k s i] denote the operation
a = b + ((a + G(b,c,d) + X[k] + T[i]) <<< s). */
#define G(x, y, z) (((x) & (z)) | ((y) & ~(z)))
#define SET(a, b, c, d, k, s, Ti)\
t = a + G(b,c,d) + X[k] + Ti;\
a = ROTATE_LEFT(t, s) + b
/* Do the following 16 operations. */
SET(a, b, c, d, 1, 5, T17);
SET(d, a, b, c, 6, 9, T18);
SET(c, d, a, b, 11, 14, T19);
SET(b, c, d, a, 0, 20, T20);
SET(a, b, c, d, 5, 5, T21);
SET(d, a, b, c, 10, 9, T22);
SET(c, d, a, b, 15, 14, T23);
SET(b, c, d, a, 4, 20, T24);
SET(a, b, c, d, 9, 5, T25);
SET(d, a, b, c, 14, 9, T26);
SET(c, d, a, b, 3, 14, T27);
SET(b, c, d, a, 8, 20, T28);
SET(a, b, c, d, 13, 5, T29);
SET(d, a, b, c, 2, 9, T30);
SET(c, d, a, b, 7, 14, T31);
SET(b, c, d, a, 12, 20, T32);
#undef SET
/* Round 3. */
/* Let [abcd k s t] denote the operation
a = b + ((a + H(b,c,d) + X[k] + T[i]) <<< s). */
#define H(x, y, z) ((x) ^ (y) ^ (z))
#define SET(a, b, c, d, k, s, Ti)\
t = a + H(b,c,d) + X[k] + Ti;\
a = ROTATE_LEFT(t, s) + b
/* Do the following 16 operations. */
SET(a, b, c, d, 5, 4, T33);
SET(d, a, b, c, 8, 11, T34);
SET(c, d, a, b, 11, 16, T35);
SET(b, c, d, a, 14, 23, T36);
SET(a, b, c, d, 1, 4, T37);
SET(d, a, b, c, 4, 11, T38);
SET(c, d, a, b, 7, 16, T39);
SET(b, c, d, a, 10, 23, T40);
SET(a, b, c, d, 13, 4, T41);
SET(d, a, b, c, 0, 11, T42);
SET(c, d, a, b, 3, 16, T43);
SET(b, c, d, a, 6, 23, T44);
SET(a, b, c, d, 9, 4, T45);
SET(d, a, b, c, 12, 11, T46);
SET(c, d, a, b, 15, 16, T47);
SET(b, c, d, a, 2, 23, T48);
#undef SET
/* Round 4. */
/* Let [abcd k s t] denote the operation
a = b + ((a + I(b,c,d) + X[k] + T[i]) <<< s). */
#define I(x, y, z) ((y) ^ ((x) | ~(z)))
#define SET(a, b, c, d, k, s, Ti)\
t = a + I(b,c,d) + X[k] + Ti;\
a = ROTATE_LEFT(t, s) + b
/* Do the following 16 operations. */
SET(a, b, c, d, 0, 6, T49);
SET(d, a, b, c, 7, 10, T50);
SET(c, d, a, b, 14, 15, T51);
SET(b, c, d, a, 5, 21, T52);
SET(a, b, c, d, 12, 6, T53);
SET(d, a, b, c, 3, 10, T54);
SET(c, d, a, b, 10, 15, T55);
SET(b, c, d, a, 1, 21, T56);
SET(a, b, c, d, 8, 6, T57);
SET(d, a, b, c, 15, 10, T58);
SET(c, d, a, b, 6, 15, T59);
SET(b, c, d, a, 13, 21, T60);
SET(a, b, c, d, 4, 6, T61);
SET(d, a, b, c, 11, 10, T62);
SET(c, d, a, b, 2, 15, T63);
SET(b, c, d, a, 9, 21, T64);
#undef SET
/* Then perform the following additions. (That is increment each
of the four registers by the value it had before this block
was started.) */
pms->abcd[0] += a;
pms->abcd[1] += b;
pms->abcd[2] += c;
pms->abcd[3] += d;
}
void
md5_init(md5_state_t *pms)
{
pms->count[0] = pms->count[1] = 0;
pms->abcd[0] = 0x67452301;
pms->abcd[1] = /*0xefcdab89*/ T_MASK ^ 0x10325476;
pms->abcd[2] = /*0x98badcfe*/ T_MASK ^ 0x67452301;
pms->abcd[3] = 0x10325476;
}
void
md5_append(md5_state_t *pms, const md5_byte_t *data, int nbytes)
{
const md5_byte_t *p = data;
int left = nbytes;
int offset = (pms->count[0] >> 3) & 63;
md5_word_t nbits = (md5_word_t)(nbytes << 3);
if (nbytes <= 0)
return;
/* Update the message length. */
pms->count[1] += nbytes >> 29;
pms->count[0] += nbits;
if (pms->count[0] < nbits)
pms->count[1]++;
/* Process an initial partial block. */
if (offset) {
int copy = (offset + nbytes > 64 ? 64 - offset : nbytes);
memcpy2(pms->buf + offset, p, copy);
if (offset + copy < 64)
return;
p += copy;
left -= copy;
md5_process(pms, pms->buf);
}
/* Process full blocks. */
for (; left >= 64; p += 64, left -= 64)
md5_process(pms, p);
/* Process a final partial block. */
if (left)
memcpy2(pms->buf, p, left);
}
void
md5_finish(md5_state_t *pms, md5_byte_t digest[16])
{
static const md5_byte_t pad[64] = {
0x80, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0,
0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0,
0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0,
0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0
};
md5_byte_t data[8];
int i;
/* Save the length before padding. */
for (i = 0; i < 8; ++i)
data[i] = (md5_byte_t)(pms->count[i >> 2] >> ((i & 3) << 3));
/* Pad to 56 bytes mod 64. */
md5_append(pms, pad, ((55 - (pms->count[0] >> 3)) & 63) + 1);
/* Append the length. */
md5_append(pms, data, 8);
for (i = 0; i < 16; ++i)
digest[i] = (md5_byte_t)(pms->abcd[i >> 2] >> ((i & 3) << 3));
}

View file

@ -0,0 +1,91 @@
/*
Copyright (C) 1999, 2002 Aladdin Enterprises. All rights reserved.
This software is provided 'as-is', without any express or implied
warranty. In no event will the authors be held liable for any damages
arising from the use of this software.
Permission is granted to anyone to use this software for any purpose,
including commercial applications, and to alter it and redistribute it
freely, subject to the following restrictions:
1. The origin of this software must not be misrepresented; you must not
claim that you wrote the original software. If you use this software
in a product, an acknowledgment in the product documentation would be
appreciated but is not required.
2. Altered source versions must be plainly marked as such, and must not be
misrepresented as being the original software.
3. This notice may not be removed or altered from any source distribution.
L. Peter Deutsch
ghost@aladdin.com
*/
/* $Id: md5.h,v 1.4 2002/04/13 19:20:28 lpd Exp $ */
/*
Independent implementation of MD5 (RFC 1321).
This code implements the MD5 Algorithm defined in RFC 1321, whose
text is available at
http://www.ietf.org/rfc/rfc1321.txt
The code is derived from the text of the RFC, including the test suite
(section A.5) but excluding the rest of Appendix A. It does not include
any code or documentation that is identified in the RFC as being
copyrighted.
The original and principal author of md5.h is L. Peter Deutsch
<ghost@aladdin.com>. Other authors are noted in the change history
that follows (in reverse chronological order):
2002-04-13 lpd Removed support for non-ANSI compilers; removed
references to Ghostscript; clarified derivation from RFC 1321;
now handles byte order either statically or dynamically.
1999-11-04 lpd Edited comments slightly for automatic TOC extraction.
1999-10-18 lpd Fixed typo in header comment (ansi2knr rather than md5);
added conditionalization for C++ compilation from Martin
Purschke <purschke@bnl.gov>.
1999-05-03 lpd Original version.
*/
#ifndef md5_INCLUDED
# define md5_INCLUDED
/*
* This package supports both compile-time and run-time determination of CPU
* byte order. If ARCH_IS_BIG_ENDIAN is defined as 0, the code will be
* compiled to run only on little-endian CPUs; if ARCH_IS_BIG_ENDIAN is
* defined as non-zero, the code will be compiled to run only on big-endian
* CPUs; if ARCH_IS_BIG_ENDIAN is not defined, the code will be compiled to
* run on either big- or little-endian CPUs, but will run slightly less
* efficiently on either one than if ARCH_IS_BIG_ENDIAN is defined.
*/
typedef unsigned char md5_byte_t; /* 8-bit byte */
typedef unsigned int md5_word_t; /* 32-bit word */
/* Define the state of the MD5 Algorithm. */
typedef struct md5_state_s {
md5_word_t count[2]; /* message length in bits, lsw first */
md5_word_t abcd[4]; /* digest buffer */
md5_byte_t buf[64]; /* accumulate block */
} md5_state_t;
#ifdef __cplusplus
extern "C"
{
#endif
/* Initialize the algorithm. */
void md5_init(md5_state_t *pms);
/* Append a string to the message. */
void md5_append(md5_state_t *pms, const md5_byte_t *data, int nbytes);
/* Finish the message and return the digest. */
void md5_finish(md5_state_t *pms, md5_byte_t digest[16]);
#ifdef __cplusplus
} /* end extern "C" */
#endif
#endif /* md5_INCLUDED */

View file

@ -1,115 +0,0 @@
unit VDSP_CRC;
{
VPatch 2 - CRC
==============
(c) 2002-2003 Van de Sande Productions
This unit can calculate the standard ZIP CRC32 for a filestream.
What's new
----------
2.0 20030811 Koen Initial documentation
}
interface
uses Classes;
//var
//this is the CRC32 table
// CRCTable: Array[0..255] of LongWord;
{= (
//this table used to be inside the exe, but now it has been replaced by a calculation routine which saves 1 KB
$0,$77073096,$EE0E612C,$990951BA,$76DC419,
$706AF48F,$E963A535,$9E6495A3,$EDB8832,$79DCB8A4,
$E0D5E91E,$97D2D988,$9B64C2B,$7EB17CBD,$E7B82D07,
$90BF1D91,$1DB71064,$6AB020F2,$F3B97148,$84BE41DE,
$1ADAD47D,$6DDDE4EB,$F4D4B551,$83D385C7,$136C9856,
$646BA8C0,$FD62F97A,$8A65C9EC,$14015C4F,$63066CD9,
$FA0F3D63,$8D080DF5,$3B6E20C8,$4C69105E,$D56041E4,
$A2677172,$3C03E4D1,$4B04D447,$D20D85FD,$A50AB56B,
$35B5A8FA,$42B2986C,$DBBBC9D6,$ACBCF940,$32D86CE3,
$45DF5C75,$DCD60DCF,$ABD13D59,$26D930AC,$51DE003A,
$C8D75180,$BFD06116,$21B4F4B5,$56B3C423,$CFBA9599,
$B8BDA50F,$2802B89E,$5F058808,$C60CD9B2,$B10BE924,
$2F6F7C87,$58684C11,$C1611DAB,$B6662D3D,$76DC4190,
$1DB7106,$98D220BC,$EFD5102A,$71B18589,$6B6B51F,
$9FBFE4A5,$E8B8D433,$7807C9A2,$F00F934,$9609A88E,
$E10E9818,$7F6A0DBB,$86D3D2D,$91646C97,$E6635C01,
$6B6B51F4,$1C6C6162,$856530D8,$F262004E,$6C0695ED,
$1B01A57B,$8208F4C1,$F50FC457,$65B0D9C6,$12B7E950,
$8BBEB8EA,$FCB9887C,$62DD1DDF,$15DA2D49,$8CD37CF3,
$FBD44C65,$4DB26158,$3AB551CE,$A3BC0074,$D4BB30E2,
$4ADFA541,$3DD895D7,$A4D1C46D,$D3D6F4FB,$4369E96A,
$346ED9FC,$AD678846,$DA60B8D0,$44042D73,$33031DE5,
$AA0A4C5F,$DD0D7CC9,$5005713C,$270241AA,$BE0B1010,
$C90C2086,$5768B525,$206F85B3,$B966D409,$CE61E49F,
$5EDEF90E,$29D9C998,$B0D09822,$C7D7A8B4,$59B33D17,
$2EB40D81,$B7BD5C3B,$C0BA6CAD,$EDB88320,$9ABFB3B6,
$3B6E20C,$74B1D29A,$EAD54739,$9DD277AF,$4DB2615,
$73DC1683,$E3630B12,$94643B84,$D6D6A3E,$7A6A5AA8,
$E40ECF0B,$9309FF9D,$A00AE27,$7D079EB1,$F00F9344,
$8708A3D2,$1E01F268,$6906C2FE,$F762575D,$806567CB,
$196C3671,$6E6B06E7,$FED41B76,$89D32BE0,$10DA7A5A,
$67DD4ACC,$F9B9DF6F,$8EBEEFF9,$17B7BE43,$60B08ED5,
$D6D6A3E8,$A1D1937E,$38D8C2C4,$4FDFF252,$D1BB67F1,
$A6BC5767,$3FB506DD,$48B2364B,$D80D2BDA,$AF0A1B4C,
$36034AF6,$41047A60,$DF60EFC3,$A867DF55,$316E8EEF,
$4669BE79,$CB61B38C,$BC66831A,$256FD2A0,$5268E236,
$CC0C7795,$BB0B4703,$220216B9,$5505262F,$C5BA3BBE,
$B2BD0B28,$2BB45A92,$5CB36A04,$C2D7FFA7,$B5D0CF31,
$2CD99E8B,$5BDEAE1D,$9B64C2B0,$EC63F226,$756AA39C,
$26D930A,$9C0906A9,$EB0E363F,$72076785,$5005713,
$95BF4A82,$E2B87A14,$7BB12BAE,$CB61B38,$92D28E9B,
$E5D5BE0D,$7CDCEFB7,$BDBDF21,$86D3D2D4,$F1D4E242,
$68DDB3F8,$1FDA836E,$81BE16CD,$F6B9265B,$6FB077E1,
$18B74777,$88085AE6,$FF0F6A70,$66063BCA,$11010B5C,
$8F659EFF,$F862AE69,$616BFFD3,$166CCF45,$A00AE278,
$D70DD2EE,$4E048354,$3903B3C2,$A7672661,$D06016F7,
$4969474D,$3E6E77DB,$AED16A4A,$D9D65ADC,$40DF0B66,
$37D83BF0,$A9BCAE53,$DEBB9EC5,$47B2CF7F,$30B5FFE9,
$BDBDF21C,$CABAC28A,$53B39330,$24B4A3A6,$BAD03605,
$CDD70693,$54DE5729,$23D967BF,$B3667A2E,$C4614AB8,
$5D681B02,$2A6F2B94,$B40BBE37,$C30C8EA1,$5A05DF1B,
$2D02EF8D}
function FileCRC(fs: TFileStream): Integer;
implementation
function FileCRC(fs: TFileStream): Integer;
const
CRCBlock = 4096;
var
CRCTable: Array[0..255] of LongWord;
c: LongWord; //!!! this must be an unsigned 32-bits var!
Block: Array[0..CRCBlock-1] of Byte;
i,j,bytesread: Integer;
begin
//this used to be the InitCRC procedure
For i:= 0 To 255 do begin
c:= i;
For j:= 0 To 7 do begin
If (c And 1)=0 Then begin
c:= (c div 2);
end Else begin
c:= (c div 2) Xor $EDB88320;
End;
end;
CRCTable[i]:= c;
end;
// InitCRC procedure end;
c:=$FFFFFFFF;
fs.Seek(0,soFromBeginning);
for i:=0 to (fs.Size div CRCBlock)+1 do begin
bytesread:=fs.Read(Block,CRCBlock);
for j:=0 to bytesread-1 do begin
c:=CRCTable[(c and $FF) xor Block[j]] xor (((c and $FFFFFF00) div 256) and $FFFFFF);
end;
end;
FileCRC:=c xor $FFFFFFFF;
end;
end.