/* * This file is part of VoidArchiveTool. * * Copyright (C) 2025 Yanczi * * Void Archive Toolis free software: you can redistribute it and/or modify * it under the terms of the GNU Lesser General Public License as published by * the Free Software Foundation, either version 3 of the License, or * (at your option) any later version. * * This program is distributed in the hope that it will be useful, * but WITHOUT ANY WARRANTY; without even the implied warranty of * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the * GNU Lesser General Public License for more details. * * You should have received a copy of the GNU Lesser General Public License * along with this program. If not, see . */ #include "CreateCargo.h" CreateCargo::CreateCargo() :compressingFlag(false) , filteringFlag(false) , signature(SIGNATURE) , extension(EXTENSION) , version(VERSION) , offset(0) { // TODO Auto-generated constructor stub } CreateCargo::~CreateCargo() { // TODO Auto-generated destructor stub } //----------------------------------------------------------------------------- // Punk wejścia do tworzenia archivum //----------------------------------------------------------------------------- bool CreateCargo::Create(const std::string& path, bool compress, bool filters) { cargoFile = path + "." + extension; catalogPath = path; compressingFlag = compress; filteringFlag = filters; //Sprawdzanie pakowanego kontentu if (!std::filesystem::is_directory(path)) { std::cerr << "Error: The specified directory is a file!" << std::endl; return false; } if (!std::filesystem::exists(path)) { std::cerr << "Error: The specified directory does not exist!" << std::endl; return false; } // Pobieranie listy plików wyjątków if (filters) { std::string filterFile = path + ".txt"; if (!std::filesystem::exists(filterFile)) { std::cerr << "Error: Missing " << filterFile << " file!" << std::endl; return false; } GetFilters(filterFile); } //Pobieranie listy plików do spakowania std::cout << "Creating a file list..." << std::endl; if (!GetFileList(path)) { std::cerr << "Error: The specified directory contains no files!" << std::endl; return false; } // Utworzenie kontenera cargo.open(cargoFile, std::ios::binary); //Rozpocznij process zapisywania danych do kontenera if (!WriteCargo()) { std::cerr << "Error: Failed to create container!" << std::endl; return false; } return true; } //----------------------------------------------------------------------------- // Tworzenie listy plików do spakowania //----------------------------------------------------------------------------- bool CreateCargo::GetFileList(const std::string& path) { for (const auto& entry : std::filesystem::directory_iterator(path)) { std::string tmpPath = entry.path().string(); if (std::filesystem::is_directory(tmpPath)) { GetFileList(tmpPath); } else { if (CheckIgnorePath(tmpPath)) { filesList.push_back(PathToUnixLike(tmpPath)); } } } return filesList.size() > 0 ? true : false; } //----------------------------------------------------------------------------- // Konwersja ścieżki na unix like //----------------------------------------------------------------------------- std::string CreateCargo::PathToUnixLike(std::string path) { std::replace(path.begin(), path.end(), '\\', '/'); return path; } //----------------------------------------------------------------------------- // Kasowanie wskazanego katalogu ze ścieżki //----------------------------------------------------------------------------- std::string CreateCargo::RemoveStartPath(const std::filesystem::path& path) { std::filesystem::path cleanPath; auto it = path.begin(); if (it != path.end()) { ++it; } for (; it != path.end(); ++it) { cleanPath /= *it; } return cleanPath.string(); } //----------------------------------------------------------------------------- // Tworzenie nagłowka pliku //----------------------------------------------------------------------------- CargoHead CreateCargo::CreateCargoHead(const uint32_t& filesLen, const uint64_t& table) { CargoHead ch; ch.signature = signature; ch.version = version; ch.files = filesLen; ch.table = table; return ch; } //----------------------------------------------------------------------------- // Sprawdza czy plik znajduje się na liście //----------------------------------------------------------------------------- uint8_t CreateCargo::CheckFileOnTheList(const std::string& path, std::vector& input, std::vector& output) { //Flaga aktywna sprawdza czy plik jest na liście. Jeśli jest to zwraca surowedane //Przeciwnie kompresuje dane CompressingManager cm; if (filteringFlag) { if (FilteringData(path)) { output = cm.compress(input); return ZIP_FILE; } else { output = std::move(input); //output = crypt.encrypt(input); return RAW_FILE; } } //Flaga aktywna kompresuje dane if (compressingFlag) { output = cm.compress(input); return ZIP_FILE; } output = std::move(input); //output = crypt.encrypt(input); return RAW_FILE; } //----------------------------------------------------------------------------- // Przygotowanie nagłówków i plików //----------------------------------------------------------------------------- std::vector CreateCargo::ComputingHeadFiles() { //Utwórz header TMP. Zabezpiecza Pierwsze bajty na właściwy nagłówek CargoHead cargoHead = CreateCargoHead(0, 0); offset += cargoHead.signature.length() + sizeof(cargoHead.version) + sizeof(cargoHead.files) + sizeof(cargoHead.table); //Zapisanie TMP nagłowka do pliku cargo.write(cargoHead.signature.data(), cargoHead.signature.length()); cargo.write(reinterpret_cast(&cargoHead.version), sizeof(cargoHead.version)); cargo.write(reinterpret_cast(&cargoHead.files), sizeof(cargoHead.files)); cargo.write(reinterpret_cast(&cargoHead.table), sizeof(cargoHead.table)); std::vector filesTable; //Tworzenie nagłówków plików for (const auto& file : filesList) { std::string path = PathToUnixLike(RemoveStartPath(file)); std::ifstream f(file, std::ios::binary | std::ios::ate); //Obliczanie rozmiaru pliku size_t size = f.tellg(); f.seekg(0, std::ios::beg); //Wczytanie pliku do pamięci std::vector buffor(size); f.read(buffor.data(), size); f.close(); //Tworzenie hashu CRC uint32_t crc = crc32(buffor); //Kompresjia std::vector zip; uint8_t method = CheckFileOnTheList(path, buffor, zip); FilesTable ft; ft.nameFile = path; ft.nameLen = path.length(); ft.hashName = fnv64(path); ft.offset = offset; ft.size = zip.size(); ft.isZip = method; ft.crc = crc; cargo.write(reinterpret_cast(zip.data()), zip.size()); filesTable.push_back(ft); offset += zip.size(); } return filesTable; } //----------------------------------------------------------------------------- // Wczytanie filtrów wyjątków //----------------------------------------------------------------------------- void CreateCargo::GetFilters(const std::string& filterFile) { std::cout << "Downloading the exception list" << std::endl; Txtpp ff(filterFile); // Lista plików do skompresowania zipList = ff.Get(KEY_ZIP); // Lista plików do pominięcia ignoreList = ff.Get(KEY_IGNORE); ff.Close(); } //----------------------------------------------------------------------------- // Znajdź wskazany element na liście //----------------------------------------------------------------------------- bool CreateCargo::FindOnTheList(const std::vector& list, const std::string& element) { auto it = std::find(list.begin(), list.end(), element); return it == list.end() ? false : true; } //----------------------------------------------------------------------------- // Rozdzielanie paternu od ścieżki //----------------------------------------------------------------------------- void CreateCargo::ExtPatternAndPathDetection(const std::vector& data, std::vector& pattern, std::vector& path) { for (const auto& d : data) { if (d.front() == '*') { std::string tmpPattern = d; tmpPattern.erase(tmpPattern.begin()); pattern.push_back(UpperString(tmpPattern)); } else { path.push_back(d); } } } //----------------------------------------------------------------------------- // Sprawdzanie rozszeżeń plików //----------------------------------------------------------------------------- bool CreateCargo::CheckFileExtension(const std::filesystem::path& p, const std::vector& patterns) { std::string ext = UpperString(p.extension().string()); return FindOnTheList(patterns, ext); } //----------------------------------------------------------------------------- // Zamień cały ciąg na duże litery //----------------------------------------------------------------------------- std::string CreateCargo::UpperString(std::string s) { std::transform(s.begin(), s.end(), s.begin(), [](unsigned char c) { return static_cast(std::toupper(c)); }); return s; } //----------------------------------------------------------------------------- // Wygenerój FNV-1a HASH //----------------------------------------------------------------------------- uint64_t CreateCargo::fnv64(const std::string& data) { const uint64_t fnvOffset = 14695981039346656037u; const uint64_t fnvPrime = 1099511628211u; uint64_t hash = fnvOffset; for (unsigned char c : data) { hash ^= c; hash *= fnvPrime; } return hash; } //----------------------------------------------------------------------------- // Wygenerój CRC32 HASH integralności //----------------------------------------------------------------------------- uint32_t CreateCargo::crc32(const std::vector& buffer) { boost::crc_32_type crc; crc.process_bytes(buffer.data(), buffer.size()); return crc.checksum(); } //----------------------------------------------------------------------------- // Sprawdzanie czy plik znajduje się na liście //----------------------------------------------------------------------------- bool CreateCargo::FilteringData(const std::string& path) { std::vector cmPatterns; std::vector cmPaths; // Rozdziel ścieżki i patterny na osobne listy ExtPatternAndPathDetection(zipList, cmPatterns, cmPaths); if (FindOnTheList(cmPatterns, ALL_FILE)) { return true; } // Sprawdż czy istnieje plik o danym rozszeżeniu if (CheckFileExtension(path, cmPatterns)) { return true; } // Sprawdź czy instnieje dany plik w danej lokalizacji if (FindOnTheList(cmPaths, path)) { return true; } return false; } //----------------------------------------------------------------------------- // Kasowanie z listy plików ignorow //----------------------------------------------------------------------------- bool CreateCargo::CheckIgnorePath(const std::string& path) { std::vector igPatterns; std::vector igPaths; ExtPatternAndPathDetection(ignoreList, igPatterns, igPaths); // Sprawdż czy istnieje plik o danym rozszeżeniu if (CheckFileExtension(path, igPatterns)) { return false; } // Obrubka ścierzki // Usuwanie katalogu root std::string cleanPath = RemoveStartPath(path); // Przekształcenie ścierzki na format unixowy std::string unixPath = PathToUnixLike(cleanPath); // Sprawdź czy instnieje dany plik w danej lokalizacji if (FindOnTheList(igPaths, unixPath)) { return false; } return true; } //----------------------------------------------------------------------------- // Trworzenie archiwum //----------------------------------------------------------------------------- bool CreateCargo::WriteCargo() { std::cout << "Packing files..." << std::endl; uint32_t filesLen = filesList.size(); //Przygotowanie nagłówków plików i przetworzenie danych std::vector filesHead = ComputingHeadFiles(); //std::ofstream cargo(cargoFile, std::ios::binary); if (!cargo.is_open()) { std::cerr << "ERROR: Failed to create container " << cargoFile << std::endl; return false; } for (const auto& head : filesHead) { // Ścieżka do plików cargo.write(reinterpret_cast(&head.nameLen), sizeof(head.nameLen)); cargo.write(head.nameFile.data(), head.nameLen); cargo.write(reinterpret_cast(&head.hashName), sizeof(head.hashName)); cargo.write(reinterpret_cast(&head.offset), sizeof(head.offset)); cargo.write(reinterpret_cast(&head.size), sizeof(head.size)); cargo.write(reinterpret_cast(&head.crc), sizeof(head.crc)); cargo.write(reinterpret_cast(&head.isZip), sizeof(head.isZip)); } //Cofnij się na początek pliku cargo.seekp(std::ios::beg); //Utwórz header CargoHead cargoHead = CreateCargoHead(filesLen, offset); //Nadpisz tymczasowy nagłówek cargo.write(cargoHead.signature.data(), cargoHead.signature.length()); cargo.write(reinterpret_cast(&cargoHead.version), sizeof(cargoHead.version)); cargo.write(reinterpret_cast(&cargoHead.files), sizeof(cargoHead.files)); cargo.write(reinterpret_cast(&cargoHead.table), sizeof(cargoHead.table)); cargo.close(); // Zapisywanie klucza szyfrującego //crypt.saveKey(catalogPath); std::cout << "The container was successfully created! " << cargoFile << std::endl; return true; }