IsoEngine/src/filepacker.cpp

193 lines
7.7 KiB
C++

//
// 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) {
CORE_LOG_ERROR("Failed to open output file");
return false;
}
std::memcpy(header.magic, static_cast<const char*>("BAP"), 3);
header.version = PACKER_VERSION;
header.fileCount = 0;
std::vector<std::pair<FileEntry, std::vector<char>>> 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) {
CORE_LOG_ERROR("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<char> fileData(fileEntry.dataSize);
inputFile.read(fileData.data(), fileEntry.dataSize);
inputFile.close();
// TODO: Implement data compression
std::vector<char> 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<FileEntry> FilePacker::listFiles(const std::string& bapFile) {
std::ifstream inputFile(bapFile, std::ios::binary);
if (!inputFile) {
CORE_LOG_FATAL("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) {
CORE_LOG_ERROR("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) {
CORE_LOG_ERROR("Failed to create output file: {}", outputPath);
return false;
}
std::vector<char> compressedData(fileEntry.compressedSize);
inputFile.seekg(fileEntry.dataOffset, std::ios::beg);
inputFile.read(compressedData.data(), compressedData.size());
std::vector<char> decompressedData = DecompressData(compressedData, fileEntry.compression);
outFile.write(decompressedData.data(), decompressedData.size());
outFile.close();
return true;
}
}
CORE_LOG_ERROR("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<const char*>(&header.version), sizeof(header.version));
outFile.write(reinterpret_cast<const char*>(&header.fileCount), sizeof(header.fileCount));
for (const auto& fileEntry : header.fileEntries) {
size_t pathLength = fileEntry.filePath.size();
outFile.write(reinterpret_cast<const char*>(&pathLength), sizeof(pathLength));
outFile.write(fileEntry.filePath.c_str(), pathLength);
outFile.write(reinterpret_cast<const char*>(&fileEntry.fileType), sizeof(fileEntry.fileType));
outFile.write(reinterpret_cast<const char*>(&fileEntry.compression), sizeof(fileEntry.compression));
outFile.write(reinterpret_cast<const char*>(&fileEntry.dataSize), sizeof(fileEntry.dataSize));
outFile.write(reinterpret_cast<const char*>(&fileEntry.compressedSize), sizeof(fileEntry.compressedSize));
outFile.write(reinterpret_cast<const char*>(&fileEntry.dataOffset), sizeof(fileEntry.dataOffset));
size_t metadataLength = fileEntry.metadata.size();
outFile.write(reinterpret_cast<const char*>(&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<char*>(&header.version), sizeof(header.version));
inFile.read(reinterpret_cast<char*>(&header.fileCount), sizeof(header.fileCount));
if (header.version != PACKER_VERSION) {
CORE_LOG_FATAL("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<char*>(&pathLength), sizeof(pathLength));
fileEntry.filePath.resize(pathLength);
inFile.read(fileEntry.filePath.data(), pathLength);
inFile.read(reinterpret_cast<char*>(&fileEntry.fileType), sizeof(fileEntry.fileType));
inFile.read(reinterpret_cast<char*>(&fileEntry.compression), sizeof(fileEntry.compression));
inFile.read(reinterpret_cast<char*>(&fileEntry.dataSize), sizeof(fileEntry.dataSize));
inFile.read(reinterpret_cast<char*>(&fileEntry.compressedSize), sizeof(fileEntry.compressedSize));
inFile.read(reinterpret_cast<char*>(&fileEntry.dataOffset), sizeof(fileEntry.dataOffset));
size_t metadataLength;
inFile.read(reinterpret_cast<char*>(&metadataLength), sizeof(metadataLength));
fileEntry.metadata.resize(metadataLength);
inFile.read(fileEntry.metadata.data(), metadataLength);
header.fileEntries.push_back(fileEntry);
}
return header;
}
std::vector<char> FilePacker::CompressData(const std::vector<char>& input, CompressionType compression) {
// IMPL: Implement actual compression on a per file basis
if (compression == COMPRESSION_NONE) {
return input;
}
return input;
}
std::vector<char> FilePacker::DecompressData(const std::vector<char>& input, CompressionType compression) {
// IMPL: Implement decompression on a per file basis
if (compression == COMPRESSION_NONE) {
return input;
}
return input;
}