A fork of https://github.com/crosspoint-reader/crosspoint-reader
1#pragma once
2#include <HalStorage.h>
3
4#include <deque>
5#include <string>
6#include <unordered_map>
7
8class ZipFile {
9 public:
10 struct FileStatSlim {
11 uint16_t method; // Compression method
12 uint32_t compressedSize; // Compressed size
13 uint32_t uncompressedSize; // Uncompressed size
14 uint32_t localHeaderOffset; // Offset of local file header
15 };
16
17 struct ZipDetails {
18 uint32_t centralDirOffset;
19 uint16_t totalEntries;
20 bool isSet;
21 };
22
23 // Target for batch uncompressed size lookup (sorted by hash, then len)
24 struct SizeTarget {
25 uint64_t hash; // FNV-1a 64-bit hash of normalized path
26 uint16_t len; // Length of path for collision reduction
27 uint16_t index; // Caller's index (e.g. spine index)
28 };
29
30 // FNV-1a 64-bit hash computed from char buffer (no std::string allocation)
31 static uint64_t fnvHash64(const char* s, size_t len) {
32 uint64_t hash = 14695981039346656037ull;
33 for (size_t i = 0; i < len; i++) {
34 hash ^= static_cast<uint8_t>(s[i]);
35 hash *= 1099511628211ull;
36 }
37 return hash;
38 }
39
40 private:
41 const std::string& filePath;
42 FsFile file;
43 ZipDetails zipDetails = {0, 0, false};
44 std::unordered_map<std::string, FileStatSlim> fileStatSlimCache;
45
46 // Cursor for sequential central-dir scanning optimization
47 uint32_t lastCentralDirPos = 0;
48 bool lastCentralDirPosValid = false;
49
50 bool loadFileStatSlim(const char* filename, FileStatSlim* fileStat);
51 long getDataOffset(const FileStatSlim& fileStat);
52 bool loadZipDetails();
53
54 public:
55 explicit ZipFile(const std::string& filePath) : filePath(filePath) {}
56 ~ZipFile() = default;
57 // Zip file can be opened and closed by hand in order to allow for quick calculation of inflated file size
58 // It is NOT recommended to pre-open it for any kind of inflation due to memory constraints
59 bool isOpen() const { return !!file; }
60 bool open();
61 bool close();
62 bool loadAllFileStatSlims();
63 bool getInflatedFileSize(const char* filename, size_t* size);
64 // Batch lookup: scan ZIP central dir once and fill sizes for matching targets.
65 // targets must be sorted by (hash, len). sizes[target.index] receives uncompressedSize.
66 // Returns number of targets matched.
67 int fillUncompressedSizes(std::deque<SizeTarget>& targets, std::deque<uint32_t>& sizes);
68 // Due to the memory required to run each of these, it is recommended to not preopen the zip file for multiple
69 // These functions will open and close the zip as needed
70 uint8_t* readFileToMemory(const char* filename, size_t* size = nullptr, bool trailingNullByte = false);
71 bool readFileToStream(const char* filename, Print& out, size_t chunkSize);
72};