1 #include "cube.h"
2 
3 ///////////////////////// file system ///////////////////////
4 
5 #ifndef WIN32
6 #include <unistd.h>
7 #include <sys/stat.h>
8 #include <sys/types.h>
9 #include <dirent.h>
10 #endif
11 
12 string homedir = "";
13 vector<char *> packagedirs;
14 
makerelpath(const char * dir,const char * file,const char * prefix,const char * cmd)15 char *makerelpath(const char *dir, const char *file, const char *prefix, const char *cmd)
16 {
17     static string tmp;
18     if(prefix) copystring(tmp, prefix);
19     else tmp[0] = '\0';
20     if(file[0]=='<')
21     {
22         const char *end = strrchr(file, '>');
23         if(end)
24         {
25             size_t len = strlen(tmp);
26             copystring(&tmp[len], file, min(sizeof(tmp)-len, size_t(end+2-file)));
27             file = end+1;
28         }
29     }
30     if(cmd) concatstring(tmp, cmd);
31     defformatstring(pname)("%s/%s", dir, file);
32     concatstring(tmp, pname);
33     return tmp;
34 }
35 
36 
path(char * s)37 char *path(char *s)
38 {
39     for(char *curpart = s;;)
40     {
41         char *endpart = strchr(curpart, '&');
42         if(endpart) *endpart = '\0';
43         if(curpart[0]=='<')
44         {
45             char *file = strrchr(curpart, '>');
46             if(!file) return s;
47             curpart = file+1;
48         }
49         for(char *t = curpart; (t = strpbrk(t, "/\\")); *t++ = PATHDIV);
50         for(char *prevdir = NULL, *curdir = s;;)
51         {
52             prevdir = curdir[0]==PATHDIV ? curdir+1 : curdir;
53             curdir = strchr(prevdir, PATHDIV);
54             if(!curdir) break;
55             if(prevdir+1==curdir && prevdir[0]=='.')
56             {
57                 memmove(prevdir, curdir+1, strlen(curdir+1)+1);
58                 curdir = prevdir;
59             }
60             else if(curdir[1]=='.' && curdir[2]=='.' && curdir[3]==PATHDIV)
61             {
62                 if(prevdir+2==curdir && prevdir[0]=='.' && prevdir[1]=='.') continue;
63                 memmove(prevdir, curdir+4, strlen(curdir+4)+1);
64                 curdir = prevdir;
65             }
66         }
67         if(endpart)
68         {
69             *endpart = '&';
70             curpart = endpart+1;
71         }
72         else break;
73     }
74     return s;
75 }
76 
unixpath(char * s)77 char *unixpath(char *s)
78 {
79     for(char *t = s; (t = strchr(t, '\\')); *t++ = '/');
80     return s;
81 }
82 
path(const char * s,bool copy)83 char *path(const char *s, bool copy)
84 {
85     static string tmp;
86     copystring(tmp, s);
87     path(tmp);
88     return tmp;
89 }
90 
parentdir(const char * directory)91 const char *parentdir(const char *directory)
92 {
93     const char *p = directory + strlen(directory);
94     while(p > directory && *p != '/' && *p != '\\') p--;
95     static string parent;
96     size_t len = p-directory+1;
97     copystring(parent, directory, len);
98     return parent;
99 }
100 
behindpath(const char * s)101 const char *behindpath(const char *s)
102 {
103     const char *t = s;
104     for( ; (s = strpbrk(s, "/\\")); t = ++s);
105     return t;
106 }
107 
fileexists(const char * path,const char * mode)108 bool fileexists(const char *path, const char *mode)
109 {
110     bool exists = true;
111     if(mode[0]=='w' || mode[0]=='a') path = parentdir(path);
112 #ifdef WIN32
113     if(GetFileAttributes(path) == INVALID_FILE_ATTRIBUTES) exists = false;
114 #else
115     if(access(path, R_OK | (mode[0]=='w' || mode[0]=='a' ? W_OK : 0)) == -1) exists = false;
116 #endif
117     return exists;
118 }
119 
createdir(const char * path)120 bool createdir(const char *path)
121 {
122     size_t len = strlen(path);
123     if(path[len-1]==PATHDIV)
124     {
125         static string strip;
126         path = copystring(strip, path, len);
127     }
128 #ifdef WIN32
129     return CreateDirectory(path, NULL)!=0;
130 #else
131     return mkdir(path, 0777)==0;
132 #endif
133 }
134 
fixpackagedir(char * dir)135 size_t fixpackagedir(char *dir)
136 {
137     path(dir);
138     size_t len = strlen(dir);
139     if(len > 0 && dir[len-1] != PATHDIV)
140     {
141         dir[len] = PATHDIV;
142         dir[len+1] = '\0';
143     }
144     return len;
145 }
146 
147 #ifdef WIN32
getregszvalue(HKEY root,const char * keystr,const char * query)148 char *getregszvalue(HKEY root, const char *keystr, const char *query)
149 {
150     HKEY key;
151     if(RegOpenKeyEx(HKEY_CURRENT_USER, keystr, 0, KEY_READ, &key)==ERROR_SUCCESS)
152     {
153         DWORD type = 0, len = 0;
154         if(RegQueryValueEx(key, query, 0, &type, 0, &len)==ERROR_SUCCESS && type==REG_SZ)
155         {
156             char *val = new char[len];
157             long result = RegQueryValueEx(key, query, 0, &type, (uchar *)val, &len);
158             if(result==ERROR_SUCCESS)
159             {
160                 RegCloseKey(key);
161                 val[len-1] = '\0';
162                 return val;
163             }
164             delete[] val;
165         }
166         RegCloseKey(key);
167     }
168     return NULL;
169 }
170 #endif
171 
sethomedir(const char * dir)172 void sethomedir(const char *dir)
173 {
174     string tmpdir;
175     copystring(tmpdir, dir);
176 
177 #ifdef WIN32
178     const char substitute[] = "?MYDOCUMENTS?";
179     if(!strncmp(dir, substitute, strlen(substitute)))
180     {
181         const char *regpath = "Software\\Microsoft\\Windows\\CurrentVersion\\Explorer\\Shell Folders";
182         char *mydocuments = getregszvalue(HKEY_CURRENT_USER, regpath, "Personal");
183         if(mydocuments)
184         {
185             formatstring(tmpdir)("%s%s", mydocuments, dir+strlen(substitute));
186             delete[] mydocuments;
187         }
188         else
189         {
190             printf("failed to retrieve 'Personal' path from '%s'\n", regpath);
191         }
192     }
193 #endif
194 
195 #ifndef STANDALONE
196     clientlogf("Using home directory: %s", tmpdir);
197 #endif
198 
199     if(fixpackagedir(tmpdir) > 0)
200     {
201         copystring(homedir, tmpdir);
202         createdir(homedir);
203     }
204 }
205 
addpackagedir(const char * dir)206 void addpackagedir(const char *dir)
207 {
208 #ifndef STANDALONE
209     clientlogf("Adding package directory: %s", dir);
210 #endif
211 
212     string pdir;
213     copystring(pdir, dir);
214     if(fixpackagedir(pdir) > 0) packagedirs.add(newstring(pdir));
215 }
216 
findfile(const char * filename,const char * mode)217 const char *findfile(const char *filename, const char *mode)
218 {
219     static string s;
220     if(homedir[0])
221     {
222         formatstring(s)("%s%s", homedir, filename);
223         if(fileexists(s, mode)) return s;
224         if(mode[0]=='w' || mode[0]=='a')
225         {
226             string dirs;
227             copystring(dirs, s);
228             char *dir = strchr(dirs[0]==PATHDIV ? dirs+1 : dirs, PATHDIV);
229             while(dir)
230             {
231                 *dir = '\0';
232                 if(!fileexists(dirs, "r") && !createdir(dirs)) return s;
233                 *dir = PATHDIV;
234                 dir = strchr(dir+1, PATHDIV);
235             }
236             return s;
237         }
238     }
239     if(mode[0]=='w' || mode[0]=='a') return filename;
240     loopv(packagedirs)
241     {
242         formatstring(s)("%s%s", packagedirs[i], filename);
243         if(fileexists(s, mode)) return s;
244     }
245     return filename;
246 }
247 
listdir(const char * dir,const char * ext,vector<char * > & files)248 bool listdir(const char *dir, const char *ext, vector<char *> &files)
249 {
250     int extsize = ext ? (int)strlen(ext)+1 : 0;
251     #if defined(WIN32)
252     defformatstring(pathname)("%s\\*.%s", dir, ext ? ext : "*");
253     WIN32_FIND_DATA FindFileData;
254     HANDLE Find = FindFirstFile(path(pathname), &FindFileData);
255     if(Find != INVALID_HANDLE_VALUE)
256     {
257         do {
258             files.add(newstring(FindFileData.cFileName, (int)strlen(FindFileData.cFileName) - extsize));
259         } while(FindNextFile(Find, &FindFileData));
260         FindClose(Find);
261         return true;
262     }
263     #else
264     string pathname;
265     copystring(pathname, dir);
266     DIR *d = opendir(path(pathname));
267     if(d)
268     {
269         struct dirent *de;
270         while((de = readdir(d)) != NULL)
271         {
272             if(!ext) files.add(newstring(de->d_name));
273             else
274             {
275                 int namelength = (int)strlen(de->d_name) - extsize;
276                 if(namelength > 0 && de->d_name[namelength] == '.' && strncmp(de->d_name+namelength+1, ext, extsize-1)==0)
277                     files.add(newstring(de->d_name, namelength));
278             }
279         }
280         closedir(d);
281         return true;
282     }
283     #endif
284     else return false;
285 }
286 
listfiles(const char * dir,const char * ext,vector<char * > & files)287 int listfiles(const char *dir, const char *ext, vector<char *> &files)
288 {
289     int dirs = 0;
290     if(listdir(dir, ext, files)) dirs++;
291     string s;
292     if(homedir[0])
293     {
294         formatstring(s)("%s%s", homedir, dir);
295         if(listdir(s, ext, files)) dirs++;
296     }
297     loopv(packagedirs)
298     {
299         formatstring(s)("%s%s", packagedirs[i], dir);
300         if(listdir(s, ext, files)) dirs++;
301     }
302 #ifndef STANDALONE
303     dirs += listzipfiles(dir, ext, files);
304 #endif
305     return dirs;
306 }
307 
delfile(const char * path)308 bool delfile(const char *path)
309 {
310     return !remove(path);
311 }
312 
copyfile(const char * source,const char * destination)313 bool copyfile(const char *source, const char *destination)
314 {
315     FILE *from = fopen(source, "rb");
316     FILE *dest = fopen(destination, "wb");
317 
318     if(!from || !dest) return false;
319     size_t len;
320     uchar buf[1024];
321     while((len = fread(&buf, sizeof(uchar), 1024, from)))
322     {
323         fwrite(&buf, sizeof(uchar), len, dest);
324     }
325     fclose(from);
326     fclose(dest);
327     return true;
328 }
329 
preparedir(const char * destination)330 bool preparedir(const char *destination)
331 {
332     string dir;
333     copystring(dir, parentdir(destination));
334     vector<char *> dirs;
335     while(!fileexists(dir, "r"))
336     {
337         dirs.add(newstring(dir));
338         copystring(dir, parentdir(dir));
339     }
340 
341     loopvrev(dirs) if(!createdir(dirs[i])) return false;
342     return true;
343 }
344 
345 #ifndef STANDALONE
rwopsseek(SDL_RWops * rw,int offset,int whence)346 static int rwopsseek(SDL_RWops *rw, int offset, int whence)
347 {
348     stream *f = (stream *)rw->hidden.unknown.data1;
349     if((!offset && whence==SEEK_CUR) || f->seek(offset, whence)) return f->tell();
350     return -1;
351 }
352 
rwopsread(SDL_RWops * rw,void * buf,int size,int nmemb)353 static int rwopsread(SDL_RWops *rw, void *buf, int size, int nmemb)
354 {
355     stream *f = (stream *)rw->hidden.unknown.data1;
356     return f->read(buf, size*nmemb)/size;
357 }
358 
rwopswrite(SDL_RWops * rw,const void * buf,int size,int nmemb)359 static int rwopswrite(SDL_RWops *rw, const void *buf, int size, int nmemb)
360 {
361     stream *f = (stream *)rw->hidden.unknown.data1;
362     return f->write(buf, size*nmemb)/size;
363 }
364 
rwopsclose(SDL_RWops * rw)365 static int rwopsclose(SDL_RWops *rw)
366 {
367     return 0;
368 }
369 
rwops()370 SDL_RWops *stream::rwops()
371 {
372     SDL_RWops *rw = SDL_AllocRW();
373     if(!rw) return NULL;
374     rw->hidden.unknown.data1 = this;
375     rw->seek = rwopsseek;
376     rw->read = rwopsread;
377     rw->write = rwopswrite;
378     rw->close = rwopsclose;
379     return rw;
380 }
381 #endif
382 
size()383 long stream::size()
384 {
385     long pos = tell(), endpos;
386     if(pos < 0 || !seek(0, SEEK_END)) return -1;
387     endpos = tell();
388     return pos == endpos || seek(pos, SEEK_SET) ? endpos : -1;
389 }
390 
getline(char * str,int len)391 bool stream::getline(char *str, int len)
392 {
393     loopi(len-1)
394     {
395         if(read(&str[i], 1) != 1) { str[i] = '\0'; return i > 0; }
396         else if(str[i] == '\n') { str[i+1] = '\0'; return true; }
397     }
398     if(len > 0) str[len-1] = '\0';
399     return true;
400 }
401 
402 #ifndef WIN32
403 #include <sys/statvfs.h>
404 const int64_t MINFSSIZE = 50000000;         // 50MB
405 #endif
406 
407 struct filestream : stream
408 {
409     FILE *file;
410 
filestreamfilestream411     filestream() : file(NULL) {}
~filestreamfilestream412     ~filestream() { close(); }
413 
openfilestream414     bool open(const char *name, const char *mode)
415     {
416         if(file) return false;
417         file = fopen(name, mode);
418 #ifndef WIN32
419         struct statvfs buf;
420         if(file && strchr(mode,'w'))
421         {
422             int fail = fstatvfs(fileno(file), &buf);
423             if (fail || (int64_t)buf.f_frsize * (int64_t)buf.f_bavail < MINFSSIZE)
424             {
425                 close();
426                 return false;
427             }
428         }
429 #endif
430         return file!=NULL;
431     }
432 
opentempfilestream433     bool opentemp(const char *name, const char *mode)
434     {
435         if(file) return false;
436 #ifdef WIN32
437         file = fopen(name, mode);
438 #else
439         file = tmpfile();
440 #endif
441         return file!=NULL;
442     }
443 
closefilestream444     void close()
445     {
446         if(file) { fclose(file); file = NULL; }
447     }
448 
endfilestream449     bool end() { return feof(file)!=0; }
tellfilestream450     long tell() { return ftell(file); }
seekfilestream451     bool seek(long offset, int whence) { return fseek(file, offset, whence) >= 0; }
readfilestream452     int read(void *buf, int len) { return (int)fread(buf, 1, len, file); }
writefilestream453     int write(const void *buf, int len) { return (int)fwrite(buf, 1, len, file); }
getcharfilestream454     int getchar() { return fgetc(file); }
putcharfilestream455     bool putchar(int c) { return fputc(c, file)!=EOF; }
getlinefilestream456     bool getline(char *str, int len) { return fgets(str, len, file)!=NULL; }
putstringfilestream457     bool putstring(const char *str) { return fputs(str, file)!=EOF; }
458 
printffilestream459     int printf(const char *fmt, ...)
460     {
461         va_list v;
462         va_start(v, fmt);
463         int result = vfprintf(file, fmt, v);
464         va_end(v);
465         return result;
466     }
467 };
468 
469 #ifndef STANDALONE
470 //VAR(dbggz, 0, 0, 1);
471 const int dbggz = 0;
472 #endif
473 
474 struct gzstream : stream
475 {
476     enum
477     {
478         MAGIC1   = 0x1F,
479         MAGIC2   = 0x8B,
480         BUFSIZE  = 16384,
481         OS_UNIX  = 0x03
482     };
483 
484     enum
485     {
486         F_ASCII    = 0x01,
487         F_CRC      = 0x02,
488         F_EXTRA    = 0x04,
489         F_NAME     = 0x08,
490         F_COMMENT  = 0x10,
491         F_RESERVED = 0xE0
492     };
493 
494     stream *file;
495     z_stream zfile;
496     uchar *buf;
497     bool reading, writing, autoclose;
498     uint crc;
499     int headersize;
500 
gzstreamgzstream501     gzstream() : file(NULL), buf(NULL), reading(false), writing(false), autoclose(false), crc(0), headersize(0)
502     {
503         zfile.zalloc = NULL;
504         zfile.zfree = NULL;
505         zfile.opaque = NULL;
506         zfile.next_in = zfile.next_out = NULL;
507         zfile.avail_in = zfile.avail_out = 0;
508     }
509 
~gzstreamgzstream510     ~gzstream()
511     {
512         close();
513     }
514 
writeheadergzstream515     void writeheader()
516     {
517         uchar header[] = { MAGIC1, MAGIC2, Z_DEFLATED, 0, 0, 0, 0, 0, 0, OS_UNIX };
518         file->write(header, sizeof(header));
519     }
520 
readbufgzstream521     void readbuf(int size = BUFSIZE)
522     {
523         if(!zfile.avail_in) zfile.next_in = (Bytef *)buf;
524         size = min(size, int(&buf[BUFSIZE] - &zfile.next_in[zfile.avail_in]));
525         int n = file->read(zfile.next_in + zfile.avail_in, size);
526         if(n > 0) zfile.avail_in += n;
527     }
528 
readbytegzstream529     int readbyte(int size = BUFSIZE)
530     {
531         if(!zfile.avail_in) readbuf(size);
532         if(!zfile.avail_in) return 0;
533         zfile.avail_in--;
534         return *(uchar *)zfile.next_in++;
535     }
536 
skipbytesgzstream537     void skipbytes(int n)
538     {
539         while(n > 0 && zfile.avail_in > 0)
540         {
541             int skipped = min(n, (int)zfile.avail_in);
542             zfile.avail_in -= skipped;
543             zfile.next_in += skipped;
544             n -= skipped;
545         }
546         if(n <= 0) return;
547         file->seek(n, SEEK_CUR);
548     }
549 
checkheadergzstream550     bool checkheader()
551     {
552         readbuf(10);
553         if(readbyte() != MAGIC1 || readbyte() != MAGIC2 || readbyte() != Z_DEFLATED) return false;
554         int flags = readbyte();
555         if(flags & F_RESERVED) return false;
556         skipbytes(6);
557         if(flags & F_EXTRA)
558         {
559             int len = readbyte(512);
560             len |= readbyte(512)<<8;
561             skipbytes(len);
562         }
563         if(flags & F_NAME) while(readbyte(512));
564         if(flags & F_COMMENT) while(readbyte(512));
565         if(flags & F_CRC) skipbytes(2);
566         headersize = file->tell() - zfile.avail_in;
567         return zfile.avail_in > 0 || !file->end();
568     }
569 
opengzstream570     bool open(stream *f, const char *mode, bool needclose, int level)
571     {
572         if(file) return false;
573         for(; *mode; mode++)
574         {
575             if(*mode=='r') { reading = true; break; }
576             else if(*mode=='w') { writing = true; break; }
577         }
578         if(reading)
579         {
580             if(inflateInit2(&zfile, -MAX_WBITS) != Z_OK) reading = false;
581         }
582         else if(writing && deflateInit2(&zfile, level, Z_DEFLATED, -MAX_WBITS, min(MAX_MEM_LEVEL, 8), Z_DEFAULT_STRATEGY) != Z_OK) writing = false;
583         if(!reading && !writing) return false;
584 
585         autoclose = needclose;
586         file = f;
587         crc = crc32(0, NULL, 0);
588         buf = new uchar[BUFSIZE];
589 
590         if(reading)
591         {
592             if(!checkheader()) { stopreading(); return false; }
593         }
594         else if(writing) writeheader();
595         return true;
596     }
597 
getcrcgzstream598     uint getcrc() { return crc; }
599 
finishreadinggzstream600     void finishreading()
601     {
602         if(!reading) return;
603 #ifndef STANDALONE
604         if(dbggz)
605         {
606             uint checkcrc = 0, checksize = 0;
607             loopi(4) checkcrc |= uint(readbyte()) << (i*8);
608             loopi(4) checksize |= uint(readbyte()) << (i*8);
609             if(checkcrc != crc)
610                 conoutf("gzip crc check failed: read %X, calculated %X", checkcrc, crc);
611             if(checksize != zfile.total_out)
612                 conoutf("gzip size check failed: read %d, calculated %d", checksize, zfile.total_out);
613         }
614 #endif
615     }
616 
stopreadinggzstream617     void stopreading()
618     {
619         if(!reading) return;
620         inflateEnd(&zfile);
621         reading = false;
622     }
623 
finishwritinggzstream624     void finishwriting()
625     {
626         if(!writing) return;
627         for(;;)
628         {
629             int err = zfile.avail_out > 0 ? deflate(&zfile, Z_FINISH) : Z_OK;
630             if(err != Z_OK && err != Z_STREAM_END) break;
631             flush();
632             if(err == Z_STREAM_END) break;
633         }
634         uchar trailer[8] =
635         {
636             uchar(crc&0xFF), uchar((crc>>8)&0xFF), uchar((crc>>16)&0xFF), uchar((crc>>24)&0xFF),
637             uchar(zfile.total_in&0xFF), uchar((zfile.total_in>>8)&0xFF), uchar((zfile.total_in>>16)&0xFF), uchar((zfile.total_in>>24)&0xFF)
638         };
639         file->write(trailer, sizeof(trailer));
640     }
641 
stopwritinggzstream642     void stopwriting()
643     {
644         if(!writing) return;
645         deflateEnd(&zfile);
646         writing = false;
647     }
648 
closegzstream649     void close()
650     {
651         if(reading) finishreading();
652         stopreading();
653         if(writing) finishwriting();
654         stopwriting();
655         DELETEA(buf);
656         if(autoclose) DELETEP(file);
657     }
658 
endgzstream659     bool end() { return !reading && !writing; }
tellgzstream660     long tell() { return reading ? zfile.total_out : (writing ? zfile.total_in : -1); }
661 
seekgzstream662     bool seek(long offset, int whence)
663     {
664         if(writing || !reading) return false;
665 
666         if(whence == SEEK_END)
667         {
668             uchar skip[512];
669             while(read(skip, sizeof(skip)) == sizeof(skip));
670             return !offset;
671         }
672         else if(whence == SEEK_CUR) offset += zfile.total_out;
673 
674         if(offset >= (int)zfile.total_out) offset -= zfile.total_out;
675         else if(offset < 0 || !file->seek(headersize, SEEK_SET)) return false;
676         else
677         {
678             if(zfile.next_in && zfile.total_in <= uint(zfile.next_in - buf))
679             {
680                 zfile.avail_in += zfile.total_in;
681                 zfile.next_in -= zfile.total_in;
682             }
683             else
684             {
685                 zfile.avail_in = 0;
686                 zfile.next_in = NULL;
687             }
688             inflateReset(&zfile);
689             crc = crc32(0, NULL, 0);
690         }
691 
692         uchar skip[512];
693         while(offset > 0)
694         {
695             int skipped = min(offset, (long)sizeof(skip));
696             if(read(skip, skipped) != skipped) { stopreading(); return false; }
697             offset -= skipped;
698         }
699 
700         return true;
701     }
702 
readgzstream703     int read(void *buf, int len)
704     {
705         if(!reading || !buf || !len) return 0;
706         zfile.next_out = (Bytef *)buf;
707         zfile.avail_out = len;
708         while(zfile.avail_out > 0)
709         {
710             if(!zfile.avail_in)
711             {
712                 readbuf(BUFSIZE);
713                 if(!zfile.avail_in) { stopreading(); break; }
714             }
715             int err = inflate(&zfile, Z_NO_FLUSH);
716             if(err == Z_STREAM_END) { crc = crc32(crc, (Bytef *)buf, len - zfile.avail_out); finishreading(); stopreading(); return len - zfile.avail_out; }
717             else if(err != Z_OK) { stopreading(); break; }
718         }
719         crc = crc32(crc, (Bytef *)buf, len - zfile.avail_out);
720         return len - zfile.avail_out;
721     }
722 
flushgzstream723     bool flush()
724     {
725         if(zfile.next_out && zfile.avail_out < BUFSIZE)
726         {
727             if(file->write(buf, BUFSIZE - zfile.avail_out) != int(BUFSIZE - zfile.avail_out))
728                 return false;
729         }
730         zfile.next_out = buf;
731         zfile.avail_out = BUFSIZE;
732         return true;
733     }
734 
writegzstream735     int write(const void *buf, int len)
736     {
737         if(!writing || !buf || !len) return 0;
738         zfile.next_in = (Bytef *)buf;
739         zfile.avail_in = len;
740         while(zfile.avail_in > 0)
741         {
742             if(!zfile.avail_out && !flush()) { stopwriting(); break; }
743             int err = deflate(&zfile, Z_NO_FLUSH);
744             if(err != Z_OK) { stopwriting(); break; }
745         }
746         crc = crc32(crc, (Bytef *)buf, len - zfile.avail_in);
747         return len - zfile.avail_in;
748     }
749 };
750 
751 
openrawfile(const char * filename,const char * mode)752 stream *openrawfile(const char *filename, const char *mode)
753 {
754     const char *found = findfile(filename, mode);
755 #ifndef STANDALONE
756     if(mode && (mode[0]=='w' || mode[0]=='a')) conoutf("writing to file: %s", found);
757 #endif
758     if(!found) return NULL;
759     filestream *file = new filestream;
760     if(!file->open(found, mode))
761     {
762 #ifndef STANDALONE
763 //         conoutf("file failure! %s",filename);
764 #endif
765         delete file; return NULL;
766     }
767     return file;
768 }
769 
openfile(const char * filename,const char * mode)770 stream *openfile(const char *filename, const char *mode)
771 {
772 #ifndef STANDALONE
773     stream *s = openzipfile(filename, mode);
774     if(s) return s;
775 #endif
776     return openrawfile(filename, mode);
777 }
778 
getfilesize(const char * filename)779 int getfilesize(const char *filename)
780 {
781     stream *f = openfile(filename, "rb");
782     if(!f) return -1;
783     int len = f->size();
784     delete f;
785     return len;
786 }
787 
opentempfile(const char * name,const char * mode)788 stream *opentempfile(const char *name, const char *mode)
789 {
790     const char *found = findfile(name, mode);
791     filestream *file = new filestream;
792     if(!file->opentemp(found ? found : name, mode)) { delete file; return NULL; }
793     return file;
794 }
795 
opengzfile(const char * filename,const char * mode,stream * file,int level)796 stream *opengzfile(const char *filename, const char *mode, stream *file, int level)
797 {
798     stream *source = file ? file : openfile(filename, mode);
799     if(!source) return NULL;
800     gzstream *gz = new gzstream;
801     if(!gz->open(source, mode, !file, level)) { if(!file) delete source; return NULL; }
802     return gz;
803 }
804 
loadfile(const char * fn,int * size,const char * mode)805 char *loadfile(const char *fn, int *size, const char *mode)
806 {
807     stream *f = openfile(fn, mode ? mode : "rb");
808     if(!f) return NULL;
809     int len = f->size();
810     if(len<=0) { delete f; return NULL; }
811     char *buf = new char[len+1];
812     if(!buf) { delete f; return NULL; }
813     buf[len] = 0;
814     int rlen = f->read(buf, len);
815     delete f;
816     if(len!=rlen && (!mode || strchr(mode, 'b')))
817     {
818         delete[] buf;
819         return NULL;
820     }
821     if(size!=NULL) *size = len;
822     return buf;
823 }
824 
825