AutoHotkey: Simple file compare

From TotalcmdWiki
Jump to navigation Jump to search

This is a simple script to compare if two files are equal or not. This script does not show the differences. Usage: Add to the button bar, menu, shortcut key or alias with the following parameters:

%P%N %T%M

The script first compares the sizes of the two files to check if they are different. If they are equal, it checks the data inside the files at several positions. The positions and the the amount of data compared are determined by two options (set inside the script as variables) - Reads and FractionOfFileSizeToReadEachTime.

Let's say a file has the size of 1'000'000 bytes. If Reads = 10, the file contents will be read at 10 incrementing equally distanced positions, so in our case it will be read at byte 0, byte 100'000, byte 200'000, etc., up to byte 900'000. If FractionOfFileSizeToReadEachTime = 100000, the amount of bytes that will be read at each position is calculated as the size of the input file divided by 100'000, so in our case it would be 10 bytes each time.

In summary, with the options set as above, the script compares the sizes of the files and if equal it reads ten bytes at position 0 and compares them to the bytes read from the same position from the other file, ten bytes at position 100'000 and compares again, ten bytes at position 200'000 etc., up until ten bytes at position 900'000 and compares them again. If at any point the script encounters any difference in the between the data read from two files, it immediately exits and displays a message that the files are different. Finally, the script calculates the MD5 hashes of both files and compares them.

The script uses the FileMD5() function that needs to be placed into a file called FileMD5.ahk. This file needs to be placed either in the Lib subdirectory of the current script, or the Lib subdirectory of AutoHotkey.exe.

Reads = 10
FractionOfFileSizeToReadEachTime = 1000000

Name1 = %1%
Name2 = %2%

FileGetSize, Size1, %Name1%
IfNotEqual, ErrorLevel, 0
{
	MsgBox, Error opening %Name1%`nError: %A_LastError%
	ExitApp
}

FileGetSize, Size2, %Name2%
IfNotEqual, ErrorLevel, 0
{
	MsgBox, Error opening %Name2%`nError: %A_LastError%
	ExitApp
}

IfNotEqual, Size1, %Size2%
	Goto, FilesAreDifferent

File1 := FileOpen(Name1, "r")
IfEqual, File1, 0
{
	MsgBox, Error opening %Name1%`nError: %A_LastError%
	ExitApp
}

File2 := FileOpen(Name2, "r")
IfEqual, File2, 0
{
	MsgBox, Error opening %Name2%`nError: %A_LastError%
	ExitApp
}

BytesToRead := Ceil(Size1/FractionOfFileSizeToReadEachTime)
Loop, %Reads%
{
	File1.Seek(Floor(Size1/Reads*(A_Index-1)))
	File2.Seek(Floor(Size1/Reads*(A_Index-1)))
	File1.RawRead(Data1, BytesToRead)
	File2.RawRead(Data2, BytesToRead)
	IfNotEqual, Data1, %Data2%
		Goto, FilesAreDifferent
}

MD51 := FileMD5(Name1)
MD52 := FileMD5(Name2)
IfNotEqual, MD51, %MD52%
	Goto, FilesAreDifferent

MsgBox, 64, Files are EQUAL, %Name1%`n%Name2%
ExitApp

FilesAreDifferent:
	MsgBox, 48, Files are DIFFERENT, %Name1%`n%Name2%


This is the FileMD5.ahk script that needs to be placed in the "Lib" subdirectory (either of the script's directory or of AutoHotkey.exe's directory):

FileMD5(path = "", chunkSize = 8) {

	If (ChunkSize < 0 || ChunkSize > 8)
		 ChunkSize := 8

	ChunkSize :=  2 ** (18 + ChunkSize)

	File := DllCall("CreateFile", Str, path, UInt, 0x80000000, Int, 3, Int, 0, Int, 3, Int, 0, Int, 0)
	If (File < 1)
		Return, File

	VarSetCapacity(Buffer, ChunkSize, 0)

	DllCall("GetFileSizeEx", UInt, File, Str, Buffer)
	FileSize := NumGet(Buffer, 0, "Int64")

	VarSetCapacity(MD5_CTX, 104, 0)

	hMod := DllCall("LoadLibrary", Str, "advapi32.dll")
	DllCall("advapi32\MD5Init", Str, MD5_CTX)

	AmountOfChunks := (FileSize // ChunkSize + !! Mod(FileSize, ChunkSize ))
	Loop %AmountOfChunks% {
		DllCall("ReadFile", UInt, File, Str, Buffer, UInt, ChunkSize, UIntP, BytesRead, UInt, 0)
		DllCall("advapi32\MD5Update", Str, MD5_CTX, Str, Buffer, UInt, BytesRead)
	}

	DllCall("advapi32\MD5Final", Str, MD5_CTX)
	DllCall("FreeLibrary", UInt, hMod)
	DllCall("CloseHandle", UInt, File)

	Hex := "123456789ABCDEF0"
	Loop % StrLen(Hex) {
		N := NumGet(MD5_CTX, 87 + A_Index, "Char")
		MD5 := MD5 SubStr(Hex, N >> 4, 1) SubStr(Hex, N & 15, 1)
	}
	Return MD5
}