FileFormatID - OOP

Applications, Games, Tools, User libs and useful stuff coded in PureBasic
User avatar
StarBootics
Addict
Addict
Posts: 984
Joined: Sun Jul 07, 2013 11:35 am
Location: Canada

FileFormatID - OOP

Post by StarBootics »

Hello everyone,

Something I came up with to solve the binary file format identification in order to avoid some file reading issues.
The use of a Magic Number

And I have made a tiny lib just for that, may be little extreme but it do the job. Use it if it fit your needs.

See the source code for the example of use.

Best regards
StarBootics

Code: Select all

; <<<<<<<<<<<<<<<<<<<<<<<<<<<<<<<<<<<<<<<<<<<<<<<<<<<
; AUTOMATICALLY GENERATED CODE, DO NOT MODIFY
; UNLESS YOU REALLY, REALLY, REALLY MEAN IT !!
; <<<<<<<<<<<<<<<<<<<<<<<<<<<<<<<<<<<<<<<<<<<<<<<<<<<
; Code generated by : Dev-Object - V1.1.0
; Project name : FileFormatID
; File name : FileFormatID - OOP.pb
; File Version : 1.0.1
; Programmation : OK
; Programmed by : StarBootics
; Creation Date : October 24th, 2020
; Last update : October 29th, 2020
; Coded for PureBasic : V5.72
; Platform : Windows, Linux, MacOS X
; <<<<<<<<<<<<<<<<<<<<<<<<<<<<<<<<<<<<<<<<<<<<<<<<<<<

; <<<<<<<<<<<<<<<<<<<<<<<<<<<<<<<<<<<<<<<<<<<<<<<<<<<<<<<<
; Programming notes
;
; When we create binary file for some data (example a
; Bitmap Font Format) it could be necessary to make sure
; the file we are reading is the right file format. 
; 
; Otherwise we can try to read wrong data in the file and 
; our program may crash in some cases.
;
; Inspired by ID Software who write a Magic Number (Long 
; Value = 860898377) at the beging of the file to identify 
; the MD3 file format. By the way it's not the only 
; software developer doing that. Search the web for "File  
; Format Magic Number" or take a look at :
;
; https://en.wikipedia.org/wiki/File_format#Magic_number
;
; See the example at the end of this file to see how
; this library can be used.
; 
; <<<<<<<<<<<<<<<<<<<<<<<<<<<<<<<<<<<<<<<<<<<<<<<<<<<<<<<<

DeclareModule FileFormatID
  
  Interface FileFormatID
    
    GetMagicNumber.l()
    SetMagicNumber(Char00.a, Char01.a, Char02.a, Char03.a)
    Compare.b(*Other)
    ReadFileFormatID(FileID.i)
    WriteFileFormatID(FileID.i)
    Free()
    
  EndInterface
  
  Declare.i New(Char00.a = 0, Char01.a = 0, Char02.a = 0, Char03.a = 0)
  
EndDeclareModule

Module FileFormatID
  
  ; <<<<<<<<<<<<<<<<<<<<<<<<<<<<<<<<<
  ; <<<<< Structure declaration <<<<<

  Structure Private_Members
    
    VirtualTable.i
    MagicNumber.l
    
  EndStructure
  
  ; <<<<<<<<<<<<<<<<<<<<<<<<<<<
  ; <<<<< The observators <<<<<

  Procedure.l GetMagicNumber(*This.Private_Members)
    
    ProcedureReturn *This\MagicNumber
  EndProcedure
  
  ; <<<<<<<<<<<<<<<<<<<<<<<<
  ; <<<<< The mutators <<<<<

  Procedure SetMagicNumber(*This.Private_Members, Char00.a, Char01.a, Char02.a, Char03.a)
    
    *This\MagicNumber = Char03 << 24 + Char02 << 16 + Char01 << 8 + Char00
    
  EndProcedure
  
  ; <<<<<<<<<<<<<<<<<<<<<<<<<<<<<<<<
  ; <<<<< The Compare operator <<<<<

  Procedure.b Compare(*This.Private_Members, *Other.Private_Members)
    
    ProcedureReturn CompareMemory(*This, *Other, SizeOf(Private_Members))
  EndProcedure
  
  ; <<<<<<<<<<<<<<<<<<<<<<<<<<<<<<<
  ; <<<<< Read in Binary file <<<<<

  Procedure ReadFileFormatID(*This.Private_Members, FileID.i)
    
    *This\MagicNumber = ReadLong(FileID)
    
  EndProcedure
  
  ; <<<<<<<<<<<<<<<<<<<<<<<<<<<<<<<<
  ; <<<<< Write in Binary file <<<<<

  Procedure WriteFileFormatID(*This.Private_Members, FileID.i)
    
    WriteLong(FileID, *This\MagicNumber)
    
  EndProcedure
  
  ; <<<<<<<<<<<<<<<<<<<<<<<<<<
  ; <<<<< The Destructor <<<<<

  Procedure Free(*This.Private_Members)
    
    FreeStructure(*This)
    
  EndProcedure
  
  ; <<<<<<<<<<<<<<<<<<<<<<<<<<<
  ; <<<<< The Constructor <<<<<

  Procedure.i New(Char00.a = 0, Char01.a = 0, Char02.a = 0, Char03.a = 0)
    
    *This.Private_Members = AllocateStructure(Private_Members)
    *This\VirtualTable = ?START_METHODS
    
    SetMagicNumber(*This, Char00, Char01, Char02, Char03)
    
    ProcedureReturn *This
  EndProcedure
  
  ; <<<<<<<<<<<<<<<<<<<<<<<<<<<<<<<<<<<<<
  ; <<<<< The Virtual Table Entries <<<<<

  DataSection
    START_METHODS:
    Data.i @GetMagicNumber()
    Data.i @SetMagicNumber()
    Data.i @Compare()
    Data.i @ReadFileFormatID()
    Data.i @WriteFileFormatID()
    Data.i @Free()
    END_METHODS:
  EndDataSection
  
EndModule

; <<<<<<<<<<<<<<<<<<<<<<<<<<<<<<<<<<<<<<<<<<<<<<<<<<<<<<<<<<<<<<<<<<<<<<<
; <<<<< Code generated in : 00.001 seconds (129000.00 lines/second) <<<<<
; <<<<<<<<<<<<<<<<<<<<<<<<<<<<<<<<<<<<<<<<<<<<<<<<<<<<<<<<<<<<<<<<<<<<<<<

CompilerIf #PB_Compiler_IsMainFile
  
 Debug "The MD3 file format (ID Software)"
  
 IDP3.FileFormatID::FileFormatID = FileFormatID::New('I', 'D', 'P', '3') 
 Debug Str(IDP3\GetMagicNumber()) + " (Should be : 860898377)"
 IDP3\Free()
 
 Debug ""
 Debug "Bitmap Font File example"
 Debug ""
 
  #Bitmap_Font_Format_V0 = 809911874
  
  If CreateFile(0, "MyFont.bff")
    
    FFID00.FileFormatID::FileFormatID = FileFormatID::New('B', 'F', 'F', '0') 
    FFID00\WriteFileFormatID(0)
    
    ; Then we proceed with the rest of the stuff we need to write on the file
    
    CloseFile(0)
    FFID00\Free()
    
  EndIf
  
  If ReadFile(1, "MyFont.bff")
    
    ExpectedFFID.FileFormatID::FileFormatID = FileFormatID::New('B', 'F', 'F', '1')
    FromFileFFID.FileFormatID::FileFormatID = FileFormatID::New()
    
    FromFileFFID\ReadFileFormatID(1)
    
    If ExpectedFFID\Compare(FromFileFFID) = 1
      
      Debug "FileFormatID OK, We proceed with the rest of the stuff we need to read from the file"
      
    Else
      
      Debug "FileFormatID Not OK, Check for old version just in case"
      Debug ""
      
      Select FromFileFFID\GetMagicNumber()
          
        Case #Bitmap_Font_Format_V0
          Debug "FileFormatID correspond to Version 0"
          Debug "Pop a message about this or put the"
          Debug "read instructions for that version."
          
        Default
          Debug "Unknown file format !"
          
      EndSelect
      
    EndIf
    
    CloseFile(1)
    ExpectedFFID\Free()
    FromFileFFID\Free()
    
    ; The delete file is for the example only
    DeleteFile("MyFont.bff")
    
  EndIf
  
CompilerEndIf

; <<<<<<<<<<<<<<<<<<<<<<<
; <<<<< END OF FILE <<<<<
; <<<<<<<<<<<<<<<<<<<<<<<
Last edited by StarBootics on Thu Mar 25, 2021 10:48 pm, edited 2 times in total.
The Stone Age did not end due to a shortage of stones !
User avatar
mk-soft
Always Here
Always Here
Posts: 5335
Joined: Fri May 12, 2006 6:51 pm
Location: Germany

Re: FileFormatID - OOP

Post by mk-soft »

Hi,

you don't need ClearStructure. FreeStructure do this internal ...
:wink:
My Projects ThreadToGUI / OOP-BaseClass / EventDesigner V3
PB v3.30 / v5.75 - OS Mac Mini OSX 10.xx - VM Window Pro / Linux Ubuntu
Downloads on my Webspace / OneDrive
User avatar
mk-soft
Always Here
Always Here
Posts: 5335
Joined: Fri May 12, 2006 6:51 pm
Location: Germany

Re: FileFormatID - OOP

Post by mk-soft »

Why is it not necessary to specify a structure in FreeStructure?!

PB stores the pointer to the structure information. If it does not contain any data that needs to be solved additionally, the pointer is zero

Code: Select all

Structure sBase
  *vt
  refcnt.i
EndStructure

Structure sObject Extends sBase
  iVal.i
  List Text.s()
EndStructure

*mem = AllocateStructure(sObject)
*p_struct = PeekI(*mem - SizeOf(Integer))
If *p_struct
  Debug "Structure ..."
  Repeat
    info = PeekI(*p_struct)
    Debug info
    *p_struct + SizeOf(Integer)
  Until info = -1
EndIf
FreeStructure(*mem)
My Projects ThreadToGUI / OOP-BaseClass / EventDesigner V3
PB v3.30 / v5.75 - OS Mac Mini OSX 10.xx - VM Window Pro / Linux Ubuntu
Downloads on my Webspace / OneDrive
User avatar
StarBootics
Addict
Addict
Posts: 984
Joined: Sun Jul 07, 2013 11:35 am
Location: Canada

Re: FileFormatID - OOP

Post by StarBootics »

Hello mk-soft,

Run this code you will see the reason why II have put ClearStructure() inside the destructor.

Code: Select all

; <<<<<<<<<<<<<<<<<<<<<<<<<<<<<<<<<<<<<<<<<<<<<<<<<<<
; AUTOMATICALLY GENERATED CODE, DO NOT MODIFY
; UNLESS YOU REALLY, REALLY, REALLY MEAN IT !!
; <<<<<<<<<<<<<<<<<<<<<<<<<<<<<<<<<<<<<<<<<<<<<<<<<<<
; Code generated by : Dev-Object - V1.2.0
; Project name : Demonstration OOP Style with PB
; File name : MyObject Destruction.pb
; File Version : 1.0.0
; Programmation : Problem demontration
; Programmed by : StarBootics
; Creation Date : 29-10-2020
; Last update : 29-10-2020
; Coded for PureBasic : V5.73 beta 2
; Platform : Windows, Linux, MacOS X
; <<<<<<<<<<<<<<<<<<<<<<<<<<<<<<<<<<<<<<<<<<<<<<<<<<<

DeclareModule MyObject
  
  Interface MyObject
    
    GetMyField.s()
    SetMyField(P_MyField.s)
    Free()
    Free2()
    
  EndInterface
  
  Declare.i New(P_MyField.s = "")
  
EndDeclareModule

Module MyObject
  
  ; <<<<<<<<<<<<<<<<<<<<<<<<<<<<<<<<<
  ; <<<<< Structure declaration <<<<<

  Structure Private_Members
    
    VirtualTable.i
    MyField.s
    
  EndStructure
  
  ; <<<<<<<<<<<<<<<<<<<<<<<<<<<
  ; <<<<< The observators <<<<<

  Procedure.s GetMyField(*This.Private_Members)
    
    ProcedureReturn *This\MyField
  EndProcedure
  
  ; <<<<<<<<<<<<<<<<<<<<<<<<
  ; <<<<< The mutators <<<<<

  Procedure SetMyField(*This.Private_Members, P_MyField.s)
    
    *This\MyField = P_MyField
    
  EndProcedure
  
  ; <<<<<<<<<<<<<<<<<<<<<<<<<<
  ; <<<<< The Destructor <<<<<

  Procedure Free(*This.Private_Members)
    
    ; ClearStructure(*This, Private_Members)
    FreeStructure(*This)
    
  EndProcedure
  
  Procedure Free2(*This.Private_Members)
    
    ClearStructure(*This, Private_Members)
    FreeStructure(*This)
    
  EndProcedure
 
  ; <<<<<<<<<<<<<<<<<<<<<<<<<<<
  ; <<<<< The Constructor <<<<<

  Procedure.i New(P_MyField.s = "")
    
    *This.Private_Members = AllocateStructure(Private_Members)
    *This\VirtualTable = ?START_METHODS
    
    *This\MyField = P_MyField
    
    ProcedureReturn *This
  EndProcedure
  
  ; <<<<<<<<<<<<<<<<<<<<<<<<<<<<<<<<<<<<<
  ; <<<<< The Virtual Table Entries <<<<<

  DataSection
    START_METHODS:
    Data.i @GetMyField()
    Data.i @SetMyField()
    Data.i @Free()
    Data.i @Free2()
    END_METHODS:
  EndDataSection
  
EndModule

; <<<<<<<<<<<<<<<<<<<<<<<<<<<<<<<<<<<<<<<<<<<<<<<<<<<<<<<<<<<<<<<<<<<<<<
; <<<<< Code generated in : 00.001 seconds (97000.00 lines/second) <<<<<
; <<<<<<<<<<<<<<<<<<<<<<<<<<<<<<<<<<<<<<<<<<<<<<<<<<<<<<<<<<<<<<<<<<<<<<

MyObject00.MyObject::MyObject = MyObject::New("Tomato")
MyObject01.MyObject::MyObject = MyObject::New("Potato")

Debug MyObject00\GetMyField()

MyObject00\Free()

Debug MyObject00\GetMyField() ; Still working on some unknown memory


Debug MyObject01\GetMyField()

MyObject01\Free2()

Debug MyObject01\GetMyField() ; Here we have an invalid memory access
                              ; and it's normal and desirable to avoid
                              ; some hard to find bugs

; <<<<<<<<<<<<<<<<<<<<<<<
; <<<<< END OF FILE <<<<<
; <<<<<<<<<<<<<<<<<<<<<<<
So you are telling me the destructor should look like that :

Code: Select all

Procedure Free(*This.Private_Members)
  
  *This\VirtualTable = #Null
  FreeStructure(*This)
  
EndProcedure
instead of that :

Code: Select all

Procedure Free(*This.Private_Members)
  
  ClearStructure(*This, Private_Members)
  FreeStructure(*This)
  
EndProcedure
Is that what you are saying ?

Best regards
StarBootics
The Stone Age did not end due to a shortage of stones !
User avatar
mk-soft
Always Here
Always Here
Posts: 5335
Joined: Fri May 12, 2006 6:51 pm
Location: Germany

Re: FileFormatID - OOP

Post by mk-soft »

With the object method Free() the object is actually invalid and access to it is no longer allowed. Unfortunately, there is no control for this and the memory area could already be used for something else and thus the pointer to the VirtualTable could already be bent.

So it does not help to set the pointer to the VirtualTable to NULL or to call ClearStructure.

So with FreeStructure it is enough to delete the resources for strings, lists, maps, etc. in the structure and free the memory.

It is up to the programmer to ensure that a released object is no longer used.

The same applies to object oriented programming in other languages. Some of them have a memory mangment, which gives the error that the object no longer exists.
My Projects ThreadToGUI / OOP-BaseClass / EventDesigner V3
PB v3.30 / v5.75 - OS Mac Mini OSX 10.xx - VM Window Pro / Linux Ubuntu
Downloads on my Webspace / OneDrive
User avatar
StarBootics
Addict
Addict
Posts: 984
Joined: Sun Jul 07, 2013 11:35 am
Location: Canada

Re: FileFormatID - OOP

Post by StarBootics »

Hello mk-soft,

Thanks for the explanation, i will make modification inside of my Dev-Object code generator. That being said this discussion should have taken place in the Dev-Object topic.

Because it's way off topic for something related to FileFormatID stuff.

Best regards
StarBootics
The Stone Age did not end due to a shortage of stones !
Post Reply