|
|
#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>
typedef BOOL PATCHAPI FN_CreatePatchFileA( IN LPCSTR OldFileName, IN LPCSTR NewFileName, OUT LPCSTR PatchFileName, IN ULONG OptionFlags, IN PPATCH_OPTION_DATA OptionData // optional
);
typedef FN_CreatePatchFileA *PFN_CreatePatchFileA;
FN_CreatePatchFileA CreatePatchFileA; // assert typedef correctness
typedef BOOL PATCHAPI FN_CreatePatchFileExA( IN ULONG OldFileCount, // maximum 255
IN PPATCH_OLD_FILE_INFO_A OldFileInfoArray, IN LPCSTR NewFileName, OUT LPCSTR PatchFileName, IN ULONG OptionFlags, IN PPATCH_OPTION_DATA OptionData, // optional
IN PPATCH_PROGRESS_CALLBACK ProgressCallback, IN PVOID CallbackContext );
typedef FN_CreatePatchFileExA *PFN_CreatePatchFileExA;
FN_CreatePatchFileExA CreatePatchFileExA; // assert typedef correctness
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; LPSTR DllFileName = 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; CHAR LoadedFileName[ MAX_PATH + 1 ]; HMODULE hLib; PFN_CreatePatchFileA pCreatePatchFileA = CreatePatchFileA; PFN_CreatePatchFileExA pCreatePatchFileExA = CreatePatchFileExA;
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 ) { void *pv = realloc( OldFileInfo[ FileNum - 1 ].IgnoreRangeArray, OldFileInfo[ FileNum - 1 ].IgnoreRangeCount * sizeof( PATCH_IGNORE_RANGE ) + sizeof( PATCH_IGNORE_RANGE ) );
if (pv) { OldFileInfo[ FileNum - 1 ].IgnoreRangeArray = pv; } else { printf( "Out of memory\n" ); exit( 1 ); }
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 ) {
void *pv = realloc( OldFileInfo[ FileNum - 1 ].RetainRangeArray, OldFileInfo[ FileNum - 1 ].RetainRangeCount * sizeof( PATCH_RETAIN_RANGE ) + sizeof( PATCH_RETAIN_RANGE ) );
if (pv) { OldFileInfo[ FileNum - 1 ].RetainRangeArray = pv; } else { printf( "Out of memory\n" ); exit( 1 ); }
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 ) { void *pv = realloc ( RiftTableArray[ FileNum - 1 ]->RiftEntryArray, RiftTableArray[ FileNum - 1 ]->RiftEntryCount * sizeof( RIFT_ENTRY ) + sizeof( RIFT_ENTRY ) );
if (pv) { RiftTableArray[ FileNum - 1 ]->RiftEntryArray = pv; } else { printf( "Out of memory\n" ); exit( 1 ); }
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(); } { void *pv = realloc( OldFileInfo[ FileNum - 1 ].IgnoreRangeArray, OldFileInfo[ FileNum - 1 ].IgnoreRangeCount * sizeof( PATCH_IGNORE_RANGE ) + sizeof( PATCH_IGNORE_RANGE ) ); if (pv) { OldFileInfo[ FileNum - 1 ].IgnoreRangeArray = pv; } else { printf( "Out of memory\n" ); exit( 1 ); } }
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(); }
{ void *pv = realloc( OldFileInfo[ FileNum - 1 ].RetainRangeArray, OldFileInfo[ FileNum - 1 ].RetainRangeCount * sizeof( PATCH_RETAIN_RANGE ) + sizeof( PATCH_RETAIN_RANGE ) ); if (pv) { OldFileInfo[ FileNum - 1 ].RetainRangeArray = pv; } else { printf( "Out of memory\n" ); exit( 1 ); } }
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 ) { void *pv = realloc( RiftTableArray[ FileNum - 1 ]->RiftEntryArray, RiftTableArray[ FileNum - 1 ]->RiftEntryCount * sizeof( RIFT_ENTRY ) + sizeof( RIFT_ENTRY ) ); if (pv) { RiftTableArray[ FileNum - 1 ]->RiftEntryArray = pv; } else { printf( "Out of memory\n" ); exit( 1 ); }
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 if ( strncmp( arg, "dll:", 4 ) == 0 ) { DllFileName = arg + 4; } 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(); }
if ( DllFileName != NULL ) {
hLib = LoadLibrary( DllFileName ); if ( hLib == NULL ) { printf( "Unable to load DLL '%s'\n", DllFileName ); exit( 1 ); }
if ( GetModuleFileName( hLib, LoadedFileName, sizeof( LoadedFileName ))) { printf( "Loaded DLL '%s'\n", LoadedFileName ); }
pCreatePatchFileA = (PFN_CreatePatchFileA) GetProcAddress( hLib, "CreatePatchFileA" ); pCreatePatchFileExA = (PFN_CreatePatchFileExA) GetProcAddress( hLib, "CreatePatchFileExA" ); if (( pCreatePatchFileExA == NULL ) || ( pCreatePatchFileExA == NULL )) { printf( "Unable to import CreatePatchFileA and CreatePatchFileExA from DLL\n" ); exit( 1 ); } }
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 = pCreatePatchFileExA( 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 = pCreatePatchFileA( 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 ); }
|