// // 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("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) { 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 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) { 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 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; } } 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(&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) { 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(&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; }