LCOV - code coverage report
Current view: top level - shared - zip.cpp (source / functions) Hit Total Coverage
Test: Libprimis Test Coverage Lines: 43 415 10.4 %
Date: 2024-11-22 05:07:59 Functions: 8 29 27.6 %

          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             : 

Generated by: LCOV version 1.14