AutoHotkey Homepage AutoHotkey Community
Let's help each other out
 
 FAQFAQ   SearchSearch   MemberlistMemberlist   RegisterRegister 
 ProfileProfile   Log in to check your private messagesLog in to check your private messages   Log inLog in 

Binary file I/O with binary buffers

 
Post new topic   Reply to topic    AutoHotkey Community Forum Index -> Scripts & Functions
View previous topic :: View next topic  
Author Message
Laszlo



Joined: 14 Feb 2005
Posts: 4016
Location: Pittsburgh

PostPosted: Sat Jul 23, 2005 3:05 am    Post subject: Binary file I/O with binary buffers Reply with quote

The simple binary file functions take and provide the data in hex form, such that the powerful string manipulating and search functions of AHK could be applied. If only pieces need to be cut and pasted, maybe into many files, there is no need for hex conversions. Large chunks of data can be manipulated much faster in binary buffers.

We could use two sets of functions with hex or with binary buffers, whichever fits the task better, or we can use the binary buffer version only, with simple functions converting binary data to hex and vice versa. The original functions should be renamed to HexWrite and HexRead.

Again, there are calling examples below. The conversion functions are simple enough (but tricky) that they did not deserve the fancy header, but you can add your own. The binary file I/O functions now store/load the data only to/from special AHK variables, which may contain NULL characters, therefore, they must not be manipulated as ordinary strings. Not even assignments or StrLen is allowed. If these variables are used as function parameters, they must be defined as ByRef, otherwise the strings get truncated at the first NULL.
Code:
file = c:\z.dat
FileDelete %file%
IfNotEqual ErrorLevel,0, MsgBox Can't delete file "%file%"`nErrorLevel = "%ErrorLevel%"

Hex2Bin(b,"000102030405060708090a0b0c0d0e0f00")
res := BinWrite(file,b,17)
MsgBox ErrorLevel = %ErrorLevel%`nBytes Written = %res%
res := BinRead(file,data)
MsgBox ErrorLevel = %ErrorLevel%`nBytes Read = %res%
Bin2Hex(h,data,res)
MsgBox Data = "%h%"

Hex2Bin(b,"aa00bb")
res := BinWrite(file,b,3,-2)
MsgBox ErrorLevel = %ErrorLevel%`nBytes Written = %res%
res := BinRead(file,data)
MsgBox ErrorLevel = %ErrorLevel%`nBytes Read = %res%
Bin2Hex(h,data,res)
MsgBox Data = "%h%"

ExitApp

Bin2Hex(ByRef h, ByRef b, n=0)      ; n bytes binary data -> stream of 2-digit hex
{                                   ; n = 0: all (SetCapacity can be larger than used!)
   format = %A_FormatInteger%       ; save original integer format
   SetFormat Integer, Hex           ; for converting bytes to hex

   m := VarSetCapacity(b)
   If (n < 1 or n > m)
       n := m
   Address := &b
   h =
   Loop %n%
   {
      x := *Address                 ; get byte in hex
      StringTrimLeft x, x, 2        ; remove 0x
      x = 0%x%                      ; pad left
      StringRight x, x, 2           ; 2 hex digits
      h = %h%%x%
      Address++
   }
   SetFormat Integer, %format%      ; restore original format
}

Hex2Bin(ByRef b, h, n=0)            ; n hex digit-pairs -> binary data
{                                   ; n = 0: all. (Only ByRef can handle binaries)
   m := Ceil(StrLen(h)/2)
   If (n < 1 or n > m)
       n := m
   Granted := VarSetCapacity(b, n, 0)
   IfLess Granted,%n%, {
      ErrorLevel = Mem=%Granted%
      Return
   }
   Address := &b
   Loop %n%
   {
      StringLeft  x, h, 2
      StringTrimLeft h, h, 2
      x = 0x%x%
      DllCall("RtlFillMemory", "UInt", Address, "UInt", 1, "UChar", x)
      Address++
   }
}

/* ;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;; BinWrite ;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;
|  - Open binary file
|  - (Over)Write n bytes (n = 0: all)
|  - From offset (offset < 0: counted from end)
|  - Close file
|  (Binary)data -> file[offset + 0..n-1], rest of file unchanged
|  Return #bytes actually written
*/ ;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;

BinWrite(file, ByRef data, n=0, offset=0)
{
   ; Open file for WRITE (0x40..), OPEN_ALWAYS (4): creates only if it does not exists
   h := DllCall("CreateFile","str",file,"Uint",0x40000000,"Uint",0,"UInt",0,"UInt",4,"Uint",0,"UInt",0)
   IfEqual h,-1, SetEnv, ErrorLevel, -1
   IfNotEqual ErrorLevel,0,Return,0 ; couldn't create the file

   m = 0                            ; seek to offset
   IfLess offset,0, SetEnv,m,2
   r := DllCall("SetFilePointerEx","Uint",h,"Int64",offset,"UInt *",p,"Int",m)
   IfEqual r,0, SetEnv, ErrorLevel, -3
   IfNotEqual ErrorLevel,0, {
      t = %ErrorLevel%              ; save ErrorLevel to be returned
      DllCall("CloseHandle", "Uint", h)
      ErrorLevel = %t%              ; return seek error
      Return 0
   }

   m := VarSetCapacity(data)        ; get the capacity ( >= used length )
   If (n < 1 or n > m)
       n := m
   result := DllCall("WriteFile","UInt",h,"Str",data,"UInt",n,"UInt *",Written,"UInt",0)
   if (!result or Written < n)
       ErrorLevel = -3
   IfNotEqual ErrorLevel,0, SetEnv,t,%ErrorLevel%

   h := DllCall("CloseHandle", "Uint", h)
   IfEqual h,-1, SetEnv, ErrorLevel, -2
   IfNotEqual t,,SetEnv, ErrorLevel, %t%-%ErrorLevel%

   Return Written
}

/* ;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;; BinRead ;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;
|  - Open binary file
|  - Read n bytes (n = 0: file size)
|  - From offset (offset < 0: counted from end)
|  - Close file
|  (Binary)data (replaced) <- file[offset + 0..n-1]
|  Return #bytes actually read
*/ ;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;

BinRead(file, ByRef data, n=0, offset=0)
{
   h := DllCall("CreateFile","Str",file,"Uint",0x80000000,"Uint",3,"UInt",0,"UInt",3,"Uint",0,"UInt",0)
   IfEqual h,-1, SetEnv, ErrorLevel, -1
   IfNotEqual ErrorLevel,0,Return,0 ; couldn't open the file

   m = 0                            ; seek to offset
   IfLess offset,0, SetEnv,m,2
   r := DllCall("SetFilePointerEx","Uint",h,"Int64",offset,"UInt *",p,"Int",m)
   IfEqual r,0, SetEnv, ErrorLevel, -3
   IfNotEqual ErrorLevel,0, {
      t = %ErrorLevel%              ; save ErrorLevel to be returned
      DllCall("CloseHandle", "Uint", h)
      ErrorLevel = %t%              ; return seek error
      Return 0
   }

   m := DllCall("GetFileSize","UInt",h,"Int64 *",r)
   If (n < 1 or n > m)
       n := m
   Granted := VarSetCapacity(data, n, 0)
   IfLess Granted,%n%, {
      ErrorLevel = Mem=%Granted%
      Return 0
   }

   result := DllCall("ReadFile","UInt",h,"Str",data,"UInt",n,"UInt *",Read,"UInt",0)

   if (!result or Read < n)
       t = -3
   IfNotEqual ErrorLevel,0, SetEnv,t,%ErrorLevel%

   h := DllCall("CloseHandle", "Uint", h)
   IfEqual h,-1, SetEnv, ErrorLevel, -2
   IfNotEqual t,,SetEnv, ErrorLevel, %t%-%ErrorLevel%

   Return Read
}
Edit 2005.07.03: StrLen and assignment don’t work with binary buffers. We cannot even compare them, If (a=b)... only compares the data until the first NULL, and ignores the rest. We have to use DLL calls to copy or compare buffers, or Bin2Hex, Hex2Bin combinations and work with the hex strings in between.

Last edited by Laszlo on Sat Jul 23, 2005 5:51 pm; edited 1 time in total
Back to top
View user's profile Send private message
Rajat



Joined: 28 Mar 2004
Posts: 1718

PostPosted: Sat Jul 23, 2005 3:22 am    Post subject: Reply with quote

very nice work!
_________________
Back to top
View user's profile Send private message
Laszlo



Joined: 14 Feb 2005
Posts: 4016
Location: Pittsburgh

PostPosted: Sat Jul 23, 2005 3:41 am    Post subject: Reply with quote

Thanks, Rajat! These functions are tricky, so they could still have bugs. Please post any abnormal behavior of them, like formatting your hard drive, corrupting the file system or sending harassing emails to your ex girlfriend.
Back to top
View user's profile Send private message
Laszlo



Joined: 14 Feb 2005
Posts: 4016
Location: Pittsburgh

PostPosted: Sat Jul 23, 2005 5:56 pm    Post subject: Reply with quote

StrLen and assignment don’t work with binary buffers. We cannot even compare them, If (a=b)... only compares the data until the first NULL, and ignores the rest. We have to use DLL calls to copy or compare buffers, or Bin2Hex, Hex2Bin combinations and work with the hex strings in between.
Back to top
View user's profile Send private message
Rajat



Joined: 28 Mar 2004
Posts: 1718

PostPosted: Sat Jul 23, 2005 8:55 pm    Post subject: Reply with quote

Bin2Hex is sending harassing emails to my current girlfriend!! fix it!!!

(ok i had nothing to say!!) Wink
_________________
Back to top
View user's profile Send private message
Display posts from previous:   
Post new topic   Reply to topic    AutoHotkey Community Forum Index -> Scripts & Functions All times are GMT
Page 1 of 1

 
Jump to:  
You can post new topics in this forum
You can reply to topics in this forum


Powered by phpBB © 2001, 2005 phpBB Group