#ifndef ZIP_LOADER_H #define ZIP_LOADER_H /*--------------------------------------------------------------------------*/ #include <SFML/System/NonCopyable.hpp> #include <string> /*--------------------------------------------------------------------------*/ // Loads files from zip - or in directory if it exists // namespace Zip { // Storage class for file loaded from zip class File : public sf::NonCopyable { public: // Creates empty file File(); // Construct file from filename, loads file File(const std::string &FileName); // Destructor ~File(); // Load file void Load(const std::string &FileName); // Close file, free allocated memory void Close(void); // Return pointer to data operator const char *() const { return myData; } // Return size unsigned int Size(void) const { return mySize; } private: // File data pointer and size char *myData; unsigned int mySize; }; }; /*--------------------------------------------------------------------------*/ #endif // ZIP_LOADER_H
#include "ZipLoader.h" #include <SFML/System.hpp> #include <iostream> #include <unzip.h> /*--------------------------------------------------------------------------*/ // Class statics namespace Zip { // Define data file const char *DataFile="Data.zip"; // Define load mutex sf::Mutex LoadMutex; // Declare loading functions const std::string AbsolutePath(const std::string &Name); char *LoadNormalFile(const std::string &Filename, unsigned int &Size); char *LoadZipFile(const std::string &Filename, unsigned int &Size); char *Load(const std::string &Filename, unsigned int &Size); } /*--------------------------------------------------------------------------*/ // Parses path to absolute value // const std::string Zip::AbsolutePath(const std::string &Name) { // All \:s need to be changed to / std::string Filename(Name); std::string::size_type Pos=0; while ((Pos=Filename.find('\\',Pos)) != std::string::npos) Filename[Pos]='/'; // Check if directory has "..", and remove it while ((Pos=Filename.find("/..")) != std::string::npos) { // Modify path to get absolute path std::string::size_type Start=Filename.rfind('/',Pos-1); if (Start!=std::string::npos) // Remove previous path and .. Filename=Filename.substr(0,Start)+Filename.substr(Pos+3); } // Return generated filename return Filename; } /*--------------------------------------------------------------------------*/ // Loads normal file // char *Zip::LoadNormalFile(const std::string &Filename, unsigned int &Size) { // Try to open the file FILE *fp=fopen(Filename.c_str(),"rb"); if (fp==NULL) return NULL; // Resolve file size fseek(fp,0,SEEK_END); Size=ftell(fp); fseek(fp,0,SEEK_SET); // Allocate memory and read file char *Buffer=new char[Size+1]; fread(Buffer,1,Size,fp); Buffer[Size]=0; // Add terminating zero fclose(fp); // Return data return Buffer; } /*--------------------------------------------------------------------------*/ // Loads file from zip // char *Zip::LoadZipFile(const std::string &Filename, unsigned int &Size) { // Open zip file unzFile ZipHandle=unzOpen(DataFile); if (ZipHandle==NULL) return NULL; // Can't open zip archive // Create absolute path std::string Path=AbsolutePath(Filename); // Find file int Ok=unzLocateFile(ZipHandle,Path.c_str(),false); if (Ok!=UNZ_OK) return NULL; // File not found // Try to open it Ok=unzOpenCurrentFile(ZipHandle); if (Ok!=UNZ_OK) return NULL; // Can't open requested file from zip // Resolve file size unz_file_info FileInfo; unzGetCurrentFileInfo(ZipHandle, &FileInfo, NULL,0, NULL,0, NULL,0); Size=FileInfo.uncompressed_size; // Allocate memory and read the file char *Buffer=new char[Size+1]; unzReadCurrentFile(ZipHandle,Buffer,Size); Buffer[Size]=0; // Close file handles unzCloseCurrentFile(ZipHandle); unzClose(ZipHandle); // Return data return Buffer; } /*--------------------------------------------------------------------------*/ // Reads file to memory // char *Zip::Load(const std::string &Filename, unsigned int &Size) { // Grab mutex sf::Lock Lock(LoadMutex); // Try to load normal file char *Data=LoadNormalFile(Filename,Size); // If that failed, load file from zip if (!Data) Data=LoadZipFile(Filename,Size); // If even that failed, then we can't load the file if (!Data) { // You could for example throw an error here, now just returning NULL std::cout << "Can't open file: " << Filename << std::endl; return NULL; } // Return loaded file return Data; } /*--------------------------------------------------------------------------*/ // Storage class for file data /*--------------------------------------------------------------------------*/ /*--------------------------------------------------------------------------*/ // Constructors // // Creates empty file Zip::File::File() : myData(NULL), mySize(0) { // Nothing here } // Construct from filename and load requested Zip::File::File(const std::string &Filename) : myData(NULL), mySize(0) { // Load file Load(Filename); } /*--------------------------------------------------------------------------*/ // Destructor // Zip::File::~File() { // Just close the file Close(); } /*--------------------------------------------------------------------------*/ // Load file // void Zip::File::Load(const std::string &Filename) { // Close old file Close(); // Load file myData=Zip::Load(Filename,mySize); } /*--------------------------------------------------------------------------*/ // Close file, free allocated memory // void Zip::File::Close(void) { // Free old memory delete[] myData; mySize=0; }
Usage for images and fonts is straightforward:
void LoadData(void) { // Load image file Zip::File File("Data/test.png"); if (!Image.LoadFromMemory(File,File.Size())) return EXIT_FAILURE; // Load font File.Load("Data/test.ttf"); if (!Font.LoadFromMemory(File,File.Size())) return EXIT_FAILURE; }
Loading music has a catch. When you load music from memory, it means that music will be played from that memory. Therefore that data must exist while music is playing, so Zip::File containing that data must not go out of scope.
void main(void) { // Load music sf::Music Music; Zip::File File("Data/test.ogg"); if (!Music.OpenFromMemory(File,File.Size())) return EXIT_FAILURE; // Play the music Music.Play(); ... do something ... // Stop music before Zip::File will get deleted Music.Stop(); }
You can load full sources with example compiled for Windows from here: ZipLoader