User Tools

Site Tools


proton:filemanager

File handling

First, you can use your good old C fwrite/fread or streams to your heart's content.

But there is a reason why you shouldn't - the Android file system sort of requires you to load your stuff from a giant zip, and using the FileManager instead hides this ugliness from you.

Loading a file into memory

Load any kind of file into memory like this:

FileInstance myFile("test.txt"); //a small text file of 5 lines or so
 
LogMsg("I just loaded a file into memory that is %d bytes.", myFile.GetSize());
 
LogMsg(myFile.GetAsChars());

That list thing where I display the text file to the output window is only possible because of two things:

  • I know it's a text file
  • I know the FileManager secretly adds a null at the end when allocating memory for the file

When the FileInstance object is destroyed as it goes out of scope, it deletes the associated memory it had loaded the file into.

Base directory vs the user data directory

Internally, FileInstance and functions like FileExists() are using the FileManager.

The FileManager does the following for you behind the scenes: * Automatically decompresses files that have been compressed with rtpack (file extension doesn't matter, it checks for a header) * Automatically decompresses from a mounted .zip file when appropriate (the asset dir of the .apk in Android is automatically mounted for you at init) * Adds the base path of your app data, so “interface/some.rtfont” will work on all platforms (it prepends GetBaseAppPath() for you if needed)

There are times when you DON'T want it to add the base path though, for instance, when saving user data, you should use GetSavePath().

Here is how we would have loaded the above from the user data directory:

FileInstance myFile(GetSavePath()+"test.txt", false); 

That false we send for the bool bAddBasePath parm says “don't prepend anything to the path, I know what I'm doing”. It's available when using most file operations.

Using FileManager directly

Instead of using FileInstance, you could just do:

int fileSizeBytes;
byte *pData = GetFileManager()->Get(GetSavePath()+"test.txt", &fileSizeBytes, true);
 
//operate on data
 
//we have to release the data ourselves or hello memory leak
SAFE_DELETE_ARRAY(pData);

How to stream a file in

To insure a hassle free Android compile you should use FileManager::GetStreaming instead of raw fopen/fread type stuff. It returns a StreamingInstance that operates sort of like a FileInstance but actually smartly loads the file as you go, which may even be from a .zip file.

Why would you want to do this and not just load it all in one chunk?

Dungeon Scroll's word dictionary is over one meg in size. Streaming it means I can load 10KB at a time and update a progress bar each frame.

Example:

int size;
m_pStreamingInstance = GetFileManager()->GetStreaming("Some file", &size, false); //m_pStreamingInstance will be NULL on error
 
//we now have a streaming instance, but we haven't actually loaded any data yet.  We'll load directory into our own buffer.
char st_buffer[4096];
 
while (m_pStreamingInstance->ReadLineOfText(st_buffer, 4096))
{
   LogMsg(st_buffer);
}
 
SAFE_DELETE(m_pStreamingInstance);

Notice:

  • We used StreamingInstance::ReadLineOfText but we could have used StreamingInstance::Read instead for pure data. (It's a lot like fread)
  • It internally cached the file for speed, for instance, if it's actually in a compressed zip and you are asking for one byte at a time it will really be decoding in 4 KB chunks behind the scenes

Mounting a .zip as a filesystem

This happens automatically when you are doing an Android target, so you can load resources by filename and not worry about where they actually are. (Android wants to keep them in your .apk you used to install)

But you may want to mount your own zips for some reason. Let's examine how it's done:

FileSystemZip *pFileSystem = new FileSystemZip();
pFileSystem->Init("somezip.file"));  //I'm not doing error handling in these examples, but it would return false on error
 
//to print out the entire contents of the zip:
 
vector<string> contents = pFileSystem->GetContents();
for (int i=0; i < contents.size(); i++)
{
   LogMsg("%s", contents[i].c_str());
}
 
//before mounting, we can optionally mount only a subdir of the zip by doing this:
pFileSystem->SetRootDirectory("assets"); //mount the assets directory only, the user won't have to have "assets" in  the filename to find things in it
 
//to actually mount it
GetFileManager()->MountFileSystem(pFileSystem);
 
//test that it worked:
 
FileInstance myFile("textFileInsideAssetsDirOfTheZip.txt");
 
if (myFile.IsLoaded()) LogMsg("Marty, it worked!");

What about writing user data files?

I just use fwrite and fread and haven't have any trouble with porting so far. As long as you use GetSavePath() you're ok as far as file permissions go.

proton/filemanager.txt · Last modified: 2012/03/09 01:05 by seth