Menu

Virtual File System – Part 4

Alright! Starting from here on, we’ll now be covering the FileSystem’s public and private methods which will require a rather lengthy explanation. But as i said before, don’t worry because most of the code are trivial thanks to how we abstracted out most of the work through delegation(private utility methods) and composition(internal data structures).

FileSystem::FileSystem( void )

: m_pHeader( NULL ), m_pGenDirs( NULL ), m_pGenFiles( NULL ),

m_pGenFileList( NULL ), m_pFileMap( NULL ), m_pPulseFile( NULL ),

m_bLoaded( FALSE )

{

}

FileSystem::~FileSystem( void )

{

ReleaseResources();

}

The FileSystem’s constructor simply sets the default value to NULL or FALSE as their default value while the destructor calls the ReleaseResources() method to cleanup and release the allocated memory.

FileSystem method – Create()

Next, we’ll take a look at the Create() method. By looking at this first, we’ll have a better understanding on how our internal PAK file structure will be organized.

First we check if the directory path specified by the user is valid. Then before we start allocating memory, make sure we don’t mess anything up by releasing already allocated resources by calling ReleaseResources(). If we don’t call ReleaseResources() here and the user calls Open() then create, we would leak memory and that is very very bad. Next we initialize our filters specified in fBitFlag so that we can use them later when we individually encode our files. The next few lines of code follows the allocation and setting up the necessary data members for creating our PAK file like m_pHeader, m_pGenDirs, m_pGenFiles. After we have allocated the necessary resources, we set up our header information. We set the header to have the right signature, ID, version, and the filters used values.

Next. You have to take note that all the files processed and packed in the PAK file will have all their absolute paths invalidated. What is important is their relative path instead. The file’s path should have the path relative to pDirectory as their root path. Let us say that pDirectory is “c:\music\” and we are currently in “c\music\michael jackson\”. Then pPAKPath is “music\michael jackson\”.

Generate table entries by calling _GeneratetableEntries then creating the PAK file by calling _CreatePulseFile(). If one of these functions fail, it sets bReturn to false then immediately jumps to EndcreatePAK: where it makes sure we don’t leak any memory before we pop out of this function.

We’ll take a look at _GenerateTableEntries() then _CreatePulseFile() next.

FileSystem method – _GenerateTableEntries()

_GenerateTableEntries() is the crème of the crop of our algorithm. This method is the one responsible for recursively going through each directory and files generating directory and file entries for our _CreatePulseFile() method to use.

DirPathPointer fileDirPath; // File entries needs this when we’re about to parse each file later

BOOL bAddSeparator = TRUE;

BOOL bReturn = TRUE;

fileDirPath = new DirPath;

*fileDirPath = pFolderPath;

DirEntryPointer pNewDir : pNewDir will be used to hold our generated Directory Entry.

WIN32_FIND_DATA nextFile : This is a win32 specific data structure that holds information about the found file.

HANDLE hFind : This find handle is used by windows to keep track of what file or directory we’re currently traversing.

DirList tempFolderPaths : tempFolderPaths may look confusing (which it is) but this is basically just a string contained in a smartpointer that is contained in a list. This will be used later on as a temporary storage for every directory that we find.

DirPathPointer fileDirPath : temporary string buffer stored in a SmartPointer<> used to hold pFolderPath. We use SmartPointer so that there will only be one copy of it per directory. And we also don’t have to worry about deleting it later.

BOOL bAddSeperator : pFolder path could end with a separator ( ‘\’ for windows and ‘/’ for linux ). This makes sure that we don’t double appened our separator for generating our paths.

BOOL bReturn : Is simple used as a return value to indicate if successful(true) or not(false).

We first check the passed in pFolder path if it ends with a separator or not. This could happen if, for example, the user could pass in a root drive path like “c:\”. In that case, we don’t need to add another separator because it would look like “c:\\” which is not valid. Remember that this function is responsible for generating the directory and file tables. Since we have pPAKPath wich is the path relative to pDirectory(Create()) we need to store it into the list (m_pGenDirs).

Next we now prepare for finding each file and directory in pFolderPath. We do this by first making sure that the pFolderPath string has an * appended at the end of the string indicating we’re searching for all items in the current directory path. To begin searching, we’ll be using the help of the Win32 API function called FindFirstFile() and FindNextFile() functions. We only need to call FindFirstFile() once to let windows know we’re starting from the start. Then we enter into a loop calling FindNextFile() until it returns 0 indicating we’ve finished searching(or a fatal error has occurred). Inside the loop, it simply checks if the found object meets the attributes. It can’t be itself(.), a previous directory(..), a system file, or hidden. If it passes, then we’re sure that it could either be a directory or a file. If it is a, directory we simply add it into a temporary list for processing later. If it is a file then we generate a new file entry and insert it into the list(m_pGenFiles).

// Prepare string path to allow for file searching

dirBuff = String(pFolderPath) + PSX_String("\\*");

PSX_ZeroMem( &nextFile, sizeof( WIN32_FIND_DATA ) );

hFind = FindFirstFile( dirBuff.GetCString(), &nextFile );

if ( hFind == INVALID_HANDLE_VALUE )

return FALSE;

do

{

// Don’t include the following attributes and directories

if ( PSX_StrCmp( nextFile.cFileName, PSX_String(".") ) == 0 ||

PSX_StrCmp( nextFile.cFileName, PSX_String("..") ) == 0 ||

(nextFile.dwFileAttributes & FILE_ATTRIBUTE_SYSTEM) ||

(nextFile.dwFileAttributes & FILE_ATTRIBUTE_HIDDEN) )

continue;

// If a directory…

if ( nextFile.dwFileAttributes & FILE_ATTRIBUTE_DIRECTORY )

{

// Store Folder name and traverse them later

DirPointer newFolder = new Directory;

*newFolder = nextFile.cFileName;

tempFolderPaths.PushBack( newFolder );

}

else// If a file…

{

// New File entry

FileEntryPointer pNewFileEntry = new DirFileEntry;

DirPathPointer pnewFileDirPath = new DirPath;

// Copy File entry info

pNewFileEntry->m_PAKData.m_size = nextFile.nFileSizeLow;

pNewFileEntry->m_PAKData.m_size += nextFile.nFileSizeHigh; // Zero if not greater than DWORD

Looking for files and directories inside the specified directory path.

The last remaining code simply traverses through tempFolderPaths list then calls itself(_GenerateTableEntries()), with proper path formatting, until it reaches the last item in the tempFolderPaths list. After its done traversing though the list, it simply clears out the allocated memory for tempFolderPaths and returns the value of bReturn.