Sign in to follow this  
Followers 0
CarlD

Use text editor to view/edit/save binary files

1 post in this topic

#1 ·  Posted (edited)

This code is a subset of an AutoIt tool I wrote for the classic DOS word-processor XyWrite. The tool allows 7-bit Ascii encoding of XyWrite Programming Language programs, with "readability aids" that mimic the way this code looks natively on the DOS screen. This is a special 7-bit encoding that we've used for many years in the XyWrite community to discuss XPL code on the XyWrite Mailing List. But that's neither here nor there.

The present subset consists of three utilities, which I offer here for what they're worth. The first, DVIEW.AU3, takes a binary file and displays it in the default Windows text editor, displaying only Ascii chars 32-127, the other chars being represented by ".". The command-line usage is:
DVIEW.AU3 <file_in><Enter>

The second, DREAD.AU3 (that's "Dee-Read", not "dread" ;) ), provides a similar display, except that characters outside the Ascii 32-127 range are represented by "{nnn}", where "nnn" is the 3-digit decimal Ascii number. (The initial "D" in these utilities' names stands for "decimal".) The output from DREAD.AU3 can be edited to make simple patches to binary files. The output file is named DREAD.TXT. The usage is:
DREAD.AU3 <file_in><Enter>

The third utility, DWRITE.AU3, takes DREAD output and writes it back to disk as a binary file. So, once you edit the output from DREAD, you write it to disk with:
DWRITE.AU3 <file_in><Enter>
The default file_in is DREAD.TXT -- i.e., the output of DREAD.AU3. The output file is named DWRITE.BIN, which can be renamed as desired.

You'll see that each of these scripts processes the input file character by character. If there's a faster way of doing this, for example by manipulating bit patterns, I'd be pleased to hear about it.

Here are the three scripts. Enjoy.

; DVIEW.AU3 -- AutoIt v3 [CarlD rev.9/27/15]
; Display a decimal view of a binary file
;
; Usage:
; DREAD.AU3 file_in

ProgressOn(@ScriptName,"Working")

Global $iLnLen = 0; Line length meter
Local $sTmp = "";   Temp string var

Local $sInFile = @ScriptDir & "\DVIEW.IN"
If $CmdLine[0] > 0 Then $sInFile = $CmdLine[1]
If Not FileExists($sInFile) Then
    ProgressOff()
    MsgBox(16, @Scriptname, $sInFile & " does not exist!", 3)
    Exit
EndIf

Local $sTmpFile = @ScriptDir & "\DVIEW.TMP"
Local $sOutFile = @ScriptDir & "\DVIEW.TXT"

If FileExists($sTmpFile) Then FileDelete($sTmpFile)
If FileExists($sOutFile) Then FileDelete($sOutFile)

Local $hWrIn = FileOpen($sInFile, 16);  Handle for source file
Local $sToEncode = FileRead($hWrIn);    Binary (hex) string to encode
FileClose($hWrIn)

Global $sEncoded = "";  Encoded output (string)
Local $aEncoded = HexToDec($sToEncode); Binary (hex) to decimal array
Local $iAsc = "";   Decimal Ascii number of current char

; Loop through each byte of input string
For $i = 1 To UBound($aEncoded) - 1
    $iAsc = StringFormat("%03u", $aEncoded[$i])
    $sTmp = ""

    If $iAsc > 31 And $iAsc < 128 Then
        $sTmp = Chr($aEncoded[$i])
    Else
        $sTmp = "."
    EndIf

    $sTmp = AddCrLf($sTmp)
    If $iLnLen = 0 And $sTmp = "." Then $sTmp = "{046}"
    If $iLnLen = 0 And $sTmp = ">" Then $sTmp = "{062}"
    $sEncoded &= $sTmp  
Next    

; Trim double CrLf to one; change trailing space to "{032}"
If StringRight($sEncoded, 2) = @CRLF Then _
    $sEncoded = StringTrimRight($sEncoded, 2)
If StringRight($sEncoded, 1) = " " Then _
    $sEncoded = StringTrimRight($sEncoded, 1) & "{032}"

; Add header and footer
Local $sHeader = "DVIEW v1.0" & @CRLF
$sEncoded = $sHeader & "b-gin [" & $sInFile & "]" & @CRLF & _
    $sEncoded & @CRLF & "-nd DVIEW" & @CRLF

; Write output file
Local $hWrOut = FileOpen($sTmpFile, 2)
FileWrite($sTmpFile, $sEncoded)
FileClose($hWrOut)
FileMove($sTmpFile, $sOutFile, 1)

ProgressSet(100, "Done")
Sleep(2000)
ProgressOff()
ShellExecute($sOutFile)

; --------- Function DeFinitions ---------

Func HexToDec($sHexIn); Convert hex string to decimal array
    $aHexChars = StringSplit($sHexIn, "")
    Local $aHexIn[UBound($aHexChars) / 2]
    Local $j = 0
    For $i = 1 To UBound($aHexChars) Step 2
        If $i + 1 <= UBound($aHexChars) Then
            $aHexIn[$j] = $aHexChars[$i] & $aHexChars[$i + 1]
            $j += 1
        Else
            ExitLoop
        EndIf
    Next
    Local $aDecOut[UBound($aHexIn)]
    For $i = 0 To UBound($aHexIn) - 1
        $aDecOut[$i] = Dec($aHexIn[$i])
    Next
    Return $aDecOut
EndFunc   ;==>HexToDec

Func AddCrLf($sIn); Add line breaks to output
    $iLnLen += StringLen($sIn)
    If $iLnLen > 74 Then
        $sIn &= @CRLF
        $iLnLen = 0
    EndIf
    Return $sIn
EndFunc   ;==>AddCrLf
; DREAD.AU3 -- AutoIt v3 [CarlD rev.9/27/15]
; Display a decimal view of a binary file
;
; Usage:
; DREAD.AU3 file_in

ProgressOn(@ScriptName,"Working")

Global $iLnLen = 0; Line length meter
Local $sTmp = "";   Temp string var

Local $sInFile = @ScriptDir & "\DREAD.IN"
If $CmdLine[0] > 0 Then $sInFile = $CmdLine[1]
If Not FileExists($sInFile) Then
    ProgressOff()
    MsgBox(16, @Scriptname, $sInFile & " does not exist!", 3)
    Exit
EndIf

Local $sTmpFile = @ScriptDir & "\DREAD.TMP"
Local $sOutFile = @ScriptDir & "\DREAD.TXT"

If FileExists($sTmpFile) Then FileDelete($sTmpFile)
If FileExists($sOutFile) Then FileDelete($sOutFile)

Local $hWrIn = FileOpen($sInFile, 16);  Handle for source file
Local $sToEncode = FileRead($hWrIn);    Binary (hex) string to encode
FileClose($hWrIn)

Global $sEncoded = "";  Encoded output (string)
Local $aEncoded = HexToDec($sToEncode); Binary (hex) to decimal array
Local $iAsc = "";   Decimal Ascii number of current char

; Loop through each byte of input string
For $i = 1 To UBound($aEncoded) - 1
    $iAsc = StringFormat("%03u", $aEncoded[$i])
    $sTmp = ""

    If $iAsc > 31 And $iAsc < 128 Then
        $sTmp = Chr($aEncoded[$i])
    Else
        $sTmp = "{" & $iAsc & "}"
    EndIf

    $sTmp = AddCrLf($sTmp)
    If $iLnLen = 0 And $sTmp = "." Then $sTmp = "{046}"
    If $iLnLen = 0 And $sTmp = ">" Then $sTmp = "{062}"
    $sEncoded &= $sTmp  
Next    

; Trim double CrLf to one; change trailing space to "{032}"
If StringRight($sEncoded, 2) = @CRLF Then _
    $sEncoded = StringTrimRight($sEncoded, 2)
If StringRight($sEncoded, 1) = " " Then _
    $sEncoded = StringTrimRight($sEncoded, 1) & "{032}"

; Add header and footer
Local $sHeader = "DeeREAD v1.0" & @CRLF
$sEncoded = $sHeader & "b-gin [" & $sInFile & "]" & @CRLF & _
    $sEncoded & @CRLF & "-nd DeeREAD" & @CRLF

; Write output file
Local $hWrOut = FileOpen($sTmpFile, 2)
FileWrite($sTmpFile, $sEncoded)
FileClose($hWrOut)
FileMove($sTmpFile, $sOutFile, 1)

ProgressSet(100, "Done")
Sleep(2000)
ProgressOff()
ShellExecute($sOutFile)

; --------- Function DeFinitions ---------

Func HexToDec($sHexIn); Convert hex string to decimal array
    $aHexChars = StringSplit($sHexIn, "")
    Local $aHexIn[UBound($aHexChars) / 2]
    Local $j = 0
    For $i = 1 To UBound($aHexChars) Step 2
        If $i + 1 <= UBound($aHexChars) Then
            $aHexIn[$j] = $aHexChars[$i] & $aHexChars[$i + 1]
            $j += 1
        Else
            ExitLoop
        EndIf
    Next
    Local $aDecOut[UBound($aHexIn)]
    For $i = 0 To UBound($aHexIn) - 1
        $aDecOut[$i] = Dec($aHexIn[$i])
    Next
    Return $aDecOut
EndFunc   ;==>HexToDec

Func AddCrLf($sIn); Add line breaks to output
    $iLnLen += StringLen($sIn)
    If $iLnLen > 74 Then
        If $sIn = " " Then $sIn = "{032}"
        $sIn &= @CRLF
        $iLnLen = 0
    EndIf
    Return $sIn
EndFunc   ;==>AddCrLf
; DWRITE.AU3 -- AutoIt v3 [CarlD rev.9/27/15]
; Write DVIEW encoding as binary file
;
;   Usage:
; DWRITE.AU3 file_in
; Output is sent to @ScriptDir & "DWRITE.BIN"

ProgressOn(@ScriptName,"Working")

Local $sInFile = @ScriptDir & "\DREAD.TXT"
If $CmdLine[0] > 0 Then $sInFile = $CmdLine[1]
If Not FileExists($sInFile) Then
    ProgressOff()
    MsgBox(16, @Scriptname, $sInFile & " does not exist!", 3)
    Exit
EndIf

Local $sTmpFile = @ScriptDir & "\DWRITE.TMP"
Local $sOutFile = @ScriptDir & "\DWRITE.BIN"

Local $hWrIn = FileOpen($sInFile);  Handle for source file
Local $sMaster = FileRead($hWrIn);  Master string to decode
FileClose($hWrIn)
Local $sToDecode = ""
Local $aTmp = ""

; Remove header|footer
If StringLeft($sMaster, 9) = "DeeREAD v" Then _
        $sMaster = StringTrimLeft($sMaster, StringInStr($sMaster, "]"))
If StringRight($sMaster, 13) = "-nd DeeREAD" & @CRLF Then _
        $sMaster = StringTrimRight($sMaster, 13)

Local $sFinished = ""
Local $iChunkSz = 512
Local $iAdd = 0

; - - - - - - Main Loop - - - - - -
While $sMaster
    If StringLen($sMaster) > $iChunkSz Then
        $sToDecode = StringLeft($sMaster, $iChunkSz)
        $sMaster = StringTrimLeft($sMaster, $iChunkSz)
        If StringRight($sToDecode, 2) <> @CRLF Then
            $iAdd = 1 + StringInStr($sMaster, @CRLF)
            $sToDecode &= StringLeft($sMaster, $iAdd)
            $sMaster = StringTrimLeft($sMaster, $iAdd)
        EndIf
    Else
        $sToDecode = $sMaster
        $sMaster = ""
    EndIf

    ;   Strip CrLfs
    $sToDecode = StringReplace($sToDecode, @CRLF, "")

    ;       "{nnn}" ==> 1-byte Ascii char;
    Local $aTmp = StringSplit($sToDecode, "{")
    Local $iAsc = -1
    For $i = 1 To UBound($aTmp) - 1
        $iAsc = StringLeft($aTmp[$i], 3)
        If StringInStr($aTmp[$i], "}") = 4 And _
                StringIsDigit($iAsc) Then
            If $iAsc > -1 And $iAsc < 256 Then
                $sToDecode = StringReplace($sToDecode, "{" & _
                        StringLeft($aTmp[$i], 4), Chr($iAsc))
            EndIf
        EndIf
    Next

    $sFinished &= $sToDecode
    $sToDecode = ""
WEnd
; - - - - - End Main Loop - - - - -

; Write output file
Local $hWrOut = FileOpen($sTmpFile, 2)
FileWrite($sTmpFile, $sFinished)
FileClose($hWrOut)
FileMove($sTmpFile, $sOutFile, 1)
ProgressOff()
MsgBox(0, @ScriptName, "Output in " & $sOutFile, 5)
; Done
Edited by CarlD

Share this post


Link to post
Share on other sites



Create an account or sign in to comment

You need to be a member in order to leave a comment

Create an account

Sign up for a new account in our community. It's easy!


Register a new account

Sign in

Already have an account? Sign in here.


Sign In Now
Sign in to follow this  
Followers 0

  • Similar Content

    • WoodGrain
      By WoodGrain
      Hi All,
      Trying to convert a number to binary zeros and ones but I'm getting a result I don't understand and looks more like hex than binary.
      Here's my basic code:
      $myNum = 11 $myNumBin = Binary($myNum) MsgBox(0, "Binary result", $myNumBin) What I want is "1011", what I get is 0x0B000000.
      Thanks!
    • theak
      By theak
      Trying to find a quick way to convert 30k+ WordPerfect files into Word.
      Will probably run it locally from an admin machine or server so user permissions won't affect it.
      My idea was just to open the file, select all, copy, open new word doc, paste, file, save....
      What would be the best way to go about scripting something in this way?
    • dejhost
      By dejhost
      Hello ,
      Here are three stepts that I would like to speed up - if possible: 
      STEP 1: I am generating an array, containing binary numbers  up to a certain amount of digits. My script adds leading zeros, so that each number has equal amount of digits.
      Example: 14 digit Binary array:
      I am using the code
      For $i = 0 to 2^$bit-1 ; $bit amount of digits. for example: 14 $binary = ( Dec2Bin($i) ) ; Check length of binary string $adig = $bit - StringLen($binary) ; Determine how many leading 0 have to be added $zeros = "" For $j = 1 To $adig ; add leading "0"s $zeros = $zeros & "0" Next $BinArray[$i] = $zeros & $binary ;Write binary-number to file, leading "0" Next Func Dec2Bin($D) Return (BitShift($D, 1) ? Dec2Bin(BitShift($D, 1)) : "") & BitAnd($D, 1) EndFunc ;==> Dec2Bin() AutoIt v3.3.12.0   to generate the binary number. 
      STEP 2: I reduce the array to unique values. In my application, the binary-numbers do not have a start-bit or end-bit. This means, that
        are actually the same numbers, and only one of them is to remain in the array. All alterations aren't unique and shall be removed. Here is my code:
      For $i = 0 to Ubound($BinArray)-1 ; shift through all rows For $j = 1 to $bit ; shift through all the bits If $i = Ubound($BinArray) Then ; exit before exceeding the arrays boundries ExitLoop 2 EndIf $BinArray[$i] = StringRight ( $BinArray[$i], 1 ) & StringLeft ( $BinArray[$i], $bit-1 ) $BinArray = _ArrayUnique($BinArray, 0, 0, 0, 0, $ARRAYUNIQUE_AUTO) If @error <> 0 Then Msgbox(0, "Error in _ArrayUnique", "The Error Code is " & @error & " Abort.") Exit EndIf Next Next STEP 3: Finally, I write the remaining array into a text-file.
      For $i = 0 to Ubound($BinArray)-1 FileWrite($hFileOpen, $i & @TAB & $BinArray[$i] & @CRLF) Next  
      So my question is: any idea how to speed up this procedure? There certainly is a way to do this smarter. Btw.: Step 2 is optional.
      Thanks for helping,
      dejhost 
    • am632
      By am632
      Hi,
      I have a binary string that I want to convert to octal, The string I want to convert is,
      10001001010100000100111001000111000011010000101000011010
       
      The String is read from a .txt file
      Once its converted it should read this,
      4   2   2   5   0   1   1   6   2   1   6   0   6   4   1   2   0   6   10
      which I want written to the .txt file to overwrite the original binary string.
      The 10 at the end should be ignored as there are not 3 digits to convert.
      I'm thinking it should read the string from left to right to get the next 3 digits before converting them to its oct value. would this be the best way to do this or is there a better way?
      If i'm on the right track can anyone give me an example of how to write this script please?
      I've looked at the StringLeft function & StringReplace but I'm not sure how to use them the correct way to accomplish what I'm trying to do.
      Thanks
       
    • toasterking
      By toasterking
      I was just working on a project that involved decoding a stream of binary data from a serial port in AutoIt.  It took me a few hours to figure out how to process the data efficiently in AutoIt and I did not find any helpful examples on how to do so, so I thought I would share my core example and maybe save someone else some time.  There may be a more efficient way to do this, but this works well for me.
       
      #cs Author: ToasterKing This is an example of a way to parse streaming binary data that follows a strict format with a header and footer. In this example, each frame is 5 bytes with a 2-byte header of 0xD5AA and a 1-byte footer of 0xAD. The _BinaryParse() function accumulates incoming data in a buffer. Once a footer is found, it searches backward for the header, and if it is in the right position, it extracts the remaining 2 bytes in the middle, then moves on to looking for the next frame. #ce ; The data source might be something asynchronous like serial or TCP, but since this is just an example, I'm just putting the data in a variable. Local $fSomeData $fSomeData = Binary("0xD5AA24B1") ; Binary data constituting almost a complete frame. _BinaryParse($fSomeData) ; Call the function with the received data. It isn't a complete frame, so it is just stored in the buffer until more data is received. $fSomeData = Binary("0xAD62D5AA92E7AD") ; Remainder of the previous frame, one garbage byte (0x62) which should be skipped, and a complete additional frame. _BinaryParse($fSomeData) ; The function should be able to parse both frames now. Func _BinaryParse($fNewData) Local Static $fBinaryReceived = Binary("") ; Buffer for received data ConsoleWrite("Hey, the function is called!" & @CRLF) ; Add new data to the buffer. ; This ridiculous monstrosity is the only way I could find to append binary data to binary data in AutoIt. It must be converted to strings first. ; Both, one, or no substrings will begin with "0x" depending on whether they contained binary data. To be converted back to binary properly, only one instance ; of "0x" must exist at the beginning of the string. $fBinaryReceived = Binary("0x" & StringReplace(String($fBinaryReceived) & String($fNewData),"0x","")) ConsoleWrite("Data in the buffer: " & String($fBinaryReceived) & @CRLF) Local $iLength = BinaryLen($fBinaryReceived) ; Count the bytes in the data If $iLength > 0 Then Local $fBinaryReceivedTemp = $fBinaryReceived ; Create temporary copy to work on Local $fByte1,$fByte2 For $i = 1 To $iLength If BinaryMid($fBinaryReceivedTemp,$i,1) = 0xAD Then ; If the 1-byte footer found ConsoleWrite("Footer found at end of " & $i & " of " & $iLength & " bytes!" & @CRLF) If BinaryMid($fBinaryReceivedTemp,$i - 4,1) = 0xD5 And BinaryMid($fBinaryReceivedTemp,$i - 3,1) = 0xAA Then ; and the 2-byte header is found 4 bytes before that ConsoleWrite("Header found before the footer!" & @CRLF) $fByte1 = BinaryMid($fBinaryReceivedTemp,$i - 2,1) ; Get 1st byte in the body (between header and footer) $fByte2 = BinaryMid($fBinaryReceivedTemp,$i - 1,1) ; Get 2nd byte in the body (between header and footer) ConsoleWrite("Here is the critical data: " & String($fByte1) & " " & String($fByte2) & @CRLF) ; Just display the 2 bytes for demonstration purposes. Normally, you'd do something more useful with it here. EndIf $fBinaryReceived = BinaryMid($fBinaryReceivedTemp,$i + 1) ; Truncate the original data to remove all of the bytes just processed, then continue processing $fBinaryReceivedTemp EndIf Next EndIf EndFunc