added "iv.dynstring"
[iv.d.git] / vfs / arc / wad2.d
blobda0e20cdc1def088f335dccca6a2beceec7742ea
1 /* Invisible Vector Library
2 * coded by Ketmar // Invisible Vector <ketmar@ketmar.no-ip.org>
3 * Understanding is not required. Only obedience.
5 * This program is free software: you can redistribute it and/or modify
6 * it under the terms of the GNU General Public License as published by
7 * the Free Software Foundation, version 3 of the License ONLY.
9 * This program is distributed in the hope that it will be useful,
10 * but WITHOUT ANY WARRANTY; without even the implied warranty of
11 * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
12 * GNU General Public License for more details.
14 * You should have received a copy of the GNU General Public License
15 * along with this program. If not, see <http://www.gnu.org/licenses/>.
17 module iv.vfs.arc.wad2 /*is aliced*/;
19 import iv.alice;
20 import iv.vfs.types : Seek;
21 import iv.vfs.error;
22 import iv.vfs.main;
23 import iv.vfs.util;
24 import iv.vfs.vfile;
25 import iv.vfs.arc.internal;
28 // ////////////////////////////////////////////////////////////////////////// //
29 mixin(VFSSimpleArchiveDetectorMixin!"Wad2");
32 // ////////////////////////////////////////////////////////////////////////// //
33 public final class VFSDriverWad2 : VFSDriver {
34 mixin VFSSimpleArchiveDriverMixin;
36 private:
37 static struct FileInfo {
38 long size;
39 long ofs; // offset in archive
40 string name; // with path
41 char type;
44 /** query various file properties; driver-specific.
45 * properties of interest:
46 * "type" -- internal type
47 * "packed" -- is file packed?
48 * "pksize" -- packed file size (for archives)
49 * "offset" -- offset in wad
50 * "size" -- file size (so we can get size without opening the file)
52 public override VFSVariant stat (usize idx, const(char)[] propname) {
53 if (idx >= dir.length) return VFSVariant();
54 if (propname == "arcname") return VFSVariant("wad2");
55 if (propname == "type") return VFSVariant(dir[idx].type);
56 if (propname == "packed") return VFSVariant(false);
57 if (propname == "pksize") return VFSVariant(dir[idx].size);
58 if (propname == "offset") return VFSVariant(dir[idx].ofs);
59 if (propname == "size") return VFSVariant(dir[idx].size);
60 return VFSVariant();
63 VFile wrap (usize idx) { return wrapStreamRO(st, dir[idx].ofs, dir[idx].size, dir[idx].name); }
65 void open (VFile fl, const(char)[] prefixpath) {
66 ulong flsize = fl.size;
67 if (flsize > 0xffff_ffffu) throw new /*VFSNamedException!"Wad2Archive"*/VFSExceptionArc("file too big");
68 char[4] sign;
69 fl.rawReadExact(sign[]);
70 if (sign != "WAD2") throw new /*VFSNamedException!"Wad2Archive"*/VFSExceptionArc("not a PAK file");
71 auto flCount = fl.readNum!uint;
72 auto dirOfs = fl.readNum!uint;
73 if (flCount > 0x3fff_ffff || dirOfs >= flsize || dirOfs+flCount*32 > flsize) throw new /*VFSNamedException!"Wad2Archive"*/VFSExceptionArc("invalid archive file");
74 // read directory
75 fl.seek(dirOfs);
76 char[16] nbuf;
77 while (flCount-- > 0) {
78 FileInfo fi;
79 fi.ofs = fl.readNum!uint;
80 fi.size = fl.readNum!uint;
81 fl.readNum!uint; // size of entry in memory, not used
82 ubyte type = fl.readNum!ubyte;
83 ubyte origtype = type;
84 if (type == '/') type = '_'; // oops
85 auto compr = fl.readNum!ubyte; // 0: none
86 fl.readNum!ushort; // not used
87 fl.rawReadExact(nbuf[0..16]);
88 char[] name;
90 name = new char[](prefixpath.length+16+2);
91 usize nbpos = prefixpath.length;
92 if (nbpos) name[0..nbpos] = prefixpath[];
93 foreach (char ch; nbuf[0..16]) {
94 if (ch == 0) break;
95 if (ch == '\\') ch = '/';
96 if (ch == '/' && (nbpos == 0 || name.ptr[nbpos-1] == '/')) continue;
97 name.ptr[nbpos++] = ch;
99 if (type) { name.ptr[nbpos++] = '.'; name.ptr[nbpos++] = cast(char)type; }
100 name = name[0..nbpos];
101 if (name.length && name[$-1] == '/') name = null;
103 // some sanity checks
104 if (fi.size > 0 && fi.ofs >= flsize || fi.size > flsize) throw new /*VFSNamedException!"Wad2Archive"*/VFSExceptionArc("invalid archive directory");
105 if (fi.ofs+fi.size > flsize) throw new /*VFSNamedException!"Wad2Archive"*/VFSExceptionArc("invalid archive directory");
106 if (compr != 0) throw new /*VFSNamedException!"Wad2Archive"*/VFSExceptionArc("invalid compression type");
107 if (name.length) {
108 fi.name = cast(string)name; // it's safe here
109 fi.type = cast(char)origtype;
110 dir.arrayAppendUnsafe(fi);
113 buildNameHashTable();