diff options
author | Aki <please@ignore.pl> | 2022-08-07 21:45:05 +0200 |
---|---|---|
committer | Aki <please@ignore.pl> | 2022-08-07 21:49:13 +0200 |
commit | 9ae3b193c461b168336d1d9e272aa834705633d7 (patch) | |
tree | cf4dde4eeba653763ab3180e4d581f359899370d /ArchiveEx | |
parent | 59af3b9729cb325fa65698f534bb87e77401c6a6 (diff) | |
download | starshatter-9ae3b193c461b168336d1d9e272aa834705633d7.zip starshatter-9ae3b193c461b168336d1d9e272aa834705633d7.tar.gz starshatter-9ae3b193c461b168336d1d9e272aa834705633d7.tar.bz2 |
Reimplemented part of archive format
This is getting reimplemented rather than refactor mostly in order to make
clear which parts are needed for backwards compatiblity (reading-wise) and
which are not. The current implementation has quite a number of quirks and
potential failure points despite not being large. Understanding them is not
worth it.
Diffstat (limited to 'ArchiveEx')
-rw-r--r-- | ArchiveEx/Archive.cpp | 82 | ||||
-rw-r--r-- | ArchiveEx/Archive.h | 51 | ||||
-rw-r--r-- | ArchiveEx/CMakeLists.txt | 13 |
3 files changed, 146 insertions, 0 deletions
diff --git a/ArchiveEx/Archive.cpp b/ArchiveEx/Archive.cpp new file mode 100644 index 0000000..d714e46 --- /dev/null +++ b/ArchiveEx/Archive.cpp @@ -0,0 +1,82 @@ +#include "Archive.h" + +#include <cstdio> +#include <memory> +#include <vector> + +#include <zlib.h> + + +namespace Archive +{ + + +using UniqueFileHandle = std::unique_ptr<std::FILE, decltype(&std::fclose)>; + + +static constexpr std::size_t DIRECTORY_MARGIN {64}; + + +static std::size_t BytesToBlocks(std::size_t bytes); + + +Archive::Archive(const char* p) : + path {p}, + header {}, + entries {} +{ + UniqueFileHandle file {std::fopen(path, "rb"), &std::fclose}; + if (!file) + throw "could not open archive"; + std::size_t length = fread(&header, sizeof(Header), 1, file.get()); + if (1 != length) + throw "could not read"; + if (VERSION != header.version) + throw "bad version"; + int err = std::fseek(file.get(), sizeof(Header) + header.directory.offset, SEEK_SET); + if (-1 == err) + throw "could not find directory in archive"; + std::vector<std::uint8_t> compressed(header.directory.compressed_size); + length = std::fread(compressed.data(), 1, header.directory.compressed_size, file.get()); + if (header.directory.compressed_size != length) + throw "could not read compressed directory"; + const std::size_t total_entries = header.total_entries + DIRECTORY_MARGIN; + entries.resize(header.total_entries); + entries.reserve(total_entries); // In original impl entries were switched on/off with their uncompressed size value. + uLongf uncompressed_size = sizeof(Entry) * total_entries; + err = ::uncompress( + reinterpret_cast<Bytef*>(entries.data()), + &uncompressed_size, + compressed.data(), + header.directory.compressed_size); + if (Z_OK != err) + throw "could not uncompress directory"; +} + + +void +Archive::PrintNamesOfEntries() const +{ + for (const auto& entry : entries) + std::printf("%s\n", entry.name); +} + + +std::size_t +Archive::DirectoryBlocks() const +{ + const std::size_t blocks = BytesToBlocks(header.total_entries * sizeof(Entry)); + return blocks == 0 ? 1 : blocks; +} + + +std::size_t +BytesToBlocks(const std::size_t bytes) +{ + const std::size_t full = bytes / BLOCK_SIZE; + const std::size_t partial = (bytes % BLOCK_SIZE) > 0; + return full + partial; +} + + +} // namespace Archive diff --git a/ArchiveEx/Archive.h b/ArchiveEx/Archive.h new file mode 100644 index 0000000..cc06dc4 --- /dev/null +++ b/ArchiveEx/Archive.h @@ -0,0 +1,51 @@ +#pragma once + +#include <cstddef> +#include <cstdint> +#include <vector> + + +namespace Archive +{ + + +static constexpr std::uint32_t VERSION {0x0010}; +static constexpr std::size_t BLOCK_SIZE {1024}; +static constexpr std::size_t NAMELEN {64}; + + +struct Header +{ + std::uint32_t version; + std::uint32_t total_entries; + struct { + std::uint32_t total_blocks; + std::uint32_t compressed_size; + std::uint32_t offset; + } directory; +}; + + +struct Entry +{ + char name[NAMELEN]; + std::uint32_t original_size; + std::uint32_t compressed_size; + std::uint32_t offset; +}; + + +class Archive +{ +public: + explicit Archive(const char* path); + void PrintNamesOfEntries() const; + std::size_t DirectoryBlocks() const; +private: + const char* path; + Header header; + std::vector<Entry> entries; +}; + + +} // namespace Archive diff --git a/ArchiveEx/CMakeLists.txt b/ArchiveEx/CMakeLists.txt new file mode 100644 index 0000000..b2a1948 --- /dev/null +++ b/ArchiveEx/CMakeLists.txt @@ -0,0 +1,13 @@ +project(ArchiveEx) +add_library( + ArchiveEx + Archive.cpp + ) +target_include_directories( + ArchiveEx + PUBLIC . + ) +target_link_libraries( + ArchiveEx + PRIVATE Zlib::zlib + ) |