linux-wallpaperengine/src/WallpaperEngine/Assets/CPackage.cpp
Alexis Maiquez b8fd1eefac chore: clang-format and code formatting done with an extra of code cleanup
Signed-off-by: Alexis Maiquez <almamu@almamu.com>
2023-12-14 02:20:34 +01:00

142 lines
4.1 KiB
C++

#include "CPackage.h"
#include "CAssetLoadException.h"
#include "CPackageLoadException.h"
#include "common.h"
#include <sstream>
#include <utility>
using namespace WallpaperEngine::Assets;
class CPackageEntry {
public:
CPackageEntry (std::string filename, uint32_t offset, uint32_t length) :
filename (std::move (filename)),
offset (offset),
length (length) {}
std::string filename;
uint32_t offset;
uint32_t length;
};
CPackage::CPackage (std::filesystem::path path) : m_path (std::move (path)) {
this->init ();
}
const void* CPackage::readFile (const std::string& filename, uint32_t* length) const {
const auto it = this->m_contents.find (filename);
if (it == this->m_contents.end ())
throw CAssetLoadException (filename, "Cannot find the file in the package");
// set file length if required
if (length != nullptr)
*length = it->second->length;
// clone original first
auto* result = new char [it->second->length];
memcpy (result, it->second->address, it->second->length);
return result;
}
void CPackage::init () {
FILE* fp = fopen (this->m_path.c_str (), "rb+");
if (fp == nullptr)
throw CPackageLoadException (this->m_path, std::to_string (errno));
// first validate header
this->validateHeader (fp);
// header is okay, load everything into memory
this->loadFiles (fp);
fclose (fp);
}
char* CPackage::readSizedString (FILE* fp) {
unsigned int length = 0;
if (fread (&length, sizeof (unsigned int), 1, fp) != 1)
sLog.exception ("Cannot read sized string length on file ", this->m_path);
// account for 0 termination of the string
length++;
char* pointer = new char [length];
memset (pointer, 0, length);
// read only the string bytes so the last one in the memory is 0
length--;
// read data from file
if (fread (pointer, sizeof (char), length, fp) != length)
sLog.exception ("Not enough bytes to read string of length ", length, " on file ", this->m_path);
return pointer;
}
uint32_t CPackage::readInteger (FILE* fp) {
uint32_t output;
if (fread (&output, sizeof (uint32_t), 1, fp) != 1)
sLog.exception ("Not enough bytes to read an integer from file ", this->m_path);
return output;
}
void CPackage::validateHeader (FILE* fp) {
const char* pointer = this->readSizedString (fp);
if (strncmp ("PKGV", pointer, 4) != 0) {
std::stringstream msg;
msg << "Expected PKGV indicator, found " << pointer;
delete [] pointer;
throw std::runtime_error (msg.str ());
}
// free memory
delete [] pointer;
}
void CPackage::loadFiles (FILE* fp) {
const uint32_t count = this->readInteger (fp);
std::vector<CPackageEntry> list;
for (uint32_t index = 0; index < count; index++) {
// first read the filename
char* filename = this->readSizedString (fp);
uint32_t offset = this->readInteger (fp);
uint32_t length = this->readInteger (fp);
// add the file to the list
list.emplace_back (filename, offset, length);
// only free filename, the file's contents are stored in a map for a later use
delete [] filename;
}
// get current baseOffset, this is where the files start
const long baseOffset = ftell (fp);
for (const auto& cur : list) {
const long offset = cur.offset + baseOffset;
// with all the data we can jump to the offset and read the content
if (fseek (fp, offset, SEEK_SET) != 0)
sLog.exception ("Cannot find file ", cur.filename, " from package ", this->m_path);
// allocate memory for the file's contents and read it from the file
char* fileContents = new char [cur.length];
if (fread (fileContents, cur.length, 1, fp) != 1) {
delete [] fileContents;
sLog.exception ("Cannot read file ", cur.filename, " contents from package ", this->m_path);
}
// add the file to the map
this->m_contents.insert_or_assign (cur.filename, new CFileEntry (fileContents, cur.length));
}
}