Line data Source code
1 :
2 : #include <cstring>
3 : #include <functional>
4 : #include <string>
5 : #include <queue>
6 : #include <map>
7 : #include <array>
8 : #include <algorithm>
9 :
10 : #include <SDL.h>
11 :
12 : #include <GL/glew.h>
13 :
14 : #include <GL/gl.h>
15 :
16 : #include <zlib.h>
17 :
18 : #include "../libprimis-headers/tools.h"
19 : #include "../libprimis-headers/command.h"
20 : #include "../libprimis-headers/consts.h"
21 :
22 : #include "stream.h"
23 :
24 : #include "../engine/interface/console.h"
25 :
26 : enum ZipFlags
27 : {
28 : Zip_LocalFileSignature = 0x04034B50,
29 : Zip_LocalFileSize = 30,
30 : Zip_FileSignature = 0x02014B50,
31 : Zip_FileSize = 46,
32 : Zip_DirectorySignature = 0x06054B50,
33 : Zip_DirectorySize = 22
34 : };
35 :
36 : struct ziplocalfileheader
37 : {
38 : uint signature;
39 : ushort version, flags, compression, modtime, moddate;
40 : uint crc32, compressedsize, uncompressedsize;
41 : ushort namelength, extralength;
42 : };
43 :
44 : struct zipfileheader
45 : {
46 : uint signature;
47 : ushort version, needversion, flags, compression, modtime, moddate;
48 : uint crc32, compressedsize, uncompressedsize;
49 : ushort namelength, extralength, commentlength, disknumber, internalattribs;
50 : uint externalattribs, offset;
51 : };
52 :
53 : struct zipdirectoryheader
54 : {
55 : uint signature;
56 : ushort disknumber, directorydisk, diskentries, entries;
57 : uint size, offset;
58 : ushort commentlength;
59 : };
60 :
61 : struct zipfile
62 : {
63 : char *name;
64 : uint header, offset, size, compressedsize;
65 :
66 0 : zipfile() : name(nullptr), header(0), offset(~0U), size(0), compressedsize(0)
67 : {
68 0 : }
69 0 : ~zipfile()
70 : {
71 0 : delete[] name;
72 0 : }
73 : };
74 :
75 : class zipstream;
76 :
77 : struct ziparchive
78 : {
79 : char *name;
80 : FILE *data;
81 : std::map<std::string, zipfile> files;
82 : int openfiles;
83 : zipstream *owner;
84 :
85 0 : ziparchive() : name(nullptr), data(nullptr), files(), openfiles(0), owner(nullptr)
86 : {
87 0 : }
88 0 : ~ziparchive()
89 : {
90 0 : delete[] name;
91 0 : if(data)
92 : {
93 0 : std::fclose(data);
94 0 : data = nullptr;
95 : }
96 0 : }
97 : };
98 :
99 0 : static bool findzipdirectory(FILE *f, zipdirectoryheader &hdr)
100 : {
101 0 : if(fseek(f, 0, SEEK_END) < 0)
102 : {
103 0 : return false;
104 : }
105 0 : long offset = ftell(f);
106 0 : if(offset < 0)
107 : {
108 0 : return false;
109 : }
110 : std::array<uchar, 1024> buf;
111 0 : uchar *src = nullptr;
112 0 : long end = std::max<long>(offset - 0xFFFFL - Zip_DirectorySize, 0L);
113 0 : size_t len = 0;
114 0 : const uint signature = static_cast<uint>(Zip_DirectorySignature);
115 0 : while(offset > end)
116 : {
117 0 : size_t carry = std::min<size_t>(len, static_cast<size_t>(Zip_DirectorySize-1)), next = std::min<size_t>(sizeof(buf) - carry, static_cast<size_t>(offset - end));
118 0 : offset -= next;
119 0 : std::memmove(&buf[next], buf.data(), carry);
120 0 : if(next + carry < Zip_DirectorySize || fseek(f, offset, SEEK_SET) < 0 || std::fread(buf.data(), 1, next, f) != next)
121 : {
122 0 : return false;
123 : }
124 0 : len = next + carry;
125 0 : uchar *search = &buf[next-1];
126 0 : for(; search >= buf.data(); search--)
127 : {
128 0 : if(*reinterpret_cast<uint *>(search) == signature)
129 : {
130 0 : break;
131 : }
132 : }
133 0 : if(search >= buf.data())
134 : {
135 0 : src = search;
136 0 : break;
137 : }
138 : }
139 0 : if(!src || &buf[len] - src < Zip_DirectorySize)
140 : {
141 0 : return false;
142 : }
143 0 : hdr.signature = *reinterpret_cast<uint *>(src); src += 4; //src is incremented by the size of the field (int is 4 bytes)
144 0 : hdr.disknumber = *reinterpret_cast<ushort *>(src); src += 2;
145 0 : hdr.directorydisk = *reinterpret_cast<ushort *>(src); src += 2;
146 0 : hdr.diskentries = *reinterpret_cast<ushort *>(src); src += 2;
147 0 : hdr.entries = *reinterpret_cast<ushort *>(src); src += 2;
148 0 : hdr.size = *reinterpret_cast<uint *>(src); src += 4;
149 0 : hdr.offset = *reinterpret_cast<uint *>(src); src += 4;
150 0 : hdr.commentlength = *reinterpret_cast<ushort *>(src); src += 2;
151 0 : if(hdr.signature != Zip_DirectorySignature || hdr.disknumber != hdr.directorydisk || hdr.diskentries != hdr.entries)
152 : {
153 0 : return false;
154 : }
155 0 : return true;
156 : }
157 :
158 : VAR(debugzip, 0, 0, 1);
159 :
160 0 : static bool readzipdirectory(const char *archname, FILE *f, int entries, int offset, uint size, std::vector<zipfile> &files)
161 : {
162 0 : uchar *buf = new uchar[size],
163 0 : *src = buf;
164 0 : if(!buf || fseek(f, offset, SEEK_SET) < 0 || std::fread(buf, 1, size, f) != size)
165 : {
166 0 : delete[] buf;
167 0 : return false;
168 : }
169 0 : for(int i = 0; i < entries; ++i)
170 : {
171 0 : if(src + Zip_FileSize > &buf[size])
172 : {
173 0 : break;
174 : }
175 : zipfileheader hdr;
176 0 : hdr.signature = *reinterpret_cast<uint *>(src); src += 4; //src is incremented by the size of the field (int is 4 bytes)
177 0 : hdr.version = *reinterpret_cast<ushort *>(src); src += 2;
178 0 : hdr.needversion = *reinterpret_cast<ushort *>(src); src += 2;
179 0 : hdr.flags = *reinterpret_cast<ushort *>(src); src += 2;
180 0 : hdr.compression = *reinterpret_cast<ushort *>(src); src += 2;
181 0 : hdr.modtime = *reinterpret_cast<ushort *>(src); src += 2;
182 0 : hdr.moddate = *reinterpret_cast<ushort *>(src); src += 2;
183 0 : hdr.crc32 = *reinterpret_cast<uint *>(src); src += 4;
184 0 : hdr.compressedsize = *reinterpret_cast<uint *>(src); src += 4;
185 0 : hdr.uncompressedsize = *reinterpret_cast<uint *>(src); src += 4;
186 0 : hdr.namelength = *reinterpret_cast<ushort *>(src); src += 2;
187 0 : hdr.extralength = *reinterpret_cast<ushort *>(src); src += 2;
188 0 : hdr.commentlength = *reinterpret_cast<ushort *>(src); src += 2;
189 0 : hdr.disknumber = *reinterpret_cast<ushort *>(src); src += 2;
190 0 : hdr.internalattribs = *reinterpret_cast<ushort *>(src); src += 2;
191 0 : hdr.externalattribs = *reinterpret_cast<uint *>(src); src += 4;
192 0 : hdr.offset = *reinterpret_cast<uint *>(src); src += 4;
193 0 : if(hdr.signature != Zip_FileSignature)
194 : {
195 0 : break;
196 : }
197 0 : if(!hdr.namelength || !hdr.uncompressedsize || (hdr.compression && (hdr.compression != Z_DEFLATED || !hdr.compressedsize)))
198 : {
199 0 : src += hdr.namelength + hdr.extralength + hdr.commentlength;
200 0 : continue;
201 : }
202 0 : if(src + hdr.namelength > &buf[size])
203 : {
204 0 : break;
205 : }
206 : string pname;
207 0 : int namelen = std::min<int>(static_cast<int>(hdr.namelength), static_cast<int>(sizeof(pname)-1));
208 0 : std::memcpy(pname, src, namelen);
209 0 : pname[namelen] = '\0';
210 0 : path(pname);
211 0 : char *name = newstring(pname);
212 : {
213 0 : zipfile zf;
214 0 : zf.name = name;
215 0 : zf.header = hdr.offset;
216 0 : zf.size = hdr.uncompressedsize;
217 0 : files.push_back(zf);
218 0 : zf.compressedsize = hdr.compression ? hdr.compressedsize : 0;
219 0 : }
220 0 : if(debugzip)
221 : {
222 0 : conoutf(Console_Debug, "%s: file %s, size %d, compress %d, flags %x", archname, name, hdr.uncompressedsize, hdr.compression, hdr.flags);
223 : }
224 0 : src += hdr.namelength + hdr.extralength + hdr.commentlength;
225 : }
226 0 : delete[] buf;
227 0 : return files.size() > 0;
228 : }
229 :
230 0 : static bool readlocalfileheader(FILE *f, ziplocalfileheader &h, uint offset)
231 : {
232 : uchar buf[Zip_LocalFileSize];
233 0 : if(fseek(f, offset, SEEK_SET) < 0 || std::fread(buf, 1, Zip_LocalFileSize, f) != Zip_LocalFileSize)
234 : {
235 0 : return false;
236 : }
237 0 : uchar *src = buf;
238 0 : h.signature = *reinterpret_cast<uint *>(src); src += 4; //src is incremented by the size of the field (int is 4 bytes e.g)
239 0 : h.version = *reinterpret_cast<ushort *>(src); src += 2;
240 0 : h.flags = *reinterpret_cast<ushort *>(src); src += 2;
241 0 : h.compression = *reinterpret_cast<ushort *>(src); src += 2;
242 0 : h.modtime = *reinterpret_cast<ushort *>(src); src += 2;
243 0 : h.moddate = *reinterpret_cast<ushort *>(src); src += 2;
244 0 : h.crc32 = *reinterpret_cast<uint *>(src); src += 4;
245 0 : h.compressedsize = *reinterpret_cast<uint *>(src); src += 4;
246 0 : h.uncompressedsize = *reinterpret_cast<uint *>(src); src += 4;
247 0 : h.namelength = *reinterpret_cast<ushort *>(src); src += 2;
248 0 : h.extralength = *reinterpret_cast<ushort *>(src); src += 2;
249 0 : if(h.signature != Zip_LocalFileSignature)
250 : {
251 0 : return false;
252 : }
253 : // h.uncompressedsize or h.compressedsize may be zero - so don't validate
254 0 : return true;
255 : }
256 :
257 : static std::vector<ziparchive *> archives;
258 :
259 2 : ziparchive *findzip(const char *name)
260 : {
261 2 : for(size_t i = 0; i < archives.size(); i++)
262 : {
263 0 : if(!std::strcmp(name, archives[i]->name))
264 : {
265 0 : return archives[i];
266 : }
267 : }
268 2 : return nullptr;
269 : }
270 :
271 0 : static bool checkprefix(std::vector<zipfile> &files, const char *prefix, int prefixlen)
272 : {
273 0 : for(const zipfile &z : files)
274 : {
275 0 : if(!std::strncmp(z.name, prefix, prefixlen))
276 : {
277 0 : return false;
278 : }
279 : }
280 0 : return true;
281 : }
282 :
283 0 : static void mountzip(ziparchive &arch, std::vector<zipfile> &files, const char *mountdir, const char *stripdir)
284 : {
285 0 : string packagesdir = "media/";
286 0 : path(packagesdir);
287 0 : size_t striplen = stripdir ? std::strlen(stripdir) : 0;
288 0 : if(!mountdir && !stripdir)
289 : {
290 0 : for(zipfile &f : files)
291 : {
292 0 : const char *foundpackages = std::strstr(f.name, packagesdir);
293 0 : if(foundpackages)
294 : {
295 0 : if(foundpackages > f.name)
296 : {
297 0 : stripdir = f.name;
298 0 : striplen = foundpackages - f.name;
299 : }
300 0 : break;
301 : }
302 0 : const char *foundogz = std::strstr(f.name, ".ogz");
303 0 : if(foundogz)
304 : {
305 0 : const char *ogzdir = foundogz;
306 0 : while(--ogzdir >= f.name && *ogzdir != PATHDIV)
307 : {
308 : //(empty body)
309 : }
310 0 : if(ogzdir < f.name || checkprefix(files, f.name, ogzdir + 1 - f.name))
311 : {
312 0 : if(ogzdir >= f.name)
313 : {
314 0 : stripdir = f.name;
315 0 : striplen = ogzdir + 1 - f.name;
316 : }
317 0 : if(!mountdir)
318 : {
319 0 : mountdir = "media/map/";
320 : }
321 0 : break;
322 : }
323 : }
324 : }
325 : }
326 0 : string mdir = "", fname;
327 0 : if(mountdir)
328 : {
329 0 : copystring(mdir, mountdir);
330 0 : if(fixpackagedir(mdir) <= 1)
331 : {
332 0 : mdir[0] = '\0';
333 : }
334 : }
335 0 : for(zipfile &f : files)
336 : {
337 0 : formatstring(fname, "%s%s", mdir, striplen && !std::strncmp(f.name, stripdir, striplen) ? &f.name[striplen] : f.name);
338 0 : if(arch.files.find(fname) != arch.files.end())
339 : {
340 0 : continue;
341 : }
342 0 : char *mname = newstring(fname);
343 0 : zipfile &mf = arch.files[mname];
344 0 : mf = f;
345 0 : mf.name = mname;
346 : }
347 0 : }
348 :
349 1 : bool addzip(const char *name, const char *mount = nullptr, const char *strip = nullptr)
350 : {
351 : string pname;
352 1 : copystring(pname, name);
353 1 : path(pname);
354 1 : size_t plen = std::strlen(pname);
355 1 : if(plen < 4 || !std::strchr(&pname[plen-4], '.'))
356 : {
357 1 : concatstring(pname, ".zip");
358 : }
359 1 : const ziparchive *exists = findzip(pname);
360 1 : if(exists)
361 : {
362 0 : conoutf(Console_Error, "already added zip %s", pname);
363 0 : return true;
364 : }
365 :
366 1 : FILE *f = fopen(findfile(pname, "rb"), "rb");
367 1 : if(!f)
368 : {
369 1 : conoutf(Console_Error, "could not open file %s", pname);
370 1 : return false;
371 : }
372 : zipdirectoryheader h;
373 0 : std::vector<zipfile> files;
374 0 : if(!findzipdirectory(f, h) || !readzipdirectory(pname, f, h.entries, h.offset, h.size, files))
375 : {
376 0 : conoutf(Console_Error, "could not read directory in zip %s", pname);
377 0 : std::fclose(f);
378 0 : return false;
379 : }
380 0 : ziparchive *arch = new ziparchive;
381 0 : arch->name = newstring(pname);
382 0 : arch->data = f;
383 0 : mountzip(*arch, files, mount, strip);
384 0 : archives.push_back(arch);
385 0 : conoutf("added zip %s", pname);
386 0 : return true;
387 0 : }
388 :
389 1 : bool removezip(const char *name)
390 : {
391 : string pname;
392 1 : copystring(pname, name);
393 1 : path(pname);
394 1 : int plen = static_cast<int>(std::strlen(pname));
395 1 : if(plen < 4 || !std::strchr(&pname[plen-4], '.'))
396 : {
397 1 : concatstring(pname, ".zip");
398 : }
399 1 : const ziparchive *exists = findzip(pname);
400 1 : if(!exists)
401 : {
402 1 : conoutf(Console_Error, "zip %s is not loaded", pname);
403 1 : return false;
404 : }
405 0 : if(exists->openfiles)
406 : {
407 0 : conoutf(Console_Error, "zip %s has open files", pname);
408 0 : return false;
409 : }
410 0 : conoutf("removed zip %s", exists->name);
411 0 : archives.erase(std::find(archives.begin(), archives.end(), exists));
412 0 : delete exists;
413 0 : return true;
414 : }
415 :
416 : class zipstream final : public stream
417 : {
418 : public:
419 : enum
420 : {
421 : Buffer_Size = 16384
422 : };
423 :
424 0 : zipstream() : arch(nullptr), info(nullptr), buf(nullptr), reading(~0U), ended(false)
425 : {
426 0 : zfile.zalloc = nullptr;
427 0 : zfile.zfree = nullptr;
428 0 : zfile.opaque = nullptr;
429 0 : zfile.next_in = zfile.next_out = nullptr;
430 0 : zfile.avail_in = zfile.avail_out = 0;
431 0 : }
432 :
433 0 : ~zipstream()
434 0 : {
435 0 : close();
436 0 : }
437 :
438 0 : void readbuf(uint size = Buffer_Size)
439 : {
440 0 : if(!zfile.avail_in)
441 : {
442 0 : zfile.next_in = static_cast<Bytef *>(buf);
443 : }
444 0 : size = std::min<uint>(size, static_cast<uint>(&buf[Buffer_Size] - &zfile.next_in[zfile.avail_in]));
445 0 : if(arch->owner != this)
446 : {
447 0 : arch->owner = nullptr;
448 0 : if(fseek(arch->data, reading, SEEK_SET) >= 0)
449 : {
450 0 : arch->owner = this;
451 : }
452 : else
453 : {
454 0 : return;
455 : }
456 : }
457 0 : uint remaining = info->offset + info->compressedsize - reading,
458 0 : n = arch->owner == this ? std::fread(zfile.next_in + zfile.avail_in, 1, std::min<uint>(size, remaining), arch->data) : 0U;
459 0 : zfile.avail_in += n;
460 0 : reading += n;
461 : }
462 :
463 0 : bool open(ziparchive *a, zipfile *f)
464 : {
465 0 : if(f->offset == ~0U)
466 : {
467 : ziplocalfileheader h;
468 0 : a->owner = nullptr;
469 0 : if(!readlocalfileheader(a->data, h, f->header))
470 : {
471 0 : return false;
472 : }
473 0 : f->offset = f->header + Zip_LocalFileSize + h.namelength + h.extralength;
474 : }
475 0 : if(f->compressedsize && inflateInit2(&zfile, -MAX_WBITS) != Z_OK)
476 : {
477 0 : return false;
478 : }
479 0 : a->openfiles++;
480 0 : arch = a;
481 0 : info = f;
482 0 : reading = f->offset;
483 0 : ended = false;
484 0 : if(f->compressedsize)
485 : {
486 0 : buf = new uchar[Buffer_Size];
487 : }
488 0 : return true;
489 : }
490 :
491 0 : void stopreading()
492 : {
493 0 : if(reading == ~0U)
494 : {
495 0 : return;
496 : }
497 0 : if(debugzip)
498 : {
499 0 : conoutf(Console_Debug, info->compressedsize ? "%s: zfile.total_out %u, info->size %u" : "%s: reading %u, info->size %u", info->name, info->compressedsize ? static_cast<uint>(zfile.total_out) : reading - info->offset, info->size);
500 : }
501 0 : if(info->compressedsize)
502 : {
503 0 : inflateEnd(&zfile);
504 : }
505 0 : reading = ~0U;
506 : }
507 :
508 0 : void close() final
509 : {
510 0 : stopreading();
511 0 : delete[] buf;
512 0 : buf = nullptr;
513 0 : if(arch)
514 : {
515 0 : arch->owner = nullptr;
516 0 : arch->openfiles--;
517 0 : arch = nullptr;
518 : }
519 0 : }
520 :
521 0 : offset size() final
522 : {
523 0 : return info->size;
524 : }
525 :
526 0 : bool end() final
527 : {
528 0 : return reading == ~0U || ended;
529 : }
530 :
531 0 : offset tell() const final
532 : {
533 0 : return reading != ~0U ? (info->compressedsize ? zfile.total_out : reading - info->offset) : offset(-1);
534 : }
535 :
536 0 : bool seek(offset pos, int whence) final
537 : {
538 0 : if(reading == ~0U)
539 : {
540 0 : return false;
541 : }
542 0 : if(!info->compressedsize)
543 : {
544 0 : switch(whence)
545 : {
546 0 : case SEEK_END:
547 : {
548 0 : pos += info->offset + info->size;
549 0 : break;
550 : }
551 0 : case SEEK_CUR:
552 : {
553 0 : pos += reading;
554 0 : break;
555 : }
556 0 : case SEEK_SET:
557 : {
558 0 : pos += info->offset;
559 0 : break;
560 : }
561 0 : default:
562 : {
563 0 : return false;
564 : }
565 : }
566 0 : pos = std::clamp(pos, static_cast<offset>(info->offset), static_cast<offset>(info->offset + info->size));
567 0 : arch->owner = nullptr;
568 0 : if(fseek(arch->data, static_cast<int>(pos), SEEK_SET) < 0)
569 : {
570 0 : return false;
571 : }
572 0 : arch->owner = this;
573 0 : reading = pos;
574 0 : ended = false;
575 0 : return true;
576 : }
577 0 : switch(whence)
578 : {
579 0 : case SEEK_END:
580 : {
581 0 : pos += info->size;
582 0 : break;
583 : }
584 0 : case SEEK_CUR:
585 : {
586 0 : pos += zfile.total_out;
587 0 : break;
588 : }
589 0 : case SEEK_SET:
590 : {
591 0 : break;
592 : }
593 0 : default:
594 : {
595 0 : return false;
596 : }
597 : }
598 0 : if(pos >= static_cast<offset>(info->size))
599 : {
600 0 : reading = info->offset + info->compressedsize;
601 0 : zfile.next_in += zfile.avail_in;
602 0 : zfile.avail_in = 0;
603 0 : zfile.total_in = info->compressedsize;
604 0 : zfile.total_out = info->size;
605 0 : arch->owner = nullptr;
606 0 : ended = false;
607 0 : return true;
608 : }
609 0 : if(pos < 0)
610 : {
611 0 : return false;
612 : }
613 0 : if(pos >= static_cast<offset>(zfile.total_out))
614 : {
615 0 : pos -= zfile.total_out;
616 : }
617 : else
618 : {
619 0 : if(zfile.next_in && zfile.total_in <= static_cast<uint>(zfile.next_in - buf))
620 : {
621 0 : zfile.avail_in += zfile.total_in;
622 0 : zfile.next_in -= zfile.total_in;
623 : }
624 : else
625 : {
626 0 : arch->owner = nullptr;
627 0 : zfile.avail_in = 0;
628 0 : zfile.next_in = nullptr;
629 0 : reading = info->offset;
630 : }
631 0 : inflateReset(&zfile);
632 : }
633 : uchar skip[512];
634 0 : while(pos > 0)
635 : {
636 0 : size_t skipped = static_cast<size_t>(std::min<offset>(pos, static_cast<offset>(sizeof(skip))));
637 0 : if(read(skip, skipped) != skipped)
638 : {
639 0 : return false;
640 : }
641 0 : pos -= skipped;
642 : }
643 :
644 0 : ended = false;
645 0 : return true;
646 : }
647 :
648 0 : size_t read(void *inbuf, size_t len) final
649 : {
650 0 : if(reading == ~0U || !inbuf || !len)
651 : {
652 0 : return 0;
653 : }
654 0 : if(!info->compressedsize)
655 : {
656 0 : if(arch->owner != this)
657 : {
658 0 : arch->owner = nullptr;
659 0 : if(fseek(arch->data, reading, SEEK_SET) < 0)
660 : {
661 0 : stopreading();
662 0 : return 0;
663 : }
664 0 : arch->owner = this;
665 : }
666 0 : size_t n = std::fread(inbuf, 1, std::min<size_t>(len, static_cast<size_t>(info->size + info->offset - reading)), arch->data);
667 0 : reading += n;
668 0 : if(n < len)
669 : {
670 0 : ended = true;
671 : }
672 0 : return n;
673 : }
674 0 : zfile.next_out = static_cast<Bytef *>(inbuf);
675 0 : zfile.avail_out = len;
676 0 : while(zfile.avail_out > 0)
677 : {
678 0 : if(!zfile.avail_in)
679 : {
680 0 : readbuf(Buffer_Size);
681 : }
682 0 : int err = inflate(&zfile, Z_NO_FLUSH);
683 0 : if(err != Z_OK)
684 : {
685 0 : if(err == Z_STREAM_END)
686 : {
687 0 : ended = true;
688 : }
689 : else
690 : {
691 0 : if(debugzip)
692 : {
693 0 : conoutf(Console_Debug, "inflate error: %s", zError(err));
694 : }
695 0 : stopreading();
696 : }
697 0 : break;
698 : }
699 : }
700 0 : return len - zfile.avail_out;
701 : }
702 :
703 : private:
704 : ziparchive *arch;
705 : const zipfile *info;
706 : z_stream zfile;
707 : uchar *buf;
708 : uint reading;
709 : bool ended;
710 : };
711 :
712 20 : stream *openzipfile(const char *name, const char *mode)
713 : {
714 56 : for(; *mode; mode++)
715 : {
716 36 : if(*mode=='w' || *mode=='a')
717 : {
718 0 : return nullptr;
719 : }
720 : }
721 20 : for(int i = archives.size(); --i >=0;) //note reverse iteration
722 : {
723 0 : ziparchive *arch = archives[i];
724 0 : auto itr = arch->files.find(name);
725 0 : zipfile *f = nullptr;
726 0 : if(itr == arch->files.end())
727 : {
728 0 : continue;
729 : }
730 0 : f = &(*(itr)).second;
731 0 : zipstream *s = new zipstream;
732 0 : if(s->open(arch, f))
733 : {
734 0 : return s;
735 : }
736 0 : delete s;
737 : }
738 20 : return nullptr;
739 : }
740 :
741 1 : bool findzipfile(const char *name)
742 : {
743 1 : for(int i = archives.size(); --i >=0;) //note reverse iteration
744 : {
745 0 : const ziparchive *arch = archives[i];
746 0 : if(arch->files.find(name) != arch->files.end())
747 : {
748 0 : return true;
749 : }
750 : }
751 1 : return false;
752 : }
753 :
754 1 : int listzipfiles(const char *dir, const char *ext, std::vector<char *> &files)
755 : {
756 1 : size_t extsize = ext ? std::strlen(ext)+1 : 0,
757 1 : dirsize = std::strlen(dir);
758 1 : int dirs = 0;
759 1 : for(int i = archives.size(); --i >=0;) //note reverse iteration
760 : {
761 0 : const ziparchive *arch = archives[i];
762 0 : uint oldsize = files.size();
763 0 : for(const auto& [k, f] : arch->files)
764 : {
765 0 : if(std::strncmp(k.c_str(), dir, dirsize))
766 : {
767 0 : continue;
768 : }
769 0 : const char *name = k.c_str() + dirsize;
770 0 : if(name[0] == PATHDIV)
771 : {
772 0 : name++;
773 : }
774 0 : if(std::strchr(name, PATHDIV))
775 : {
776 0 : continue;
777 : }
778 0 : if(!ext)
779 : {
780 0 : files.push_back(newstring(name));
781 : }
782 : else
783 : {
784 0 : size_t namelen = std::strlen(name);
785 0 : if(namelen > extsize)
786 : {
787 0 : namelen -= extsize;
788 0 : if(name[namelen] == '.' && std::strncmp(name+namelen+1, ext, extsize-1)==0)
789 : {
790 0 : files.push_back(newstring(name, namelen));
791 : }
792 : }
793 : }
794 : }
795 0 : if(files.size() > oldsize)
796 : {
797 0 : dirs++;
798 : }
799 : }
800 1 : return dirs;
801 : }
802 :
803 1 : void initzipcmds()
804 : {
805 2 : addcommand("addzip", reinterpret_cast<identfun>(+[](const char *name, const char *mount, const char *strip){addzip(name, mount[0] ? mount : nullptr, strip[0] ? strip : nullptr);}), "sss", Id_Command);
806 1 : addcommand("removezip", reinterpret_cast<identfun>(removezip), "s", Id_Command);
807 1 : }
808 :
|