/* * 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() : signature(SIGNATURE) , extension(EXTENSION) , version(VERSION) , methodFlags(0) , 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, int8_t flag) { cargoFile = path + "." + extension; catalogPath = path; methodFlags = flag; //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 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; } // Pobieranie listy plików wyjątków if (flag == -1) { std::string filterFile = path + ".json"; if (!std::filesystem::exists(filterFile)) { std::cerr << "Error: Missing " << filterFile << " file!" << std::endl; return false; } GetFilters(filterFile); } else { // Dodaj tu coś mordo } // 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; } // Zapisywanie klucza szyfrującego if (flag == 2 || flag == 3) { crypt.saveKey(catalogPath); } 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)) { PathConf pc; pc.path = PathToUnixLike(tmpPath); pc.parameter = methodFlags; filesPaths.push_back(pc); } } } return filesPaths.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 //----------------------------------------------------------------------------- void CreateCargo::computingBytes(const int8_t& flag, 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; switch (flag) { case 1: std::cout << "COMPRESSING" << std::endl; output = cm.compress(input); break; case 2: std::cout << "ENCRYPTION" << std::endl; output = crypt.encrypt(input); break; case 3: std::cout << "ZIP ENC" << std::endl; output = crypt.encrypt(cm.compress(input)); break; default: std::cout << "RAW" << std::endl; output = std::move(input); break; } } //----------------------------------------------------------------------------- // 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 tymczasowego nagłowka jako rezerwacja miejsca 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 jednocześnie zapisywanie plików for (const auto& file : filesPaths) { std::string path = PathToUnixLike(RemoveStartPath(file.path)); std::ifstream f(file.path, 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 buffer(size); f.read(buffer.data(), size); f.close(); //Tworzenie hashu CRC const uint64_t crc = XXH64(buffer.data(), buffer.size(), VERSION); //Kompresjia std::vector pakBuffer; computingBytes(file.parameter, buffer, pakBuffer); FilesTable ft; ft.nameFile = path; ft.nameLen = path.length(); ft.hashName = fnv64(path); ft.offset = offset; ft.size = pakBuffer.size(); ft.flag = file.parameter; ft.crc = crc; cargo.write(reinterpret_cast(pakBuffer.data()), pakBuffer.size()); filesTable.push_back(ft); offset += pakBuffer.size(); } return filesTable; } //----------------------------------------------------------------------------- // Wczytanie filtrów wyjątków //----------------------------------------------------------------------------- void CreateCargo::GetFilters(const std::string& filterFile) { std::cout << "Downloading the exception list" << std::endl; std::ifstream file(filterFile); nlohmann::json jslist; file >> jslist; file.close(); // Lista plików do skompresowania std::vector zip = jslist[KEY_ZIP].get>(); // Lista plików do zaszyfrowania std::vector enc = jslist[KEY_ENCRYPT].get>(); // Lista plików do pominięcia std::vector ignore = jslist[KEY_IGNORE].get>(); PrepareList(zip, enc, ignore); } //----------------------------------------------------------------------------- // Przygotuj listę plików do spakowania //----------------------------------------------------------------------------- void CreateCargo::PrepareList(const std::vector& zip, const std::vector& enc, const std::vector& ignore) { PathConf pc; for (const auto& item : filesList) { if (!FindOnTheList(ignore, item)) { if (FindOnTheList(zip, item)) { pc.parameter = FindOnTheList(enc, item) ? 3 : 1; } else { pc.parameter = FindOnTheList(enc, item) ? 2 : 0; } pc.path = item; filesPaths.push_back(pc); } } } //----------------------------------------------------------------------------- // 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; } //----------------------------------------------------------------------------- // 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 = filesPaths.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.flag), sizeof(head.flag)); } //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(); std::cout << "The container was successfully created! " << cargoFile << std::endl; return true; }