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; } } }