Some PureBasic instructions, for example those from the Cipher library and many operating system API calls, require a pointer to a memory buffer as an argument rather than the data directly itself. PureBasic provides a number of instructions to manipulate memory buffers to facilitate this.
This example uses a buffer to read a file from disk into memory. It then converts the buffer content into a hexadecimal and text display in a List Icon Gadget as a simple hex viewer application.
An Explorer List Gadget is used to display the contents of the user's home directory, initially, and to allow selection of a file. Two buttons are provided, one to display a file and another to clear the display.
The List Icon Gadget is divided into nine columns, the first shows the base offset of each line in the list, the next show eight byte values offset from the base value and the ninth shows the string equivalent of these eight values.
Two pointers are used - the first (*Buffer) contains the memory address of the complete file. The second (*Byte), in the procedure "FileDisplay", demonstrates the use of pointer arithmetic and the "Peek" instruction to obtain individual values from within the buffer.
Finally, the "FileClose" procedure demonstrates the use of the "FillMemory" instruction to overwrite the buffer's contents and "FreeMemory" to de-allocate the memory buffer.
Code: Select all
;- Compiler Directives
EnableExplicit
;- Constants
; Window
Enumeration
#wdwHex
EndEnumeration
; Gadgets
Enumeration
#xlsFiles
#btnOpen
#btnClose
#lsiHex
EndEnumeration
;- Variables
Define.L Event, EventWindow, EventGadget, EventType, EventMenu
Define.L lngLength
Define.S strFile
Define *Buffer
;- Declarations
Declare WindowCreate()
Declare WindowResize()
Declare FileClose()
Declare FileDisplay()
Declare FileRead()
;- Implementation
Procedure WindowCreate()
; Create the window.
Protected.L lngCol
Protected.S strLabel
If OpenWindow(#wdwHex, 50, 50, 440, 400, "Hex View", #PB_Window_SystemMenu | #PB_Window_SizeGadget | #PB_Window_MinimizeGadget | #PB_Window_TitleBar)
; Set minimum window size.
WindowBounds(#wdwHex, 175, 175, #PB_Ignore, #PB_Ignore)
; Create Explorer List and set to user's home directory.
ExplorerListGadget(#xlsFiles, 5, 5, 430, 175, GetHomeDirectory())
; Buttons.
ButtonGadget(#btnOpen, 5, 185, 80, 25, "Open")
ButtonGadget(#btnClose, 90, 185, 80, 25, "Close")
; List Icon Gadget.
ListIconGadget(#lsiHex, 5, 215, 430, 180, "Offset", 80, #PB_ListIcon_AlwaysShowSelection | #PB_ListIcon_GridLines | #PB_ListIcon_FullRowSelect)
; Column Headings.
For lngCol = 0 To 7
strLabel = RSet(Hex(lngCol, #PB_Byte), 2, "0")
AddGadgetColumn(#lsiHex, lngCol + 1, strLabel, 30)
Next lngCol
AddGadgetColumn(#lsiHex, 9, "Text", 80)
EndIf
EndProcedure
Procedure WindowResize()
; Resize gadgets to new window size.
Protected.L lngX, lngY, lngW, lngH
; Explorer List
lngW = WindowWidth(#wdwHex) - 10
lngH = (WindowHeight(#wdwHex) - 35) / 2
ResizeGadget(#xlsFiles, #PB_Ignore, #PB_Ignore, lngW, lngH)
; Buttons
lngY = GadgetHeight(#xlsFiles) + 10
ResizeGadget(#btnOpen, #PB_Ignore, lngY, #PB_Ignore, #PB_Ignore)
ResizeGadget(#btnClose, #PB_Ignore, lngY, #PB_Ignore, #PB_Ignore)
; List Icon View
lngY = (WindowHeight(#wdwHex) / 2) + 23
lngW = WindowWidth(#wdwHex) - 10
lngH = WindowHeight(#wdwHex) - (lngY + 5)
ResizeGadget(#lsiHex, #PB_Ignore, lngY, lngW, lngH)
EndProcedure
Procedure FileClose()
; Clear the list view and release the memory buffer.
Shared lngLength, *Buffer
ClearGadgetItems(#lsiHex)
FillMemory(*Buffer, lngLength)
FreeMemory(*Buffer)
EndProcedure
Procedure FileDisplay()
; Display the file buffer in the list view.
Shared lngLength, *Buffer
Protected *Byte
Protected bytPeek
Protected.L lngRows, lngCols, lngOffset
Protected.S strOffset, strRow, strString
; Clear current contents.
ClearGadgetItems(#lsiHex)
; Loop through rows.
For lngRows = 0 To lngLength - 1 Step 8
; Clear the text value for each row.
strString = ""
; Convert the offset value to a fixed length string.
strRow = RSet(Hex(lngRows, #PB_Long), 6, "0") + Chr(10)
; Loop through columns.
For lngCols = 0 To 7
; Calculate the offset for the current column.
lngOffset = lngRows + lngCols
; Compare the offset with the file length.
If lngOffset < lngLength
; The offset is less than the length of the file.
; Obtain the byte from the buffer.
*Byte = *Buffer + lngOffset
bytPeek = PeekB(*Byte)
; Convert the byte to text.
strRow + RSet(Hex(bytPeek, #PB_Byte), 2, "0") + Chr(10)
; Add the character to the text version.
Select bytPeek
Case 0 To 31, 127
; Unprintable characters.
strString + Chr(129)
Default
; Printable characters.
strString + Chr(bytPeek)
EndSelect
Else
; The offset is greater than the length of the file.
; Add an empty column.
strRow + Chr(10)
EndIf
Next lngCols
; Add the text version at the end of the hex columns.
strRow + strString
; Add the completed row to the list view.
AddGadgetItem(#lsiHex, -1, strRow)
Next lngRows
EndProcedure
Procedure FileRead()
; Read the file into the memory buffer.
Shared lngLength, strFile, *Buffer
Protected.B bytRead
Protected.L lngFile, lngRead, lngSize
; Stop if file is empty.
If strFile = ""
ProcedureReturn
EndIf
; Stop if file size is invalid.
lngSize = FileSize(strFile)
If lngSize < 1
ProcedureReturn
EndIf
; Open the file.
lngFile = OpenFile(#PB_Any, strFile)
lngLength = Lof(lngFile)
If lngFile And lngLength
; Allocate a memory buffer to hold the file.
*Buffer = AllocateMemory(lngLength)
; Read the file into the buffer.
lngLength = ReadData(lngFile, *Buffer, lngLength)
EndIf
; Close the file.
CloseFile(lngFile)
EndProcedure
;- Main
WindowCreate()
;- Event Loop
Repeat
; Obtain event parameters.
Event = WaitWindowEvent()
EventGadget = EventGadget()
EventType = EventType()
EventWindow = EventWindow()
; Handle events.
Select Event
Case #PB_Event_Gadget
If EventGadget = #xlsFiles
; Do nothing.
ElseIf EventGadget = #btnOpen
strFile = GetGadgetText(#xlsFiles) + GetGadgetItemText(#xlsFiles, GetGadgetState(#xlsFiles))
If FileSize(strFile) > 0
FileRead()
FileDisplay()
EndIf
ElseIf EventGadget = #btnClose
FileClose()
ElseIf EventGadget = #lsiHex
; Do nothing.
EndIf
Case #PB_Event_CloseWindow
If EventWindow = #wdwHex
CloseWindow(#wdwHex)
Break
EndIf
Case #PB_Event_SizeWindow
WindowResize()
EndSelect
ForEver