summaryrefslogtreecommitdiffhomepage
diff options
context:
space:
mode:
authorAki <please@ignore.pl>2022-08-07 21:45:05 +0200
committerAki <please@ignore.pl>2022-08-07 21:49:13 +0200
commit9ae3b193c461b168336d1d9e272aa834705633d7 (patch)
treecf4dde4eeba653763ab3180e4d581f359899370d
parent59af3b9729cb325fa65698f534bb87e77401c6a6 (diff)
downloadstarshatter-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.
-rw-r--r--ArchiveEx/Archive.cpp82
-rw-r--r--ArchiveEx/Archive.h51
-rw-r--r--ArchiveEx/CMakeLists.txt13
-rw-r--r--CMakeLists.txt1
4 files changed, 147 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
+ )
diff --git a/CMakeLists.txt b/CMakeLists.txt
index fdb33d6..fea3356 100644
--- a/CMakeLists.txt
+++ b/CMakeLists.txt
@@ -14,6 +14,7 @@ if(MSVC)
set(WINDOWSSDK_LIBPATH "C:/Program Files (x86)/Windows Kits/10/Lib/10.0.19041.0" CACHE FILEPATH "Path to a versioned lib directory of selected Windows SDK")
set(WINDOWSSDK_PATH "C:/Program Files (x86)/Windows Kits/10/Include/10.0.19041.0" CACHE FILEPATH "Path to a versioned include directory of selected Windows SDK")
endif()
+add_subdirectory(ArchiveEx)
add_subdirectory(contrib)
add_subdirectory(data)
add_subdirectory(Datafile)