LCOV - code coverage report
Current view: top level - shared - zip.cpp (source / functions) Coverage Total Hit
Test: Libprimis Test Coverage Lines: 10.4 % 415 43
Test Date: 2026-04-10 06:41:26 Functions: 27.6 % 29 8

            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              : 
        

Generated by: LCOV version 2.0-1