[Tool] PureLibrary Explorer

Share your advanced PureBasic knowledge/code with the community.
User avatar
Keya
Addict
Addict
Posts: 1891
Joined: Thu Jun 04, 2015 7:10 am

[Tool] PureLibrary Explorer

Post by Keya »

Supports x86 + x64 - Windows, Linux, Mac.
It understands four different types of PureLibrary which are all the ones i know of (IMP, LIB, EDL, LSO, user libraries included), the format of which is different so the displayed output of each type also differs. PureLibrary format is undocumented and I didnt want to waste any of Freds valuable time so i spent all day in hex editor
Feedback welcome!

Sample screenshot, looking at an undocumented function where it has decoded the parameters, after using the Deep-Scan search to instantly find and jump to the function:
Image

Code: Select all

;COMPILE IN ASCII
EnableExplicit
#VERSION$ = "v1.21"

Enumeration FormWindow
  #DlgMain
EndEnumeration

Enumeration FormGadget
  #lblPath
  #txtPath
  #btnBrowsePath
  #listMainLibs
  #txtFind
  #btnFindFirst
  #btnFindNext
  #lblFind
  #btnDeepScan
EndEnumeration

Declare ResizeGadgetsDlgMain()
Declare btnFindNext(EventType)
Declare listMainLibs(EventType)
Declare btnFindFirst(EventType)
Declare btnBrowsePath(EventType)
Declare btnDeepScan(EventType)

Procedure OpenDlgMain(x = 0, y = 0, width = 550, height = 380)
  OpenWindow(#DlgMain, x, y, width, height, "PureLibrary Explorer v1.01 - Double-click a library...", #PB_Window_SystemMenu | #PB_Window_MinimizeGadget | #PB_Window_MaximizeGadget | #PB_Window_SizeGadget | #PB_Window_ScreenCentered)
  TextGadget(#lblPath, 10, 10, 98, 20, "PureLibraries:")
  StringGadget(#txtPath, 110, 10, 396, 20, "", #PB_String_ReadOnly)
  SetGadgetColor(#txtPath, #PB_Gadget_BackColor,RGB(255,255,255))
  ButtonGadget(#btnBrowsePath, 510, 6, 30, 26, "...")
  ListIconGadget(#listMainLibs, 10, 40, 530, 300, "Type", 50, #PB_ListIcon_FullRowSelect | #PB_ListIcon_AlwaysShowSelection)
  AddGadgetColumn(#listMainLibs, 1, "File Name", 230)
  AddGadgetColumn(#listMainLibs, 2, "Internal", 150)
  AddGadgetColumn(#listMainLibs, 3, "File Size", 80)
  StringGadget(#txtFind, 60, 350, 200, 20, "")
  ButtonGadget(#btnFindFirst, 268, 348, 82, 25, "Find First")
  ButtonGadget(#btnFindNext, 353, 348, 82, 25, "Find Next")
  TextGadget(#lblFind, 10, 350, 40, 20, "Find:", #PB_Text_Right)
  ButtonGadget(#btnDeepScan, 450, 348, 90, 25, "Deep Scan...")
EndProcedure

Procedure ResizeGadgetsDlgMain()
  Protected FormWindowWidth, FormWindowHeight
  FormWindowWidth = WindowWidth(#DlgMain)
  FormWindowHeight = WindowHeight(#DlgMain)
  ResizeGadget(#txtPath, 110, 10, FormWindowWidth - 154, 20)
  ResizeGadget(#btnBrowsePath, FormWindowWidth - 40, 6, 30, 26)
  ResizeGadget(#listMainLibs, 10, 40, FormWindowWidth - 20, FormWindowHeight - 80)
  ResizeGadget(#txtFind, 60, FormWindowHeight - 30, FormWindowWidth - 350, 20)
  ResizeGadget(#btnFindFirst, FormWindowWidth - 282, FormWindowHeight - 32, 82, 25)
  ResizeGadget(#btnFindNext, FormWindowWidth - 197, FormWindowHeight - 32, 82, 25)
  ResizeGadget(#lblFind, 10, FormWindowHeight - 30, 40, 20)
  ResizeGadget(#btnDeepScan, FormWindowWidth - 100, FormWindowHeight - 32, 90, 25)
EndProcedure

Procedure DlgMain_Events(event)
  Select event
    Case #PB_Event_SizeWindow
      ResizeGadgetsDlgMain()
    Case #PB_Event_CloseWindow
      ProcedureReturn #False
    Case #PB_Event_Menu
      Select EventMenu()
      EndSelect
    Case #PB_Event_Gadget
      Select EventGadget()
        Case #btnBrowsePath
          btnBrowsePath(EventType())          
        Case #listMainLibs
          listMainLibs(EventType())          
        Case #btnFindFirst
          btnFindFirst(EventType())          
        Case #btnFindNext
          btnFindNext(EventType())          
        Case #btnDeepScan
          btnDeepScan(EventType())          
      EndSelect
  EndSelect
  ProcedureReturn #True
EndProcedure

Enumeration FormWindow
  #DlgViewer
EndEnumeration
Enumeration FormGadget
  #listExtLibs
  #listPureLibs
  #frameFuncs
  #lblFlags
  #txtData
  #txtDesc
  #lblParams
  #txtParams
  #frameExtLibs
  #framePureLibs
  #listFuncs
EndEnumeration

Declare ResizeGadgetsDlgViewer()
Declare listPureLibs(EventType)
Declare listFuncs(EventType)

Procedure OpenDlgViewer(x = 0, y = 0, width = 490, height = 450)
  OpenWindow(#DlgViewer, x, y, width, height, "PureLibrary Explorer", #PB_Window_SystemMenu | #PB_Window_MinimizeGadget | #PB_Window_MaximizeGadget | #PB_Window_SizeGadget | #PB_Window_ScreenCentered | #PB_Window_WindowCentered)
  ListViewGadget(#listExtLibs, 20, 30, 210, 70)
  ListViewGadget(#listPureLibs, 260, 30, 210, 70)
  FrameGadget(#frameFuncs, 10, 120, 470, 320, "Functions: 0")
  TextGadget(#lblFlags, 20, 390, 50, 20, "Data:")
  StringGadget(#txtData, 80, 390, 390, 20, "")
  EditorGadget(#txtDesc, 20, 320, 450, 60, #PB_Editor_ReadOnly | #PB_Editor_WordWrap)
  SetGadgetColor(#txtDesc, #PB_Gadget_BackColor,RGB(242,242,242))
  TextGadget(#lblParams, 20, 410, 50, 20, "Params:")
  StringGadget(#txtParams, 80, 410, 390, 20, "")
  FrameGadget(#frameExtLibs, 10, 10, 230, 100, "Library imports: 0")
  FrameGadget(#framePureLibs, 250, 10, 230, 100, "PureLib dependencies: 0")
  ListIconGadget(#listFuncs, 20, 140, 450, 180, "#", 50, #PB_ListIcon_FullRowSelect | #PB_ListIcon_AlwaysShowSelection)
  AddGadgetColumn(#listFuncs, 1, "Params", 60)
  AddGadgetColumn(#listFuncs, 2, "Offset", 60)
  AddGadgetColumn(#listFuncs, 3, "Name", 270)
EndProcedure

Procedure ResizeGadgetsDlgViewer()
  Protected FormWindowWidth, FormWindowHeight
  FormWindowWidth = WindowWidth(#DlgViewer)
  FormWindowHeight = WindowHeight(#DlgViewer)
  ResizeGadget(#listExtLibs, 20, 30, FormWindowWidth - 280, 70)
  ResizeGadget(#listPureLibs, FormWindowWidth - 230, 30, 210, 70)
  ResizeGadget(#frameFuncs, 10, 120, FormWindowWidth - 20, FormWindowHeight - 130)
  ResizeGadget(#lblFlags, 20, FormWindowHeight - 60, 50, 20)
  ResizeGadget(#txtData, 80, FormWindowHeight - 60, FormWindowWidth - 100, 20)
  ResizeGadget(#txtDesc, 20, FormWindowHeight - 130, FormWindowWidth - 40, 60)
  ResizeGadget(#lblParams, 20, FormWindowHeight - 40, 50, 20)
  ResizeGadget(#txtParams, 80, FormWindowHeight - 40, FormWindowWidth - 100, 20)
  ResizeGadget(#frameExtLibs, 10, 10, FormWindowWidth - 260, 100)
  ResizeGadget(#framePureLibs, FormWindowWidth - 240, 10, 230, 100)
  ResizeGadget(#listFuncs, 20, 140, FormWindowWidth - 40, FormWindowHeight - 270)
EndProcedure

Procedure DlgViewer_Events(event)
  Select event
    Case #PB_Event_SizeWindow
      ResizeGadgetsDlgViewer()
    Case #PB_Event_CloseWindow
      ProcedureReturn #False
    Case #PB_Event_Menu
      Select EventMenu()
      EndSelect
    Case #PB_Event_Gadget
      Select EventGadget()
        Case #listPureLibs
          listPureLibs(EventType())          
        Case #listFuncs
          listFuncs(EventType())          
      EndSelect
  EndSelect
  ProcedureReturn #True
EndProcedure

Enumeration FormWindow
  #DlgDeep
EndEnumeration
Enumeration FormGadget
  #listDeep
EndEnumeration

Declare ResizeGadgetsDlgDeep()
Declare listDeep(EventType)

Procedure OpenDlgDeep(x = 0, y = 0, width = 560, height = 320)
  OpenWindow(#DlgDeep, x, y, width, height, "Deep-Scan Function Names", #PB_Window_SystemMenu | #PB_Window_MinimizeGadget | #PB_Window_MaximizeGadget | #PB_Window_SizeGadget | #PB_Window_ScreenCentered | #PB_Window_WindowCentered)
  ListIconGadget(#listDeep, 0, 0, 560, 320, "Type", 60, #PB_ListIcon_FullRowSelect | #PB_ListIcon_AlwaysShowSelection)
  AddGadgetColumn(#listDeep, 1, "File", 235)
  AddGadgetColumn(#listDeep, 2, "Function", 250)
EndProcedure

Procedure ResizeGadgetsDlgDeep()
  Protected FormWindowWidth, FormWindowHeight
  FormWindowWidth = WindowWidth(#DlgDeep)
  FormWindowHeight = WindowHeight(#DlgDeep)
  ResizeGadget(#listDeep, 0, 0, FormWindowWidth - 0, FormWindowHeight - 0)
EndProcedure

Procedure DlgDeep_Events(event)
  Select event
    Case #PB_Event_SizeWindow
      ResizeGadgetsDlgDeep()
    Case #PB_Event_CloseWindow
      ProcedureReturn #False
    Case #PB_Event_Menu
      Select EventMenu()
      EndSelect
    Case #PB_Event_Gadget
      Select EventGadget()
        Case #listDeep
          listDeep(EventType())          
      EndSelect
  EndSelect
  ProcedureReturn #True
EndProcedure

Global *gmem, gViewMODE.s, gStrDeepFind.s, gDeepFile.s, gDeepScan.i, sLastSearch.s

Procedure AppEnd()
  RemoveKeyboardShortcut(#DlgMain, #PB_Shortcut_Return)
  End
EndProcedure

Procedure SafeCloseWindow(targetwnd)
  If IsWindow(targetwnd):
 CloseWindow(targetwnd):
 EndIf
EndProcedure


CompilerIf #PB_Compiler_OS = #PB_OS_Windows
  #Slash$ = "\"
CompilerElse
  #Slash$ = "/"
CompilerEndIf


Procedure.s DataToHex(*pmem, buflen.i)
  If buflen <= 0: ProcedureReturn "": EndIf
  Protected i.i, *pchar.ASCII, sOut.s
  For *pchar = *pmem To *pmem + buflen-1
    sOut + RSet(Hex(*pchar\a),2,"0") 
  Next
  ProcedureReturn sOut
EndProcedure

Procedure.s SetReturns(sTxt.s)
  Protected offset.i, *pchar.ASCII = @sTxt + Len(sTxt)-1
  Repeat
    *pchar - 1
  Until *pchar\a = ' '
  offset = *pchar - @sTxt
  ProcedureReturn Left(sTxt, offset) + " retn " + Right(sTxt, Len(sTxt) - offset - 1)
EndProcedure

Procedure.s ParamDataToHex(*pmem, buflen.i)
  Protected i.i, *pchar.ASCII, sOut.s, LastAddr.i, sTmp.s
  If buflen <= 0: ProcedureReturn "": EndIf  
  LastAddr = *pmem + buflen-1    
  For *pchar = *pmem To LastAddr
    Select gViewMODE 
      Case "LIB":
        Select *pchar\a
          Case $00: sOut + "Nul"
          Case $40: sOut + "Str$"            
          Case $41: sOut + "*Float"
          Case $42: sOut + "*Str"          
          Case $43: sOut + "*Float/Dbl"
          Case $44: sOut + "*Dbl"
          Case $20: sOut + "Float"
          Case $04: sOut + "*Int"
          Case $02,$08: sOut + "Int"
          Case $10: sOut + "Str$"
          Default:
            sOut + RSet(Hex(*pchar\a),2,"0")
        EndSelect
      Case "EBL", "LSO":
        Select *pchar\a
          Case $0C: sOut + "Dbl"
          Case $15: sOut + "Int"
          Default:
            sOut + RSet(Hex(*pchar\a),2,"0")
        EndSelect
    EndSelect
    sOut + ", "
  Next
  If gViewMODE = "EBL" Or gViewMODE = "LSO"
    If Len(sOut) > 4
      sOut = Left(sOut,2) + ": " + Right(sOut, Len(sOut) - 4)
    EndIf
  ElseIf gViewMODE = "LIB"
    sOut = SetReturns(sOut)
  EndIf
  ProcedureReturn Left(sOut,Len(sOut)-2)
EndProcedure

Global gFindLib.s, qFindSize.q

Procedure.i EnumDirFilesForSize (path$, pattern$) 
  Protected id.i, file$, fsize.q, sType.s, lenRootpath.i, sTmpFname.s
  lenRootpath = Len(GetGadgetText(#txtPath))
  id = ExamineDirectory(#PB_Any, path$, pattern$)
  If id = 0: ProcedureReturn 0: EndIf  
  While NextDirectoryEntry(id)
    If DirectoryEntryType(id) & #PB_DirectoryEntry_File
      sTmpFname = LCase(DirectoryEntryName(id))
      If GetExtensionPart(sTmpFname) <> "imp"
        sTmpFname = Left(sTmpFname, Len(sTmpFname) - Len(GetExtensionPart(sTmpFname) ))
        If Right(sTmpFname,1) = ".": sTmpFName = Left(sTmpFName, Len(sTmpFName) - 1): EndIf
        If sTmpFname = gFindLib         
          file$ = path$ + DirectoryEntryName(id)
          qFindSize = FileSize(file$)
          gFindLib = GetExtensionPart(file$)
          If gFindLib <> "": gFindLib = " " + gFindLib: EndIf
          Break
        EndIf
      EndIf
    EndIf
  Wend
  FinishDirectory(id)   
  ProcedureReturn 1  
EndProcedure

Procedure.i EnumDirsForSize (path$, pattern$)  
  Protected id.i, exitCode.i   
  exitCode = EnumDirFilesForSize(path$, pattern$)   
  id = ExamineDirectory(#PB_Any, path$, "")
  If id = 0: ProcedureReturn 0: EndIf 
  While (exitCode = 1) And NextDirectoryEntry(id)
    If qFindSize <> 0: Break: EndIf
    If DirectoryEntryType(id) & #PB_DirectoryEntry_Directory
      If FindString("..", DirectoryEntryName(id)) = 0
        exitCode = EnumDirsForSize(path$ + DirectoryEntryName(id) + #Slash$, pattern$)
      EndIf
    EndIf    
  Wend
  FinishDirectory(id)   
  ProcedureReturn exitCode
EndProcedure

Procedure.s FindLibSize(sLib.s)
  Protected sPath.s, sTmpLib.s
  sTmpLib = Trim(LCase(sLib))
  If GetExtensionPart(sTmpLib) <> ""
    sTmpLib = Left(sTmpLib, Len(sTmpLib) - Len(GetExtensionPart(sTmpLib) + 1))
  EndIf
  gFindLib = Trim(LCase(sTmpLib))        
  qFindSize = 0
  sPath = GetGadgetText(#txtPath)
  If Right(sPath,1) <> #Slash$: sPath = sPath + #Slash$: EndIf
  EnumDirsForSize(sPath, "*")
  If qFindSize > 0:  ProcedureReturn " (" + StrF(qFindSize / 1024,1) + "kb" + gFindLib + ")": EndIf
EndProcedure

Procedure.s GetPurelibType(file$)       
  Protected hFile.i, sMagic.s{5}, sType.s, sInternal.s
  hFile = ReadFile(#PB_Any, file$, #PB_File_SharedRead)
  If hFile
    ReadData(hFile, @sMagic, 4)
    If sMagic = "ERUP"
      FileSeek(hFile,4)
      ReadData(hFile, @sMagic, 4)
      If PeekL(@sMagic) = 'EDLL'
        sType = "EDL"
        FileSeek(hFile,$0D)
      ElseIf PeekL(@sMagic) = 'LSO1'
        sType = "LSO"
        FileSeek(hFile,$0D)
      Else
        FileSeek(hFile,8)  
        ReadData(hFile, @sMagic, 4)
        sType = Chr(PeekA(@sMagic+3)) + Chr(PeekA(@sMagic+2)) + Chr(PeekA(@sMagic+1))
        FileSeek(hFile,$14)
      EndIf
      sInternal = ReadString(hFile,#PB_Ascii)
      CloseFile(hFile)
      ProcedureReturn sType + "|" + sInternal
    EndIf
  EndIf
EndProcedure

Procedure ReadPureLSection(*pPureSect, lSize.l)
  If PeekA(*pPureSect+8) <> 3: ProcedureReturn: EndIf
  Protected sFunc.s, nParams.a, numfuncs.i, FuncAddr.i
  Protected LastAddr = *pPureSect + lSize
  Protected *gmem = *pPureSect + 13
  Protected sName.s = PeekS(*gmem, -1, #PB_Ascii)
  *gmem + Len(sName) + 1
  While *gmem < LastAddr
    FuncAddr = *gmem - *pPureSect
    sFunc = PeekS(*gmem,-1,#PB_Ascii)
    If gDeepScan = 1
      If FindString(LCase(sFunc), gStrDeepFind) > 0
         AddGadgetItem(#listDeep, -1, gViewMODE + #LF$ + gDeepFile + #LF$ + sFunc)
      EndIf
    EndIf
    *gmem + Len(sFunc) + 1
    nParams = PeekA(*gmem)
    numfuncs + 1
    If gDeepScan = 0: AddGadgetItem(#listFuncs, -1, Str(numfuncs) + #LF$ + Str(nParams) + #LF$ + Hex(FuncAddr) + #LF$ + sFunc): EndIf
    If PeekA(*gmem+1) > 1: *gmem + nParams: EndIf
    *gmem + 2
  Wend  
EndProcedure

Procedure ReadIMPFunc(*pmem)
  Protected sFunc.s, nParams.a, sFunc2.s, sCmt.s, FuncAddr.i, sNew.s
  sFunc = PeekS(*pmem,-1,#PB_Ascii)
  *pmem + Len(sFunc) + 1   
  sFunc2 = PeekS(*pmem,-1,#PB_Ascii)
  *pmem + Len(sFunc2) + 1
  If gDeepScan = 1
    If FindString(LCase(sFunc), gStrDeepFind) > 0
      AddGadgetItem(#listDeep, -1, gViewMODE + #LF$ + gDeepFile + #LF$ + sFunc)
    ElseIf FindString(LCase(sFunc2), gStrDeepFind) > 0
      AddGadgetItem(#listDeep, -1, gViewMODE + #LF$ + gDeepFile + #LF$ + sFunc2)
    EndIf
    ProcedureReturn
  EndIf
  sCmt = PeekS(*pmem,-1, #PB_Ascii)
  *pmem + Len(sCmt) + 1    
  nParams = PeekA(*pmem+14)
  SetGadgetText(#txtDesc, sFunc2 + #CRLF$ + sCmt)
  SetGadgetText(#txtParams, DataToHex(*pmem+18,nParams))
  sFunc = DataToHex(*pmem,18)
  sNew = Left(sFunc,8)+" "+Mid(sFunc,9,8)+" "+Mid(sFunc,17,8)+" "+Right(sFunc,Len(sFunc)-24)
  SetGadgetText(#txtData, sNew)  
EndProcedure

Procedure ReadPureIMPSection(*pPureSect, lSize.l)
  Protected sFunc.s, nParams.a, sFunc2.s, sCmt.s, numfuncs.i, FuncAddr.i
  Protected LastAddr.i = *pPureSect + lSize
  Protected *pmem = *pPureSect + $14
  Protected sLib.s = PeekS(*pmem, -1, #PB_Ascii)  
  If gDeepScan = 0: AddGadgetItem(#listExtLibs, -1, sLib + FindLibSize(sLib)): EndIf
  *pmem + Len(sLib) + 1
  While *pmem < LastAddr
    FuncAddr = *pmem - *pPureSect
    sFunc = PeekS(*pmem,-1,#PB_Ascii)    
    If gDeepScan = 1
      If FindString(LCase(sFunc), gStrDeepFind) > 0
        AddGadgetItem(#listDeep, -1, gViewMODE + #LF$ + gDeepFile + #LF$ + sFunc)
      EndIf
    EndIf
    *pmem + Len(sFunc) + 1   
    sFunc2 = PeekS(*pmem,-1,#PB_Ascii)
    *pmem + Len(sFunc2) + 1
    sCmt = PeekS(*pmem,-1, #PB_Ascii)
    *pmem + Len(sCmt) + 1    
    nParams = PeekA(*pmem+14)
    *pmem + nParams + 18
    numfuncs + 1
    If gDeepScan = 0: AddGadgetItem(#listFuncs, -1, Str(numfuncs) + #LF$ + Str(nParams) + #LF$ + Hex(FuncAddr) + #LF$ + sFunc): EndIf
  Wend  
EndProcedure

Procedure ReadPureLIBSection(*pPureSect, lSize.l)
  Protected cnt.i, sTmp.s, i.i, LastAddr.i, numfuncs.i, FuncParams.i, FuncAddr.i
  Protected sFuncName.s, sFuncDesc.s
  Protected *pmem = *pPureSect + 20
  Protected sPureLibName.s, sPureInternalName.s
  LastAddr = *pPureSect + lSize
  sPureLibName = PeekS(*pmem,-1,#PB_Ascii)
  *pmem + Len(sPureLibName) + 1  
  While *pmem < LastAddr
    Select PeekA(*pmem)
      Case 01:
        cnt = PeekA(*pmem+1)
        If cnt
          *pmem + 2
          For i = 1 To cnt
            sTmp = PeekS(*pmem,-1,#PB_Ascii)
            If gDeepScan = 0: AddGadgetItem(#listExtLibs, -1, sTmp + FindLibSize(sTmp)): EndIf
            *pmem + Len(sTmp) + 1          
          Next i
        Else
          *pmem + 2
        EndIf
      Case 02:
        *pmem + 1
        sPureInternalName = PeekS(*pmem,-1,#PB_Ascii)
        *pmem + Len(sPureInternalName)      
        cnt = PeekA(*pmem+1)
        If cnt
          *pmem + 2
          For i = 1 To cnt
            sTmp = PeekS(*pmem,-1,#PB_Ascii)
            If gDeepScan = 0: AddGadgetItem(#listPureLibs, -1, sTmp + FindLibSize(sTmp)): EndIf
            *pmem + Len(sTmp) + 1          
          Next i
        Else
          *pmem + 2
        EndIf        
        While *pmem < LastAddr
          FuncAddr = *pmem - *pPureSect
          sFuncName = PeekS(*pmem,-1,#PB_Ascii)
          If gDeepScan = 1
            If FindString(LCase(sFuncName), gStrDeepFind) > 0
               AddGadgetItem(#listDeep, -1, gViewMODE + #LF$ + gDeepFile + #LF$ + sFuncName)
            EndIf
          EndIf
          *pmem + Len(sFuncName) + 1          
          FuncParams = PeekA(*pmem)
          *pmem + 5 + FuncParams 
          sFuncDesc = PeekS(*pmem,-1,#PB_Ascii)          
          *pmem + Len(sFuncDesc) + 1          
          numfuncs + 1
          If gDeepScan = 0: AddGadgetItem(#listFuncs, -1, Str(numfuncs) + #LF$ + Str(FuncParams) + #LF$ + Hex(FuncAddr) + #LF$ + sFuncName): EndIf
        Wend
        Break
      Default:
        If gDeepScan = 0: MessageRequester("Error","Parse error, aborting the rest"): Break: EndIf
    EndSelect     
  Wend
EndProcedure

Procedure.s ReadLIBDesc(qOffset.q)
  Protected sFuncName.s, FuncParams.i, sFuncDesc.s, *pmem
  *pmem = *gmem + qOffset
  sFuncName = PeekS(*pmem,-1,#PB_Ascii)
  *pmem + Len(sFuncName) + 1          
  FuncParams = PeekA(*pmem)
  *pmem + 5 + FuncParams 
  SetGadgetText(#txtData, DataToHex(*pmem-4,4))
  sFuncDesc = PeekS(*pmem,-1,#PB_Ascii)    
  ProcedureReturn Trim(sFuncDesc)
EndProcedure

Procedure ShowPureLibrary(sFile.s, sType.s)  
  Protected hFile.i, qLof.q
  If *gmem
    FreeMemory(*gmem): *gmem = 0
  EndIf
  gDeepScan = 0
  hFile = ReadFile(#PB_Any, sFile, #PB_File_SharedRead)
  If hFile
    gViewMODE = sType
    OpenDlgViewer()
    qLof = Lof(hFile)
    *gmem = AllocateMemory(qLof)
    If *gmem
      ReadData(hFile, *gmem, qLof)
    Else
      MessageRequester("Error","AllocateMemory failed"): CloseFile(hFile): ProcedureReturn
    EndIf
    CloseFile(hFile)
    SetWindowTitle(#DlgViewer, "PureLibrary Viewer - " + GetFilePart(sFile) + " [" + gViewMODE + "]")
    Select gViewMODE
      Case "EDL", "LSO":
        DisableGadget(#frameExtLibs,1): DisableGadget(#framePureLibs,1): DisableGadget(#listExtLibs,1): DisableGadget(#listPureLibs,1)
        DisableGadget(#txtDesc,1): DisableGadget(#lblFlags,1): DisableGadget(#txtData,1)
        SetGadgetText(#frameExtLibs, "Library imports: n/a"):  SetGadgetText(#framePureLibs, "PureLib dependencies: n/a")        
        ReadPureLSection(*gmem, qLof)                
      Case "IMP":
        ReadPureIMPSection(*gmem, qLof)                
      Case "LIB":
        ReadPureLIBSection(*gmem, PeekL(*gmem+4))         
    EndSelect    
    SetGadgetText(#frameFuncs, "Functions: " + Str(CountGadgetItems(#listFuncs)))
    SetGadgetText(#frameExtLibs, "Library imports: " + Str(CountGadgetItems(#listExtLibs)))
    SetGadgetText(#framePureLibs, "PureLib dependencies: " + Str(CountGadgetItems(#listPureLibs)))
  Else
    MessageRequester("Error","Couldn't open file")
  EndIf
EndProcedure

Procedure ScanPureLibrary(sFile.s, sShortFile.s, sType.s)
  Protected hFile.i, qLof.q
  If *gmem
    FreeMemory(*gmem): *gmem = 0
  EndIf
  hFile = ReadFile(#PB_Any, sFile, #PB_File_SharedRead)
  If hFile
    gViewMODE = sType
    qLof = Lof(hFile)
    *gmem = AllocateMemory(qLof)
    If *gmem
      ReadData(hFile, *gmem, qLof)
    Else
      CloseFile(hFile):
 	ProcedureReturn
    EndIf
    CloseFile(hFile)
    gDeepFile = sShortFile
    Select gViewMODE
      Case "EDL", "LSO":
        ReadPureLSection(*gmem, qLof)                
      Case "IMP":
        ReadPureIMPSection(*gmem, qLof)                
      Case "LIB":
        ReadPureLIBSection(*gmem, PeekL(*gmem+4))         
    EndSelect
  EndIf  
EndProcedure

Global cntEDL.i, cntIMP.i, cntLIB.i, cntLSO.i, gDeepScan.i

Procedure.i EnumDirFiles (path$, pattern$) 
  Protected id.i, file$, fsize.q, sType.s, lenRootpath.i
  lenRootpath = Len(GetGadgetText(#txtPath))
  id = ExamineDirectory(#PB_Any, path$, pattern$)
  If id = 0: ProcedureReturn 0: EndIf  
  While NextDirectoryEntry(id)
    If DirectoryEntryType(id) & #PB_DirectoryEntry_File
      file$ = path$ + DirectoryEntryName(id)
      fsize = FileSize(file$)
      If fsize > 0
        sType = GetPurelibType(file$)
        Select Left(sType,3)
          Case "LSO":
 		cntLSO + 1
          Case "EDL": 
		cntEDL + 1
          Case "IMP":
 		cntIMP + 1
          Case "LIB":
 		cntLIB + 1
          Default:
            Continue
        EndSelect        
        If gDeepScan = 1
          ScanPureLibrary(file$, Right(file$, Len(file$) - lenRootpath), Left(sType,3))
        Else
          file$ = Right(file$, Len(file$) - lenRootpath)
          AddGadgetItem(#listMainLibs,-1, Left(sType,3) + #LF$ + file$ + #LF$ + Right(sType, Len(sType)-4) + #LF$ + Str(fSize))
        EndIf
      EndIf
    EndIf
  Wend
  FinishDirectory(id)   
  ProcedureReturn 1  
EndProcedure

Procedure.i EnumDirs (path$, pattern$)  
  Protected id.i, exitCode.i   
  exitCode = EnumDirFiles(path$, pattern$)   
  id = ExamineDirectory(#PB_Any, path$, "")
  If id = 0: ProcedureReturn 0: EndIf 
  While (exitCode = 1) And NextDirectoryEntry(id)
    If DirectoryEntryType(id) & #PB_DirectoryEntry_Directory
      If FindString("..", DirectoryEntryName(id)) = 0
        exitCode = EnumDirs(path$ + DirectoryEntryName(id) + #Slash$, pattern$)
      EndIf
    EndIf
  Wend
  FinishDirectory(id)   
  ProcedureReturn exitCode
EndProcedure

Procedure BrowseFolderForPurelibs(sPath.s)
  If FileSize(sPath) <> -2  
    MessageRequester("Error","Not a directory: " + sPath)
  Else
    ClearGadgetItems(#listMainLibs)
    If Right(sPath,1) <> #Slash$: sPath = sPath + #Slash$: EndIf
    SetWindowTitle(#DlgMain, "PureLibrary Explorer " + #VERSION$ + " - Scanning " + sPath + " ...")
    SetGadgetText(#txtPath, sPath)
    cntEDL.i = 0: cntIMP.i = 0: cntLIB.i = 0: cntLSO.i = 0
    EnumDirs(sPath, "*")
    SetWindowTitle(#DlgMain, "PureLibrary Explorer " + #VERSION$ + " - Found " + Str(cntLIB + cntIMP + cntEDL + cntLSO) + "  (" +                               Str(cntLIB) + " LIB, " + Str(cntIMP) + " IMP, " + Str(cntEDL) + " EDL, " + Str(cntLSO) + " LSO)")
  EndIf  
EndProcedure

Procedure btnBrowsePath(EventType)
  Protected sCurdir.s = GetGadgetText(#txtPath)
  Protected sPath.s = PathRequester("Select PureLibraries path...", sCurdir)
  If sPath <> ""
    BrowseFolderForPurelibs(sPath)
  EndIf
EndProcedure

Procedure listFuncs(EventType)
  Protected iItem.i, sOffset.s, iOffset.i, sParams.s, sFunc.s, sDesc.s, sHexParams.s
  Select EventType
    Case #PB_EventType_Change:
      iItem = GetGadgetState(#listFuncs)
      If iItem < 0: ProcedureReturn: EndIf
      sParams = GetGadgetItemText(#listFuncs, iItem, 1)
      sOffset = GetGadgetItemText(#listFuncs, iItem, 2)
      sFunc = GetGadgetItemText(#listFuncs, iItem, 3)
      iOffset = Val("$" + sOffset)
      If gViewMODE = "LIB"
        If Val(sParams) > 0
          If PeekA(*gmem + iOffset + Len(sFunc) + 2) > 1
            sHexParams = DataToHex(*gmem + Len(sFunc) + 2 + iOffset, Val(sParams)+1)
            SetGadgetText(#txtParams, sHexParams + ": " + ParamDataToHex(*gmem + Len(sFunc) + 2 + iOffset, Val(sParams)+1) )
          Else
            sHexParams = DataToHex(*gmem + Len(sFunc) + 2 + iOffset, 1)
            SetGadgetText(#txtParams, sHexParams + ": " + ParamDataToHex(*gmem + Len(sFunc) + 2 + iOffset, 1))
          EndIf                                                         
        Else
          SetGadgetText(#txtParams, "")
        EndIf
        sDesc = ReadLIBDesc(Val("$"+sOffset))
        SetGadgetText(#txtDesc, sDesc)        
      ElseIf gViewMODE = "IMP"
        ReadIMPFunc(*gmem + Val("$"+sOffset))
      EndIf      
  EndSelect
EndProcedure

Procedure listPureLibs(EventType)
  Protected sPath.s, sPureLib.s, iPos.i
  If EventType = #PB_EventType_LeftDoubleClick
    If GetGadgetState(#listPureLibs) < 0: ProcedureReturn: EndIf  
    sPath = GetGadgetText(#txtPath)
    If Right(sPath,1) <> #Slash$: sPath = sPath + #Slash$: EndIf
    sPureLib = GetGadgetText(#listPureLibs)
    iPos = FindString(sPureLib, " (")
    If iPos: sPurelib = Left(sPureLib, iPos-1): EndIf
    ShowPureLibrary (sPath + sPureLib, "LIB")        
  EndIf
EndProcedure

Procedure btnDeepScan(EventType)
  OpenDlgDeep()
  Protected sPath.s = Trim(GetGadgetText(#txtPath))
  If sPath <> ""
    gStrDeepFind = LCase(GetGadgetText(#txtFind))
    gDeepScan = 1
    SetWindowTitle(#DlgDeep, "Deep-Scan Function Names - Scanning...")
    EnumDirs(sPath, "*")
    SetWindowTitle(#DlgDeep, "Deep-Scan Function Names")
    gDeepScan = 0
  EndIf
EndProcedure

Procedure listDeep(EventType)
  Protected sPath.s, sPureLib.s, sPureType.s, sPureFunc.s, iPos.i, i.i, cnt.i
  If EventType = #PB_EventType_LeftDoubleClick
    iPos = GetGadgetState(#listDeep)
    If iPos < 0: ProcedureReturn: EndIf  
    sPath = GetGadgetText(#txtPath)
    If Right(sPath,1) <> #Slash$: sPath = sPath + #Slash$: EndIf
    sPureType = GetGadgetItemText(#listDeep, iPos,0)
    sPureLib = GetGadgetItemText(#listDeep, iPos,1)
    ShowPureLibrary(sPath + sPureLib, sPureType)
    sPureFunc = GetGadgetItemText(#listDeep, iPos,2)    
    cnt = CountGadgetItems(#listFuncs)
    If cnt <= 0: ProcedureReturn:  EndIf
    For i = 0 To cnt-1
      If GetGadgetItemText(#listFuncs,i,3) = sPureFunc
        SetGadgetState(#listFuncs,i)
        listFuncs(#PB_EventType_Change)
        Break
      EndIf
    Next i  
  EndIf
EndProcedure

Procedure.i SearchList(listid.i, sFind.s, startpos.i=0)
  Protected i.i, cnt.i
  cnt = CountGadgetItems(listid)
  If cnt <= 0: ProcedureReturn: EndIf
  sFind = LCase(sFind)
  For i = startpos To cnt-1
    If FindString(LCase(GetGadgetItemText(listid,i,1)), sFind) > 0       
      SetGadgetState(listid,i):
	 ProcedureReturn i
    ElseIf FindString(LCase(GetGadgetItemText(listid,i,2)), sFind) > 0   
      SetGadgetState(listid,i):
	 ProcedureReturn i      
    EndIf
  Next i  
  ProcedureReturn -1
EndProcedure

Procedure  btnFindFirst(EventType)
  Protected sFind.s
  sFind = GetGadgetText(#txtFind)
  If sFind = "": ProcedureReturn: EndIf
  SetActiveGadget(#btnFindNext)
  If SearchList(#listMainLibs, sFind, 0) = -1
    If MessageRequester("Find", "Not found. Deep-scan?", #PB_MessageRequester_YesNo) = #PB_MessageRequester_Yes
      btnDeepScan(0)
    EndIf 
  EndIf
EndProcedure

Procedure btnFindNext(EventType)
  Protected sFind.s, curpos.i
  sFind = GetGadgetText(#txtFind)
  If sFind = "": ProcedureReturn: EndIf
  curpos = GetGadgetState(#listMainLibs)
  If SearchList(#listMainLibs, sFind, curpos+1) = -1
    If sLastSearch = sFind:
	 MessageRequester("Find", "End of search"):
    Else:
	 MessageRequester("Find", "Not found"):
    EndIf
  EndIf
  sLastSearch = sFind
EndProcedure

Procedure listMainLibs(EventType)
  Protected sName.s, curpos.i, sPath.s
  If EventType = #PB_EventType_LeftDoubleClick
    curpos = GetGadgetState(#listMainLibs)
    If curpos < 0: ProcedureReturn: EndIf
    sName = GetGadgetItemText(#listMainLibs, curpos, 1)
    sPath = GetGadgetText(#txtPath)
    If Right(sPath,1) <> #Slash$: sPath = sPath + #Slash$: EndIf
    ShowPureLibrary (sPath + sName, GetGadgetItemText(#listMainLibs, curpos, 0))
  EndIf
EndProcedure

Procedure AppMain()
  Protected eventId.i, eventWnd.i, sPurelibsPath.s  
  If SizeOf(Character) = 2
    MessageRequester("Compile Error","My bad, Unicode not supported - please use Ascii compile"):
    End
  EndIf  
  OpenDlgMain()
  WindowBounds(#DlgMain, 350, 150, #PB_Ignore, #PB_Ignore)
  AddKeyboardShortcut(#DlgMain, #PB_Shortcut_Return, #PB_Key_Return)
  sPurelibsPath = #PB_Compiler_Home + "purelibraries"
  BrowseFolderForPurelibs(sPurelibsPath)  
  SetActiveGadget(#txtFind)
  Repeat
    eventId = WaitWindowEvent()
    eventWnd = EventWindow()
    Select eventId  
      Case #PB_Event_Menu 
        If EventMenu() = #PB_Key_Return
          Select GetActiveGadget() 
            Case #txtFind
              btnFindFirst(0)              
            Case #btnFindFirst
              btnFindFirst(0)
            Case #btnFindNext
              btnFindNext(0)            
          EndSelect
        EndIf
      Case #PB_Event_CloseWindow
        If eventWnd = #DlgMain
          AppEnd()
        Else
          SafeCloseWindow(eventWnd)
          If *gmem: FreeMemory(*gmem): *gmem = 0: EndIf
        EndIf
      Default:
        Select eventWnd
          Case #DlgMain
            DlgMain_Events(eventId)  
          Case #DlgViewer            
            DlgViewer_Events(eventId)
          Case #DlgDeep
            DlgDeep_Events(eventId)
        EndSelect
    EndSelect
  ForEver
EndProcedure

AppMain()
AppEnd()
Last edited by Keya on Thu Feb 25, 2016 7:07 am, edited 15 times in total.
wilbert
PureBasic Expert
PureBasic Expert
Posts: 3870
Joined: Sun Aug 08, 2004 5:21 am
Location: Netherlands

Re: [New tool] PureLibrary Explorer

Post by wilbert »

Very nice !!! :)
Windows (x64)
Raspberry Pi OS (Arm64)
User avatar
Kwai chang caine
Always Here
Always Here
Posts: 5342
Joined: Sun Nov 05, 2006 11:42 pm
Location: Lyon - France

Re: [New tool] PureLibrary Explorer

Post by Kwai chang caine »

Waooouuhh !!! works fine ..
A tool perhaps a little bit hard for my little knowledge, but thanks for sharing 8)
ImageThe happiness is a road...
Not a destination
User avatar
ts-soft
Always Here
Always Here
Posts: 5756
Joined: Thu Jun 24, 2004 2:44 pm
Location: Berlin - Germany

Re: [New tool] PureLibrary Explorer

Post by ts-soft »

thanks for sharing!
PureBasic 5.73 | SpiderBasic 2.30 | Windows 10 Pro (x64) | Linux Mint 20.1 (x64)
Old bugs good, new bugs bad! Updates are evil: might fix old bugs and introduce no new ones.
Image
User avatar
blueb
Addict
Addict
Posts: 1041
Joined: Sat Apr 26, 2003 2:15 pm
Location: Cuernavaca, Mexico

Re: [New tool] PureLibrary Explorer

Post by blueb »

Keya
Great addition to PureBasic.. thanks.

Im using Win 10 Pro PB 5.40 b7...

If I go to the Http Lib (same as your JPG), I get a strange number 'added' in the
Parameter display box.

Examples...

Process has one Param: HTTPProgress will diplay "0400: *Int, retn Nul"

Process has two Params: ReceiveHTTPFile will display "404008: Str$, Str$, retn Int"

Process has three Params: ReceiveHTTPFile2 will display "40400408: Str$, Str$, *Int, retn Int"
- It was too lonely at the top.

System : PB 6.10 Beta 9 (x64) and Win Pro 11 (x64)
Hardware: AMD Ryzen 9 5900X w/64 gigs Ram, AMD RX 6950 XT Graphics w/16gigs Mem
User avatar
Keya
Addict
Addict
Posts: 1891
Joined: Thu Jun 04, 2015 7:10 am

Re: [New tool] PureLibrary Explorer

Post by Keya »

Thanks all :)
blueb, yes i added that to the 1.04 update so that it not only shows you the decoded parameters (like v1.01 in the screenshot), but also the raw (hex) bytes it decoded from. You can see for example that 404008 decodes to Str$, Str$, Int.

Will release another update soon, adding a global function name search to make it easy to find which module a function is in
Oma
Enthusiast
Enthusiast
Posts: 312
Joined: Thu Jun 26, 2014 9:17 am
Location: Germany

Re: [New tool] PureLibrary Explorer

Post by Oma »

Hi Keya,

well done and usefull. Thank you. :)

But one little thing i've to note:
I got the 'Not a directory: "/home/charly/Programming/PB5.40B4/PureLibraries"' - Messages at start without listed libraries.

The Linux filesystem is Case-Sensitive and my PB installation wrote it as "/purelibraries"
See at line 728: 'sPurelibsPath = #PB_Compiler_Home + "PureLibraries"'

Bye, Charly
PureBasic 5.4-5.7, Linux: (X/L/K)Ubuntus+Mint - Windows XP (32Bit)
PureBasic Linux-API-Library & Viewer: http://www.chabba.de
User avatar
Keya
Addict
Addict
Posts: 1891
Joined: Thu Jun 04, 2015 7:10 am

Re: [New tool] PureLibrary Explorer

Post by Keya »

Updated to v1.21, this has a Deep-Scan feature so you can just search for whatever function you're after, for example "getprocaddr", it will then search the function names in every library and present you with a list of all functions containing that name, and simply clicking on any function in the list will take you instantly to that function in that module.
btw you can also double-click an item in the Purelibrary Dependencies list to instantly navigate to that module.

Oma, ive changed "PureLibraries" to "purelibraries" for that reference which should get the job done for now, but ill add a proper fix later. Strangely it wasn't tripping up on my Linux Mint
Last edited by Keya on Wed Sep 23, 2015 7:47 pm, edited 1 time in total.
said
Enthusiast
Enthusiast
Posts: 342
Joined: Thu Apr 14, 2011 6:07 pm

Re: [New tool] PureLibrary Explorer

Post by said »

Looks interesting, not sure how and when i can use it :mrgreen: Thanks for sharing :D
User avatar
Keya
Addict
Addict
Posts: 1891
Joined: Thu Jun 04, 2015 7:10 am

Re: [New tool] PureLibrary Explorer

Post by Keya »

Hi said,
basically for the first time it allows the exploration of PureLibraries ... to summarize:
- listing all functions, including undocumented ones
- see associated raw data with functions including parameters, undocumented flags, offset to raw data etc
- see how many parameters each function expects
- even see which parameter types they are (int, float, str$ etc)
- allowing you to see all related functions in a module
- check to see what parameters are expected when you get compile errors
- see which external libraries a module imports
- see which PureLibraries it depends on
- search to find which module owns a particular function
- statistical counts of modules, functions, dependencies etc
- allows you to search for related functions... "ssl", "cipher", "database" for example
- simply browse and explore to find new features to use in your apps! it's a different perspective to exploring via the helpfile
Going further you can use it to cross-reference against a PB /COMMENTED assembly to see which libraries your program is importing and which functions its calling, and whether or not you're actually making calls into those libraries or if they're being included for no reason. You can manually do this with it already but i'll hopefully get a chance to enhance it to help automate that.

In the 'compile errors' link above (http://purebasic.fr/english/viewtopic.php?f=4&t=63447) is someone who noted that GetProcAddress now only accepts a string, so there's seemingly no way to give it an Ordinal value instead of a function name... that's another type of situation where we can use this tool to quickly examine the function more closely, and in some cases even give a more accurate representation of the function than the helpfile.
Last edited by Keya on Wed Sep 23, 2015 8:28 pm, edited 6 times in total.
GPI
PureBasic Expert
PureBasic Expert
Posts: 1394
Joined: Fri Apr 25, 2003 6:41 pm

Re: [New tool] PureLibrary Explorer

Post by GPI »

It is intressting, but undocumentet features are features, which may change in the next release.
User avatar
Keya
Addict
Addict
Posts: 1891
Joined: Thu Jun 04, 2015 7:10 am

Re: [New tool] PureLibrary Explorer

Post by Keya »

GPI, no im not saying use undocumented functions! and that's not what this program is about, it's just that this utility has the added capability to let you explore them - an interesting but unintended "side-effect" of the data it reads, if you will.
It also lets you explore all the documented functions, and it's for those that i wrote it in the first place. I can't just "ignore" undocumented functions however, as they're exported just like the documented ones. But please dont confuse it, i just chose a screenshot with undocumented function as it seemed more interesting. :) I appreciate that not all programmers will have a use for this
User avatar
majikeyric
Enthusiast
Enthusiast
Posts: 179
Joined: Mon Oct 21, 2013 5:21 pm
Location: France
Contact:

Re: [New tool] PureLibrary Explorer

Post by majikeyric »

Thanks for sharing :)
User avatar
Danilo
Addict
Addict
Posts: 3037
Joined: Sat Apr 26, 2003 8:26 am
Location: Planet Earth

Re: [New tool] PureLibrary Explorer

Post by Danilo »

Keya wrote:But please dont confuse it, i just chose a screenshot with undocumented function as it seemed more interesting. :)
If you mean 'ReceiveHttpFile' and 'ReceiveHttpFile2', it's not undocumented. It's the same function within PB ('ReceiveHttpFile'),
and the library may contain more functions (2, 3, 4, ..). Those functions have to do with optional parameter/arguments, and
it was explained in the Library SDK that came with PB/Windows.

Simple example:

Code: Select all

Result = GadgetY(#Gadget [, Mode])
The ASM/C library contains 2 functions:

Code: Select all

int PB_GadgetY(int gadget)
int PB_GadgetY2(int gadget, int mode)
If you use the function with 1 argument, PB generates an ASM call to PB_GadgetY, and if you
use both arguments, PB_GadgetY2 is called.
User avatar
Keya
Addict
Addict
Posts: 1891
Joined: Thu Jun 04, 2015 7:10 am

Re: [New tool] PureLibrary Explorer

Post by Keya »

Danilo thanks for the clarification re the functions with '2', '3' etc after them, makes sense :) (also explains why the Parameter count for those excludes Optionals)
Post Reply