I've seen a few people asking about how to create resource files for their games etc.
Instead of answering all of them, I desided to make a tutorial on the subject.
The first thing you need to know when creating a file format (a resource file in this case) is what to put in it, and where to put it.
I've created a rather simple format which can store any sort of file in binary mode. The layout is made up of three sections: header, dir, and data.
The header contains information about the resource file itself. The dir part contains info about each seperate file stored in the resource file. The data section contains the data from all files stored.
Header:
ID
Version
FileCount
ID contains a string that is 4 bytes long. It identifies the file, so that you know what format you have loaded in your app.
Version contains the version of the format. In case you need to improve the format in a later program/game version you will need to make your program know what load function to run at different versions of the format.
FileCount stores the number of files that are loaded in the resource file.
Dir:
FileName
FileSize
Dir is an array that have one instance for each file.
FileName stores the name of each particular file (not the whole path though). It is locked at 64 characters in this case, so you cannot have longer filenames than that.
FileSize stores the exact size (in bytes) of each file.
Data:
Data
The data section only contains the information inside each file. All files are stacked upon each other.
This is how the file will look like. Now we can head on to the coding.
It is probably a good idea to start making UDTs (User Defined Types) for each section of the file. Based on the info above, they will look like this:
VB Code:
Private Type HeaderUDT
ID As String * 4 'the FourCC (holds a string to identify what filetype it is)
Version As Integer 'version number
FileCount As Long 'how many files are in the pack
End Type
Private Type DirUDT
FileName As String * 64 'name of each file. 64 characters long
FileSize As Long 'size of each file
End Type
Private Type DataUDT
Data() As Byte 'the files themselves
End Type
Now we also need a UDT for the file itself.
VB Code:
Private Type ResourceFileUDT
Header As HeaderUDT 'the header information for the resource file
Dir() As DirUDT 'array of file information
FileData() As DataUDT 'array of file data
End Type
Public ResourceFile As ResourceFileUDT 'everything is held in this valiable
I've made the file public, so any data can be reached from any module/class/form or whatever.
What's left now is to create functions that can load/add/export etc.
I will start of by explaining the NewFile function.
VB Code:
Public Sub NewFile()
'set the basic header information for a empty resource file
With ResourceFile
.Header.ID = "RESF" 'i chose this fourCC, but you can use whatever you want for your filetype
.Header.Version = 1 '1st version
.Header.FileCount = 0 'no files in it yet
ReDim .Dir(0) 'create empty array
ReDim .FileData(0)
End With
End Sub
This will put the information needed in the header and also reset the arrays.
The next function is AddFile. It will add an external file into the resource file.
VB Code:
Public Sub AddFile(FileName As String)
With ResourceFile
.Header.FileCount = .Header.FileCount + 1 'filecount is increased by one.
ReDim Preserve .Dir(.Header.FileCount - 1) 'extend the arrays to hold another element (minus one because we want it zero-based)
ReDim Preserve .FileData(.Header.FileCount - 1)
.Dir(.Header.FileCount - 1).FileName = FormatFileName(FileName) 'get the filename without the directory path
Open FileName For Binary As #1 'open the file for binary input
.Dir(.Header.FileCount - 1).FileSize = LOF(1) 'get the size of the file
ReDim .FileData(.Header.FileCount - 1).Data(LOF(1) - 1) 'redimention the data array (zero based)
Get 1, 1, .FileData(.Header.FileCount - 1).Data() 'read the data to the array
Close #1
End With
End Sub
First I add one to the FileCount value to tell that we've added a file.
The next thing I do is to resize the arrays to be able to hold the new file.
Then I call a function called FormatFileName. I will explain that function in a minute.
Now all that's left to do is to open the file, get its filesize using LOF, and store the data.
The FormatFileName function removes the path and only returns the name of the file. Like this:
VB Code:
Private Function FormatFileName(FileName As String) As String
Dim i As Long
'step through the filename backwards until we hit a '\' or '/'
For i = Len(FileName) To 1 Step -1
If Mid(FileName, i, 1) = "/" Or Mid(FileName, i, 1) = "\" Then Exit For
Next
'return the filename
FormatFileName = Mid(FileName, i + 1, Len(FileName) - i)
End Function
It searches for the last "/" or "\" and then gets the rest of the string.
Now that we can add file, we would also like to be able to export them.
VB Code:
Public Sub ExportFile(Index As Long, FileName As String)
'index should point to the element in the directory array to the file you want to export
'filename should be whatever you want to name the exported file
Open FileName For Binary As #1 'open the new file
Put #1, , ResourceFile.FileData(Index).Data 'write the data to it
Close #1
End Sub
Don't think this needs much explanation... It just puts the data into a file.
Now on to the really important things - Saving the resource file.
VB Code:
Public Function SaveFile(FileName As String) As Boolean
'save the entire resource file
'returns true on success
Dim i As Long
Open FileName For Binary As #1 'open the filename
With ResourceFile
'check to see that the values are correct, and that we dont try to save an empty file
If .Header.ID <> "RESF" Then GoTo SaveError
If .Header.Version <> 1 Then GoTo SaveError
If .Header.FileCount < 1 Then GoTo SaveError
Put #1, , .Header 'write data in correct order
Put #1, , .Dir
For i = 0 To .Header.FileCount - 1
Put #1, , .FileData(i).Data 'stack the data from each file
Next
End With
Close #1
SaveFile = True
Exit Function
SaveError:
SaveFile = False
End Function
It will start off by checking that all the header values are correct, then save the header and the dir.
The it loops though the array of file data and saves all files on top of each other.
Loading the resource file is a bit harded (but not that much)...
VB Code:
Public Function LoadFile(FileName As String) As Boolean
'load a resource file
'returns true on success
Dim i As Long
Dim j As Long
Open FileName For Binary As #1 'open the file
With ResourceFile
Get #1, , .Header 'read its header
'check to see that this is in fact the correct file type
If .Header.ID <> "RESF" Then GoTo LoadError
'check to see if the version is correct
If .Header.Version <> 1 Then GoTo LoadError
'there must be files within the resource file
If .Header.FileCount < 1 Then GoTo LoadError
'redim the arrays to the correct amount
ReDim .Dir(.Header.FileCount - 1)
ReDim .FileData(.Header.FileCount - 1)
For i = 0 To .Header.FileCount - 1
Get #1, , .Dir(i) 'get the directory information for each file
Next
For j = 0 To .Header.FileCount - 1
ReDim .FileData(j).Data(.Dir(j).FileSize - 1) 'redim the data array to correct size
Get #1, , .FileData(j).Data 'read data to array
Next
End With
Close #1
LoadFile = True
Exit Function
LoadError:
LoadFile = False
End Function
Start by getting the header.
Then we need to check that we've loaded a valid file.
Next thing is to set the sizes of the arrays to match what we have in the header.
Then load the dir data and the file data.
Since we load everything in the same order as we stored it, we don't need to keep track on the offset of each file and dir.
I've also attached a finished module file containing all of these functions.