LCOV - code coverage report
Current view: top level - shared - stream.cpp (source / functions) Coverage Total Hit
Test: Libprimis Test Coverage Lines: 31.5 % 644 203
Test Date: 2026-04-10 06:41:26 Functions: 37.3 % 67 25

            Line data    Source code
       1              : /* stream.cpp: utilities for character streams
       2              :  *
       3              :  * stream.cpp defines character handling to enable character streams to be written
       4              :  * into and out of files
       5              :  * also included is utilities for gz archive support
       6              :  */
       7              : #include <sstream>
       8              : 
       9              : #include <cstring>
      10              : #include <functional>
      11              : #include <queue>
      12              : #include <stack>
      13              : #include <array>
      14              : #include <algorithm>
      15              : 
      16              : #include <SDL.h>
      17              : 
      18              : #include <GL/glew.h>
      19              : 
      20              : #include <GL/gl.h>
      21              : 
      22              : #include <zlib.h>
      23              : 
      24              : #include "../libprimis-headers/tools.h"
      25              : #include "../libprimis-headers/command.h"
      26              : #include "../libprimis-headers/consts.h"
      27              : 
      28              : #include "stream.h"
      29              : 
      30              : #include "../engine/interface/console.h"
      31              : 
      32              : ///////////////////////// character conversion /////////////////////////////////
      33              : 
      34              : #define CUBECTYPE(s, p, d, a, A, u, U) \
      35              :     0, U, U, U, U, U, U, U, U, s, s, s, s, s, U, U, \
      36              :     U, U, U, U, U, U, U, U, U, U, U, U, U, U, U, U, \
      37              :     s, p, p, p, p, p, p, p, p, p, p, p, p, p, p, p, \
      38              :     d, d, d, d, d, d, d, d, d, d, p, p, p, p, p, p, \
      39              :     p, A, A, A, A, A, A, A, A, A, A, A, A, A, A, A, \
      40              :     A, A, A, A, A, A, A, A, A, A, A, p, p, p, p, p, \
      41              :     p, a, a, a, a, a, a, a, a, a, a, a, a, a, a, a, \
      42              :     a, a, a, a, a, a, a, a, a, a, a, p, p, p, p, U, \
      43              :     U, u, u, u, u, u, u, u, u, u, u, u, u, u, u, u, \
      44              :     u, u, u, u, u, u, u, u, u, u, u, u, u, u, u, U, \
      45              :     u, U, u, U, u, U, u, U, u, U, u, U, u, U, u, U, \
      46              :     u, U, u, U, u, U, u, U, u, U, u, U, u, U, u, U, \
      47              :     u, U, u, U, u, U, u, U, U, u, U, u, U, u, U, U, \
      48              :     U, U, U, U, U, U, U, U, U, U, U, U, U, U, U, U, \
      49              :     U, U, U, U, u, u, u, u, u, u, u, u, u, u, u, u, \
      50              :     u, u, u, u, u, u, u, u, u, u, u, u, u, u, U, u
      51              : 
      52              : /* note here:
      53              :  * these vars are declared extern inline to allow a `const` (implicitly also
      54              :  * `static`) to be linked to other files as a `const`.
      55              :  *
      56              :  * these vars cannot be `constexpr` due to it not being legal to define constexpr
      57              :  * prototypes in headers
      58              :  */
      59              : 
      60              : extern const uchar cubectype[256] =
      61              : {
      62              :     CUBECTYPE(CubeType_Space,
      63              :               CubeType_Print,
      64              :               CubeType_Print | CubeType_Digit,
      65              :               CubeType_Print | CubeType_Alpha | CubeType_Lower,
      66              :               CubeType_Print | CubeType_Alpha | CubeType_Upper,
      67              :               CubeType_Print | CubeType_Unicode | CubeType_Alpha | CubeType_Lower,
      68              :               CubeType_Print | CubeType_Unicode | CubeType_Alpha | CubeType_Upper)
      69              : };
      70              : 
      71          696 : size_t encodeutf8(uchar *dstbuf, size_t dstlen, const uchar *srcbuf, size_t srclen, size_t *carry)
      72              : {
      73          696 :     uchar *dst = dstbuf,
      74          696 :           *dstend = &dstbuf[dstlen];
      75          696 :     const uchar *src = srcbuf,
      76          696 :                 *srcend = &srcbuf[srclen];
      77          696 :     if(src < srcend && dst < dstend)
      78              :     {
      79              :         do
      80              :         {
      81          696 :             int uni = cube2uni(*src);
      82          696 :             if(uni <= 0x7F)
      83              :             {
      84          696 :                 if(dst >= dstend)
      85              :                 {
      86            0 :                     goto done;
      87              :                 }
      88          696 :                 const uchar *end = std::min<const uchar *>(srcend, &src[dstend-dst]);
      89              :                 do
      90              :                 {
      91        13134 :                     if(uni == '\f')
      92              :                     {
      93            0 :                         if(++src >= srcend)
      94              :                         {
      95            0 :                             goto done;
      96              :                         }
      97            0 :                         goto uni1;
      98              :                     }
      99        13134 :                     *dst++ = uni;
     100        13134 :                     if(++src >= end)
     101              :                     {
     102          696 :                         goto done;
     103              :                     }
     104        12438 :                     uni = cube2uni(*src);
     105        12438 :                 } while(uni <= 0x7F);
     106              :             }
     107            0 :             if(uni <= 0x7FF)
     108              :             {
     109            0 :                 if(dst + 2 > dstend)
     110              :                 {
     111            0 :                     goto done;
     112              :                 }
     113            0 :                 *dst++ = 0xC0 | (uni>>6);
     114            0 :                 goto uni2;
     115              :             }
     116            0 :             else if(uni <= 0xFFFF)
     117              :             {
     118            0 :                 if(dst + 3 > dstend)
     119              :                 {
     120            0 :                     goto done;
     121              :                 }
     122            0 :                 *dst++ = 0xE0 | (uni>>12);
     123            0 :                 goto uni3;
     124              :             }
     125            0 :             else if(uni <= 0x1FFFFF)
     126              :             {
     127            0 :                 if(dst + 4 > dstend)
     128              :                 {
     129            0 :                     goto done;
     130              :                 }
     131            0 :                 *dst++ = 0xF0 | (uni>>18);
     132            0 :                 goto uni4;
     133              :             }
     134            0 :             else if(uni <= 0x3FFFFFF)
     135              :             {
     136            0 :                 if(dst + 5 > dstend)
     137              :                 {
     138            0 :                     goto done;
     139              :                 }
     140            0 :                 *dst++ = 0xF8 | (uni>>24);
     141            0 :                 goto uni5;
     142              :             }
     143              :             else if(uni <= 0x7FFFFFFF)
     144              :             {
     145            0 :                 if(dst + 6 > dstend)
     146              :                 {
     147            0 :                     goto done;
     148              :                 }
     149            0 :                 *dst++ = 0xFC | (uni>>30);
     150            0 :                 goto uni6;
     151              :             }
     152              :             else
     153              :             {
     154              :                 goto uni1;
     155              :             }
     156            0 :         uni6: *dst++ = 0x80 | ((uni>>24)&0x3F);
     157            0 :         uni5: *dst++ = 0x80 | ((uni>>18)&0x3F);
     158            0 :         uni4: *dst++ = 0x80 | ((uni>>12)&0x3F);
     159            0 :         uni3: *dst++ = 0x80 | ((uni>>6)&0x3F);
     160            0 :         uni2: *dst++ = 0x80 | (uni&0x3F);
     161            0 :         uni1:;
     162            0 :         } while(++src < srcend);
     163              :     }
     164            0 : done:
     165          696 :     if(carry)
     166              :     {
     167          696 :         *carry += src - srcbuf;
     168              :     }
     169          696 :     return dst - dstbuf;
     170              : }
     171              : 
     172              : ///////////////////////// file system ///////////////////////
     173              : 
     174              : #ifdef WIN32
     175              : #include <shlobj.h>
     176              : #else
     177              : #include <unistd.h>
     178              : #include <sys/stat.h>
     179              : #include <sys/types.h>
     180              : #include <dirent.h>
     181              : #endif
     182              : 
     183              : std::string homedir = "";
     184              : struct packagedir
     185              : {
     186              :     std::string dir;
     187              :     std::string filter;
     188              : };
     189              : static std::vector<packagedir> packagedirs;
     190              : 
     191           12 : char *makerelpath(const char *dir, const char *file, const char *prefix, const char *cmd)
     192              : {
     193              :     static string tmp;
     194           12 :     if(prefix)
     195              :     {
     196            0 :         copystring(tmp, prefix);
     197              :     }
     198              :     else
     199              :     {
     200           12 :         tmp[0] = '\0';
     201              :     }
     202           12 :     if(file[0]=='<')
     203              :     {
     204            0 :         const char *end = std::strrchr(file, '>');
     205            0 :         if(end)
     206              :         {
     207            0 :             size_t len = std::strlen(tmp);
     208            0 :             copystring(&tmp[len], file, std::min<size_t>(sizeof(tmp)-len, static_cast<size_t>(end+2-file)));
     209            0 :             file = end+1;
     210              :         }
     211              :     }
     212           12 :     if(cmd)
     213              :     {
     214            0 :         concatstring(tmp, cmd);
     215              :     }
     216           12 :     if(dir)
     217              :     {
     218           12 :         DEF_FORMAT_STRING(pname, "%s/%s", dir, file);
     219           12 :         concatstring(tmp, pname);
     220              :     }
     221              :     else
     222              :     {
     223            0 :         concatstring(tmp, file);
     224              :     }
     225           12 :     return tmp;
     226              : }
     227              : 
     228           29 : char *path(char *s)
     229              : {
     230           29 :     for(char *curpart = s;;)
     231              :     {
     232           33 :         char *endpart = std::strchr(curpart, '&');
     233           33 :         if(endpart)
     234              :         {
     235            4 :             *endpart = '\0';
     236              :         }
     237           33 :         if(curpart[0]=='<')
     238              :         {
     239            4 :             char *file = std::strrchr(curpart, '>');
     240            4 :             if(!file)
     241              :             {
     242            0 :                 return s;
     243              :             }
     244            4 :             curpart = file+1;
     245              :         }
     246          111 :         for(char *t = curpart; (t = std::strpbrk(t, "/\\")); *t++ = PATHDIV)
     247              :         {
     248              :             //(empty body)
     249              :         }
     250           33 :         for(char *prevdir = nullptr, *curdir = curpart;;)
     251              :         {
     252          104 :             prevdir = curdir[0]==PATHDIV ? curdir+1 : curdir;
     253          104 :             curdir = std::strchr(prevdir, PATHDIV);
     254          104 :             if(!curdir)
     255              :             {
     256           33 :                 break;
     257              :             }
     258           71 :             if(prevdir+1==curdir && prevdir[0]=='.')
     259              :             {
     260            4 :                 std::memmove(prevdir, curdir+1, std::strlen(curdir+1)+1);
     261            4 :                 curdir = prevdir;
     262              :             }
     263           67 :             else if(curdir[1]=='.' && curdir[2]=='.' && curdir[3]==PATHDIV)
     264              :             {
     265            8 :                 if(prevdir+2==curdir && prevdir[0]=='.' && prevdir[1]=='.')
     266              :                 {
     267            0 :                     continue;
     268              :                 }
     269            8 :                 std::memmove(prevdir, curdir+4, std::strlen(curdir+4)+1);
     270            8 :                 if(prevdir-2 >= curpart && prevdir[-1]==PATHDIV)
     271              :                 {
     272            1 :                     prevdir -= 2;
     273            2 :                     while(prevdir-1 >= curpart && prevdir[-1] != PATHDIV)
     274              :                     {
     275            1 :                         --prevdir;
     276              :                     }
     277              :                 }
     278            8 :                 curdir = prevdir;
     279              :             }
     280              :         }
     281           33 :         if(endpart)
     282              :         {
     283            4 :             *endpart = '&';
     284            4 :             curpart = endpart+1;
     285              :         }
     286              :         else
     287              :         {
     288           29 :             break;
     289              :         }
     290            4 :     }
     291           29 :     return s;
     292              : }
     293              : 
     294           58 : std::string path(std::string s)
     295              : {
     296           58 :     std::string truncated_path, processed_path;
     297              : 
     298           58 :     size_t path_begin = 0,
     299           58 :            path_end = s.length();
     300              : 
     301              :     // Find the in-line command segment and skip it
     302           58 :     if(s.find('<') != std::string::npos)
     303              :     {
     304            4 :         size_t command_end = s.rfind('>');
     305            4 :         if(command_end != std::string::npos)
     306              :         {
     307            4 :             path_begin = command_end + 1;
     308              :         }
     309              :     }
     310              : 
     311              :     // Find the conjugated path and cut it
     312           58 :     if(s.find('&') != std::string::npos)
     313              :     {
     314            4 :         path_end = s.find('&');
     315              :     }
     316              : 
     317           58 :     truncated_path = s.substr(path_begin, path_end - path_begin);
     318              : 
     319              :     // Handle "."" and ".."" in the path
     320           58 :     std::istringstream path_stream(truncated_path);
     321           58 :     std::stack<std::string> path_stack;
     322           58 :     std::string token;
     323              : 
     324              :     // Construct a stack of path tokens
     325          271 :     while(std::getline(path_stream, token, '/'))
     326              :     {
     327          213 :         if(token == "..")
     328              :         {
     329            8 :             if(!path_stack.empty())
     330              :             {
     331            6 :                 path_stack.pop();
     332              :             }
     333              :             else
     334              :             {
     335            2 :                 path_stack.push(token);
     336              :             }
     337              :         }
     338          205 :         else if(!token.empty() && token != ".")
     339              :         {
     340          198 :             path_stack.push(token);
     341              :         }
     342              :     }
     343              : 
     344              :     // Re-construct the processed path from the stack
     345          252 :     while(!path_stack.empty())
     346              :     {
     347          194 :         if(path_stack.size() > 1)
     348              :         {
     349          142 :             processed_path = "/" + path_stack.top() + processed_path;
     350              :         }
     351              :         else
     352              :         {
     353           52 :             processed_path = path_stack.top() + processed_path;
     354              :         }
     355          194 :         path_stack.pop();
     356              :     }
     357              : 
     358          116 :     return processed_path;
     359           58 : }
     360              : 
     361            2 : char *copypath(const char *s)
     362              : {
     363              :     static string tmp;
     364            2 :     copystring(tmp, s);
     365            2 :     path(tmp);
     366            2 :     return tmp;
     367              : }
     368              : 
     369            7 : const char *parentdir(const char *directory)
     370              : {
     371            7 :     const char *p = directory + std::strlen(directory);
     372           48 :     while(p > directory && *p != '/' && *p != '\\')
     373              :     {
     374           41 :         p--;
     375              :     }
     376              :     static string parent;
     377            7 :     size_t len = p-directory+1;
     378            7 :     copystring(parent, directory, len);
     379            7 :     return parent;
     380              : }
     381              : 
     382            2 : bool fileexists(const char *path, const char *mode)
     383              : {
     384            2 :     bool exists = true;
     385            2 :     if(mode[0]=='w' || mode[0]=='a')
     386              :     {
     387            1 :         path = parentdir(path);
     388              :     }
     389              : #ifdef WIN32
     390              :     if(GetFileAttributes(path[0] ? path : ".\\") == INVALID_FILE_ATTRIBUTES)
     391              :     {
     392              :         exists = false;
     393              :     }
     394              : #else
     395            2 :     if(access(path[0] ? path : ".", mode[0]=='w' || mode[0]=='a' ? W_OK : (mode[0]=='d' ? X_OK : R_OK)) == -1)
     396              :     {
     397            1 :         exists = false;
     398              :     }
     399              : #endif
     400            2 :     return exists;
     401              : }
     402              : 
     403            1 : bool createdir(const char *path)
     404              : {
     405            1 :     size_t len = std::strlen(path);
     406            1 :     if(path[len-1] == PATHDIV)
     407              :     {
     408              :         static string strip;
     409            1 :         path = copystring(strip, path, len);
     410              :     }
     411              : #ifdef WIN32
     412              :     return CreateDirectory(path, nullptr) != 0;
     413              : #else
     414            1 :     return mkdir(path, 0777) == 0;
     415              : #endif
     416              : }
     417              : 
     418            3 : size_t fixpackagedir(char *dir)
     419              : {
     420            3 :     path(dir);
     421            3 :     size_t len = std::strlen(dir);
     422            3 :     if(len > 0 && dir[len-1] != PATHDIV)
     423              :     {
     424            1 :         dir[len] = PATHDIV;
     425            1 :         dir[len+1] = '\0';
     426              :     }
     427            3 :     return len;
     428              : }
     429              : 
     430            0 : bool subhomedir(char *dst, int len, const char *src)
     431              : {
     432            0 :     const char *sub = std::strstr(src, "$HOME");
     433            0 :     if(!sub)
     434              :     {
     435            0 :         sub = std::strchr(src, '~');
     436              :     }
     437            0 :     if(sub && sub-src < len)
     438              :     {
     439              : #ifdef WIN32
     440              :         char home[MAX_PATH+1];
     441              :         home[0] = '\0';
     442              :         if(SHGetFolderPath(nullptr, CSIDL_PERSONAL, nullptr, 0, home) != S_OK || !home[0])
     443              :         {
     444              :             return false;
     445              :         }
     446              : #else
     447            0 :         const char *home = getenv("HOME");
     448            0 :         if(!home || !home[0])
     449              :         {
     450            0 :             return false;
     451              :         }
     452              : #endif
     453            0 :         dst[sub-src] = '\0';
     454            0 :         concatstring(dst, home, len);
     455            0 :         concatstring(dst, sub+(*sub == '~' ? 1 : std::strlen("$HOME")), len);
     456              :     }
     457            0 :     return true;
     458              : }
     459              : 
     460            0 : const char *sethomedir(const char *dir)
     461              : {
     462              :     string pdir;
     463            0 :     copystring(pdir, dir);
     464            0 :     if(!subhomedir(pdir, sizeof(pdir), dir) || !fixpackagedir(pdir))
     465              :     {
     466            0 :         return nullptr;
     467              :     }
     468            0 :     homedir = pdir;
     469            0 :     return homedir.c_str();
     470              : }
     471              : 
     472            0 : const char *addpackagedir(const char *dir)
     473              : {
     474              :     string pdir;
     475            0 :     copystring(pdir, dir);
     476            0 :     if(!subhomedir(pdir, sizeof(pdir), dir) || !fixpackagedir(pdir))
     477              :     {
     478            0 :         return nullptr;
     479              :     }
     480            0 :     char *filter = pdir;
     481              :     for(;;)
     482              :     {
     483              :         static int len = std::strlen("media");
     484            0 :         filter = std::strstr(filter, "media");
     485            0 :         if(!filter)
     486              :         {
     487            0 :             break;
     488              :         }
     489            0 :         if(filter > pdir && filter[-1] == PATHDIV && filter[len] == PATHDIV)
     490              :         {
     491            0 :             break;
     492              :         }
     493            0 :         filter += len;
     494            0 :     }
     495            0 :     packagedir pf;
     496            0 :     pf.dir = filter ? std::string(pdir, filter-pdir) : std::string(pdir);
     497            0 :     pf.filter = filter ? std::string(filter) : "";
     498            0 :     packagedirs.push_back(pf);
     499            0 :     return pf.dir.c_str();
     500            0 : }
     501              : 
     502           22 : const char *findfile(const char *filename, const char *mode)
     503              : {
     504              :     static string s;
     505           22 :     if(homedir[0])
     506              :     {
     507            0 :         formatstring(s, "%s%s", homedir.c_str(), filename);
     508            0 :         if(fileexists(s, mode))
     509              :         {
     510            0 :             return s;
     511              :         }
     512            0 :         if(mode[0] == 'w' || mode[0] == 'a')
     513              :         {
     514              :             string dirs;
     515            0 :             copystring(dirs, s);
     516            0 :             char *dir = std::strchr(dirs[0] == PATHDIV ? dirs+1 : dirs, PATHDIV);
     517            0 :             while(dir)
     518              :             {
     519            0 :                 *dir = '\0';
     520            0 :                 if(!fileexists(dirs, "d") && !createdir(dirs))
     521              :                 {
     522            0 :                     return s;
     523              :                 }
     524            0 :                 *dir = PATHDIV;
     525            0 :                 dir = std::strchr(dir+1, PATHDIV);
     526              :             }
     527            0 :             return s;
     528              :         }
     529              :     }
     530           22 :     if(mode[0] == 'w' || mode[0] == 'a')
     531              :     {
     532            1 :         return filename;
     533              :     }
     534           21 :     for(const packagedir &pf : packagedirs)
     535              :     {
     536            0 :         if(pf.filter.size() > 0 && std::strncmp(filename, pf.filter.c_str(), pf.filter.size()))
     537              :         {
     538            0 :             continue;
     539              :         }
     540            0 :         formatstring(s, "%s%s", pf.dir.c_str(), filename);
     541            0 :         if(fileexists(s, mode))
     542              :         {
     543            0 :             return s;
     544              :         }
     545              :     }
     546           21 :     if(mode[0]=='e')
     547              :     {
     548            0 :         return nullptr;
     549              :     }
     550           21 :     return filename;
     551              : }
     552              : 
     553            1 : bool listdir(const char *dirname, bool rel, const char *ext, std::vector<char *> &files)
     554              : {
     555            1 :     size_t extsize = ext ? std::strlen(ext)+1 : 0;
     556              : #ifdef WIN32
     557              :     DEF_FORMAT_STRING(pathname, rel ? ".\\%s\\*.%s" : "%s\\*.%s", dirname, ext ? ext : "*");
     558              :     WIN32_FIND_DATA FindFileData;
     559              :     HANDLE Find = FindFirstFile(pathname, &FindFileData);
     560              :     if(Find != INVALID_HANDLE_VALUE)
     561              :     {
     562              :         do {
     563              :             if(!ext)
     564              :             {
     565              :                 files.push_back(newstring(FindFileData.cFileName));
     566              :             }
     567              :             else
     568              :             {
     569              :                 size_t namelen = std::strlen(FindFileData.cFileName);
     570              :                 if(namelen > extsize)
     571              :                 {
     572              :                     namelen -= extsize;
     573              :                     if(FindFileData.cFileName[namelen] == '.' && std::strncmp(FindFileData.cFileName+namelen+1, ext, extsize-1)==0)
     574              :                     {
     575              :                         files.push_back(newstring(FindFileData.cFileName, namelen));
     576              :                     }
     577              :                 }
     578              :             }
     579              :         } while(FindNextFile(Find, &FindFileData));
     580              :         FindClose(Find);
     581              :         return true;
     582              :     }
     583              : #else
     584            1 :     DEF_FORMAT_STRING(pathname, rel ? "./%s" : "%s", dirname);
     585            1 :     DIR *d = opendir(pathname);
     586            1 :     if(d)
     587              :     {
     588              :         struct dirent *de;
     589           82 :         while((de = readdir(d)) != nullptr)
     590              :         {
     591           81 :             if(!ext)
     592              :             {
     593           81 :                 files.push_back(newstring(de->d_name));
     594              :             }
     595              :             else
     596              :             {
     597            0 :                 size_t namelen = std::strlen(de->d_name);
     598            0 :                 if(namelen > extsize)
     599              :                 {
     600            0 :                     namelen -= extsize;
     601            0 :                     if(de->d_name[namelen] == '.' && std::strncmp(de->d_name+namelen+1, ext, extsize-1)==0)
     602              :                     {
     603            0 :                         files.push_back(newstring(de->d_name, namelen));
     604              :                     }
     605              :                 }
     606              :             }
     607              :         }
     608            1 :         closedir(d);
     609            1 :         return true;
     610              :     }
     611              : #endif
     612              :     else
     613              :     {
     614            0 :         return false;
     615              :     }
     616              : }
     617              : 
     618            1 : int listfiles(const char *dir, const char *ext, std::vector<char *> &files)
     619              : {
     620              :     string dirname;
     621            1 :     copystring(dirname, dir);
     622            1 :     path(dirname);
     623            1 :     size_t dirlen = std::strlen(dirname);
     624            1 :     while(dirlen > 1 && dirname[dirlen-1] == PATHDIV)
     625              :     {
     626            0 :         dirname[--dirlen] = '\0';
     627              :     }
     628            1 :     int dirs = 0;
     629            1 :     if(listdir(dirname, true, ext, files))
     630              :     {
     631            1 :         dirs++;
     632              :     }
     633              :     string s;
     634            1 :     if(homedir[0])
     635              :     {
     636            0 :         formatstring(s, "%s%s", homedir.c_str(), dirname);
     637            0 :         if(listdir(s, false, ext, files))
     638              :         {
     639            0 :             dirs++;
     640              :         }
     641              :     }
     642            1 :     for(const packagedir &pf : packagedirs)
     643              :     {
     644            0 :         if(pf.filter.size() && std::strncmp(dirname, pf.filter.c_str(), dirlen == pf.filter.size()-1 ? dirlen : pf.filter.size()))
     645              :         {
     646            0 :             continue;
     647              :         }
     648            0 :         formatstring(s, "%s%s", pf.dir.c_str(), dirname);
     649            0 :         if(listdir(s, false, ext, files))
     650              :         {
     651            0 :             dirs++;
     652              :         }
     653              :     }
     654            1 :     dirs += listzipfiles(dirname, ext, files);
     655            1 :     return dirs;
     656              : }
     657              : 
     658            0 : static Sint64 rwopsseek(SDL_RWops *rw, Sint64 pos, int whence)
     659              : {
     660            0 :     stream *f = static_cast<stream *>(rw->hidden.unknown.data1);
     661            0 :     if((!pos && whence==SEEK_CUR) || f->seek(pos, whence))
     662              :     {
     663            0 :         return static_cast<int>(f->tell());
     664              :     }
     665            0 :     return -1;
     666              : }
     667              : 
     668            0 : static size_t rwopsread(SDL_RWops *rw, void *buf, size_t size, size_t nmemb)
     669              : {
     670            0 :     stream *f = static_cast<stream *>(rw->hidden.unknown.data1);
     671            0 :     return f->read(buf, size*nmemb)/size;
     672              : }
     673              : 
     674            0 : static size_t rwopswrite(SDL_RWops *rw, const void *buf, size_t size, size_t nmemb)
     675              : {
     676            0 :     stream *f = static_cast<stream *>(rw->hidden.unknown.data1);
     677            0 :     return f->write(buf, size*nmemb)/size;
     678              : }
     679              : 
     680            1 : SDL_RWops *stream::rwops()
     681              : {
     682            1 :     SDL_RWops *rw = SDL_AllocRW();
     683            1 :     if(!rw)
     684              :     {
     685            0 :         return nullptr;
     686              :     }
     687            1 :     rw->hidden.unknown.data1 = this;
     688            1 :     rw->seek = rwopsseek;
     689            1 :     rw->read = rwopsread;
     690            1 :     rw->write = rwopswrite;
     691            1 :     rw->close = 0;
     692            1 :     return rw;
     693              : }
     694              : 
     695            2 : stream::offset stream::size()
     696              : {
     697            2 :     offset pos = tell(),
     698              :                  endpos;
     699            2 :     if(pos < 0 || !seek(0, SEEK_END))
     700              :     {
     701            2 :         return -1;
     702              :     }
     703            0 :     endpos = tell();
     704            0 :     return pos == endpos || seek(pos, SEEK_SET) ? endpos : -1;
     705              : }
     706              : 
     707            1 : bool stream::getline(char *str, size_t len)
     708              : {
     709            1 :     for(int i = 0; i < static_cast<int>(len-1); ++i)
     710              :     {
     711            0 :         if(read(&str[i], 1) != 1)
     712              :         {
     713            0 :             str[i] = '\0';
     714            0 :             return i > 0;
     715              :         }
     716            0 :         else if(str[i] == '\n')
     717              :         {
     718            0 :             str[i+1] = '\0';
     719            0 :             return true;
     720              :         }
     721              :     }
     722            1 :     if(len > 0)
     723              :     {
     724            0 :         str[len-1] = '\0';
     725              :     }
     726            1 :     return true;
     727              : }
     728              : 
     729            1 : size_t stream::printf(const char *fmt, ...)
     730              : {
     731              :     char buf[512];
     732            1 :     char *str = buf;
     733              :     va_list args;
     734              : #if defined(WIN32) && !defined(__GNUC__)
     735              :     va_start(args, fmt);
     736              :     int len = _vscprintf(fmt, args);
     737              :     if(len <= 0)
     738              :     {
     739              :         va_end(args);
     740              :         return 0;
     741              :     }
     742              :     if(len >= static_cast<int>(sizeof(buf)))
     743              :     {
     744              :         str = new char[len+1];
     745              :     }
     746              :     _vsnprintf(str, len+1, fmt, args);
     747              :     va_end(args);
     748              : #else
     749            1 :     va_start(args, fmt);
     750            1 :     int len = vsnprintf(buf, sizeof(buf), fmt, args);
     751            1 :     va_end(args);
     752            1 :     if(len <= 0)
     753              :     {
     754            0 :         return 0;
     755              :     }
     756            1 :     if(len >= static_cast<int>(sizeof(buf)))
     757              :     {
     758            0 :         str = new char[len+1];
     759            0 :         va_start(args, fmt);
     760            0 :         vsnprintf(str, len+1, fmt, args);
     761            0 :         va_end(args);
     762              :     }
     763              : #endif
     764            1 :     size_t n = write(str, len);
     765            1 :     if(str != buf)
     766              :     {
     767            0 :         delete[] str;
     768              :     }
     769            1 :     return n;
     770              : }
     771              : 
     772              : struct filestream final : stream
     773              : {
     774              :     FILE *file;
     775              : 
     776           11 :     filestream() : file(nullptr) {}
     777           22 :     ~filestream() { close(); }
     778              : 
     779           11 :     bool open(const char *name, const char *mode)
     780              :     {
     781           11 :         if(file)
     782              :         {
     783            0 :             return false;
     784              :         }
     785           11 :         file = fopen(name, mode);
     786           11 :         return file!=nullptr;
     787              :     }
     788              :     #ifdef WIN32
     789              :         bool opentemp(const char *name, const char *mode)
     790              :         {
     791              :             if(file)
     792              :             {
     793              :                 return false;
     794              :             }
     795              :             file = fopen(name, mode);
     796              :             return file!=nullptr;
     797              :         }
     798              :     #else
     799              :         bool opentemp(const char *, const char *)
     800              :         {
     801              :             if(file)
     802              :             {
     803              :                 return false;
     804              :             }
     805              :             file = tmpfile();
     806              :             return file!=nullptr;
     807              :         }
     808              :     #endif
     809           11 :     void close() override final
     810              :     {
     811           11 :         if(file)
     812              :         {
     813            2 :             fclose(file);
     814            2 :             file = nullptr;
     815              :         }
     816           11 :     }
     817              : 
     818            0 :     bool end() override final
     819              :     {
     820            0 :         return feof(file)!=0;
     821              :     }
     822              : 
     823            0 :     offset tell() const override final
     824              :     {
     825              : #ifdef WIN32
     826              : #if defined(__GNUC__) && !defined(__MINGW32__)
     827              :         offset off = ftello64(file);
     828              : #else
     829              :         offset off = _ftelli64(file);
     830              : #endif
     831              : #else
     832            0 :         offset off = ftello(file);
     833              : #endif
     834              :         // ftello returns LONG_MAX for directories on some platforms
     835            0 :         return off + 1 >= 0 ? off : -1;
     836              :     }
     837              : 
     838            0 :     bool seek(offset pos, int whence) override final
     839              :     {
     840              : #ifdef WIN32
     841              : #if defined(__GNUC__) && !defined(__MINGW32__)
     842              :         return fseeko64(file, pos, whence) >= 0;
     843              : #else
     844              :         return _fseeki64(file, pos, whence) >= 0;
     845              : #endif
     846              : #else
     847            0 :         return fseeko(file, pos, whence) >= 0;
     848              : #endif
     849              :     }
     850              : 
     851            0 :     size_t read(void *buf, size_t len) override final
     852              :     {
     853            0 :         return fread(buf, 1, len, file);
     854              :     }
     855              : 
     856            0 :     size_t write(const void *buf, size_t len) override final
     857              :     {
     858            0 :         return fwrite(buf, 1, len, file);
     859              :     }
     860              : 
     861            0 :     bool flush() override final
     862              :     {
     863            0 :         return !fflush(file);
     864              :     }
     865              : 
     866            0 :     int getchar() override final
     867              :     {
     868            0 :         return fgetc(file);
     869              :     }
     870              : 
     871            0 :     bool putchar(int c) override final
     872              :     {
     873            0 :         return fputc(c, file)!=EOF;
     874              :     }
     875              : 
     876         1454 :     bool getline(char *str, size_t len) override final
     877              :     {
     878         1454 :         return fgets(str, len, file)!=nullptr;
     879              :     }
     880              : 
     881            0 :     bool putstring(const char *str) override final
     882              :     {
     883            0 :         return fputs(str, file)!=EOF;
     884              :     }
     885              : 
     886            0 :     size_t printf(const char *fmt, ...) override final
     887              :     {
     888              :         va_list v;
     889            0 :         va_start(v, fmt);
     890            0 :         int result = std::vfprintf(file, fmt, v);
     891            0 :         va_end(v);
     892            0 :         return std::max<int>(result, 0);
     893              :     }
     894              : };
     895              : 
     896              : VAR(debuggz, 0, 0, 1); //toggles gz checking routines
     897              : 
     898              : class gzstream final : public stream
     899              : {
     900              :     public:
     901            0 :         gzstream() : file(nullptr), buf(nullptr), reading(false), writing(false), autoclose(false), crc(0), headersize(0)
     902              :         {
     903            0 :             zfile.zalloc = nullptr;
     904            0 :             zfile.zfree = nullptr;
     905            0 :             zfile.opaque = nullptr;
     906            0 :             zfile.next_in = zfile.next_out = nullptr;
     907            0 :             zfile.avail_in = zfile.avail_out = 0;
     908            0 :         }
     909              : 
     910            0 :         ~gzstream()
     911            0 :         {
     912            0 :             close();
     913            0 :         }
     914              : 
     915            0 :         void writeheader()
     916              :         {
     917            0 :             uchar header[] = { MAGIC1, MAGIC2, Z_DEFLATED, 0, 0, 0, 0, 0, 0, OS_UNIX };
     918            0 :             file->write(header, sizeof(header));
     919            0 :         }
     920              : 
     921            0 :         void readbuf(size_t size = BUFSIZE)
     922              :         {
     923            0 :             if(!zfile.avail_in)
     924              :             {
     925            0 :                 zfile.next_in = static_cast<Bytef *>(buf);
     926              :             }
     927            0 :             size = std::min<size_t>(size, static_cast<size_t>(&buf[BUFSIZE] - &zfile.next_in[zfile.avail_in]));
     928            0 :             size_t n = file->read(zfile.next_in + zfile.avail_in, size);
     929            0 :             if(n > 0)
     930              :             {
     931            0 :                 zfile.avail_in += n;
     932              :             }
     933            0 :         }
     934              : 
     935            0 :         uchar readbyte(size_t size = BUFSIZE)
     936              :         {
     937            0 :             if(!zfile.avail_in)
     938              :             {
     939            0 :                 readbuf(size);
     940              :             }
     941            0 :             if(!zfile.avail_in)
     942              :             {
     943            0 :                 return 0;
     944              :             }
     945            0 :             zfile.avail_in--;
     946            0 :             return *static_cast<uchar *>(zfile.next_in++);
     947              :         }
     948              : 
     949            0 :         void skipbytes(size_t n)
     950              :         {
     951            0 :             while(n > 0 && zfile.avail_in > 0)
     952              :             {
     953            0 :                 size_t skipped = std::min<size_t>(n, static_cast<size_t>(zfile.avail_in));
     954            0 :                 zfile.avail_in -= skipped;
     955            0 :                 zfile.next_in += skipped;
     956            0 :                 n -= skipped;
     957              :             }
     958            0 :             if(n <= 0)
     959              :             {
     960            0 :                 return;
     961              :             }
     962            0 :             file->seek(n, SEEK_CUR);
     963              :         }
     964              : 
     965            0 :         bool checkheader()
     966              :         {
     967            0 :             readbuf(10);
     968            0 :             if(readbyte() != MAGIC1 || readbyte() != MAGIC2 || readbyte() != Z_DEFLATED)
     969              :             {
     970            0 :                 return false;
     971              :             }
     972            0 :             uchar flags = readbyte();
     973            0 :             if(flags & F_RESERVED)
     974              :             {
     975            0 :                 return false;
     976              :             }
     977            0 :             skipbytes(6);
     978            0 :             if(flags & F_EXTRA)
     979              :             {
     980            0 :                 size_t len = readbyte(512);
     981            0 :                 len |= static_cast<size_t>(readbyte(512))<<8;
     982            0 :                 skipbytes(len);
     983              :             }
     984            0 :             if(flags & F_NAME)
     985              :             {
     986            0 :                 while(readbyte(512))
     987              :                 {
     988              :                     //(empty body)
     989              :                 }
     990              :             }
     991            0 :             if(flags & F_COMMENT)
     992              :             {
     993            0 :                 while(readbyte(512))
     994              :                 {
     995              :                     //(empty body)
     996              :                 }
     997              :             }
     998            0 :             if(flags & F_CRC)
     999              :             {
    1000            0 :                 skipbytes(2);
    1001              :             }
    1002            0 :             headersize = static_cast<size_t>(file->tell() - zfile.avail_in);
    1003            0 :             return zfile.avail_in > 0 || !file->end();
    1004              :         }
    1005              : 
    1006            0 :         bool open(stream *f, const char *mode, bool needclose, int level)
    1007              :         {
    1008            0 :             if(file)
    1009              :             {
    1010            0 :                 return false;
    1011              :             }
    1012            0 :             for(; *mode; mode++)
    1013              :             {
    1014            0 :                 if(*mode=='r')
    1015              :                 {
    1016            0 :                     reading = true;
    1017            0 :                     break;
    1018              :                 }
    1019            0 :                 else if(*mode=='w')
    1020              :                 {
    1021            0 :                     writing = true;
    1022            0 :                     break;
    1023              :                 }
    1024              :             }
    1025            0 :             if(reading)
    1026              :             {
    1027            0 :                 if(inflateInit2(&zfile, -MAX_WBITS) != Z_OK)
    1028              :                 {
    1029            0 :                     reading = false;
    1030              :                 }
    1031              :             }
    1032            0 :             else if(writing && deflateInit2(&zfile, level, Z_DEFLATED, -MAX_WBITS, std::min<int>(MAX_MEM_LEVEL, 8), Z_DEFAULT_STRATEGY) != Z_OK)
    1033              :             {
    1034            0 :                 writing = false;
    1035              :             }
    1036            0 :             if(!reading && !writing)
    1037              :             {
    1038            0 :                 return false;
    1039              :             }
    1040            0 :             file = f;
    1041            0 :             crc = crc32(0, nullptr, 0);
    1042            0 :             buf = new uchar[BUFSIZE];
    1043            0 :             if(reading)
    1044              :             {
    1045            0 :                 if(!checkheader())
    1046              :                 {
    1047            0 :                     stopreading();
    1048            0 :                     return false;
    1049              :                 }
    1050              :             }
    1051            0 :             else if(writing)
    1052              :             {
    1053            0 :                 writeheader();
    1054              :             }
    1055            0 :             autoclose = needclose;
    1056            0 :             return true;
    1057              :         }
    1058              : 
    1059            0 :         uint getcrc() override final
    1060              :         {
    1061            0 :             return crc;
    1062              :         }
    1063              : 
    1064            0 :         void finishreading()
    1065              :         {
    1066            0 :             if(!reading)
    1067              :             {
    1068            0 :                 return;
    1069              :             }
    1070            0 :             if(debuggz)
    1071              :             {
    1072            0 :                 uint checkcrc = 0,
    1073            0 :                      checksize = 0;
    1074            0 :                 for(int i = 0; i < 4; ++i)
    1075              :                 {
    1076            0 :                     checkcrc |= static_cast<uint>(readbyte()) << (i*8);
    1077              :                 }
    1078            0 :                 for(int i = 0; i < 4; ++i)
    1079              :                 {
    1080            0 :                     checksize |= static_cast<uint>(readbyte()) << (i*8);
    1081              :                 }
    1082            0 :                 if(checkcrc != crc)
    1083              :                 {
    1084            0 :                     conoutf(Console_Debug, "gzip crc check failed: read %X, calculated %X", checkcrc, crc);
    1085              :                 }
    1086            0 :                 if(checksize != zfile.total_out)
    1087              :                 {
    1088            0 :                     conoutf(Console_Debug, "gzip size check failed: read %u, calculated %u", checksize, static_cast<uint>(zfile.total_out));
    1089              :                 }
    1090              :             }
    1091              :         }
    1092              : 
    1093            0 :         void stopreading()
    1094              :         {
    1095            0 :             if(!reading)
    1096              :             {
    1097            0 :                 return;
    1098              :             }
    1099            0 :             inflateEnd(&zfile);
    1100            0 :             reading = false;
    1101              :         }
    1102              : 
    1103            0 :         void finishwriting()
    1104              :         {
    1105            0 :             if(!writing)
    1106              :             {
    1107            0 :                 return;
    1108              :             }
    1109              :             for(;;)
    1110              :             {
    1111            0 :                 int err = zfile.avail_out > 0 ? deflate(&zfile, Z_FINISH) : Z_OK;
    1112            0 :                 if(err != Z_OK && err != Z_STREAM_END)
    1113              :                 {
    1114            0 :                     break;
    1115              :                 }
    1116            0 :                 flushbuf();
    1117            0 :                 if(err == Z_STREAM_END)
    1118              :                 {
    1119            0 :                     break;
    1120              :                 }
    1121            0 :             }
    1122              :             uchar trailer[8] =
    1123              :             {
    1124            0 :                 static_cast<uchar>(crc&0xFF), static_cast<uchar>((crc>>8)&0xFF), static_cast<uchar>((crc>>16)&0xFF), static_cast<uchar>((crc>>24)&0xFF),
    1125            0 :                 static_cast<uchar>(zfile.total_in&0xFF), static_cast<uchar>((zfile.total_in>>8)&0xFF), static_cast<uchar>((zfile.total_in>>16)&0xFF), static_cast<uchar>((zfile.total_in>>24)&0xFF)
    1126            0 :             };
    1127            0 :             file->write(trailer, sizeof(trailer));
    1128              :         }
    1129              : 
    1130            0 :         void stopwriting()
    1131              :         {
    1132            0 :             if(!writing)
    1133              :             {
    1134            0 :                 return;
    1135              :             }
    1136            0 :             deflateEnd(&zfile);
    1137            0 :             writing = false;
    1138              :         }
    1139              : 
    1140            0 :         void close() override final
    1141              :         {
    1142            0 :             if(reading)
    1143              :             {
    1144            0 :                 finishreading();
    1145              :             }
    1146            0 :             stopreading();
    1147            0 :             if(writing)
    1148              :             {
    1149            0 :                 finishwriting();
    1150              :             }
    1151            0 :             stopwriting();
    1152            0 :             delete[] buf;
    1153            0 :             buf = nullptr;
    1154            0 :             if(autoclose)
    1155              :             {
    1156            0 :                 if(file)
    1157              :                 {
    1158            0 :                     delete file;
    1159            0 :                     file = nullptr;
    1160              :                 }
    1161              :             }
    1162            0 :         }
    1163              : 
    1164            0 :         bool end() override final
    1165              :         {
    1166            0 :             return !reading && !writing;
    1167              :         }
    1168              : 
    1169            0 :         offset tell() const override final
    1170              :         {
    1171            0 :             return reading ? zfile.total_out : (writing ? zfile.total_in : offset(-1));
    1172              :         }
    1173              : 
    1174            0 :         offset rawtell() const override final
    1175              :         {
    1176            0 :             return file ? file->tell() : offset(-1);
    1177              :         }
    1178              : 
    1179            0 :         offset size() override final
    1180              :         {
    1181            0 :             if(!file)
    1182              :             {
    1183            0 :                 return -1;
    1184              :             }
    1185            0 :             offset pos = tell();
    1186            0 :             if(!file->seek(-4, SEEK_END))
    1187              :             {
    1188            0 :                 return -1;
    1189              :             }
    1190            0 :             uint isize = file->get<uint>();
    1191            0 :             return file->seek(pos, SEEK_SET) ? isize : offset(-1);
    1192              :         }
    1193              : 
    1194            0 :         offset rawsize() override final
    1195              :         {
    1196            0 :             return file ? file->size() : offset(-1);
    1197              :         }
    1198              : 
    1199            0 :         bool seek(offset pos, int whence) override final
    1200              :         {
    1201            0 :             if(writing || !reading)
    1202              :             {
    1203            0 :                 return false;
    1204              :             }
    1205              : 
    1206            0 :             if(whence == SEEK_END)
    1207              :             {
    1208              :                 uchar skip[512];
    1209            0 :                 while(read(skip, sizeof(skip)) == sizeof(skip))
    1210              :                 {
    1211              :                     //(empty body)
    1212              :                 }
    1213            0 :                 return !pos;
    1214              :             }
    1215            0 :             else if(whence == SEEK_CUR)
    1216              :             {
    1217            0 :                 pos += zfile.total_out;
    1218              :             }
    1219            0 :             if(pos >= static_cast<offset>(zfile.total_out))
    1220              :             {
    1221            0 :                 pos -= zfile.total_out;
    1222              :             }
    1223            0 :             else if(pos < 0 || !file->seek(headersize, SEEK_SET))
    1224              :             {
    1225            0 :                 return false;
    1226              :             }
    1227              :             else
    1228              :             {
    1229            0 :                 if(zfile.next_in && zfile.total_in <= static_cast<uint>(zfile.next_in - buf))
    1230              :                 {
    1231            0 :                     zfile.avail_in += zfile.total_in;
    1232            0 :                     zfile.next_in -= zfile.total_in;
    1233              :                 }
    1234              :                 else
    1235              :                 {
    1236            0 :                     zfile.avail_in = 0;
    1237            0 :                     zfile.next_in = nullptr;
    1238              :                 }
    1239            0 :                 inflateReset(&zfile);
    1240            0 :                 crc = crc32(0, nullptr, 0);
    1241              :             }
    1242              : 
    1243              :             std::array<uchar, 512> skip;
    1244            0 :             while(pos > 0)
    1245              :             {
    1246            0 :                 size_t skipped = static_cast<size_t>(std::min<offset>(pos, static_cast<offset>(skip.size())));
    1247            0 :                 if(read(skip.data(), skipped) != skipped)
    1248              :                 {
    1249            0 :                     stopreading();
    1250            0 :                     return false;
    1251              :                 }
    1252            0 :                 pos -= skipped;
    1253              :             }
    1254              : 
    1255            0 :             return true;
    1256              :         }
    1257              : 
    1258            0 :         size_t read(void *outbuf, size_t len) override final
    1259              :         {
    1260            0 :             if(!reading || !outbuf || !len)
    1261              :             {
    1262            0 :                 return 0;
    1263              :             }
    1264            0 :             zfile.next_out = static_cast<Bytef *>(outbuf);
    1265            0 :             zfile.avail_out = len;
    1266            0 :             while(zfile.avail_out > 0)
    1267              :             {
    1268            0 :                 if(!zfile.avail_in)
    1269              :                 {
    1270            0 :                     readbuf(BUFSIZE);
    1271            0 :                     if(!zfile.avail_in)
    1272              :                     {
    1273            0 :                         stopreading();
    1274            0 :                         break;
    1275              :                     }
    1276              :                 }
    1277            0 :                 int err = inflate(&zfile, Z_NO_FLUSH);
    1278            0 :                 if(err == Z_STREAM_END)
    1279              :                 {
    1280            0 :                     crc = crc32(crc, static_cast<Bytef *>(outbuf), len - zfile.avail_out);
    1281            0 :                     finishreading();
    1282            0 :                     stopreading();
    1283            0 :                     return len - zfile.avail_out;
    1284              :                 }
    1285            0 :                 else if(err != Z_OK)
    1286              :                 {
    1287            0 :                     stopreading();
    1288            0 :                     break;
    1289              :                 }
    1290              :             }
    1291            0 :             crc = crc32(crc, reinterpret_cast<Bytef *>(outbuf), len - zfile.avail_out);
    1292            0 :             return len - zfile.avail_out;
    1293              :         }
    1294              : 
    1295            0 :         bool flushbuf(bool full = false)
    1296              :         {
    1297            0 :             if(full)
    1298              :             {
    1299            0 :                 deflate(&zfile, Z_SYNC_FLUSH);
    1300              :             }
    1301            0 :             if(zfile.next_out && zfile.avail_out < BUFSIZE)
    1302              :             {
    1303            0 :                 if(file->write(buf, BUFSIZE - zfile.avail_out) != BUFSIZE - zfile.avail_out || (full && !file->flush()))
    1304              :                 {
    1305            0 :                     return false;
    1306              :                 }
    1307              :             }
    1308            0 :             zfile.next_out = buf;
    1309            0 :             zfile.avail_out = BUFSIZE;
    1310            0 :             return true;
    1311              :         }
    1312              : 
    1313            0 :         bool flush() override final
    1314              :         {
    1315            0 :             return flushbuf(true);
    1316              :         }
    1317              : 
    1318            0 :         size_t write(const void *newbuf, size_t len) override final
    1319              :         {
    1320            0 :             if(!writing || !newbuf || !len)
    1321              :             {
    1322            0 :                 return 0;
    1323              :             }
    1324            0 :             zfile.next_in = static_cast<Bytef *>(const_cast<void *>(newbuf)); //cast away constness, then to Bytef
    1325            0 :             zfile.avail_in = len;
    1326            0 :             while(zfile.avail_in > 0)
    1327              :             {
    1328            0 :                 if(!zfile.avail_out && !flushbuf())
    1329              :                 {
    1330            0 :                     stopwriting();
    1331            0 :                     break;
    1332              :                 }
    1333            0 :                 int err = deflate(&zfile, Z_NO_FLUSH);
    1334            0 :                 if(err != Z_OK)
    1335              :                 {
    1336            0 :                     stopwriting();
    1337            0 :                     break;
    1338              :                 }
    1339              :             }
    1340            0 :             crc = crc32(crc, static_cast<Bytef *>(const_cast<void *>(newbuf)), len - zfile.avail_in);
    1341            0 :             return len - zfile.avail_in;
    1342              :         }
    1343              : 
    1344              :     private:
    1345              :         enum GzHeader
    1346              :         {
    1347              :             MAGIC1   = 0x1F,
    1348              :             MAGIC2   = 0x8B,
    1349              :             BUFSIZE  = 16384,
    1350              :             OS_UNIX  = 0x03
    1351              :         };
    1352              : 
    1353              :         enum GzFlags
    1354              :         {
    1355              :             F_ASCII    = 0x01,
    1356              :             F_CRC      = 0x02,
    1357              :             F_EXTRA    = 0x04,
    1358              :             F_NAME     = 0x08,
    1359              :             F_COMMENT  = 0x10,
    1360              :             F_RESERVED = 0xE0
    1361              :         };
    1362              : 
    1363              :         stream *file;
    1364              :         z_stream zfile;
    1365              :         uchar *buf;
    1366              :         bool reading, writing, autoclose;
    1367              :         uint crc;
    1368              :         size_t headersize;
    1369              : };
    1370              : 
    1371           11 : stream *openrawfile(const char *filename, const char *mode)
    1372              : {
    1373           11 :     const char *found = findfile(filename, mode);
    1374           11 :     if(!found)
    1375              :     {
    1376            0 :         return nullptr;
    1377              :     }
    1378           11 :     filestream *file = new filestream;
    1379           11 :     if(!file->open(found, mode))
    1380              :     {
    1381            9 :         delete file;
    1382            9 :         return nullptr;
    1383              :     }
    1384            2 :     return file;
    1385              : }
    1386              : 
    1387           11 : stream *openfile(const char *filename, const char *mode)
    1388              : {
    1389           11 :     stream *s = openzipfile(filename, mode);
    1390           11 :     if(s)
    1391              :     {
    1392            0 :         return s;
    1393              :     }
    1394           11 :     return openrawfile(filename, mode);
    1395              : }
    1396              : 
    1397            0 : stream *opengzfile(const char *filename, const char *mode, stream *file, int level)
    1398              : {
    1399            0 :     stream *source = file ? file : openfile(filename, mode);
    1400            0 :     if(!source)
    1401              :     {
    1402            0 :         return nullptr;
    1403              :     }
    1404            0 :     gzstream *gz = new gzstream;
    1405            0 :     if(!gz->open(source, mode, !file, level))
    1406              :     {
    1407            0 :         if(!file)
    1408              :         {
    1409            0 :             delete source;
    1410              :         }
    1411            0 :         delete gz;
    1412            0 :         return nullptr;
    1413              :     }
    1414            0 :     return gz;
    1415              : }
    1416              : 
    1417            7 : char *loadfile(const char *fn, size_t *size, bool utf8)
    1418              : {
    1419            7 :     stream *f = openfile(fn, "rb");
    1420            7 :     if(!f)
    1421              :     {
    1422            7 :         return nullptr;
    1423              :     }
    1424            0 :     stream::offset fsize = f->size();
    1425            0 :     if(fsize <= 0)
    1426              :     {
    1427            0 :         delete f;
    1428            0 :         return nullptr;
    1429              :     }
    1430            0 :     size_t len = fsize;
    1431            0 :     char *buf = new char[len+1];
    1432            0 :     if(!buf)
    1433              :     {
    1434            0 :         delete f;
    1435            0 :         return nullptr;
    1436              :     }
    1437            0 :     size_t offset = 0;
    1438            0 :     if(utf8 && len >= 3)
    1439              :     {
    1440            0 :         if(f->read(buf, 3) != 3)
    1441              :         {
    1442            0 :             delete f;
    1443            0 :             delete[] buf;
    1444            0 :             return nullptr;
    1445              :         }
    1446            0 :         if((reinterpret_cast<uchar*>(buf))[0] == 0xEF &&
    1447            0 :            (reinterpret_cast<uchar*>(buf))[1] == 0xBB &&
    1448            0 :            (reinterpret_cast<uchar*>(buf))[2] == 0xBF)
    1449              :         {
    1450            0 :             len -= 3;
    1451              :         }
    1452              :         else
    1453              :         {
    1454            0 :             offset += 3;
    1455              :         }
    1456              :     }
    1457            0 :     size_t rlen = f->read(&buf[offset], len-offset);
    1458            0 :     delete f;
    1459            0 :     if(rlen != len-offset)
    1460              :     {
    1461            0 :         delete[] buf;
    1462            0 :         return nullptr;
    1463              :     }
    1464            0 :     buf[len] = '\0';
    1465            0 :     if(size!=nullptr)
    1466              :     {
    1467            0 :         *size = len;
    1468              :     }
    1469            0 :     return buf;
    1470              : }
    1471              : 
        

Generated by: LCOV version 2.0-1