193 lines
7.7 KiB
C++
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;
|
|
} |