Jump to content

Convert Hex to Binary and extract specific Bits


Recommended Posts

I tried the Binary() function for starters but since AU3 stores binary as hex I'm lost as to how to extract specific bits out of it.

Here's an example of what I'm trying to do:

1) Original Hex: 303425637013293133FDA4D5

2) Convert to Binary: 001100000011010000100101011000110111000000010011001010010011000100110011111111011010010011010101

3) Extract Bits 39 - 58 from binary: 00000100110010100100

4) Convert extracted bits to decimal: 19620

 

While I don't see that long binary string in step 2 I'll assume it's available internally in the script after Binary() so it's step 3 where I'm stuck.

I did try BinaryMid() but that just returned nothing with the parameters 39, 17 wrapped around the Binary() function.

And yes I did include 0x in front of my Hex number.

 

Any help is appreciated.

Thanks,

Kenny

Edited by ken82m

 "I believe that when we leave a place, part of it goes with us and part of us remains... Go anywhere, when it is quiet, and just listen.. After a while, you will hear the echoes of all our conversations, every thought and word we've exchanged.... Long after we are gone our voices will linger in these walls for as long as this place remains."

Link to comment
Share on other sites

Maybe this helps you. I suspect that MSB for bits 39-58 are not correct by I stayed with your version. If the order of your most significant bit is reversed just reverse $BinNumber before extracting bits with StringMid().

 

$HexNumber = '303425637013293133FDA4D5'
$BinNumer = HexToBin($HexNumber)
$Bits39_58 = StringMid($BinNumer, 39, 20) ; 58 - 39 + 1 = 20
$Decimal = Bin2Dec($Bits39_58)
ConsoleWrite($Decimal & @CRLF)

Func HexToBin($dHex)
    If StringLeft($dHex, 2) = '0x' Then $dHex = StringTrimLeft($dHex, 2)
    Local $aSplit = StringSplit($dHex, '')
    Local $sBin, $aBits = StringSplit('0000|0001|0010|0011|0100|0101|0110|0111|1000|1001|1010|1011|1100|1101|1110|1111', '|', 2)
    For $Index = 1 To $aSplit[0]
        $sBin &= $aBits[Dec($aSplit[$Index])]
    Next
    Return $sBin
EndFunc

Func Bin2Dec($sBin)
    Local $iDecimal = 0
    For $Index = 1 To StringLen($sBin)
        $iDecimal += Number(StringMid($sBin, $Index , 1)) * (2 ^ (StringLen($sBin) - $Index))
    Next
    Return $iDecimal
EndFunc

 

When the words fail... music speaks.

Link to comment
Share on other sites

Why on earth would you need to do this?  :)

#AutoIt3Wrapper_AU3Check_Parameters=-w 3 -w 4 -w 5 -w 6 -d

#include <Constants.au3>

example()

Func example()
    Const $BIN_VALUE   = Binary("0x303425637013293133FDA4D5")

    Local $sBase2Value = _BinaryBits($BIN_VALUE, False), _
          $sBits39to58 = StringMid($sBase2Value, 39, 20)

    Local $iInt32Value = _StringToInt32($sBits39to58, 2)

    ConsoleWrite("Base16 value:              " & $BIN_VALUE   & @CRLF)
    ConsoleWrite("Base2 value:               " & _BinaryBits($BIN_VALUE) & @CRLF)
    ConsoleWrite("Bits 39-58                 " & $sBits39to58 & @CRLF)
    ConsoleWrite("Int32 value of bits 39-58: " & $iInt32Value  & @CRLF)
EndFunc

; #FUNCTION# ====================================================================================================================
; Name ..........: _BinaryBits
; Description ...: Convert data to its base2 value (with or without a space between every 4 bits)
; Syntax ........: _BinaryBits($vData[, $bAddSeparator = True])
; Parameters ....: $vData           - a string or number value.
;                  $bAddSeparator   - [optional] a boolean value. Default is True.
; Return values .: A string representing the base2 value
; Author ........: TheXman
; ===============================================================================================================================
Func _BinaryBits($vData, $bAddSeparator = True)

    Local $sBits = ""

    Local $xBinaryData = Binary($vData), _
          $xByte       = Binary("")

    Local $iBinaryDataLength = BinaryLen($xBinaryData)


    ;Process binary data from left-most byte to right-most byte
    For $i = 1 To $iBinaryDataLength
        ;Get byte
        $xByte = BinaryMid($xBinaryData, $i, 1)

        ;Spin thru each bit of byte (msb to lsb)
        For $j = 7 To 0 Step -1
            ;If separator requested and this is 5th bit, then add a nibble separator
            If $bAddSeparator And $j = 3 Then $sBits &= " "

            ;If bit is set, then prepend 1 else prepend 0
            $sBits &= (BitAND($xByte, 2 ^ $j) ? "1" : "0")
        Next

        ;If this isn't the last byte to be processed, then add a byte separator
        If $i < $iBinaryDataLength And $bAddSeparator Then $sBits &= " "
    Next

    Return $sBits

EndFunc

; #FUNCTION# ====================================================================================================================
; Name ..........: _StringToInt32
; Description ...: Convert a string, in the specified number base, to an int32.
; Syntax ........: _StringToInt64($sString, $iBase)
; Parameters ....: $sString        A string representation of a integer value (in a base from 2 to 36).
;                  $iBase          An integer value from 2 to 36.
; Return values .: Success         An int32 value of the string.
;                  Failure         0 and sets @error to a non-zero value.
;                                  @error  1 = Invalid number base
;                                          2 = DllCall failed.  @extended = DllCall @error
; Author ........: TheXman
; ===============================================================================================================================
Func _StringToInt32($sString, $iBase)

    Local $aResult[0]

    If $iBase < 2 Or $iBase > 36 Then Return SetError(1, 0, "")
    $aResult = DllCall('msvcrt.dll', 'int64:cdecl', '_wcstoui64', _
                       'wstr'  , $sString, _
                       'wstr*' , Null, _
                       'int'   , $iBase)
    If @error Then Return SetError(2, @error, 0)

    Return Number($aResult[0], $NUMBER_32BIT)
EndFunc

Output:

Base16 value:              0x303425637013293133FDA4D5
Base2 value:               0011 0000 0011 0100 0010 0101 0110 0011 0111 0000 0001 0011 0010 1001 0011 0001 0011 0011 1111 1101 1010 0100 1101 0101
Bits 39-58                 00000100110010100100
Int32 value of bits 39-58: 19620

 

Edited by TheXman
Link to comment
Share on other sites

4 minutes ago, TheXman said:

Why on earth would you need to do this?  :)

#AutoIt3Wrapper_AU3Check_Parameters=-w 3 -w 4 -w 5 -w 6 -d

#include <Constants.au3>

example()

Func example()
    Const $BIN_VALUE   = Binary("0x303425637013293133FDA4D5")

    Local $sBase2Value = _BinaryBits($BIN_VALUE, False), _
          $sBits39to58 = StringMid($sBase2Value, 39, 20)

    Local $iInt32Value = _StringToInt32($sBits39to58, 2)

    ConsoleWrite("Binary value:              " & $BIN_VALUE   & @CRLF)
    ConsoleWrite("Base2 value:               " & $sBase2Value & @CRLF)
    ConsoleWrite("Bits 39-58                 " & $sBits39to58 & @CRLF)
    ConsoleWrite("Int32 value of bits 39-58: " & $iInt32Value  & @CRLF)
EndFunc

; #FUNCTION# ====================================================================================================================
; Name ..........: _BinaryBits
; Description ...: Convert data to its base2 value (with or without a space between every 4 bits)
; Syntax ........: _BinaryBits($vData[, $bAddSeparator = True])
; Parameters ....: $vData           - a string or number value.
;                  $bAddSeparator   - [optional] a boolean value. Default is True.
; Return values .: A string representing the base2 value
; Author ........: TheXman
; ===============================================================================================================================
Func _BinaryBits($vData, $bAddSeparator = True)

    Local $sBits = ""

    Local $xBinaryData = Binary($vData), _
          $xByte       = Binary("")

    Local $iBinaryDataLength = BinaryLen($xBinaryData)


    ;Process binary data from left-most byte to right-most byte
    For $i = 1 To $iBinaryDataLength
        ;Get byte
        $xByte = BinaryMid($xBinaryData, $i, 1)

        ;Spin thru each bit of byte (msb to lsb)
        For $j = 7 To 0 Step -1
            ;If separator requested and this is 5th bit, then add a nibble separator
            If $bAddSeparator And $j = 3 Then $sBits &= " "

            ;If bit is set, then prepend 1 else prepend 0
            $sBits &= (BitAND($xByte, 2 ^ $j) ? "1" : "0")
        Next

        ;If this isn't the last byte to be processed, then add a byte separator
        If $i < $iBinaryDataLength And $bAddSeparator Then $sBits &= " "
    Next

    Return $sBits

EndFunc

; #FUNCTION# ====================================================================================================================
; Name ..........: _StringToInt32
; Description ...: Convert a string, in the specified number base, to an int32.
; Syntax ........: _StringToInt64($sString, $iBase)
; Parameters ....: $sString        A string representation of a integer value (in a base from 2 to 36).
;                  $iBase          An integer value from 2 to 36.
; Return values .: Success         An int32 value of the string.
;                  Failure         0 and sets @error to a non-zero value.
;                                  @error  1 = Invalid number base
;                                          2 = DllCall failed.  @extended = DllCall @error
; Author ........: TheXman
; ===============================================================================================================================
Func _StringToInt32($sString, $iBase)

    Local $aResult[0]

    If $iBase < 2 Or $iBase > 36 Then Return SetError(1, 0, "")
    $aResult = DllCall('msvcrt.dll', 'int64:cdecl', '_wcstoui64', _
                       'wstr'  , $sString, _
                       'wstr*' , Null, _
                       'int'   , $iBase)
    If @error Then Return SetError(2, @error, 0)

    Return Number($aResult[0], $NUMBER_32BIT)
EndFunc

Output:

Binary value:              0x303425637013293133FDA4D5
Base2 value:               001100000011010000100101011000110111000000010011001010010011000100110011111111011010010011010101
Bits 39-58                 00000100110010100100
Int32 value of bits 39-58: 19620

 

Uggg don't get me started. Some stupid vendor we're unfortunately willing to accommodate has this weird damn structure that's a combination of data that produces that hex number.

And when it gets transmitted back to them in volume it requires that piece of it converted back to decimal (which can vary) as part of the return data. It was a big enough pain putting this together with a piece of software. But then having to unravel this again which is a big enough pain to create to extract that specific section as part of the file that goes back to them. And of course I have to make all this junk work.

Alternatively I could write these to a database as we create them with that decimal value in another column which probably makes more sense. But honestly nobody here gives a damn about any of these values so I'd rather not have yet another database to administer. If I can just have a script these users can feed an input file to (which contains a lot of unnecessary fields) process that and produce the final output going back stand alone I can live with that.

I've though about a few other options but I can see users constantly screwing something up constantly lol.

 

Thank you all for your help. I've done plenty in AutoIT but I have never had to deal with actual binary directly in AU3.

 

 "I believe that when we leave a place, part of it goes with us and part of us remains... Go anywhere, when it is quiet, and just listen.. After a while, you will hear the echoes of all our conversations, every thought and word we've exchanged.... Long after we are gone our voices will linger in these walls for as long as this place remains."

Link to comment
Share on other sites

A different approach:

#NoTrayIcon
#include ".\Eigen4AutoIt.au3"

_Eigen_StartUp("int")

$HexNumber = '303425637013293133FDA4D5'

$firstbit = 39  ; base-1 index
$lastbit = 58
$totalbits = 1+$lastbit-$firstbit
If $totalbits>32 Then Exit  ; value too large to fit into one int32

; store hex string as conseucutive int32s
$ints=Ceiling(StringLen($HexNumber)/8)
$vector=_Eigen_CreateVector(_Max(32,$ints)) ; ensure enough space for result
For $rc=0 To $ints-1
    _Eigen_WriteMatrixValue($vector,$rc,0,Number("0x" & StringMid($HexNumber,1+$rc*8,8)))
Next

; unpack all bits, store desired bits, and repack
$matA=_Eigen_Unpack($vector)
_Eigen_SetZero($vector) ; prezero results buffer
_Eigen_Copy_Ablock_ToBblock($matA,$vector,$firstbit-1,0,$totalbits,1,32-$totalbits,0)
_Eigen_Pack($vector,$matA)

ConsoleWrite("Selected Bits value = " & _Eigen_ReadMatrixValue($matA,0,0) & @CRLF)

_Eigen_CleanUp()

If the last int32 is can be incomplete, just pad it with extra zeroes before storing it in the vector:

#include <String.au3>
$HexNumber&=_StringRepeat("0",8-Mod(StringLen($HexNumber),8))

 

Edited by RTFC
Link to comment
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
 Share

  • Recently Browsing   0 members

    • No registered users viewing this page.
×
×
  • Create New...