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