summaryrefslogtreecommitdiffhomepage
path: root/ArchiveEx/Archive.cpp
diff options
context:
space:
mode:
Diffstat (limited to 'ArchiveEx/Archive.cpp')
-rw-r--r--ArchiveEx/Archive.cpp82
1 files changed, 82 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