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.
This will put the information needed in the header and also reset the arrays.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
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:
It searches for the last "/" or "\" and then gets the rest of the string.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
Now that we can add file, we would also like to be able to export them.
Don't think this needs much explanation... It just puts the data into a file.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
Now on to the really important things - Saving the resource file.
It will start off by checking that all the header values are correct, then save the header and the dir.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
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)...
Start by getting the header.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
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.


Reply With Quote

