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-05-09 04:28:55 Functions: 37.3 % 67 25

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

Generated by: LCOV version 2.0-1