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: 2025-02-21 06:59:27 Functions: 27.6 % 29 8

            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(*reinterpret_cast<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 = *reinterpret_cast<uint *>(src); src += 4; //src is incremented by the size of the field (int is 4 bytes)
     124            0 :     hdr.disknumber = *reinterpret_cast<ushort *>(src); src += 2;
     125            0 :     hdr.directorydisk = *reinterpret_cast<ushort *>(src); src += 2;
     126            0 :     hdr.diskentries = *reinterpret_cast<ushort *>(src); src += 2;
     127            0 :     hdr.entries = *reinterpret_cast<ushort *>(src); src += 2;
     128            0 :     hdr.size = *reinterpret_cast<uint *>(src); src += 4;
     129            0 :     hdr.offset = *reinterpret_cast<uint *>(src); src += 4;
     130            0 :     hdr.commentlength = *reinterpret_cast<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   = *reinterpret_cast<uint *>(src); src += 4; //src is incremented by the size of the field (int is 4 bytes)
     157            0 :         hdr.version     = *reinterpret_cast<ushort *>(src); src += 2;
     158            0 :         hdr.needversion = *reinterpret_cast<ushort *>(src); src += 2;
     159            0 :         hdr.flags       = *reinterpret_cast<ushort *>(src); src += 2;
     160            0 :         hdr.compression = *reinterpret_cast<ushort *>(src); src += 2;
     161            0 :         hdr.modtime     = *reinterpret_cast<ushort *>(src); src += 2;
     162            0 :         hdr.moddate     = *reinterpret_cast<ushort *>(src); src += 2;
     163            0 :         hdr.crc32            = *reinterpret_cast<uint *>(src); src += 4;
     164            0 :         hdr.compressedsize   = *reinterpret_cast<uint *>(src); src += 4;
     165            0 :         hdr.uncompressedsize = *reinterpret_cast<uint *>(src); src += 4;
     166            0 :         hdr.namelength       = *reinterpret_cast<ushort *>(src); src += 2;
     167            0 :         hdr.extralength      = *reinterpret_cast<ushort *>(src); src += 2;
     168            0 :         hdr.commentlength    = *reinterpret_cast<ushort *>(src); src += 2;
     169            0 :         hdr.disknumber       = *reinterpret_cast<ushort *>(src); src += 2;
     170            0 :         hdr.internalattribs  = *reinterpret_cast<ushort *>(src); src += 2;
     171            0 :         hdr.externalattribs  = *reinterpret_cast<uint *>(src); src += 4;
     172            0 :         hdr.offset           = *reinterpret_cast<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 = *reinterpret_cast<uint *>(src); src += 4; //src is incremented by the size of the field (int is 4 bytes e.g)
     217            0 :     h.version = *reinterpret_cast<ushort *>(src); src += 2;
     218            0 :     h.flags = *reinterpret_cast<ushort *>(src); src += 2;
     219            0 :     h.compression = *reinterpret_cast<ushort *>(src); src += 2;
     220            0 :     h.modtime = *reinterpret_cast<ushort *>(src); src += 2;
     221            0 :     h.moddate = *reinterpret_cast<ushort *>(src); src += 2;
     222            0 :     h.crc32 = *reinterpret_cast<uint *>(src); src += 4;
     223            0 :     h.compressedsize = *reinterpret_cast<uint *>(src); src += 4;
     224            0 :     h.uncompressedsize = *reinterpret_cast<uint *>(src); src += 4;
     225            0 :     h.namelength = *reinterpret_cast<ushort *>(src); src += 2;
     226            0 :     h.extralength = *reinterpret_cast<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(size_t 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(const zipfile &z : files)
     252              :     {
     253            0 :         if(!std::strncmp(z.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 = static_cast<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 = static_cast<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 = static_cast<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              : 
        

Generated by: LCOV version 2.0-1