1

Тема: AHK: Патч файла

Функция FilePatch в примере ниже пишет данные в указанное место уже существующего файла (патчит его). Она принимает три аргумента: полный путь к файлу, смещение от начала файла (в байтах), по которому нужно произвести запись, и сами данные — в виде строки из шестнадцатеричных цифр. Каждый байт данных должен быть обозначен двумя цифрами — т.е. если он имеет значение 0, нужно писать его как 00, и т.д. Байты в строке можно разделять пробелами для удобства восприятия, а также строка может состоять из нескольких строчек. Возвращает функция количество реально записанных байтов.

FilePath = C:\Temp\temp.bin  ; Путь к файлу.

Offset = 0x1000      ; Смещение в файле, по которому писать.

Data =               ; Данные для записи. Пробелы необязательны.
(
FF FF FF FE 00 00 00 00 00 00 00 00 00 00 00 00
00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00
00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00
00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00
00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00
00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00
00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00
00 00 00 00 00 00 00 00 00 00 00 00 EF FF FF FF
)

Result := FilePatch(FilePath, Offset, Data)

MsgBox, Записано байтов: %Result%


; ===================== Функция записи в файл. ======================

FilePatch(SrcFile, Offset, HexString) 
{
  If not FileExist(SrcFile) {
    MsgBox, 16, %A_ThisFunc%, Не найден файл:`n%SrcFile%
    Return 0
  }

  FileGetSize, FileSize, %SrcFile%

  If(Offset >= FileSize) {
    MsgBox, 16, %A_ThisFunc%, Смещение за пределами файла.
    Return 0
  }

  If RegExMatch(HexString, "i)0x") {
    MsgBox, 16, %A_ThisFunc%,
    (LTrim
      Неверный формат данных: префикс "0x".
      Функция не предназначена для записи чисел,
      только последовательностей байтов.
    )
    Return 0
  }

  HexString := RegExReplace(HexString, "\s")

  If HexString is not xDigit
  {
    MsgBox, 16, %A_ThisFunc%,
    (LTrim
      Строка данных содержит недопустимые символы.
      Допустимы только 0123456789ABCDEF.
    )
    Return 0
  }

  Len := StrLen(HexString)

  If Mod(Len, 2)
  {
    MsgBox, 16, %A_ThisFunc%,
    (LTrim
      В строке данных нечётное количество символов.
      Каждый байт нужно обозначать двумя цифрами.
    )
    Return 0
  }

  cBytes := Len / 2

  VarSetCapacity(Buf, cBytes, 0)

  Pos = 1

  Loop, % cBytes
  {
    Byte := "0x" . SubStr(HexString, Pos, 2)
    Pos += 2
    NumPut(Byte, Buf, A_Index - 1, "Char")
  }

  OPEN_EXISTING = 3
  FILE_WRITE_DATA = 2
  FILE_BEGIN = 0

  VarSetCapacity(BytesWritten, 4, 0)

  hFile := DllCall( "CreateFile", "Str",  SrcFile
                                , "UInt", FILE_WRITE_DATA
                                , "UInt", 0
                                , "UInt", 0
                                , "UInt", OPEN_EXISTING
                                , "UInt", 0
                                , "UInt", 0 )

  If(hFile = -1) {
    MsgBox, 16, %A_ThisFunc%, Не удалось открыть файл.
    Return 0
  }

  DllCall( "SetFilePointer", "UInt",  hFile
                           , "UInt", Offset
                           , "UInt", 0
                           , "UInt", FILE_BEGIN )

  If(A_LastError) {
    DllCall( "CloseHandle", "UInt", hFile )
    MsgBox, 16, %A_ThisFunc%
              , Ошибка при установке указателя файла: %A_LastError%
    Return 0
  }

  Ret := DllCall( "WriteFile", "UInt", hFile
                             , "UInt", &Buf
                             , "UInt", cBytes
                             , "UInt", &BytesWritten
                             , "UInt", 0 )

  If(Ret = 0) {
    DllCall( "CloseHandle", "UInt", hFile )
    MsgBox, 16, %A_ThisFunc%, Не удалось записать в файл.
    Return 0
  }

  DllCall( "CloseHandle", "UInt", hFile )

  Return NumGet(BytesWritten, 0, "UInt")
}