textlayouter: added method to get marked text
[iv.d.git] / vfs / arc / bsa.d
blob426803babab8b030ca055638a0810ccfff09cde5
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.bsa /*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!"BSA");
32 // ////////////////////////////////////////////////////////////////////////// //
33 public final class VFSDriverBSA : 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 long pksize;
42 bool packed;
45 /** query various file properties; driver-specific.
46 * properties of interest:
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("bsa");
55 if (propname == "packed") return VFSVariant(dir[idx].packed);
56 if (propname == "pksize") return VFSVariant(dir[idx].pksize);
57 if (propname == "offset") return VFSVariant(dir[idx].ofs);
58 if (propname == "size") return VFSVariant(dir[idx].size);
59 return VFSVariant();
62 VFile wrap (usize idx) {
63 //{ import core.stdc.stdio; printf("[%.*s]; packed=%s; pksize=%u; size=%u; ofs=0x%08x\n", cast(uint)dir[idx].name.length, dir[idx].name.ptr, (dir[idx].packed ? "tan".ptr : "ona".ptr) , cast(uint)dir[idx].pksize, cast(uint)dir[idx].size, cast(uint)dir[idx].ofs); }
64 if (dir[idx].packed) return wrapZLibStreamRO(st, VFSZLibMode.ZLib, dir[idx].size, dir[idx].ofs, dir[idx].pksize, dir[idx].name);
65 return wrapStreamRO(st, dir[idx].ofs, dir[idx].size, dir[idx].name);
69 void open (VFile fl, const(char)[] prefixpath) {
70 try {
71 open1(fl, prefixpath);
72 } catch (Exception e) {
73 import core.stdc.stdio;
74 auto s = e.toString;
75 printf("\n-----------------\n%.*s\n", cast(uint)s.length, s.ptr);
76 throw e;
81 void open (VFile fl, const(char)[] prefixpath) {
82 string loadBCStr(bool asdir) () {
83 auto len = fl.readNum!ubyte;
84 if (len == 0) return null; // oops
85 auto res = new char[](len+1); // for '/'
86 fl.rawReadExact(res[0..$-1]);
87 res[$-1] = 0; // for now
88 foreach (immutable idx, ref char ch; res) {
89 if (ch == 0) {
90 // asciiz
91 static if (asdir) {
92 ch = '/'; // 'cause dir name should end with slash
93 res = res[0..idx+1];
94 } else {
95 res = res[0..idx];
97 break;
99 //if (ch >= 'A' && ch <= 'Z') ch += 32; // poor man's tolower
100 if (ch == '\\') ch = '/'; // fuck off, shitdoze
102 while (res.length > 1 && res[$-1] == '/' && res[$-2] == '/') res = res[0..$-1];
103 if (res == "/") return null; // just in case
104 return cast(string)res; // it is safe to cast here
107 ulong flsize = fl.size;
108 if (flsize > 0xffff_ffffu) throw new /*VFSNamedException!"BSAArchive"*/VFSExceptionArc("file too big");
109 char[4] sign;
110 fl.rawReadExact(sign[]);
111 if (sign != "BSA\x00") throw new /*VFSNamedException!"BSAArchive"*/VFSExceptionArc("not a BSA file");
112 //{ import core.stdc.stdio; printf("TRYING BSA! [%.*s]\n", cast(uint)fl.name.length, fl.name.ptr); }
113 auto ver = fl.readNum!uint;
114 if (ver != 0x67 && ver != 0x68) throw new /*VFSNamedException!"BSAArchive"*/VFSExceptionArc("invalid BSA version");
115 auto fatofs = fl.readNum!uint;
116 auto flags = fl.readNum!uint;
117 version(bsa_dump) {
118 writeln("flags:");
119 if (flags&0x01) writeln(" has names for directories");
120 if (flags&0x02) writeln(" has names for files");
121 if (flags&0x04) writeln(" compressed by default");
122 if (flags&0x40) writeln(" shitbox archive");
124 if ((flags&0x03) != 0x03) throw new /*VFSNamedException!"BSAArchive"*/VFSExceptionArc("invalid BSA flags (no names)");
125 if (flags&0x40) throw new /*VFSNamedException!"BSAArchive"*/VFSExceptionArc("no support for shitbox BSA archives yet");
126 auto dircount = fl.readNum!uint;
127 auto filecount = fl.readNum!uint;
128 auto dirnmsize = fl.readNum!uint;
129 auto filenmsize = fl.readNum!uint;
130 auto fileflags = fl.readNum!uint;
132 version(bsa_dump) writefln("dirs=%u; files=%u; dirnmsize=%u; filenmsize=%u; fileflags=0x%08x", dircount, filecount, dirnmsize, filenmsize, fileflags);
134 // load dir counts
135 uint[] dircnt;
136 fl.seek(fatofs);
137 foreach (immutable didx; 0..dircount) {
138 fl.readNum!ulong; // name hash, skip it
139 uint fcount = fl.readNum!uint;
140 fl.readNum!uint; // dir name offset
141 dircnt.arrayAppendUnsafe(fcount);
144 // load file entries
145 //bool xxdmp = false;
146 foreach (uint defcount; dircnt) {
147 string dirname = loadBCStr!true();
148 version(bsa_dump) writeln("directory [", dirname, "]: ", defcount, " files");
149 //if (!xxdmp) { /*xxdmp = true;*/ import iv.vfs.io; writefln("ffofs: 0x%08x\n", cast(uint)fl.tell); }
150 // load actual file entries
151 foreach (immutable _; 0..defcount) {
152 FileInfo fe;
153 fl.readNum!ulong; // name hash, skip it
154 fe.size = fl.readNum!uint;
155 fe.ofs = fl.readNum!uint;
156 fe.name = dirname; // will be fixed later
157 if (fe.size&0x8000_0000U) assert(0, "wtf?!");
158 fe.packed = ((fe.size&0x4000_0000U) != 0);
159 if (flags&0x04) fe.packed = !fe.packed;
160 fe.size &= 0x3fff_ffffU;
161 dir.arrayAppendUnsafe(fe);
165 // load file names
166 foreach (ref FileInfo fe; dir) {
167 // asciiz
168 for (;;) {
169 char ch;
170 fl.rawReadExact((&ch)[0..1]);
171 if (ch == 0) break;
172 //if (ch >= 'A' && ch <= 'Z') ch += 32; // poor man's tolower
173 if (ch == '\\' || ch == '/') ch = '_'; // fuck off, shitdoze
174 fe.name ~= ch;
178 // load rest of the info
179 foreach (immutable fidx, ref FileInfo fe; dir) {
180 if (fe.size == 0) {
181 fe.pksize = 0;
182 fe.packed = false;
183 continue;
185 if (fe.packed) {
186 fl.seek(fe.ofs);
187 if (fe.packed) {
188 assert(fe.size >= 4);
189 fe.pksize = fe.size-4;
190 fe.ofs += 4;
191 fe.size = fl.readNum!uint;
192 if (fe.pksize == 0) {
193 assert(fe.size == 0);
194 fe.packed = false;
200 buildNameHashTable();