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