initial commit
😎
This commit is contained in:
6
.gitignore
vendored
Normal file
6
.gitignore
vendored
Normal file
@@ -0,0 +1,6 @@
|
||||
/.idea
|
||||
/.cache
|
||||
/.ccls-cache
|
||||
/compile_commands.json
|
||||
/cmake-build-debug
|
||||
/build
|
15
CMakeLists.txt
Normal file
15
CMakeLists.txt
Normal file
@@ -0,0 +1,15 @@
|
||||
cmake_minimum_required(VERSION 3.18..3.28)
|
||||
project(ReArchive)
|
||||
|
||||
set(CMAKE_CXX_STANDARD 20)
|
||||
|
||||
file(GLOB_RECURSE HEADERS "include/*.h" "include/*.hpp")
|
||||
file(GLOB_RECURSE SOURCES "src/*.c" "src/*.cpp")
|
||||
|
||||
add_library(ReArchive SHARED ${SOURCES})
|
||||
|
||||
set_target_properties(ReArchive PROPERTIES LINKER_LANGUAGE CXX)
|
||||
target_include_directories(ReArchive PUBLIC ${PROJECT_SOURCE_DIR}/include)
|
||||
|
||||
add_executable(ReArchive_Demo main.cpp)
|
||||
target_link_libraries(ReArchive_Demo PUBLIC ReArchive)
|
46
include/ReArchive/ReArchive.h
Normal file
46
include/ReArchive/ReArchive.h
Normal file
@@ -0,0 +1,46 @@
|
||||
#pragma once
|
||||
#include <ReArchive/types/FileTable.h>
|
||||
#include <filesystem>
|
||||
#include <vector>
|
||||
|
||||
// TODO compression.
|
||||
namespace ReArchive {
|
||||
/// Creates a new empty archive.
|
||||
/// @param filesystem_path where the archive is to be created.
|
||||
/// @param use_compression whether you'd like the file to use compression.
|
||||
/// @param running_tally If you're keeping track of the file table in your program, Providing it here will update it to reflect our changes.
|
||||
/// @note use_compression currently does nothing.
|
||||
/// @returns True if success.
|
||||
[[nodiscard]] bool CreateArchive(const std::filesystem::path& archive, bool use_compression = false, FileTable* running_tally = nullptr);
|
||||
|
||||
/// Add a file to an archive.
|
||||
/// @param archive The archive on the disk.
|
||||
/// @param file_data The raw data of the file to be written.
|
||||
/// @param file_path The std::filesystem::path you would use to retrieve the file from the archive.
|
||||
/// @param byte_count The length of the file in bytes.
|
||||
/// @param running_tally If you're keeping track of the file table in your program, Providing it here will update it to reflect our changes.
|
||||
/// @returns True if success.
|
||||
[[nodiscard]] bool WriteFile(const std::filesystem::path& archive, const std::filesystem::path& file_path, const unsigned char* file_data, const int64_t& byte_count, FileTable* running_tally = nullptr);
|
||||
|
||||
/// Overwrite a file which already exists in the archive.
|
||||
/// @param archive The archive on the disk.
|
||||
/// @param file_data The raw data of the file to be written.
|
||||
/// @param file_path The std::filesystem::path file in the archive to be overwritten.
|
||||
/// @param byte_count The length of the file in bytes.
|
||||
/// @note It is expected that byte_count will the the same as the file size.
|
||||
/// @returns True if success.
|
||||
[[nodiscard]] bool OverwriteFile(const std::filesystem::path& archive, const std::filesystem::path& file_path, const unsigned char* file_data, const int64_t& byte_count);
|
||||
|
||||
/// Remove a file from the archive.
|
||||
/// @param archive The archive on the disk.
|
||||
/// @param file_path The std::filesystem::path file in the archive to be removed.
|
||||
/// @param running_tally If you're keeping track of the file table in your program, Providing it here will update it to reflect our changes.
|
||||
/// @returns True if success.
|
||||
[[nodiscard]] bool EraseFile(const std::filesystem::path& archive, const std::filesystem::path& file_path, FileTable* running_tally = nullptr);
|
||||
|
||||
/// Read a file from a given archive
|
||||
/// @param archive The archive on the disk.
|
||||
/// @param file_path The std::filesystem::path you specified for the given file.
|
||||
/// @note An empty vector is returned in the event that no such file exists or there was an error reading it back.
|
||||
std::vector<unsigned char> ReadFile(const std::filesystem::path& archive, const std::filesystem::path& file_path);
|
||||
}
|
34
include/ReArchive/types/FileEntry.h
Normal file
34
include/ReArchive/types/FileEntry.h
Normal file
@@ -0,0 +1,34 @@
|
||||
#pragma once
|
||||
#include <string>
|
||||
#include <cstdint>
|
||||
#include <utility>
|
||||
#include <vector>
|
||||
#include <filesystem>
|
||||
#include <array>
|
||||
|
||||
namespace ReArchive {
|
||||
class FileEntry;
|
||||
}
|
||||
|
||||
class ReArchive::FileEntry {
|
||||
protected:
|
||||
//uint64 string length.
|
||||
std::filesystem::path path;
|
||||
int64_t data_size;
|
||||
int64_t data_offset;
|
||||
public:
|
||||
[[nodiscard]] std::filesystem::path Path() const { return path; }
|
||||
[[nodiscard]] int64_t Size() const { return data_size; }
|
||||
[[nodiscard]] int64_t Offset() const { return data_offset; }
|
||||
public:
|
||||
/// @param data_size The number of bytes long the file is.
|
||||
/// @param data_offset How many bytes away from the beginning of the archive this file is stored.
|
||||
/// @param file_path The file name including any path you want to be included in the archive.
|
||||
FileEntry(const int64_t& data_size, const int64_t& data_offset, std::filesystem::path path) : path(std::move(path)), data_size(data_size), data_offset(data_offset)
|
||||
{};
|
||||
~FileEntry() = default;
|
||||
public:
|
||||
static std::vector<unsigned char> Serialize(const FileEntry& file);
|
||||
public:
|
||||
bool operator ==(const FileEntry& other) const;
|
||||
};
|
25
include/ReArchive/types/FileTable.h
Normal file
25
include/ReArchive/types/FileTable.h
Normal file
@@ -0,0 +1,25 @@
|
||||
#pragma once
|
||||
|
||||
#include <vector>
|
||||
#include <cstdint>
|
||||
#include <ReArchive/types/FileEntry.h>
|
||||
|
||||
namespace ReArchive {
|
||||
class FileTable;
|
||||
}
|
||||
|
||||
class ReArchive::FileTable {
|
||||
protected:
|
||||
// count
|
||||
std::vector<FileEntry> entries;
|
||||
public:
|
||||
void Append(const FileEntry& file_entry);
|
||||
void Remove(const FileEntry& file_entry);
|
||||
[[nodiscard]] std::vector<FileEntry> GetEntries() const { return entries; }
|
||||
[[nodiscard]] int64_t Count() const { return entries.size(); }
|
||||
public:
|
||||
[[nodiscard]] static std::vector<unsigned char> Serialize(const FileTable& file_table);
|
||||
public:
|
||||
FileTable() = default;
|
||||
|
||||
};
|
39
include/ReArchive/types/Header.h
Normal file
39
include/ReArchive/types/Header.h
Normal file
@@ -0,0 +1,39 @@
|
||||
#pragma once
|
||||
|
||||
#include <array>
|
||||
#include <vector>
|
||||
#include <cstdint>
|
||||
|
||||
|
||||
namespace ReArchive {
|
||||
class Header;
|
||||
static constexpr std::array<unsigned char, 3> magic { 'R', 'S', 'A' };
|
||||
}
|
||||
|
||||
class ReArchive::Header {
|
||||
protected:
|
||||
// The first 3 bytes of the file would always be this.
|
||||
std::array<unsigned char, 3> magic { 'R', 'S', 'A' };
|
||||
|
||||
// Whether the file is compressed.
|
||||
bool use_compression = false;
|
||||
|
||||
//TODO Whether the archive is currently in a state that we have to wait to read or write.
|
||||
//bool locked = false;
|
||||
|
||||
// The distance from the beginning of the file to the "file table".
|
||||
int64_t file_table_offset;
|
||||
public:
|
||||
[[nodiscard]] bool Compressed() const { return use_compression; }
|
||||
[[nodiscard]] int64_t FileTableOffset() const { return file_table_offset; }
|
||||
void FileTableOffset(const int64_t& offset) { file_table_offset = offset; }
|
||||
public:
|
||||
Header(bool use_compression, int64_t file_table_offset) : use_compression(use_compression), file_table_offset(file_table_offset) {}
|
||||
~Header() = default;
|
||||
public:
|
||||
static std::vector<unsigned char> Serialize(const Header& header);
|
||||
static Header DeSerialize(const unsigned char* serialized_header);
|
||||
|
||||
// We can't use sizeof(Header) because it includes the RTTI :/ - Redacted.
|
||||
static int64_t Size() { return sizeof(int64_t) + sizeof(bool) + ReArchive::magic.size(); }
|
||||
};
|
32
main.cpp
Normal file
32
main.cpp
Normal file
@@ -0,0 +1,32 @@
|
||||
#include <ReArchive/ReArchive.h>
|
||||
#include <iostream>
|
||||
|
||||
int main() {
|
||||
ReArchive::FileTable running_tally;
|
||||
|
||||
if(std::filesystem::exists("test.rsa"))
|
||||
std::filesystem::remove("test.rsa");
|
||||
|
||||
if (!ReArchive::CreateArchive("test.rsa"))
|
||||
return -1;
|
||||
std::string some_string = "some other string0.";
|
||||
if (!ReArchive::WriteFile("test.rsa", "assets/test0.png", reinterpret_cast<const unsigned char *>(some_string.data()), some_string.size(), &running_tally))
|
||||
return -1;
|
||||
|
||||
some_string = "some other string1.";
|
||||
if (!ReArchive::WriteFile("test.rsa", "assets/test1.png", reinterpret_cast<const unsigned char *>(some_string.data()), some_string.size(), &running_tally))
|
||||
return -1;
|
||||
|
||||
|
||||
auto retrieved = ReArchive::ReadFile("test.rsa", "assets/test0.png");
|
||||
std::cout << std::string( retrieved.begin(), retrieved.end()) << std::endl;
|
||||
|
||||
if (!ReArchive::EraseFile("test.rsa", "assets/test0.png", &running_tally))
|
||||
return -1;
|
||||
|
||||
auto retrieved2 = ReArchive::ReadFile("test.rsa", "assets/test1.png");
|
||||
std::cout << std::string( retrieved2.begin(), retrieved2.end()) << std::endl;
|
||||
|
||||
for (auto& e : running_tally.GetEntries())
|
||||
std::cout << e.Path() << std::endl;
|
||||
}
|
270
src/ReArchive.cpp
Normal file
270
src/ReArchive.cpp
Normal file
@@ -0,0 +1,270 @@
|
||||
#include <ReArchive/ReArchive.h>
|
||||
|
||||
#include <ReArchive/types/Header.h>
|
||||
#include <ReArchive/types/FileTable.h>
|
||||
#include <ReArchive/types/FileEntry.h>
|
||||
#include <fstream>
|
||||
|
||||
using ReArchive::Header;
|
||||
using ReArchive::FileTable;
|
||||
using ReArchive::FileEntry;
|
||||
|
||||
Header GetHeader(const unsigned char* archive) {
|
||||
ReArchive::Header h = ReArchive::Header::DeSerialize(archive);
|
||||
return h;
|
||||
}
|
||||
|
||||
Header GetHeader(const std::filesystem::path& archive) {
|
||||
std::ifstream file(archive, std::ios::binary);
|
||||
|
||||
if (!file)
|
||||
throw std::runtime_error("Trying to get the header of an archive which doesn't exist?");
|
||||
|
||||
std::vector<unsigned char> buffer(ReArchive::Header::Size());
|
||||
file.read(reinterpret_cast<char *>(buffer.data()), (int64_t) buffer.size());
|
||||
file.close();
|
||||
|
||||
return GetHeader(buffer.data());
|
||||
}
|
||||
|
||||
/// @param header our header.
|
||||
/// @param in Our input stream to the file.
|
||||
/// @note Does not close the input stream.
|
||||
FileTable GetFileTable(const Header& header, std::ifstream& in) {
|
||||
FileTable result;
|
||||
|
||||
std::vector<unsigned char> buffer;
|
||||
|
||||
in.seekg(header.FileTableOffset(), std::ios::beg);
|
||||
buffer.resize(sizeof(int64_t));
|
||||
|
||||
in.read(reinterpret_cast<char *>(buffer.data()), (int64_t) buffer.size());
|
||||
int64_t file_table_entry_count = be64toh(*reinterpret_cast<int64_t*>(buffer.data()));
|
||||
|
||||
if (file_table_entry_count) {
|
||||
// To put us at the first "string size" for each FileEntry.
|
||||
in.seekg(header.FileTableOffset() + 8, std::ios::beg);
|
||||
|
||||
// for each file entry,
|
||||
for (int64_t i = 0; i < file_table_entry_count; i++) {
|
||||
in.read(reinterpret_cast<char *>(buffer.data()), (int64_t) buffer.size());
|
||||
int64_t string_size = be64toh(*reinterpret_cast<int64_t*>(buffer.data()));
|
||||
|
||||
buffer.resize(string_size);
|
||||
in.read(reinterpret_cast<char *>(buffer.data()), (int64_t) buffer.size());
|
||||
std::string path(buffer.begin(), buffer.end());
|
||||
|
||||
buffer.resize(sizeof(int64_t));
|
||||
|
||||
in.read(reinterpret_cast<char *>(buffer.data()), (int64_t) buffer.size());
|
||||
int64_t data_size = be64toh(*reinterpret_cast<int64_t*>(buffer.data()));
|
||||
|
||||
in.read(reinterpret_cast<char *>(buffer.data()), (int64_t) buffer.size());
|
||||
int64_t data_offset = be64toh(*reinterpret_cast<int64_t*>(buffer.data()));
|
||||
|
||||
result.Append(FileEntry(data_size, data_offset, path));
|
||||
}
|
||||
}
|
||||
return result;
|
||||
}
|
||||
|
||||
bool ReArchive::CreateArchive(const std::filesystem::path& filesystem_path, bool use_compression, FileTable* running_tally) {
|
||||
if (std::filesystem::exists(filesystem_path))
|
||||
return false;
|
||||
|
||||
std::ofstream file(filesystem_path, std::ios::binary);
|
||||
if (!file)
|
||||
return false;
|
||||
|
||||
auto serialized_file_header = Header::Serialize(Header(use_compression, Header::Size()));
|
||||
auto file_table = FileTable();
|
||||
auto serialized_file_table = FileTable::Serialize(file_table);
|
||||
|
||||
file.write(reinterpret_cast<const char*>(serialized_file_header.data()), (int64_t) serialized_file_header.size());
|
||||
file.write(reinterpret_cast<const char*>(serialized_file_table.data()), (int64_t) serialized_file_table.size());
|
||||
file.close();
|
||||
|
||||
if (running_tally)
|
||||
*running_tally = file_table;
|
||||
return true;
|
||||
}
|
||||
|
||||
bool ReArchive::WriteFile(const std::filesystem::path& archive, const std::filesystem::path& file_path, const unsigned char* file_data, const int64_t& byte_count, FileTable* running_tally) {
|
||||
if (!std::filesystem::exists(archive))
|
||||
return false;
|
||||
|
||||
std::ifstream in(archive, std::ios::binary);
|
||||
if (!in)
|
||||
return false;
|
||||
|
||||
in.seekg(0, std::ios::end);
|
||||
if (in.tellg() < Header::Size())
|
||||
return false;
|
||||
in.seekg(0, std::ios::beg);
|
||||
|
||||
std::vector<unsigned char> buffer (Header::Size());
|
||||
in.read(reinterpret_cast<char *>(buffer.data()), (int64_t) buffer.size());
|
||||
if (buffer[0] != 'R' || buffer[1] != 'S' || buffer[2] != 'A')
|
||||
return false;
|
||||
|
||||
auto header = GetHeader(buffer.data());
|
||||
auto file_table = GetFileTable(header, in);
|
||||
|
||||
for (const auto& e : file_table.GetEntries())
|
||||
if (e.Path() == file_path)
|
||||
return false;
|
||||
in.close();
|
||||
|
||||
std::ofstream out(archive, std::ios::binary | std::ios::out | std::ios::in);
|
||||
if (!out)
|
||||
return false;
|
||||
|
||||
out.seekp(header.FileTableOffset(), std::ios::beg);
|
||||
out.write(reinterpret_cast<const char *>(file_data), byte_count);
|
||||
|
||||
file_table.Append(FileEntry(byte_count, header.FileTableOffset(), file_path));
|
||||
header.FileTableOffset(out.tellp());
|
||||
|
||||
auto new_file_table = FileTable::Serialize(file_table);
|
||||
out.write(reinterpret_cast<const char *>(new_file_table.data()), (int64_t) new_file_table.size());
|
||||
|
||||
auto new_header = Header::Serialize(header);
|
||||
out.seekp(0, std::ios::beg);
|
||||
out.write(reinterpret_cast<const char *>(new_header.data()), (int64_t) new_header.size());
|
||||
out.close();
|
||||
|
||||
if (running_tally)
|
||||
*running_tally = file_table;
|
||||
return true;
|
||||
}
|
||||
|
||||
bool ReArchive::OverwriteFile(const std::filesystem::path& archive, const std::filesystem::path& file_path, const unsigned char* file_data, const int64_t& byte_count) {
|
||||
if (!std::filesystem::exists(archive))
|
||||
return false;
|
||||
|
||||
std::ifstream in(archive, std::ios::binary);
|
||||
if (!in)
|
||||
return false;
|
||||
|
||||
in.seekg(0, std::ios::end);
|
||||
if (in.tellg() < Header::Size())
|
||||
return false;
|
||||
in.seekg(0, std::ios::beg);
|
||||
|
||||
std::vector<unsigned char> buffer (Header::Size());
|
||||
in.read(reinterpret_cast<char *>(buffer.data()), (int64_t) buffer.size());
|
||||
if (buffer[0] != 'R' || buffer[1] != 'S' || buffer[2] != 'A')
|
||||
return false;
|
||||
|
||||
auto header = GetHeader(buffer.data());
|
||||
auto file_table = GetFileTable(header, in);
|
||||
|
||||
const FileEntry* target = nullptr;
|
||||
for (const auto& e : file_table.GetEntries())
|
||||
if (e.Path() == file_path)
|
||||
target = &e;
|
||||
|
||||
if (!target)
|
||||
return false;
|
||||
|
||||
if (byte_count != target->Size())
|
||||
return false;
|
||||
|
||||
in.close();
|
||||
std::ofstream out(archive, std::ios::binary | std::ios::out | std::ios::in);
|
||||
if (!out)
|
||||
return false;
|
||||
|
||||
out.seekp(target->Offset(), std::ios::beg);
|
||||
out.write(reinterpret_cast<const char *>(file_data), byte_count);
|
||||
out.close();
|
||||
|
||||
return true;
|
||||
}
|
||||
|
||||
std::vector<unsigned char> ReArchive::ReadFile(const std::filesystem::path& archive, const std::filesystem::path& file_path) {
|
||||
if (!std::filesystem::exists(archive))
|
||||
return {};
|
||||
|
||||
std::ifstream in(archive, std::ios::binary);
|
||||
if (!in)
|
||||
return {};
|
||||
|
||||
in.seekg(0, std::ios::end);
|
||||
if (in.tellg() < Header::Size())
|
||||
return {};
|
||||
in.seekg(0, std::ios::beg);
|
||||
|
||||
std::vector<unsigned char> buffer (Header::Size());
|
||||
in.read(reinterpret_cast<char *>(buffer.data()), (int64_t) buffer.size());
|
||||
if (buffer[0] != 'R' || buffer[1] != 'S' || buffer[2] != 'A')
|
||||
return {};
|
||||
|
||||
auto header = GetHeader(buffer.data());
|
||||
auto file_table = GetFileTable(header, in);
|
||||
|
||||
const FileEntry* target = nullptr;
|
||||
for (const auto& e : file_table.GetEntries())
|
||||
if (e.Path() == file_path)
|
||||
target = &e;
|
||||
|
||||
if (!target)
|
||||
return {};
|
||||
|
||||
std::vector<unsigned char> result(target->Size());
|
||||
in.seekg(target->Offset(), std::ios::beg);
|
||||
in.read(reinterpret_cast<char*>(result.data()), (int64_t) result.size());
|
||||
in.close();
|
||||
|
||||
return result;
|
||||
}
|
||||
|
||||
// I tried to do this several different ways but this seems to be the best approach - Redacted.
|
||||
bool ReArchive::EraseFile(const std::filesystem::path& archive, const std::filesystem::path& file_path, FileTable* running_tally) {
|
||||
if (!std::filesystem::exists(archive))
|
||||
return false;
|
||||
|
||||
std::ifstream in(archive, std::ios::binary);
|
||||
if (!in)
|
||||
return false;
|
||||
|
||||
in.seekg(0, std::ios::end);
|
||||
int64_t file_size = in.tellg();
|
||||
if (file_size < Header::Size())
|
||||
return false;
|
||||
in.seekg(0, std::ios::beg);
|
||||
|
||||
std::vector<unsigned char> buffer (Header::Size());
|
||||
in.read(reinterpret_cast<char *>(buffer.data()), (int64_t) buffer.size());
|
||||
if (buffer[0] != 'R' || buffer[1] != 'S' || buffer[2] != 'A')
|
||||
return false;
|
||||
|
||||
auto current_header = GetHeader(buffer.data());
|
||||
auto current_file_table = GetFileTable(current_header, in);
|
||||
|
||||
if (!CreateArchive(archive.string() + ".tmp", current_header.Compressed()))
|
||||
return false;
|
||||
|
||||
for (auto& e : current_file_table.GetEntries())
|
||||
if (e.Path() == file_path) {
|
||||
current_file_table.Remove(e);
|
||||
break;
|
||||
}
|
||||
|
||||
for (auto& e : current_file_table.GetEntries()) {
|
||||
auto file_buffer = ReadFile(archive, e.Path());
|
||||
if (!WriteFile(archive.string() + ".tmp", e.Path(), file_buffer.data(), (int64_t) file_buffer.size())) {
|
||||
std::filesystem::remove(archive.string() + ".tmp");
|
||||
return false;
|
||||
}
|
||||
}
|
||||
|
||||
std::filesystem::remove(archive);
|
||||
std::filesystem::rename(archive.string() + ".tmp", archive);
|
||||
|
||||
// TODO read the header from the file we just wrote.
|
||||
if (running_tally)
|
||||
*running_tally = current_file_table;
|
||||
|
||||
return true;
|
||||
}
|
38
src/types/FileEntry.cpp
Normal file
38
src/types/FileEntry.cpp
Normal file
@@ -0,0 +1,38 @@
|
||||
#include <ReArchive/types/FileEntry.h>
|
||||
#include <cstring>
|
||||
|
||||
|
||||
using namespace ReArchive;
|
||||
|
||||
std::vector<unsigned char> FileEntry::Serialize(const FileEntry& file) {
|
||||
std::string path_string = file.path.string();
|
||||
int64_t path_size = path_string.size();
|
||||
|
||||
std::vector<unsigned char> result(sizeof(int64_t) + path_size + 2 * sizeof(int64_t));
|
||||
unsigned char* ptr = result.data();
|
||||
|
||||
auto network_path_size = htobe64(path_size);
|
||||
memcpy(ptr, &network_path_size, sizeof(int64_t));
|
||||
ptr += sizeof(int64_t);
|
||||
|
||||
memcpy(ptr, path_string.data(), path_size);
|
||||
ptr += path_size;
|
||||
|
||||
auto network_data_size = htobe64(file.data_size);
|
||||
memcpy(ptr, &network_data_size, sizeof(int64_t));
|
||||
ptr += sizeof(int64_t);
|
||||
|
||||
auto network_data_offset = htobe64(file.data_offset);
|
||||
memcpy(ptr, &network_data_offset, sizeof(int64_t));
|
||||
return result;
|
||||
}
|
||||
|
||||
bool FileEntry::operator==(const FileEntry& rhs) const {
|
||||
if (data_offset != rhs.data_offset)
|
||||
return false;
|
||||
if (data_size != rhs.data_size)
|
||||
return false;
|
||||
if (path != rhs.path)
|
||||
return false;
|
||||
return true;
|
||||
}
|
44
src/types/FileTable.cpp
Normal file
44
src/types/FileTable.cpp
Normal file
@@ -0,0 +1,44 @@
|
||||
#include <ReArchive/types/FileTable.h>
|
||||
#include <cstring>
|
||||
|
||||
using namespace ReArchive;
|
||||
|
||||
void FileTable::Append(const FileEntry& file_entry) {
|
||||
for (const auto& e : entries)
|
||||
if (e.Path() == file_entry.Path())
|
||||
return;
|
||||
|
||||
entries.push_back(file_entry);
|
||||
}
|
||||
|
||||
void FileTable::Remove(const FileEntry& file_entry) {
|
||||
if (entries.empty())
|
||||
return;
|
||||
|
||||
for (int64_t i = 0; i < entries.size(); i++) {
|
||||
if (entries[i] == file_entry) {
|
||||
entries.erase(entries.begin() + i);
|
||||
break;
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
std::vector<unsigned char> FileTable::Serialize(const FileTable& file_table) {
|
||||
auto files = file_table.GetEntries();
|
||||
int64_t count = files.size();
|
||||
auto network_count = htobe64(count);
|
||||
std::vector<unsigned char> result(reinterpret_cast<unsigned char*>(&network_count),
|
||||
reinterpret_cast<unsigned char*>(&network_count) + sizeof(network_count));
|
||||
|
||||
if (files.empty())
|
||||
return result;
|
||||
|
||||
for (const auto& file : files) {
|
||||
size_t current_size = result.size();
|
||||
auto serialization = FileEntry::Serialize(file);
|
||||
|
||||
result.resize(current_size + serialization.size());
|
||||
memcpy(result.data() + current_size, serialization.data(), serialization.size());
|
||||
}
|
||||
return result;
|
||||
}
|
26
src/types/Header.cpp
Normal file
26
src/types/Header.cpp
Normal file
@@ -0,0 +1,26 @@
|
||||
#include <ReArchive/types/Header.h>
|
||||
#include <cstring>
|
||||
#include <stdexcept>
|
||||
|
||||
std::vector<unsigned char> ReArchive::Header::Serialize(const ReArchive::Header& header) {
|
||||
std::vector<unsigned char> result;
|
||||
result.insert(result.end(), ReArchive::magic.begin(), ReArchive::magic.end());
|
||||
|
||||
result.push_back(header.use_compression);
|
||||
|
||||
size_t current_size = result.size();
|
||||
result.resize(current_size + sizeof(int64_t));
|
||||
auto network_file_table_offset = htobe64(header.file_table_offset);
|
||||
memcpy(result.data() + current_size, &network_file_table_offset, sizeof(int64_t));
|
||||
|
||||
return result;
|
||||
}
|
||||
|
||||
ReArchive::Header ReArchive::Header::DeSerialize(const unsigned char* serialized_header) {
|
||||
bool use_c; int64_t file_table_off;
|
||||
|
||||
use_c = serialized_header[ReArchive::magic.size()];
|
||||
memcpy(&file_table_off, serialized_header + ReArchive::Header::Size() - sizeof(int64_t), sizeof(int64_t));
|
||||
|
||||
return { use_c, (int64_t) be64toh(file_table_off)};
|
||||
}
|
Reference in New Issue
Block a user