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 :
|