HxD plugin Data Inspector Example

Share your advanced PureBasic knowledge/code with the community.
infratec
Always Here
Always Here
Posts: 7576
Joined: Sun Sep 07, 2008 12:45 pm
Location: Germany

HxD plugin Data Inspector Example

Post by infratec »

It is possible to write own plugins with PB.

I ported the C example to PB:

Code: Select all

;
; https://github.com/maelh/hxd-plugin-framework
;
; ported to PureBasic by infratec
;
; https://www.purebasic.fr/english/viewtopic.php?p=565301#p565301
;
; 2021-02-04 first public release
;

EnableExplicit


CompilerIf #PB_Compiler_ExecutableFormat <> #PB_Compiler_DLL And Not #PB_Compiler_Debugger
  CompilerError "Set the excutable format to 'Shared DLL'"
CompilerEndIf


#Debugging = #False

#MaxConverterCount = 10



;-Macros
Macro TByteOrders
  Ascii
EndMacro

Macro TConverterClassID
  i
EndMacro

Macro PConverterClassID
  Integer
EndMacro

Macro TDataTypeWidth
  Integer
EndMacro


;-Enumerations
Enumeration TByteOrder
  #boLittleEndian
  #boBigEndian
EndEnumeration


Enumeration TDataTypeWidth
  #dtwVariable
  #dtwFixed
EndEnumeration


Enumeration TIntegerDisplayOption
  #idoDecimal
  #idoHexadecimalUpperCase
  #idoHexadecimalLowerCase
EndEnumeration


Enumeration TBytesToStrError
  #btseNone
  #btseInvalidBytes
  #btseBytesTooShort
EndEnumeration


Enumeration TStrToBytesError
  #stbeNone
  #stbeInvalidString
  #stbeUnderflow
  #stbeOverflow
  #stbeOutOfRange   ;If unclear whether underflow Or overflow
EndEnumeration


Structure TInt32ConverterInstance
  ; NOTE: For simpliciy the Array sizes were chosen statically For the
  ; int32_t type, but you may need To dynamically allocate memory For other
  ; converter types To Not waste memory; in this case, make sure to allocate
  ; And initialize in CreateConverter, And deallocate in DestroyConverter.
  ; What matters is that each converter instance has its own memory, that
  ; holds converted Data returned by BytesToStr And StrToBytes (see the
  ; comment at file start).
  ConvertedStr.s{128} ; // smaller should be fine, but depends on maximal length of formatted string
  ConvertedBytes.a[SizeOf(Long)]
EndStructure


Structure TConverterClassIDStructure
  i.i[#MaxConverterCount]
EndStructure


Prototype TClassIDOrFactoryFunc()

;-Declarations (needed public procedures)
DeclareDLL AttachProcess(Instance.i)
DeclareDLL.i GetDataTypeConverters(*ClassIDsOrFactoryFuncs.PConverterClassID, *Count.Integer)
DeclareDLL.i CreateConverter(ClassIDOrFactoryFunc.TConverterClassID, *TypeName.Integer, *FriendlyTypeName.Integer, *Width.TDataTypeWidth, *MaxTypeSize.Integer, *SupportedByteOrders.TByteOrders)
DeclareDLL DestroyConverter(*ThisPtr)
DeclareDLL AssignConverter(*ThisPtr.TInt32ConverterInstance, *Source.TInt32ConverterInstance)
DeclareDLL ChangeByteOrder(*ThisPtr, *Bytes, ByteCount.i, TargetByteOrder.i)
DeclareDLL.i BytesToStr(*ThisPtr, *Bytes, ByteCount.i, IntegerDisplayOption.i, *ConvertedByteCount.Integer, *ConvertedStr.Integer)
DeclareDLL.i StrToBytes(*ThisPtr, Str$, IntegerDisplayOption.i, *ConvertedBytes.Integer, *ConvertedByteCount.Integer)


;-Globals
Global InternalClassIDsOrFactoryFuncs.TConverterClassIDStructure
Global ConverterCount.i
Global Int32ConverterClassID.i



;-Debug tool
Macro DebugLogging(Text)
  CompilerIf Defined(Debugging, #PB_Constant)
    CompilerIf #Debugging
      Logging(Text)
    CompilerEndIf
  CompilerEndIf
EndMacro


CompilerIf Defined(Debugging, #PB_Constant)
  CompilerIf #Debugging
    
    Procedure Logging(Text$)
      
      Protected File.i
      
      
      ;File = OpenFile(#PB_Any, GetPathPart(ProgramFilename()) + GetFilePart(ProgramFilename(), #PB_FileSystem_NoExtension) + ".log", #PB_File_Append)
      File = OpenFile(#PB_Any, GetFilePart(ProgramFilename(), #PB_FileSystem_NoExtension) + ".log", #PB_File_Append)
      If File
        ;WriteStringN(File, FormatDate("%yyyy.%mm.%dd %hh:%ii:%ss", Date()) + " " + Text$)
        WriteStringN(File, Text$)
        CloseFile(File)
      EndIf
    EndProcedure
    
  CompilerEndIf
CompilerEndIf






;-Main procedures


Procedure RegisterDataTypeConverter(ClassIDOrFactoryFunc.TClassIDOrFactoryFunc)
  
  DebugLogging("RegisterDataTypeConverter")
  
  If ConverterCount < #MaxConverterCount
    DebugLogging("RegisterDataTypeConverter inside if ClassIDOrFactoryFunc: [" + Str(ConverterCount) + "] " + Hex(ClassIDOrFactoryFunc))
    InternalClassIDsOrFactoryFuncs\i[ConverterCount] = ClassIDOrFactoryFunc
    ConverterCount + 1
  EndIf
  
  ;DebugLogging("RegisterDataTypeConverter End")
  
EndProcedure




ProcedureDLL.i GetDataTypeConverterClassIDs(*ClassIDsOrFactoryFuncs.PConverterClassID, *Count.Long)
  
  DebugLogging("GetDataTypeConverterClassIDs")
  
  If ConverterCount > 0
    *ClassIDsOrFactoryFuncs\i = @InternalClassIDsOrFactoryFuncs\i[0]
  Else
    *ClassIDsOrFactoryFuncs\i = #Null
  EndIf
  
  *Count\l = ConverterCount
  
  ;DebugLogging("GetDataTypeConverterClassIDs end")
  
  ProcedureReturn #True
  
EndProcedure






ProcedureDLL AttachProcess(Instance.i)
  
  ;DebugLogging("AttachProcess")
  
  Int32ConverterClassID = 66
  
  RegisterDataTypeConverter(@Int32ConverterClassID)
  
EndProcedure




ProcedureDLL AttachThread(Instance.i)
  
  ;DebugLogging("AttachThread")
  
EndProcedure




ProcedureDLL DetachThread(Instance.i)
  
  ;DebugLogging("DetachThread")
  
EndProcedure




ProcedureDLL DetachProcess(Instance.i)
  
  ;DebugLogging("DetachProcess")
  
EndProcedure



Procedure.l ByteSwap(UI32.l)
  ProcedureReturn ((UI32 & $000000FF) << 24) | ((UI32 & $0000FF00) << 8) | ((UI32 & $00FF0000) >> 8) | ((UI32 & $FF000000) >> 24)
EndProcedure




ProcedureDLL.i CreateConverter(ClassIDOrFactoryFunc.TConverterClassID, *TypeName.Integer, *FriendlyTypeName.Integer, *Width.TDataTypeWidth, *MaxTypeSize.Integer, *SupportedByteOrders.TByteOrders)
  
  Protected *Converter.TInt32ConverterInstance
  
  DebugLogging("CreateConverter")
  
  
  ; ClassIDOrFactoryFunc can be used To delegate creation To constructor
  ; functions As needed. See the C++ plugin For an example.
  
  ; Here we just support one converter type, so it suffices To check we were
  ; called To construct the right converter.
  
  If ClassIDOrFactoryFunc = @Int32ConverterClassID
    
    *TypeName\i = @"PB - Int32"
    *FriendlyTypeName\i = *TypeName\i
    *Width\i = #dtwFixed
    *MaxTypeSize\i = SizeOf(Long)
    *SupportedByteOrders\a =  1 << #boLittleEndian | 1 << #boBigEndian
    
    *Converter = AllocateMemory(SizeOf(TInt32ConverterInstance))
  EndIf
  
  DebugLogging("CreateConverter End")
  
  ProcedureReturn *Converter
  
EndProcedure




ProcedureDLL DestroyConverter(*ThisPtr)
  
  DebugLogging("DestroyConverter")
  
  FreeMemory(*ThisPtr)
  
  DebugLogging("DestroyConverter End")
  
EndProcedure




ProcedureDLL AssignConverter(*ThisPtr.TInt32ConverterInstance, *Source.TInt32ConverterInstance)
  
  DebugLogging("AssignConverter")
  
  CopyMemory(@*Source\ConvertedBytes[0], @*ThisPtr\ConvertedBytes[0], SizeOf(*Source\ConvertedBytes))
  CopyMemory(@*Source\ConvertedStr, @*ThisPtr\ConvertedStr, SizeOf(*Source\ConvertedStr))
  
  DebugLogging("AssignConverter End")
  
EndProcedure




ProcedureDLL ChangeByteOrder(*ThisPtr, *Bytes, ByteCount.i, TargetByteOrder.i)
  
  DebugLogging("ChangeByteOrder")
  
  If TargetByteOrder = #boBigEndian And ByteCount >= SizeOf(long)
    PokeL(*Bytes, ByteSwap(PeekL(*Bytes)))
  EndIf
  
  DebugLogging("ChangeByteOrder End")
  
EndProcedure




ProcedureDLL.i BytesToStr(*ThisPtr, *Bytes, ByteCount.i, IntegerDisplayOption.i, *ConvertedByteCount.Integer, *ConvertedStr.Integer)
  
  Protected Result.l
  Protected *Converter.TInt32ConverterInstance
  
  DebugLogging("BytesToStr")
  
  *Converter = *ThisPtr
  
  If ByteCount >= SizeOf(long)
    Select IntegerDisplayOption
      Case #idoDecimal
        DebugLogging("idoDecimal")
        *Converter\ConvertedStr = Str(PeekL(*Bytes))
      Case #idoHexaDecimalUpperCase
        DebugLogging("idoHexaDecimalUpperCase")
        ;*Converter\ConvertedStr = UCase(RSet(Hex(PeekL(*Bytes) & $FFFFFFFF), 8, "0"))
        *Converter\ConvertedStr = UCase(Hex(PeekL(*Bytes) & $FFFFFFFF))
      Case #idoHexaDecimalLowerCase
        DebugLogging("idoHexaDecimalLowerCase")
        ;*Converter\ConvertedStr = LCase(RSet(Hex(PeekL(*Bytes) & $FFFFFFFF), 8, "0"))
        *Converter\ConvertedStr = LCase(Hex(PeekL(*Bytes) & $FFFFFFFF))
    EndSelect
    *ConvertedStr\i = @*Converter\ConvertedStr
    *ConvertedByteCount\i = SizeOf(long)
    Result = #btseNone
  Else
    *Converter\ConvertedStr = ""
    *ConvertedStr\i = @*Converter\ConvertedStr
    *ConvertedByteCount\i = 0
    Result = #btseBytesTooShort
  EndIf
  
  DebugLogging("BytesToStr End")
  
  ProcedureReturn Result
  
EndProcedure




ProcedureDLL.i StrToBytes(*ThisPtr, Str$, IntegerDisplayOption.i, *ConvertedBytes.Integer, *ConvertedByteCount.Integer)
  
  Protected Result.l, Value.q
  Protected *Converter.TInt32ConverterInstance
  
  
  DebugLogging("StrToBytes")
  
  *Converter = *ThisPtr
  
  Str$ = LTrim(Str$)
  
  If IntegerDisplayOption = #idoHexaDecimalUpperCase Or IntegerDisplayOption = #idoHexaDecimalLowerCase
    Str$ = "$" + Str$
  EndIf
  
  Value = Val(Str$)
  
  If Value > $FFFFFFFF
    PokeQ(@*Converter\ConvertedBytes[0], Value)
    *ConvertedByteCount\i = SizeOf(Quad)
  Else
    PokeL(@*Converter\ConvertedBytes[0], Value)
    *ConvertedByteCount\i = SizeOf(Integer)
  EndIf
  
  *ConvertedBytes\i = @*Converter\ConvertedBytes[0]
  
  Result = #stbeNone
  
  DebugLogging("StrToBytes End")
  
  ProcedureReturn Result
  
EndProcedure
BarryG
Addict
Addict
Posts: 4122
Joined: Thu Apr 18, 2019 8:17 am

Re: HxD plugin Data Inspector Example

Post by BarryG »

Thanks, infratec!
User avatar
Tenaja
Addict
Addict
Posts: 1959
Joined: Tue Nov 09, 2010 10:15 pm

Re: HxD plugin Data Inspector Example

Post by Tenaja »

Thanks for sharing!
User avatar
Kwai chang caine
Always Here
Always Here
Posts: 5494
Joined: Sun Nov 05, 2006 11:42 pm
Location: Lyon - France

Re: HxD plugin Data Inspector Example

Post by Kwai chang caine »

Thanks for sharing 8)
ImageThe happiness is a road...
Not a destination
Post Reply