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

Generated by: LCOV version 2.0-1