windows-nt/Source/XPSP1/NT/base/mspatch/api/samples/mpatch/mpatch.c

1415 lines
44 KiB
C
Raw Normal View History

2020-09-26 03:20:57 -05:00
#ifndef _WIN32_WINNT
#define _WIN32_WINNT 0x0400
#endif
#ifndef WIN32
#define WIN32 0x0400
#endif
#pragma warning( disable: 4001 4035 4115 4200 4201 4204 4209 4214 4514 4699 )
#include <windows.h>
#include <wincrypt.h>
#pragma warning( disable: 4201 )
#include <imagehlp.h>
#pragma warning( disable: 4001 4035 4115 4200 4201 4204 4209 4214 4514 4699 )
#include <stdlib.h>
#include <stdio.h>
#include <ctype.h>
#include "patchapi.h"
#include "patchprv.h"
#include <ntverp.h>
#include <common.ver>
void CopyRight( void ) {
printf(
"\n"
"MPATCH " VER_PRODUCTVERSION_STR " Patch Creation Utility\n"
VER_LEGALCOPYRIGHT_STR
"\n\n"
);
}
void Usage( void ) {
printf(
"Usage: MPATCH [options] OldFile[;OldFile2[;OldFile3]] NewFile TargetPatchFile\n"
"\n"
" Options:\n"
"\n"
" -NOBINDFIX Turn off automatic compensation for bound imports in\n"
" the the old file. The default is to ignore binding\n"
" data in the old file during patch creation which will\n"
" cause the application of the patch to succeed whether\n"
" or not the old file on the target machine is bound, not\n"
" bound, or even bound to different import addresses.\n"
" If the files are not Win32 binaries, this option is\n"
" ignored and has no effect.\n"
"\n"
" -NOLOCKFIX Turn off automatic compensation for smashed lock prefix\n"
" instructions. If the files are not Win32 binaries,\n"
" this option is ignored and has no effect.\n"
"\n"
" -NOREBASE Turn off automatic internal rebasing of old file to new\n"
" file's image base address. If the files are not Win32\n"
" binaries, this option is ignored and has no effect.\n"
"\n"
" -NORESTIME Turn off automatic fixup of resource section timestamps\n"
" (ignored if not Win32 binaries).\n"
"\n"
" -NOSTORETIME Don't store the timestamp of the new file in the patch\n"
" file. Instead, set the timestamp of the patch file to\n"
" the timestamp of the new file.\n"
"\n"
" -IGNORE:Offset,Length[,FileNumber]\n"
"\n"
" Ignore a range of bytes in the OldFile because those\n"
" bytes might be different in the old file being patched\n"
" on the target machine.\n"
"\n"
" -RETAIN:Offset,Length[,OffsetInNewFile[,FileNumber]]\n"
"\n"
" When applying the patch, preserve the range of bytes in\n"
" the old file and copy them to the new file at the given\n"
" OffsetInNewFile.\n"
"\n"
#if 0
" -RIFTINFO:FileName[,FileNumber]\n"
"\n"
" Use rift table information from FileName (produced from\n"
" riftinfo.exe).\n"
"\n"
#endif
" -FAILBIGGER If patch file is bigger than simple compressed file,\n"
" don't create the patch file (takes longer).\n"
"\n"
" -FAILIFSAME If old and new files are the same (ignoring binding\n"
" differences, etc), don't create the patch file.\n"
"\n"
" -NOCOMPARE Don't compare patch compression against ordinary non-\n"
" patch compression (saves time).\n"
"\n"
" -NOPROGRESS Don't display percent complete while building patch.\n"
"\n"
" -NEWSYMPATH:PathName[;PathName]\n"
"\n"
" For NewFile, search for symbol file(s) in these path\n"
" locations (recursive search each path until found).\n"
" The default is to search for symbol files(s) starting\n"
" in the same directory as the NewFile.\n"
"\n"
" -OLDSYMPATH:PathName[;PathName][,FileNumber]\n"
"\n"
" For OldFile, search for symbol file(s) in these path\n"
" locations (recursive search each path until found).\n"
" The default is to search for symbol files(s) starting\n"
" in the same directory as the OldFile.\n"
"\n"
" -UNDECORATED After matching decorated symbol names, match remaining\n"
" symbols using undecorated names.\n"
"\n"
" -NOSYMS Don't use debug symbol files when creating the patch.\n"
"\n"
" -NOSYMFAIL Don't fail to create patch if symbols cannot be loaded.\n"
"\n"
" -NOSYMWARN Don't warn if symbols can't be found or don't match the\n"
" corresponding file (symbol checksum mismatch).\n"
"\n"
" -USEBADSYMS Rather than ignoring symbols if the checksums don't\n"
" match the corresponding files, use the bad symbols.\n"
"\n"
" -E8 Force E8 call translation for x86 binaries.\n"
"\n"
" -NOE8 Force no E8 call translation for x86 binaries.\n"
"\n"
" If neither -E8 or -NOE8 are specified, and the files\n"
" are x86 binaries, the patch will be built internally\n"
" twice and the smaller will be chosen for output.\n"
"\n"
" -MSPATCH194COMPAT Assure the patch file can be used with version\n"
" 1.94 of MSPATCH*.DLL. May increase size of patch\n"
" file if old or new file is larger than 4Mb.\n"
"\n"
" MPATCH will also look for environment variables named \"MPATCH\"\n"
" followed by an underscore and the name of the option. Command line\n"
" specified options override environment variable options. Examples:\n"
"\n"
" MPATCH_NOCOMPARE=1\n"
" MPATCH_NEWSYMPATH=c:\\winnt\\symbols;\\\\server\\share\\symbols\n"
"\n"
);
exit( 1 );
}
BOOL bNoProgress;
BOOL bNoSymWarn;
BOOL bUseBadSyms;
DWORDLONG
GetFileSizeByName(
IN LPCSTR FileName
)
{
DWORDLONG FileSizeReturn;
ULONG FileSizeHigh;
ULONG FileSizeLow;
HANDLE hFile;
FileSizeReturn = 0xFFFFFFFFFFFFFFFF;
hFile = CreateFile(
FileName,
GENERIC_READ,
FILE_SHARE_READ | FILE_SHARE_WRITE,
NULL,
OPEN_EXISTING,
0,
NULL
);
if ( hFile != INVALID_HANDLE_VALUE ) {
FileSizeLow = GetFileSize( hFile, &FileSizeHigh );
if (( FileSizeLow != 0xFFFFFFFF ) || ( GetLastError() == NO_ERROR )) {
FileSizeReturn = ((DWORDLONG)FileSizeHigh << 32 ) | FileSizeLow;
}
CloseHandle( hFile );
}
return FileSizeReturn;
}
BOOL
GetMpatchEnvironString(
IN LPCSTR VarName,
OUT LPSTR Buffer,
IN DWORD BufferSize
)
{
CHAR EnvironName[ 256 ];
sprintf( EnvironName, "mpatch_%s", VarName );
if ( GetEnvironmentVariable( EnvironName, Buffer, BufferSize )) {
return TRUE;
}
return FALSE;
}
BOOL
GetMpatchEnvironValue(
IN LPCSTR VarName
)
{
CHAR LocalBuffer[ 256 ];
if ( GetMpatchEnvironString( VarName, LocalBuffer, sizeof( LocalBuffer ))) {
if (( *LocalBuffer == '0' ) && ( strtoul( LocalBuffer, NULL, 0 ) == 0 )) {
return FALSE;
}
return TRUE;
}
return FALSE;
}
BOOL
CALLBACK
MyProgressCallback(
PVOID CallbackContext,
ULONG CurrentPosition,
ULONG MaximumPosition
)
{
UNREFERENCED_PARAMETER( CallbackContext );
if ( MaximumPosition != 0 ) {
fprintf( stderr, "\r%3.1f%% complete", ( CurrentPosition * 100.0 ) / MaximumPosition );
}
return TRUE;
}
BOOL
CALLBACK
MySymLoadCallback(
IN ULONG WhichFile,
IN LPCSTR SymbolFileName,
IN ULONG SymType,
IN ULONG SymbolFileCheckSum,
IN ULONG SymbolFileTimeDate,
IN ULONG ImageFileCheckSum,
IN ULONG ImageFileTimeDate,
IN PVOID CallbackContext
)
{
LPCSTR *FileNameArray = CallbackContext;
LPCSTR SymTypeText;
if (( SymType == SymNone ) || ( SymType == SymExport )) {
//
// Symbols could not be found.
//
if ( ! bNoSymWarn ) {
printf(
"\n"
"WARNING: no debug symbols for %s\n\n",
FileNameArray[ WhichFile ]
);
}
return TRUE;
}
//
// Note that the Old file checksum is the checksum AFTER normalization,
// so if the original .dbg file was updated with bound checksum, the
// old file's checksum will not match the symbol file's checksum. But,
// binding a file does not change its TimeDateStamp, so that should be
// a valid comparison. But, .sym files don't have a TimeDateStamp, so
// the SymbolFileTimeDate may be zero. If either the checksums match
// or the timedate stamps match, we'll say its valid.
//
if (( ImageFileCheckSum == SymbolFileCheckSum ) ||
( ImageFileTimeDate == SymbolFileTimeDate )) {
return TRUE;
}
if ( ! bNoSymWarn ) {
switch ( SymType ) {
case SymNone: SymTypeText = "No"; break;
case SymCoff: SymTypeText = "Coff"; break;
case SymCv: SymTypeText = "CodeView"; break;
case SymPdb: SymTypeText = "Pdb"; break;
case SymExport: SymTypeText = "Export"; break;
case SymDeferred: SymTypeText = "Deferred"; break;
case SymSym: SymTypeText = "Sym"; break;
default: SymTypeText = "Unknown"; break;
}
printf(
"\n"
"WARNING: %s symbols %s don't match %s:\n"
" symbol file checksum (%08X) does not match image (%08X), and\n"
" symbol file timedate (%08X) does not match image (%08X).\n\n",
SymTypeText,
SymbolFileName,
FileNameArray[ WhichFile ],
SymbolFileCheckSum,
ImageFileCheckSum,
SymbolFileTimeDate,
ImageFileTimeDate
);
}
return bUseBadSyms;
}
PRIFT_TABLE RiftTableArray[ 256 ];
PATCH_OLD_FILE_INFO_A OldFileInfo[ 256 ];
LPSTR OldFileSymPathArray[ 256 ];
LPSTR NewFileSymPath;
LPSTR FileNameArray[ 257 ];
PATCH_OPTION_DATA OptionData = { sizeof( PATCH_OPTION_DATA ) };
CHAR TextBuffer[ 65000 ];
void __cdecl main( int argc, char *argv[] ) {
LPSTR OldFileName = NULL;
LPSTR NewFileName = NULL;
LPSTR PatchFileName = NULL;
ULONG OptionFlags = PATCH_OPTION_USE_LZX_BEST | PATCH_OPTION_USE_LZX_LARGE;
BOOL Success;
LPSTR arg;
LPSTR p, q;
LPSTR FileName;
int i, j, n;
ULONG OldOffset;
ULONG NewOffset;
ULONG Length;
ULONG FileNum;
ULONG OldFileCount;
ULONG ErrorCode;
ULONG NewFileSize;
ULONG PatchFileSize;
ULONG OldFileRva;
ULONG NewFileRva;
BOOL bNoCompare = FALSE;
FILE *RiftFile;
LPSTR FileNamePart;
SetErrorMode( SEM_FAILCRITICALERRORS );
#ifndef DEBUG
SetErrorMode( SEM_NOALIGNMENTFAULTEXCEPT | SEM_FAILCRITICALERRORS );
#endif
#ifdef TESTCODE
bNoCompare = TRUE;
#endif
CopyRight();
for ( i = 1; i < argc; i++ ) {
arg = argv[ i ];
if ( strchr( arg, '?' )) {
Usage();
}
}
//
// First get environment arguments because command-line args will
// override them.
//
if ( GetMpatchEnvironValue( "e8" )) {
OptionFlags &= ~PATCH_OPTION_USE_LZX_A;
}
if ( GetMpatchEnvironValue( "noe8" )) {
OptionFlags &= ~PATCH_OPTION_USE_LZX_B;
}
if ( GetMpatchEnvironValue( "mspatch194compat" )) {
OptionFlags &= ~PATCH_OPTION_USE_LZX_LARGE;
}
if ( GetMpatchEnvironValue( "nobindfix" )) {
OptionFlags |= PATCH_OPTION_NO_BINDFIX;
}
if ( GetMpatchEnvironValue( "nolockfix" )) {
OptionFlags |= PATCH_OPTION_NO_LOCKFIX;
}
if ( GetMpatchEnvironValue( "norebase" )) {
OptionFlags |= PATCH_OPTION_NO_REBASE;
}
if ( GetMpatchEnvironValue( "norestime" )) {
OptionFlags |= PATCH_OPTION_NO_RESTIMEFIX;
}
if ( GetMpatchEnvironValue( "nostoretime" )) {
OptionFlags |= PATCH_OPTION_NO_TIMESTAMP;
}
if ( GetMpatchEnvironValue( "failbigger" )) {
OptionFlags |= PATCH_OPTION_FAIL_IF_BIGGER;
}
if ( GetMpatchEnvironValue( "failifbigger" )) {
OptionFlags |= PATCH_OPTION_FAIL_IF_BIGGER;
}
if ( GetMpatchEnvironValue( "failsame" )) {
OptionFlags |= PATCH_OPTION_FAIL_IF_SAME_FILE;
}
if ( GetMpatchEnvironValue( "failifsame" )) {
OptionFlags |= PATCH_OPTION_FAIL_IF_SAME_FILE;
}
if ( GetMpatchEnvironValue( "nocompare" )) {
bNoCompare = TRUE;
}
if ( GetMpatchEnvironValue( "noprogress" )) {
bNoProgress = TRUE;
}
if ( GetMpatchEnvironValue( "undecorated" )) {
OptionData.SymbolOptionFlags |= PATCH_SYMBOL_UNDECORATED_TOO;
}
if ( GetMpatchEnvironValue( "nosyms" )) {
OptionData.SymbolOptionFlags |= PATCH_SYMBOL_NO_IMAGEHLP;
}
if ( GetMpatchEnvironValue( "nosymfail" )) {
OptionData.SymbolOptionFlags |= PATCH_SYMBOL_NO_FAILURES;
}
if ( GetMpatchEnvironValue( "nosymwarn" )) {
bNoSymWarn = TRUE;
}
if ( GetMpatchEnvironValue( "usebadsyms" )) {
bUseBadSyms = TRUE;
}
if ( GetMpatchEnvironString( "ignore", TextBuffer, sizeof( TextBuffer ))) {
p = TextBuffer;
q = strchr( p, ',' );
if ( q != NULL ) {
*q = 0;
OldOffset = strtoul( p, NULL, 0 );
p = q + 1;
q = strchr( p, ',' );
if ( q ) {
*q = 0;
}
Length = strtoul( p, NULL, 0 );
FileNum = 1;
if ( q ) {
p = q + 1;
FileNum = strtoul( p, NULL, 0 );
if ( FileNum == 0 ) {
FileNum = 1;
}
}
if ( FileNum <= 255 ) {
OldFileInfo[ FileNum - 1 ].IgnoreRangeArray = realloc(
OldFileInfo[ FileNum - 1 ].IgnoreRangeArray,
OldFileInfo[ FileNum - 1 ].IgnoreRangeCount * sizeof( PATCH_IGNORE_RANGE ) + sizeof( PATCH_IGNORE_RANGE )
);
OldFileInfo[ FileNum - 1 ].IgnoreRangeArray[ OldFileInfo[ FileNum - 1 ].IgnoreRangeCount ].OffsetInOldFile = OldOffset;
OldFileInfo[ FileNum - 1 ].IgnoreRangeArray[ OldFileInfo[ FileNum - 1 ].IgnoreRangeCount ].LengthInBytes = Length;
OldFileInfo[ FileNum - 1 ].IgnoreRangeCount++;
}
}
}
if ( GetMpatchEnvironString( "retain", TextBuffer, sizeof( TextBuffer ))) {
p = TextBuffer;
q = strchr( p, ',' );
if ( q != NULL ) {
*q = 0;
OldOffset = strtoul( p, NULL, 0 );
p = q + 1;
q = strchr( p, ',' );
if ( q ) {
*q = 0;
}
Length = strtoul( p, NULL, 0 );
NewOffset = OldOffset;
FileNum = 1;
if ( q ) {
p = q + 1;
q = strchr( p, ',' );
if ( q ) {
*q = 0;
}
NewOffset = strtoul( p, NULL, 0 );
if ( q ) {
p = q + 1;
FileNum = strtoul( p, NULL, 0 );
if ( FileNum == 0 ) {
FileNum = 1;
}
}
}
if ( FileNum <= 255 ) {
OldFileInfo[ FileNum - 1 ].RetainRangeArray = realloc(
OldFileInfo[ FileNum - 1 ].RetainRangeArray,
OldFileInfo[ FileNum - 1 ].RetainRangeCount * sizeof( PATCH_RETAIN_RANGE ) + sizeof( PATCH_RETAIN_RANGE )
);
OldFileInfo[ FileNum - 1 ].RetainRangeArray[ OldFileInfo[ FileNum - 1 ].RetainRangeCount ].OffsetInOldFile = OldOffset;
OldFileInfo[ FileNum - 1 ].RetainRangeArray[ OldFileInfo[ FileNum - 1 ].RetainRangeCount ].OffsetInNewFile = NewOffset;
OldFileInfo[ FileNum - 1 ].RetainRangeArray[ OldFileInfo[ FileNum - 1 ].RetainRangeCount ].LengthInBytes = Length;
OldFileInfo[ FileNum - 1 ].RetainRangeCount++;
}
}
}
if ( GetMpatchEnvironString( "riftinfo", TextBuffer, sizeof( TextBuffer ))) {
p = TextBuffer;
q = strchr( p, ',' );
if ( q ) {
*q = 0;
}
FileName = p;
FileNum = 1;
if ( q ) {
p = q + 1;
FileNum = strtoul( p, NULL, 0 );
if ( FileNum == 0 ) {
FileNum = 1;
}
}
if ( FileNum <= 255 ) {
RiftTableArray[ FileNum - 1 ] = malloc( sizeof( RIFT_TABLE ));
if ( RiftTableArray[ FileNum - 1 ] == NULL ) {
printf( "Out of memory\n" );
exit( 1 );
}
RiftTableArray[ FileNum - 1 ]->RiftEntryCount = 0;
RiftTableArray[ FileNum - 1 ]->RiftEntryAlloc = 0;
RiftTableArray[ FileNum - 1 ]->RiftEntryArray = NULL;
RiftTableArray[ FileNum - 1 ]->RiftUsageArray = NULL;
RiftFile = fopen( FileName, "rt" );
if ( RiftFile == NULL ) {
printf( "Could not open %s\n", FileName );
exit( 1 );
}
while ( fgets( TextBuffer, sizeof( TextBuffer ), RiftFile )) {
//
// Line looks like "00001456 00002345" where each number
// is an RVA in hexadecimal and the first column is the
// OldFileRva and the second column is the NewFileRva.
// Any text beyond column 17 is considered a comment, and
// any line that does not begin with a digit is ignored.
//
if (( isxdigit( *TextBuffer )) && ( strlen( TextBuffer ) >= 17 )) {
OldFileRva = strtoul( TextBuffer, NULL, 16 );
NewFileRva = strtoul( TextBuffer + 9, NULL, 16 );
if (( OldFileRva + NewFileRva ) != 0 ) {
RiftTableArray[ FileNum - 1 ]->RiftEntryArray = realloc(
RiftTableArray[ FileNum - 1 ]->RiftEntryArray,
RiftTableArray[ FileNum - 1 ]->RiftEntryCount * sizeof( RIFT_ENTRY ) + sizeof( RIFT_ENTRY )
);
RiftTableArray[ FileNum - 1 ]->RiftEntryArray[ RiftTableArray[ FileNum - 1 ]->RiftEntryCount ].OldFileRva = OldFileRva;
RiftTableArray[ FileNum - 1 ]->RiftEntryArray[ RiftTableArray[ FileNum - 1 ]->RiftEntryCount ].NewFileRva = NewFileRva;
RiftTableArray[ FileNum - 1 ]->RiftEntryCount++;
}
}
}
fclose( RiftFile );
if ( RiftTableArray[ FileNum - 1 ]->RiftEntryCount ) {
RiftTableArray[ FileNum - 1 ]->RiftEntryAlloc = RiftTableArray[ FileNum - 1 ]->RiftEntryCount;
RiftTableArray[ FileNum - 1 ]->RiftUsageArray = malloc( RiftTableArray[ FileNum - 1 ]->RiftEntryCount );
if ( RiftTableArray[ FileNum - 1 ]->RiftUsageArray == NULL ) {
printf( "Out of memory\n" );
exit( 1 );
}
ZeroMemory( RiftTableArray[ FileNum - 1 ]->RiftUsageArray, RiftTableArray[ FileNum - 1 ]->RiftEntryCount );
}
OptionData.SymbolOptionFlags |= PATCH_SYMBOL_EXTERNAL_RIFT;
}
}
if ( GetMpatchEnvironString( "oldsympath", TextBuffer, sizeof( TextBuffer ))) {
p = TextBuffer;
q = strchr( p, ',' );
if ( q ) {
*q = 0;
}
FileName = p;
FileNum = 1;
if ( q ) {
p = q + 1;
FileNum = strtoul( p, NULL, 0 );
if ( FileNum == 0 ) {
FileNum = 1;
}
}
if ( FileNum <= 255 ) {
OldFileSymPathArray[ FileNum - 1 ] = _strdup( FileName );
}
}
if ( GetMpatchEnvironString( "newsympath", TextBuffer, sizeof( TextBuffer ))) {
p = TextBuffer;
q = strchr( p, ',' );
if ( q ) {
*q = 0;
}
FileName = p;
FileNum = 1;
if ( q ) {
p = q + 1;
FileNum = strtoul( p, NULL, 0 );
if ( FileNum == 0 ) {
FileNum = 1;
}
}
if ( FileNum == 1 ) {
NewFileSymPath = _strdup( FileName );
}
}
//
// Now process commandline args
//
for ( i = 1; i < argc; i++ ) {
arg = argv[ i ];
if ( strchr( arg, '?' )) {
Usage();
}
if (( *arg == '-' ) || ( *arg == '/' )) {
_strlwr( ++arg );
if ( strcmp( arg, "e8" ) == 0 ) {
OptionFlags &= ~PATCH_OPTION_USE_LZX_A;
}
else if ( strcmp( arg, "noe8" ) == 0 ) {
OptionFlags &= ~PATCH_OPTION_USE_LZX_B;
}
else if ( strcmp( arg, "mspatch194compat" ) == 0 ) {
OptionFlags &= ~PATCH_OPTION_USE_LZX_LARGE;
}
else if ( strcmp( arg, "nobindfix" ) == 0 ) {
OptionFlags |= PATCH_OPTION_NO_BINDFIX;
}
else if ( strcmp( arg, "bindfix" ) == 0 ) {
OptionFlags &= ~PATCH_OPTION_NO_BINDFIX;
}
else if ( strcmp( arg, "nolockfix" ) == 0 ) {
OptionFlags |= PATCH_OPTION_NO_LOCKFIX;
}
else if ( strcmp( arg, "lockfix" ) == 0 ) {
OptionFlags &= ~PATCH_OPTION_NO_LOCKFIX;
}
else if ( strcmp( arg, "norebase" ) == 0 ) {
OptionFlags |= PATCH_OPTION_NO_REBASE;
}
else if ( strcmp( arg, "rebase" ) == 0 ) {
OptionFlags &= ~PATCH_OPTION_NO_REBASE;
}
else if ( strcmp( arg, "norestime" ) == 0 ) {
OptionFlags |= PATCH_OPTION_NO_RESTIMEFIX;
}
else if ( strcmp( arg, "norestimefix" ) == 0 ) {
OptionFlags |= PATCH_OPTION_NO_RESTIMEFIX;
}
else if ( strcmp( arg, "restime" ) == 0 ) {
OptionFlags &= ~PATCH_OPTION_NO_RESTIMEFIX;
}
else if ( strcmp( arg, "restimefix" ) == 0 ) {
OptionFlags &= ~PATCH_OPTION_NO_RESTIMEFIX;
}
else if ( strcmp( arg, "nostoretime" ) == 0 ) {
OptionFlags |= PATCH_OPTION_NO_TIMESTAMP;
}
else if ( strcmp( arg, "storetime" ) == 0 ) {
OptionFlags &= ~PATCH_OPTION_NO_TIMESTAMP;
}
else if ( strcmp( arg, "failbigger" ) == 0 ) {
OptionFlags |= PATCH_OPTION_FAIL_IF_BIGGER;
}
else if ( strcmp( arg, "nofailbigger" ) == 0 ) {
OptionFlags &= ~PATCH_OPTION_FAIL_IF_BIGGER;
}
else if ( strcmp( arg, "failifbigger" ) == 0 ) {
OptionFlags |= PATCH_OPTION_FAIL_IF_BIGGER;
}
else if ( strcmp( arg, "nofailifbigger" ) == 0 ) {
OptionFlags &= ~PATCH_OPTION_FAIL_IF_BIGGER;
}
else if ( strcmp( arg, "failifsame" ) == 0 ) {
OptionFlags |= PATCH_OPTION_FAIL_IF_SAME_FILE;
}
else if ( strcmp( arg, "failsame" ) == 0 ) {
OptionFlags |= PATCH_OPTION_FAIL_IF_SAME_FILE;
}
else if ( strcmp( arg, "nofailifsame" ) == 0 ) {
OptionFlags &= ~PATCH_OPTION_FAIL_IF_SAME_FILE;
}
else if ( strcmp( arg, "nofailsame" ) == 0 ) {
OptionFlags &= ~PATCH_OPTION_FAIL_IF_SAME_FILE;
}
else if ( strcmp( arg, "nocompare" ) == 0 ) {
bNoCompare = TRUE;
}
else if ( strcmp( arg, "compare" ) == 0 ) {
bNoCompare = FALSE;
}
else if ( strcmp( arg, "noprogress" ) == 0 ) {
bNoProgress = TRUE;
}
else if ( strcmp( arg, "progress" ) == 0 ) {
bNoProgress = FALSE;
}
else if ( strcmp( arg, "decorated" ) == 0 ) {
OptionData.SymbolOptionFlags &= ~PATCH_SYMBOL_UNDECORATED_TOO;
}
else if ( strcmp( arg, "undecorated" ) == 0 ) {
OptionData.SymbolOptionFlags |= PATCH_SYMBOL_UNDECORATED_TOO;
}
else if ( strcmp( arg, "nosyms" ) == 0 ) {
OptionData.SymbolOptionFlags |= PATCH_SYMBOL_NO_IMAGEHLP;
}
else if ( strcmp( arg, "syms" ) == 0 ) {
OptionData.SymbolOptionFlags &= ~PATCH_SYMBOL_NO_IMAGEHLP;
}
else if ( strcmp( arg, "nosymfail" ) == 0 ) {
OptionData.SymbolOptionFlags |= PATCH_SYMBOL_NO_FAILURES;
}
else if ( strcmp( arg, "symfail" ) == 0 ) {
OptionData.SymbolOptionFlags &= ~PATCH_SYMBOL_NO_FAILURES;
}
else if ( strcmp( arg, "nosymwarn" ) == 0 ) {
bNoSymWarn = TRUE;
}
else if ( strcmp( arg, "symwarn" ) == 0 ) {
bNoSymWarn = FALSE;
}
else if ( strcmp( arg, "usebadsyms" ) == 0 ) {
bUseBadSyms = TRUE;
}
else if ( strcmp( arg, "nousebadsyms" ) == 0 ) {
bUseBadSyms = FALSE;
}
else if ( strcmp( arg, "nobadsyms" ) == 0 ) {
bUseBadSyms = FALSE;
}
else if ( memcmp( arg, "ignore:", 7 ) == 0 ) {
p = strchr( arg, ':' ) + 1;
q = strchr( p, ',' );
if ( q == NULL ) {
Usage();
}
*q = 0;
OldOffset = strtoul( p, NULL, 0 );
p = q + 1;
q = strchr( p, ',' );
if ( q ) {
*q = 0;
}
Length = strtoul( p, NULL, 0 );
FileNum = 1;
if ( q ) {
p = q + 1;
FileNum = strtoul( p, NULL, 0 );
if ( FileNum == 0 ) {
FileNum = 1;
}
}
if ( FileNum > 255 ) {
Usage();
}
OldFileInfo[ FileNum - 1 ].IgnoreRangeArray = realloc(
OldFileInfo[ FileNum - 1 ].IgnoreRangeArray,
OldFileInfo[ FileNum - 1 ].IgnoreRangeCount * sizeof( PATCH_IGNORE_RANGE ) + sizeof( PATCH_IGNORE_RANGE )
);
OldFileInfo[ FileNum - 1 ].IgnoreRangeArray[ OldFileInfo[ FileNum - 1 ].IgnoreRangeCount ].OffsetInOldFile = OldOffset;
OldFileInfo[ FileNum - 1 ].IgnoreRangeArray[ OldFileInfo[ FileNum - 1 ].IgnoreRangeCount ].LengthInBytes = Length;
OldFileInfo[ FileNum - 1 ].IgnoreRangeCount++;
}
else if ( memcmp( arg, "retain:", 7 ) == 0 ) {
p = strchr( arg, ':' ) + 1;
q = strchr( p, ',' );
if ( q == NULL ) {
Usage();
}
*q = 0;
OldOffset = strtoul( p, NULL, 0 );
p = q + 1;
q = strchr( p, ',' );
if ( q ) {
*q = 0;
}
Length = strtoul( p, NULL, 0 );
NewOffset = OldOffset;
FileNum = 1;
if ( q ) {
p = q + 1;
q = strchr( p, ',' );
if ( q ) {
*q = 0;
}
NewOffset = strtoul( p, NULL, 0 );
if ( q ) {
p = q + 1;
FileNum = strtoul( p, NULL, 0 );
if ( FileNum == 0 ) {
FileNum = 1;
}
}
}
if ( FileNum > 255 ) {
Usage();
}
OldFileInfo[ FileNum - 1 ].RetainRangeArray = realloc(
OldFileInfo[ FileNum - 1 ].RetainRangeArray,
OldFileInfo[ FileNum - 1 ].RetainRangeCount * sizeof( PATCH_RETAIN_RANGE ) + sizeof( PATCH_RETAIN_RANGE )
);
OldFileInfo[ FileNum - 1 ].RetainRangeArray[ OldFileInfo[ FileNum - 1 ].RetainRangeCount ].OffsetInOldFile = OldOffset;
OldFileInfo[ FileNum - 1 ].RetainRangeArray[ OldFileInfo[ FileNum - 1 ].RetainRangeCount ].OffsetInNewFile = NewOffset;
OldFileInfo[ FileNum - 1 ].RetainRangeArray[ OldFileInfo[ FileNum - 1 ].RetainRangeCount ].LengthInBytes = Length;
OldFileInfo[ FileNum - 1 ].RetainRangeCount++;
}
else if ( memcmp( arg, "riftinfo:", 9 ) == 0 ) {
OptionData.SymbolOptionFlags |= PATCH_SYMBOL_EXTERNAL_RIFT;
p = strchr( arg, ':' ) + 1;
q = strchr( p, ',' );
if ( q ) {
*q = 0;
}
FileName = p;
FileNum = 1;
if ( q ) {
p = q + 1;
FileNum = strtoul( p, NULL, 0 );
if ( FileNum == 0 ) {
FileNum = 1;
}
}
if ( FileNum > 255 ) {
Usage();
}
RiftTableArray[ FileNum - 1 ] = malloc( sizeof( RIFT_TABLE ));
if ( RiftTableArray[ FileNum - 1 ] == NULL ) {
printf( "Out of memory\n" );
exit( 1 );
}
RiftTableArray[ FileNum - 1 ]->RiftEntryCount = 0;
RiftTableArray[ FileNum - 1 ]->RiftEntryAlloc = 0;
RiftTableArray[ FileNum - 1 ]->RiftEntryArray = NULL;
RiftTableArray[ FileNum - 1 ]->RiftUsageArray = NULL;
RiftFile = fopen( FileName, "rt" );
if ( RiftFile == NULL ) {
printf( "Could not open %s\n", FileName );
exit( 1 );
}
while ( fgets( TextBuffer, sizeof( TextBuffer ), RiftFile )) {
//
// Line looks like "00001456 00002345" where each number
// is an RVA in hexadecimal and the first column is the
// OldFileRva and the second column is the NewFileRva.
// Any text beyond column 17 is considered a comment, and
// any line that does not begin with a digit is ignored.
//
if (( isxdigit( *TextBuffer )) && ( strlen( TextBuffer ) >= 17 )) {
OldFileRva = strtoul( TextBuffer, NULL, 16 );
NewFileRva = strtoul( TextBuffer + 9, NULL, 16 );
if (( OldFileRva + NewFileRva ) != 0 ) {
RiftTableArray[ FileNum - 1 ]->RiftEntryArray = realloc(
RiftTableArray[ FileNum - 1 ]->RiftEntryArray,
RiftTableArray[ FileNum - 1 ]->RiftEntryCount * sizeof( RIFT_ENTRY ) + sizeof( RIFT_ENTRY )
);
RiftTableArray[ FileNum - 1 ]->RiftEntryArray[ RiftTableArray[ FileNum - 1 ]->RiftEntryCount ].OldFileRva = OldFileRva;
RiftTableArray[ FileNum - 1 ]->RiftEntryArray[ RiftTableArray[ FileNum - 1 ]->RiftEntryCount ].NewFileRva = NewFileRva;
RiftTableArray[ FileNum - 1 ]->RiftEntryCount++;
}
}
}
fclose( RiftFile );
if ( RiftTableArray[ FileNum - 1 ]->RiftEntryCount ) {
RiftTableArray[ FileNum - 1 ]->RiftEntryAlloc = RiftTableArray[ FileNum - 1 ]->RiftEntryCount;
RiftTableArray[ FileNum - 1 ]->RiftUsageArray = malloc( RiftTableArray[ FileNum - 1 ]->RiftEntryCount );
if ( RiftTableArray[ FileNum - 1 ]->RiftUsageArray == NULL ) {
printf( "Out of memory\n" );
exit( 1 );
}
ZeroMemory( RiftTableArray[ FileNum - 1 ]->RiftUsageArray, RiftTableArray[ FileNum - 1 ]->RiftEntryCount );
}
}
else if ( memcmp( arg, "oldsympath:", 11 ) == 0 ) {
p = strchr( arg, ':' ) + 1;
q = strchr( p, ',' );
if ( q ) {
*q = 0;
}
FileName = p;
FileNum = 1;
if ( q ) {
p = q + 1;
FileNum = strtoul( p, NULL, 0 );
if ( FileNum == 0 ) {
FileNum = 1;
}
}
if ( FileNum > 255 ) {
Usage();
}
OldFileSymPathArray[ FileNum - 1 ] = _strdup( FileName );
}
else if ( memcmp( arg, "newsympath:", 11 ) == 0 ) {
p = strchr( arg, ':' ) + 1;
q = strchr( p, ',' );
if ( q ) {
*q = 0;
}
FileName = p;
FileNum = 1;
if ( q ) {
p = q + 1;
FileNum = strtoul( p, NULL, 0 );
if ( FileNum == 0 ) {
FileNum = 1;
}
}
if ( FileNum != 1 ) {
Usage();
}
NewFileSymPath = _strdup( FileName );
}
else {
Usage();
}
}
else if ( OldFileName == NULL ) {
OldFileName = arg;
}
else if ( NewFileName == NULL ) {
NewFileName = arg;
}
else if ( PatchFileName == NULL ) {
PatchFileName = arg;
}
else {
Usage();
}
}
if (( OldFileName == NULL ) || ( NewFileName == NULL ) || ( PatchFileName == NULL )) {
Usage();
}
OldFileCount = 0;
p = OldFileName;
q = strchr( OldFileName, ';' );
while ( q ) {
*q = 0;
if ( *p ) {
OldFileInfo[ OldFileCount ].SizeOfThisStruct = sizeof( PATCH_OLD_FILE_INFO_A );
OldFileInfo[ OldFileCount ].OldFileName = p;
OldFileCount++;
}
p = q + 1;
q = strchr( p, ';' );
}
if ( *p ) {
OldFileInfo[ OldFileCount ].SizeOfThisStruct = sizeof( PATCH_OLD_FILE_INFO_A );
OldFileInfo[ OldFileCount ].OldFileName = p;
OldFileCount++;
}
//
// Make sure rift tables are ascending and don't contain duplicate
// OldRva values (ambiguous).
//
for ( i = 0; i < (int)OldFileCount; i++ ) {
if (( RiftTableArray[ i ] ) && ( RiftTableArray[ i ]->RiftEntryCount > 1 )) {
n = RiftTableArray[ i ]->RiftEntryCount - 1;
RiftQsort( &RiftTableArray[ i ]->RiftEntryArray[ 0 ], &RiftTableArray[ i ]->RiftEntryArray[ n ] );
#ifdef TESTCODE
for ( j = 0; j < n; j++ ) {
if ( RiftTableArray[ i ]->RiftEntryArray[ j ].OldFileRva >
RiftTableArray[ i ]->RiftEntryArray[ j + 1 ].OldFileRva ) {
printf( "\nRift sort failed at index %d of %d\n", j, n + 1 );
for ( j = 0; j <= n; j++ ) {
printf( "%08X\n", RiftTableArray[ i ]->RiftEntryArray[ j ].OldFileRva );
}
exit( 1 );
break;
}
}
#endif // TESTCODE
for ( j = 0; j < n; j++ ) {
while (( j < n ) &&
( RiftTableArray[ i ]->RiftEntryArray[ j ].OldFileRva ==
RiftTableArray[ i ]->RiftEntryArray[ j + 1 ].OldFileRva )) {
if ( RiftTableArray[ i ]->RiftEntryArray[ j ].NewFileRva !=
RiftTableArray[ i ]->RiftEntryArray[ j + 1 ].NewFileRva ) {
//
// This is an ambiguous entry since the OldRva values
// match but the NewRva values do not. Report and
// discard the former.
//
printf(
"RiftInfo for %s contains ambiguous entries:\n"
" OldRva:%08X NewRva:%08X (discarded)\n"
" OldRva:%08X NewRva:%08X (kept)\n\n",
OldFileInfo[ i ].OldFileName,
RiftTableArray[ i ]->RiftEntryArray[ j ].OldFileRva,
RiftTableArray[ i ]->RiftEntryArray[ j ].NewFileRva,
RiftTableArray[ i ]->RiftEntryArray[ j + 1 ].OldFileRva,
RiftTableArray[ i ]->RiftEntryArray[ j + 1 ].NewFileRva
);
}
else {
//
// This is a completely duplicate entry, so just
// silently remove it.
//
}
MoveMemory(
&RiftTableArray[ i ]->RiftEntryArray[ j ],
&RiftTableArray[ i ]->RiftEntryArray[ j + 1 ],
( n - j ) * sizeof( RIFT_ENTRY )
);
--n;
}
}
RiftTableArray[ i ]->RiftEntryCount = n + 1;
}
}
for ( i = 0; i < (int)OldFileCount; i++ ) {
if ( OldFileSymPathArray[ i ] == NULL ) {
GetFullPathName( OldFileInfo[ i ].OldFileName, sizeof( TextBuffer ), TextBuffer, &FileNamePart );
if (( FileNamePart > TextBuffer ) && ( *( FileNamePart - 1 ) == '\\' )) {
*( FileNamePart - 1 ) = 0;
}
OldFileSymPathArray[ i ] = _strdup( TextBuffer );
}
}
if ( NewFileSymPath == NULL ) {
GetFullPathName( NewFileName, sizeof( TextBuffer ), TextBuffer, &FileNamePart );
if (( FileNamePart > TextBuffer ) && ( *( FileNamePart - 1 ) == '\\' )) {
*( FileNamePart - 1 ) = 0;
}
NewFileSymPath = _strdup( TextBuffer );
}
OptionData.NewFileSymbolPath = NewFileSymPath;
OptionData.OldFileSymbolPathArray = OldFileSymPathArray;
if ( OptionData.SymbolOptionFlags & PATCH_SYMBOL_EXTERNAL_RIFT ) {
OptionData.OldFileSymbolPathArray = (PVOID)RiftTableArray;
}
OptionData.SymLoadCallback = MySymLoadCallback;
OptionData.SymLoadContext = FileNameArray;
FileNameArray[ 0 ] = (LPSTR)NewFileName;
for ( i = 0; i < (int)OldFileCount; i++ ) {
FileNameArray[ i + 1 ] = (LPSTR)OldFileInfo[ i ].OldFileName;
}
Success = CreatePatchFileEx(
OldFileCount,
OldFileInfo,
NewFileName,
PatchFileName,
OptionFlags,
&OptionData,
bNoProgress ? NULL : MyProgressCallback,
NULL
);
ErrorCode = GetLastError();
printf( "\n\n" );
if ( ! Success ) {
CHAR ErrorText[ 16 ];
sprintf( ErrorText, ( ErrorCode < 0x10000000 ) ? "%d" : "%X", ErrorCode );
printf( "Failed to create patch (%s)\n", ErrorText );
exit( 1 );
}
NewFileSize = (ULONG) GetFileSizeByName( NewFileName );
PatchFileSize = (ULONG) GetFileSizeByName( PatchFileName );
if (( NewFileSize != 0xFFFFFFFF ) && ( NewFileSize != 0 ) && ( PatchFileSize != 0xFFFFFFFF )) {
printf( "%d bytes (%3.1f%% compression, %.1f:1)\n",
PatchFileSize,
((((LONG)NewFileSize - (LONG)PatchFileSize ) * 100.0 ) / NewFileSize ),
((double)NewFileSize / PatchFileSize )
);
if ( ! bNoCompare ) {
CHAR TempFile1[ MAX_PATH ];
CHAR TempFile2[ MAX_PATH ];
HANDLE hFile;
GetTempPath( MAX_PATH, TempFile1 );
GetTempPath( MAX_PATH, TempFile2 );
strcat( TempFile1, "\\tt$$src.$$$" );
strcat( TempFile2, "\\tt$$pat.$$$" );
hFile = CreateFile(
TempFile1,
GENERIC_READ | GENERIC_WRITE,
FILE_SHARE_READ,
NULL,
CREATE_ALWAYS,
FILE_ATTRIBUTE_TEMPORARY,
NULL
);
if ( hFile != INVALID_HANDLE_VALUE ) {
CloseHandle( hFile );
Success = CreatePatchFile(
TempFile1,
NewFileName,
TempFile2,
OptionFlags & ~PATCH_OPTION_FAIL_IF_BIGGER,
NULL
);
if ( Success ) {
ULONG CompFileSize = (ULONG) GetFileSizeByName( TempFile2 );
if (( CompFileSize != 0xFFFFFFFF ) && ( CompFileSize != 0 )) {
if ( CompFileSize <= PatchFileSize ) {
printf( "\nWARNING: Simply compressing %s would be %d bytes smaller (%3.1f%%)\n",
NewFileName,
PatchFileSize - CompFileSize,
((((LONG)PatchFileSize - (LONG)CompFileSize ) * 100.0 ) / CompFileSize )
);
}
else if ( NewFileSize != 0 ) {
printf( "\n%d bytes saved (%3.1f%%) over non-patching compression\n",
CompFileSize - PatchFileSize,
((((LONG)CompFileSize - (LONG)PatchFileSize ) * 100.0 ) / NewFileSize )
);
}
}
}
}
DeleteFile( TempFile1 );
DeleteFile( TempFile2 );
}
}
else {
printf( "OK\n" );
}
exit( 0 );
}