From 9f6271f9ba8e4105e862849f01a77e1e3f3b6a3a Mon Sep 17 00:00:00 2001 From: illyum Date: Fri, 13 Sep 2024 07:43:19 -0600 Subject: [PATCH] feat(assetpacker): add asset packing --- CMakeLists.txt | 12 +-- src/filepacker.cpp | 193 +++++++++++++++++++++++++++++++++++++++++++++ src/filepacker.h | 55 +++++++++++++ 3 files changed, 252 insertions(+), 8 deletions(-) create mode 100644 src/filepacker.cpp create mode 100644 src/filepacker.h diff --git a/CMakeLists.txt b/CMakeLists.txt index 48f4aa2..c2d14c2 100644 --- a/CMakeLists.txt +++ b/CMakeLists.txt @@ -104,19 +104,15 @@ set(ENGINE_SOURCES src/enet_client.cpp src/enet_server.cpp - src/components/sprite_component.cpp src/Logger.cpp - src/Logger.h - src/components/transform_component.h + + src/components/sprite_component.cpp src/components/physics_component.cpp - src/components/physics_component.h src/components/input_component.cpp - src/components/input_component.h - src/components/health_component.h src/components/ui_component.cpp - src/components/ui_component.h + src/scene_manager.cpp - src/components/layer_component.h + src/filepacker.cpp ) add_library(IsoEngine ${ENGINE_SOURCES}) diff --git a/src/filepacker.cpp b/src/filepacker.cpp new file mode 100644 index 0000000..45ef59f --- /dev/null +++ b/src/filepacker.cpp @@ -0,0 +1,193 @@ +// +// Created by illyum on 9/13/2024. +// + +#include "filepacker.h" + +#include "Logger.h" + +bool FilePacker::Pack(const std::string &directoryPath, const std::string &outputBapFile) { + BapHeader header; + std::ofstream outputFile(outputBapFile, std::ios::binary); + + if (!outputFile) { + Logger::GetInstance().LogError("FilePacker::Pack(): Failed to open output file."); + return false; + } + + std::memcpy(header.magic, static_cast("BAP"), 3); + header.version = PACKER_VERSION; + header.fileCount = 0; + + std::vector>> filesData; + size_t headerSize = 3 + sizeof(header.version) + sizeof(header.fileCount); + + for (const auto& entry : std::filesystem::directory_iterator(directoryPath)) { + if (entry.is_regular_file()) { + FileEntry fileEntry; + fileEntry.filePath = entry.path().string(); + + // TODO: Implement filetype detection + fileEntry.fileType = RAWBINARY; + fileEntry.compressedSize = COMPRESSION_NONE; + + std::ifstream inputFile(entry.path(), std::ios::binary | std::ios::ate); + if (!inputFile) { + Logger::GetInstance().LogError("FilePacker::Pack(): Failed to open file for packing: " + entry.path().string()); + return false; + } + fileEntry.dataSize = inputFile.tellg(); + inputFile.seekg(0, std::ios::beg); + std::vector fileData(fileEntry.dataSize); + inputFile.read(fileData.data(), fileEntry.dataSize); + inputFile.close(); + + // TODO: Implement data compression + std::vector compressedData = CompressData(fileData, fileEntry.compression); + fileEntry.compressedSize = compressedData.size(); + + filesData.emplace_back(fileEntry, std::move(compressedData)); + size_t pathLength = fileEntry.filePath.size(); + size_t metadataLength = fileEntry.metadata.size(); + + headerSize += sizeof(pathLength) + pathLength + sizeof(fileEntry.fileType) + sizeof(fileEntry.compression) + + sizeof(fileEntry.dataSize) + sizeof(fileEntry.compressedSize) + sizeof(fileEntry.dataOffset) + + sizeof(metadataLength) + metadataLength; + + header.fileEntries.push_back(fileEntry); + header.fileCount++; + } + } + outputFile.seekp(headerSize, std::ios::beg); + for (size_t i = 0; i < filesData.size(); ++i) { + auto& fileEntry = header.fileEntries[i]; + const auto& compressedData = filesData[i].second; + fileEntry.dataOffset = outputFile.tellp(); + outputFile.write(compressedData.data(), compressedData.size()); + } + + outputFile.seekp(0, std::ios::beg); + WriteHeader(outputFile, header); + outputFile.close(); + return true; +} + +std::vector FilePacker::listFiles(const std::string& bapFile) { + std::ifstream inputFile(bapFile, std::ios::binary); + if (!inputFile) { + Logger::GetInstance().LogCritical("Failed to open .bap file for listing: " + bapFile); + throw std::runtime_error("File could not be opened"); + } + + BapHeader header = ReadHeader(inputFile); + inputFile.close(); + return header.fileEntries; +} + +bool FilePacker::ExtractFile(const std::string& bapFile, const std::string& fileName, const std::string& outputPath) { + std::ifstream inputFile(bapFile, std::ios::binary); + if (!inputFile) { + Logger::GetInstance().LogError("Failed to open .bap file for extraction: " + bapFile); + return false; + } + + BapHeader header = ReadHeader(inputFile); + for (const auto& fileEntry : header.fileEntries) { + if (fileEntry.filePath == fileName) { + std::ofstream outFile(outputPath, std::ios::binary); + if (!outFile) { + Logger::GetInstance().LogError("Failed to create output file: " + outputPath); + return false; + } + + std::vector compressedData(fileEntry.compressedSize); + inputFile.seekg(fileEntry.dataOffset, std::ios::beg); + inputFile.read(compressedData.data(), compressedData.size()); + std::vector decompressedData = DecompressData(compressedData, fileEntry.compression); + outFile.write(decompressedData.data(), decompressedData.size()); + outFile.close(); + return true; + } + } + + Logger::GetInstance().LogError("File not found in .bap archive: " + fileName); + return false; +} + +void FilePacker::WriteHeader(std::ofstream& outFile, const BapHeader& header) { + outFile.write(header.magic, 3); + outFile.write(reinterpret_cast(&header.version), sizeof(header.version)); + outFile.write(reinterpret_cast(&header.fileCount), sizeof(header.fileCount)); + + for (const auto& fileEntry : header.fileEntries) { + size_t pathLength = fileEntry.filePath.size(); + outFile.write(reinterpret_cast(&pathLength), sizeof(pathLength)); + outFile.write(fileEntry.filePath.c_str(), pathLength); + outFile.write(reinterpret_cast(&fileEntry.fileType), sizeof(fileEntry.fileType)); + outFile.write(reinterpret_cast(&fileEntry.compression), sizeof(fileEntry.compression)); + outFile.write(reinterpret_cast(&fileEntry.dataSize), sizeof(fileEntry.dataSize)); + outFile.write(reinterpret_cast(&fileEntry.compressedSize), sizeof(fileEntry.compressedSize)); + outFile.write(reinterpret_cast(&fileEntry.dataOffset), sizeof(fileEntry.dataOffset)); + + size_t metadataLength = fileEntry.metadata.size(); + outFile.write(reinterpret_cast(&metadataLength), sizeof(metadataLength)); + outFile.write(fileEntry.metadata.c_str(), metadataLength); + } +} + +BapHeader FilePacker::ReadHeader(std::ifstream& inFile) { + BapHeader header; + + inFile.read(header.magic, 3); + inFile.read(reinterpret_cast(&header.version), sizeof(header.version)); + inFile.read(reinterpret_cast(&header.fileCount), sizeof(header.fileCount)); + + if (header.version != PACKER_VERSION) { + Logger::GetInstance().LogCritical("FilePacker::ReadHeader(): Wrong .bap file version."); + throw std::runtime_error("Invalid bap file version"); + } + + for (int i = 0; i < header.fileCount; ++i) { + FileEntry fileEntry; + + size_t pathLength; + inFile.read(reinterpret_cast(&pathLength), sizeof(pathLength)); + + fileEntry.filePath.resize(pathLength); + inFile.read(fileEntry.filePath.data(), pathLength); + + inFile.read(reinterpret_cast(&fileEntry.fileType), sizeof(fileEntry.fileType)); + inFile.read(reinterpret_cast(&fileEntry.compression), sizeof(fileEntry.compression)); + inFile.read(reinterpret_cast(&fileEntry.dataSize), sizeof(fileEntry.dataSize)); + inFile.read(reinterpret_cast(&fileEntry.compressedSize), sizeof(fileEntry.compressedSize)); + inFile.read(reinterpret_cast(&fileEntry.dataOffset), sizeof(fileEntry.dataOffset)); + + size_t metadataLength; + inFile.read(reinterpret_cast(&metadataLength), sizeof(metadataLength)); + + fileEntry.metadata.resize(metadataLength); + inFile.read(fileEntry.metadata.data(), metadataLength); + + header.fileEntries.push_back(fileEntry); + } + + return header; +} + +std::vector FilePacker::CompressData(const std::vector& input, CompressionType compression) { + // IMPL: Implement actual compression on a per file basis + if (compression == COMPRESSION_NONE) { + return input; + } + + return input; +} + +std::vector FilePacker::DecompressData(const std::vector& input, CompressionType compression) { + // IMPL: Implement decompression on a per file basis + if (compression == COMPRESSION_NONE) { + return input; + } + + return input; +} \ No newline at end of file diff --git a/src/filepacker.h b/src/filepacker.h new file mode 100644 index 0000000..1f12379 --- /dev/null +++ b/src/filepacker.h @@ -0,0 +1,55 @@ +// +// Created by illyum on 9/13/2024. +// + +#ifndef FILEPACKER_H +#define FILEPACKER_H + +#include + +#define BAP_MAGIC "BAP" +#define PACKER_VERSION 1 + +enum CompressionType { + COMPRESSION_NONE, + COMPRESSION_LZMA +}; + +enum FileType { + PNG, + RAWTEXT, + RAWBINARY +}; + +struct FileEntry { + std::string filePath; + FileType fileType; + CompressionType compression; + size_t dataSize; + size_t compressedSize; + std::string metadata; + size_t dataOffset; +}; + +struct BapHeader { + char magic[3]; + int version; + int fileCount; + std::vector fileEntries; +}; + +class FilePacker { +public: + bool Pack(const std::string& directoryPath, const std::string& outputBapFile); + bool Unpack(const std::string& bapFile, const std::string& outputDirectory); + std::vector listFiles(const std::string& bapFile); + bool ExtractFile(const std::string& bapFile, const std::string& fileName, const std::string& outputPath); + +private: + void WriteHeader(std::ofstream& outFile, const BapHeader& header); + BapHeader ReadHeader(std::ifstream& inFile); + std::vector CompressData(const std::vector& input, CompressionType compression); + std::vector DecompressData(const std::vector& input, CompressionType compression); +}; + +#endif //FILEPACKER_H