As many games do, I would like to pack the assets of the demos in a ZIP file, instead of having thousands of files (scripts, models, images, etc…), which gives an impression of an unpolished product.
After looking on several available libraries (and considering developing my own library), I have decided to use PhysicsFS (github project here) As mentioned in it’s home page: PhysicsFS is a library to provide abstract access to various archives.
One of the mandatory requirements to meet was to access the demo assets “as always” in order to keep the compatibility with the demoeditor (meaning that the demoengine should be able to access directly to files, instead of opening them from a zipped file), so, I have been working on a FileManager class that allows you to decide how to access the files: using PhysicsFS or accessing directly the files.
I recommend you to check out this github project that I made to know how PhysicsFS works and how I implemented the FileManager class, in case you cannot wait, here you have the class 🙂
The next step is to include this file management in the demoengine, as soon as I do it, I’ll post the changes! (for sure this class needs to be changed after implementing it properly :P)
#pragma once
#include <string>
#include <string_view>
#include <vector>
#include <memory>
namespace Phoenix
{
// Class File
class File;
using SP_File = std::shared_ptr<File>;
class File final {
public:
std::string m_filePath; // Path used to avoid uploading the same file twice
char* m_fileData; // File data in memory
uint32_t m_fileSize; // File size in bytes
public:
File();
virtual ~File();
bool load(const std::string_view filePath, bool WorkingWithDataFolder, const std::string Password = "");
};
// Class File Manager
class FileManager final {
public:
FileManager();
~FileManager();
public:
void init(const char* argv0); // Init PhysFS
void deinit(); // Deinit PysFS and unmount any file
const std::string getVersion() { return m_physfsVersion; };
bool mountData(const std::string_view dataFilePath); // Mount a Data file or folder
void setPassword(const std::string_view password) { m_password = password; }; // Set a password for files
void clearPassword() { m_password = ""; };
void setCache(bool cacheEnabled) { m_fileCache = cacheEnabled; };
SP_File loadFile(const std::string_view filePath); // Load a file to memory
void clear(); // delete all files from memory
private:
bool m_workingWithDataFolder;
bool m_fileCache; // If cache is disabled, file data will be refreshed even if the file exists
std::string m_password;
std::string m_physfsVersion;
public:
std::vector<SP_File> m_files; // file list
uint32_t m_mem = 0; // Memory in bytes loaded to memory
};
}
#include "FileManager.h"
#include <cstring>
#include <filesystem>
#include <fstream>
#include <memory>
#include <physfs.h>
namespace Phoenix
{
FileManager::FileManager()
{
clear();
m_workingWithDataFolder = true;
m_fileCache = false;
}
FileManager::~FileManager()
{
deinit();
clear();
}
void FileManager::init(const char* argv0)
{
static const char* mainArgv0 = argv0;
deinit();
PHYSFS_init(mainArgv0);
// Update version
PHYSFS_Version compiled;
PHYSFS_VERSION(&compiled);
m_physfsVersion = std::to_string(compiled.major) + "." + std::to_string(compiled.minor) + "." + std::to_string(compiled.patch);
}
void FileManager::deinit()
{
if (PHYSFS_isInit() != 0)
{
PHYSFS_deinit();
}
}
bool FileManager::mountData(const std::string_view dataFilePath)
{
if (std::filesystem::is_directory(dataFilePath.data()))
{
m_workingWithDataFolder = true;
return true;
}
else {
if (0 == PHYSFS_mount((const char*)dataFilePath.data(), NULL, 0))
return false;
m_workingWithDataFolder = false;
}
return true;
}
SP_File FileManager::loadFile(const std::string_view filePath)
{
SP_File p_file;
for (auto& pFile : m_files) {
if (pFile->m_filePath.compare(filePath.data()) == 0)
p_file = pFile;
}
if (p_file == nullptr) { // If the file has not been found, we need to load it for the first time
SP_File new_file = std::make_shared<File>();
if (new_file->load(filePath, m_workingWithDataFolder, m_password)) {
m_files.push_back(new_file);
m_mem += new_file->m_fileSize;
p_file = new_file;
printf("File lodaded ok!: %s\n", new_file->m_filePath.c_str());
}
else {
printf("File not loaded: %s\n", filePath.data());
}
}
else { // If the file is in cache we should not do anything, unless we have been told to upload it again
if (!m_fileCache) {
m_mem -= p_file->m_fileSize; // Decrease the overall memory
if (p_file->load(filePath, m_workingWithDataFolder, m_password)) {
m_mem += p_file->m_fileSize;
printf("File force lodaded ok!: %s\n", p_file->m_filePath.c_str());
}
else {
printf("File not loaded: %s\n", filePath.data());
}
}
else {
printf("File already in memory: %s\n", filePath.data());
}
}
return p_file;
}
void FileManager::clear()
{
m_files.clear();
m_mem = 0;
}
File::File()
:
m_filePath(""),
m_fileData(nullptr),
m_fileSize(0)
{
}
File::~File()
{
if (m_fileData)
free(m_fileData);
m_fileData = nullptr;
}
bool File::load(const std::string_view filePath, bool WorkingWithDataFolder, const std::string Password)
{
// Init file data
if (m_fileData != nullptr)
free(m_fileData);
m_filePath = filePath;
m_fileSize = 0;
if (WorkingWithDataFolder) {
// Working with files
//
// Open the stream to 'lock' the file.
std::ifstream f(filePath.data(), std::ios::in | std::ios::binary);
if (f) {
m_fileSize = static_cast<uint32_t>(std::filesystem::file_size(filePath.data()));
// Allocate storage for the buffer
m_fileData = new char[m_fileSize + 1];
// Read the whole file into the buffer.
f.read(m_fileData, m_fileSize);
m_fileData[m_fileSize] = '\0'; // add the terminator
f.close();
return true;
}
else
return false;
}
else {
std::string filePathWithPassword = filePath.data();
if (!Password.empty())
filePathWithPassword += std::string("$") + Password;
PHYSFS_file* file = PHYSFS_openRead(filePathWithPassword.c_str());
if (file == NULL) {
return false;
}
m_fileSize = static_cast<int>(PHYSFS_fileLength(file));
m_fileData = new char[m_fileSize + 1];
PHYSFS_read(file, m_fileData, 1, static_cast<PHYSFS_uint32>(m_fileSize));
m_fileData[m_fileSize] = '\0'; // add the terminator
PHYSFS_close(file);
return true;
}
}
}