LCOV - code coverage report
Current view: top level - shared - stream.cpp (source / functions) Hit Total Coverage
Test: Libprimis Test Coverage Lines: 203 644 31.5 %
Date: 2024-11-22 05:07:59 Functions: 25 67 37.3 %

          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          70 :         while((de = readdir(d)) != nullptr)
     574             :         {
     575          69 :             if(!ext)
     576             :             {
     577          69 :                 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 1.14