NSIS/Contrib/VPatch/Source/Plugin/apply_patch.c
wizou 752d7d239a Jim Park's Unicode NSIS merging - Step 1 : switch to TCHARs where relevant.
Compiler output is identical before & after this step

git-svn-id: https://svn.code.sf.net/p/nsis/code/NSIS/branches/wizou@6036 212acab6-be3b-0410-9dea-997c60f758d6
2010-03-24 17:22:56 +00:00

241 lines
8 KiB
C

//---------------------------------------------------------------------------
// apply_patch.c
//---------------------------------------------------------------------------
// -=* 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.
//
// Reviewed for Unicode support by Jim Park -- 08/29/2007
#include "apply_patch.h"
#include "checksum.h"
/* ------------------------ patch application ----------------- */
#define BLOCKSIZE 16384
int DoPatch(HANDLE hPatch, HANDLE hSource, HANDLE hDest) {
static char block[BLOCKSIZE];
unsigned long temp = 0;
unsigned long read;
unsigned long source_crc = 0;
md5_byte_t source_md5[16];
unsigned long patch_dest_crc = 0;
md5_byte_t patch_dest_md5[16];
int MD5Mode = 0;
unsigned long patches = 0;
int already_uptodate = 0;
FILETIME targetModifiedTime;
// special 'addition' for the dll: since the patch file is now
// in a seperate file, the VPAT header might be right at the start
// of the file, and a pointer at the end of the file is probably missing
// (because all patch generator versions don't append it, the linker/gui
// does this).
SetFilePointer(hPatch, 0, NULL, FILE_BEGIN);
ReadFile(hPatch, &temp, 4, &read, NULL);
// it's not at the start of file -> there must be a pointer at the end of
// file then
if (temp != 0x54415056) {
SetFilePointer(hPatch, -4, NULL, FILE_END);
ReadFile(hPatch, &temp, 4, &read, NULL);
SetFilePointer(hPatch, temp, NULL, FILE_BEGIN);
ReadFile(hPatch, &temp, 4, &read, NULL);
if (temp != 0x54415056)
return PATCH_CORRUPT;
}
// target file date is by default the current system time
GetSystemTimeAsFileTime(&targetModifiedTime);
// read the number of patches in the file
ReadFile(hPatch, &patches, 4, &read, NULL);
if(patches & 0x80000000) MD5Mode = 1;
// MSB is now reserved for future extensions, anyone wanting more than
// 16 million patches in a single file is nuts anyway
patches = patches & 0x00FFFFFF;
if(!MD5Mode) {
if (!FileCRC(hSource, &source_crc))
return PATCH_ERROR;
} else {
if (!FileMD5(hSource, source_md5))
return PATCH_ERROR;
}
while (patches--) {
long patch_blocks = 0, patch_size = 0;
// flag which needs to be set by one of the checksum checks
int currentPatchMatchesChecksum = 0;
// read the number of blocks this patch has
if(!ReadFile(hPatch, &patch_blocks, 4, &read, NULL)) {
return PATCH_CORRUPT;
}
// read checksums
if(!MD5Mode) {
unsigned long patch_source_crc = 0;
if(!ReadFile(hPatch, &patch_source_crc, 4, &read, NULL)) {
return PATCH_CORRUPT;
}
if(!ReadFile(hPatch, &patch_dest_crc, 4, &read, NULL)) {
return PATCH_CORRUPT;
}
// check to see if it's already up-to-date for some patch
if (source_crc == patch_dest_crc) {
already_uptodate = 1;
}
if (source_crc == patch_source_crc) {
currentPatchMatchesChecksum = 1;
}
} else {
int md5index;
md5_byte_t patch_source_md5[16];
if(!ReadFile(hPatch, patch_source_md5, 16, &read, NULL)) {
return PATCH_CORRUPT;
}
if(!ReadFile(hPatch, patch_dest_md5, 16, &read, NULL)) {
return PATCH_CORRUPT;
}
// check to see if it's already up-to-date for some patch
for(md5index = 0; md5index < 16; md5index++) {
if(source_md5[md5index] != patch_dest_md5[md5index]) break;
if(md5index == 15) already_uptodate = 1;
}
for(md5index = 0; md5index < 16; md5index++) {
if(source_md5[md5index] != patch_source_md5[md5index]) break;
if(md5index == 15) currentPatchMatchesChecksum = 1;
}
}
// read the size of the patch, we can use this to skip over it
if(!ReadFile(hPatch, &patch_size, 4, &read, NULL)) {
return PATCH_CORRUPT;
}
if(currentPatchMatchesChecksum) {
while (patch_blocks--) {
unsigned char blocktype = 0;
unsigned long blocksize = 0;
if(!ReadFile(hPatch, &blocktype, 1, &read, NULL)) {
return PATCH_CORRUPT;
}
switch (blocktype) {
case 1:
case 2:
case 3:
if (blocktype == 1)
{ unsigned char x; blocksize = ReadFile(hPatch,&x,1,&read,NULL)? x: 0; }
else if (blocktype == 2)
{ unsigned short x; blocksize = ReadFile(hPatch,&x,2,&read,NULL)? x: 0; }
else
{ unsigned long x; blocksize = ReadFile(hPatch,&x,4,&read,NULL)? x:0; }
if (!blocksize || !ReadFile(hPatch, &temp, 4, &read, NULL) || read != 4)
return PATCH_CORRUPT;
SetFilePointer(hSource, temp, 0, FILE_BEGIN);
do {
if(!ReadFile(hSource, block, min(BLOCKSIZE, blocksize), &read, NULL)) {
return PATCH_ERROR;
}
WriteFile(hDest, block, read, &temp, NULL);
if (temp != min(BLOCKSIZE, blocksize))
return PATCH_ERROR;
blocksize -= temp;
} while (temp);
break;
case 5:
case 6:
case 7:
if (blocktype == 5)
{ unsigned char x; blocksize = ReadFile(hPatch,&x,1,&read,NULL)? x:0; }
else if (blocktype == 6)
{ unsigned short x; blocksize = ReadFile(hPatch,&x,2,&read,NULL)? x:0; }
else
{ unsigned long x; blocksize = ReadFile(hPatch,&x,4,&read,NULL)? x:0; }
if (!blocksize)
return PATCH_CORRUPT;
do {
if(!ReadFile(hPatch, block, min(BLOCKSIZE, blocksize), &read, NULL)) {
return PATCH_CORRUPT;
}
WriteFile(hDest, block, read, &temp, NULL);
if (temp != min(BLOCKSIZE, blocksize))
return PATCH_ERROR;
blocksize -= temp;
} while (temp);
break;
case 255: // read the file modified time from the patch
if(!ReadFile(hPatch,&targetModifiedTime,sizeof(targetModifiedTime),&read,NULL)) {
return PATCH_CORRUPT;
}
break;
default:
return PATCH_CORRUPT;
}
}
if(!MD5Mode) {
unsigned long dest_crc = 0;
if(!FileCRC(hDest, &dest_crc)) {
return PATCH_ERROR;
}
if (dest_crc != patch_dest_crc)
return PATCH_ERROR;
} else {
int md5index;
md5_byte_t dest_md5[16];
if(!FileMD5(hDest, dest_md5)) {
return PATCH_ERROR;
}
for(md5index = 0; md5index < 16; md5index++) {
if(dest_md5[md5index] != patch_dest_md5[md5index]) return PATCH_ERROR;
}
}
// set file time
SetFileTime(hDest, NULL, NULL, &targetModifiedTime);
return PATCH_SUCCESS;
} else {
SetFilePointer(hPatch, patch_size, NULL, FILE_CURRENT);
}
}
// if already up to date, it doesn't matter that we didn't match
if(already_uptodate) {
return PATCH_UPTODATE;
} else {
return PATCH_NOMATCH;
}
}