载入中...
搜索中...
未找到
zip_writer.h
浏览该文件的文档.
1#pragma once
2
9#include <cstdint>
10#include <cstdio>
11#include <cstring>
12#include <filesystem>
13#include <fstream>
14#include <string>
15#include <vector>
16
17#include <zlib.h>
18
19namespace eve::cmdline {
20
22#define EVE_ZIP_METHOD 8
23
24namespace detail {
25
27inline bool rawDeflate(const char* in, size_t inLen, std::vector<char>& out) {
28 z_stream stream = {};
29 stream.next_in = reinterpret_cast<Bytef*>(const_cast<char*>(in));
30 stream.avail_in = static_cast<uInt>(inLen);
31
32 if (deflateInit2(&stream, Z_DEFAULT_COMPRESSION, Z_DEFLATED, -15, 8, Z_DEFAULT_STRATEGY) != Z_OK)
33 return false;
34
35 out.resize(inLen > 0 ? static_cast<size_t>(deflateBound(&stream, inLen)) : 1024);
36
37 stream.next_out = reinterpret_cast<Bytef*>(out.data());
38 stream.avail_out = static_cast<uInt>(out.size());
39
40 int ret = deflate(&stream, Z_FINISH);
41 bool ok = (ret == Z_STREAM_END);
42 deflateEnd(&stream);
43
44 if (!ok) return false;
45 out.resize(stream.total_out);
46 return true;
47}
48
50inline void put16(std::vector<char>& b, uint16_t v) {
51 b.push_back(static_cast<char>(v & 0xFF));
52 b.push_back(static_cast<char>((v >> 8) & 0xFF));
53}
54inline void put32(std::vector<char>& b, uint32_t v) {
55 b.push_back(static_cast<char>(v & 0xFF));
56 b.push_back(static_cast<char>((v >> 8) & 0xFF));
57 b.push_back(static_cast<char>((v >> 16) & 0xFF));
58 b.push_back(static_cast<char>((v >> 24) & 0xFF));
59}
60
61} // namespace detail
62
64class ZipWriter {
65public:
67 bool open(const std::string& outPath) {
68 outFile_.open(outPath, std::ios::binary | std::ios::trunc);
69 return outFile_.is_open();
70 }
71
73 bool addFile(const std::string& relPath, const char* data, size_t size) {
74 const uint32_t localOffset = static_cast<uint32_t>(offset_);
75 uint32_t crc = static_cast<uint32_t>(crc32(0, reinterpret_cast<const Bytef*>(data),
76 static_cast<uInt>(size)));
77
78 std::vector<char> compressed;
79#if EVE_ZIP_METHOD == 8
80 if (!detail::rawDeflate(data, size, compressed)) return false;
81#else
82 compressed.assign(data, data + size);
83#endif
84
85 std::vector<char> lfh;
86 lfh.reserve(30 + relPath.size());
87 detail::put32(lfh, 0x04034B50); // local file header signature
88 detail::put16(lfh, 20); // version needed
89 detail::put16(lfh, 0); // general purpose bit flag
90 detail::put16(lfh, EVE_ZIP_METHOD); // compression method
91 detail::put16(lfh, 0); // last mod time
92 detail::put16(lfh, 0x21); // last mod date (1980-01-01)
93 detail::put32(lfh, crc);
94 detail::put32(lfh, static_cast<uint32_t>(compressed.size()));
95 detail::put32(lfh, static_cast<uint32_t>(size));
96 detail::put16(lfh, static_cast<uint16_t>(relPath.size()));
97 detail::put16(lfh, 0); // extra field length
98 lfh.insert(lfh.end(), relPath.begin(), relPath.end());
99
100 if (!writeRaw(lfh)) return false;
101 if (!writeRaw(compressed)) return false;
102
103 CentralEntry e;
104 e.relPath = relPath;
105 e.crc = crc;
106 e.compressedSz = static_cast<uint32_t>(compressed.size());
107 e.uncompressedSz = static_cast<uint32_t>(size);
108 e.localOffset = localOffset;
109 entries_.push_back(std::move(e));
110
111 return true;
112 }
113
115 bool finish() {
116 if (!outFile_.is_open()) return false;
117 const uint32_t cdStart = static_cast<uint32_t>(offset_);
118 for (const auto& e : entries_) {
119 std::vector<char> cd;
120 cd.reserve(46 + e.relPath.size());
121 detail::put32(cd, 0x02014B50); // central directory signature
122 detail::put16(cd, 20); // version made by
123 detail::put16(cd, 20); // version needed
124 detail::put16(cd, 0); // flags
125 detail::put16(cd, EVE_ZIP_METHOD); // method
126 detail::put16(cd, 0); // mod time
127 detail::put16(cd, 0x21); // mod date
128 detail::put32(cd, e.crc);
129 detail::put32(cd, e.compressedSz);
130 detail::put32(cd, e.uncompressedSz);
131 detail::put16(cd, static_cast<uint16_t>(e.relPath.size()));
132 detail::put16(cd, 0); // extra
133 detail::put16(cd, 0); // comment
134 detail::put16(cd, 0); // disk number
135 detail::put16(cd, 0); // internal attrs
136 detail::put32(cd, 0); // external attrs
137 detail::put32(cd, e.localOffset);
138 cd.insert(cd.end(), e.relPath.begin(), e.relPath.end());
139
140 if (!writeRaw(cd)) return false;
141 }
142 const uint32_t cdEnd = static_cast<uint32_t>(offset_);
143
144 std::vector<char> eocd;
145 detail::put32(eocd, 0x06054B50); // end of central directory
146 detail::put16(eocd, 0); // disk number
147 detail::put16(eocd, 0); // cd start disk
148 detail::put16(eocd, static_cast<uint16_t>(entries_.size()));
149 detail::put16(eocd, static_cast<uint16_t>(entries_.size()));
150 detail::put32(eocd, cdEnd - cdStart);
151 detail::put32(eocd, cdStart);
152 detail::put16(eocd, 0); // comment length
153 if (!writeRaw(eocd)) return false;
154
155 outFile_.close();
156 return true;
157 }
158
159private:
160 struct CentralEntry {
161 std::string relPath;
162 uint32_t crc = 0;
163 uint32_t compressedSz = 0;
164 uint32_t uncompressedSz = 0;
165 uint32_t localOffset = 0;
166 };
167
168 bool writeRaw(const std::vector<char>& data) {
169 if (!outFile_.is_open()) return false;
170 outFile_.write(data.data(), static_cast<std::streamsize>(data.size()));
171 offset_ += data.size();
172 return static_cast<bool>(outFile_);
173 }
174
175 std::ofstream outFile_;
176 uint64_t offset_ = 0;
177 std::vector<CentralEntry> entries_;
178};
179
180 // Walk a game directory and write every regular file into a single .eve archive.
181 inline bool createGameArchive(const std::filesystem::path& gameDir,
182 const std::filesystem::path& outArchive) {
183 std::error_code ec;
184 if (!std::filesystem::is_directory(gameDir, ec)) return false;
185
186 ZipWriter writer;
187 if (!writer.open(outArchive.string())) return false;
188
189 // Recursively collect files, preserving relative paths with '/' separators.
190 std::vector<std::filesystem::path> files;
191 std::filesystem::recursive_directory_iterator it(
192 gameDir, std::filesystem::directory_options::skip_permission_denied, ec);
193 std::filesystem::recursive_directory_iterator end;
194 for (; it != end; it.increment(ec)) {
195 if (ec) { ec.clear(); continue; }
196 if (it->is_regular_file()) files.push_back(it->path());
197 }
198
199 for (const auto& file : files) {
200 std::string rel = std::filesystem::relative(file, gameDir, ec).generic_string();
201 if (rel.empty()) continue;
202 // Always use '/' inside the archive.
203 for (char& c : rel) if (c == '\\') c = '/';
204
205 std::ifstream in(file, std::ios::binary);
206 if (!in) continue;
207 in.seekg(0, std::ios::end);
208 const auto size = static_cast<size_t>(in.tellg());
209 in.seekg(0, std::ios::beg);
210
211 std::vector<char> buf(size);
212 if (size > 0) in.read(buf.data(), static_cast<std::streamsize>(size));
213 if (!in && size > 0) continue;
214
215 if (!writer.addFile(rel, buf.data(), buf.size())) return false;
216 }
217
218 return writer.finish();
219 }
220
221} // namespace eve::cmdline
222
filesystem::File * file
uint32_t b
uint32_t c
Light2D::Data * data
int v
流式 ZIP 写入器:open → addFile×N → finish。
Definition zip_writer.h:64
bool open(const std::string &outPath)
打开目标归档;必须在任何 addFile() 之前调用。
Definition zip_writer.h:67
bool finish()
写入中央目录 + EOCD 并关闭归档。
Definition zip_writer.h:115
bool addFile(const std::string &relPath, const char *data, size_t size)
添加一个普通文件;relPath 使用 '/' 分隔、相对归档根。
Definition zip_writer.h:73
void put16(std::vector< char > &b, uint16_t v)
以小端序写入 uint16 / uint32。
Definition zip_writer.h:50
bool rawDeflate(const char *in, size_t inLen, std::vector< char > &out)
Raw deflate(无 zlib/gzip 包装),ZIP method 8 条目需要。
Definition zip_writer.h:27
void put32(std::vector< char > &b, uint32_t v)
Definition zip_writer.h:54
Minimal streaming ZIP writer used by eve zip / eve package to produce a PhysFS-mountable game archive...
Definition zip_writer.h:19
bool createGameArchive(const std::filesystem::path &gameDir, const std::filesystem::path &outArchive)
Definition zip_writer.h:181
#define EVE_ZIP_METHOD
0 = STORED(不压缩),8 = DEFLATE。
Definition zip_writer.h:22