1 module wadarc
is aliced
;
4 static import core
.sync
.mutex
;
5 import std
.stdio
: File
;
6 //import arsd.characterencodings;
13 // ////////////////////////////////////////////////////////////////////////// //
14 public string
koi8lotranslit (string s
) {
16 while (pos
< s
.length
&& s
.ptr
[pos
] < 0x80) ++pos
;
17 if (pos
>= s
.length
) return s
; // nothing to do
18 string res
= s
[0..pos
];
19 while (pos
< s
.length
) {
20 char ch
= s
.ptr
[pos
++];
21 if (ch
== '\xe1' || ch
== '\xc1') res
~= "a";
22 else if (ch
== '\xe2' || ch
== '\xc2') res
~= "b";
23 else if (ch
== '\xf7' || ch
== '\xd7') res
~= "v";
24 else if (ch
== '\xe7' || ch
== '\xc7') res
~= "g";
25 else if (ch
== '\xe4' || ch
== '\xc4') res
~= "d";
26 else if (ch
== '\xe5' || ch
== '\xc5') res
~= "e";
27 else if (ch
== '\xb3' || ch
== '\xa3') res
~= "yo";
28 else if (ch
== '\xf6' || ch
== '\xd6') res
~= "zh";
29 else if (ch
== '\xfa' || ch
== '\xda') res
~= "z";
30 else if (ch
== '\xe9' || ch
== '\xc9') res
~= "i";
31 else if (ch
== '\xea' || ch
== '\xca') res
~= "j";
32 else if (ch
== '\xeb' || ch
== '\xcb') res
~= "k";
33 else if (ch
== '\xec' || ch
== '\xcc') res
~= "l";
34 else if (ch
== '\xed' || ch
== '\xcd') res
~= "m";
35 else if (ch
== '\xee' || ch
== '\xce') res
~= "n";
36 else if (ch
== '\xef' || ch
== '\xcf') res
~= "o";
37 else if (ch
== '\xf0' || ch
== '\xd0') res
~= "p";
38 else if (ch
== '\xf2' || ch
== '\xd2') res
~= "r";
39 else if (ch
== '\xf3' || ch
== '\xd3') res
~= "s";
40 else if (ch
== '\xf4' || ch
== '\xd4') res
~= "t";
41 else if (ch
== '\xf5' || ch
== '\xd5') res
~= "u";
42 else if (ch
== '\xe6' || ch
== '\xc6') res
~= "f";
43 else if (ch
== '\xe8' || ch
== '\xc8') res
~= "h";
44 else if (ch
== '\xe3' || ch
== '\xc3') res
~= "c";
45 else if (ch
== '\xfe' || ch
== '\xde') res
~= "ch";
46 else if (ch
== '\xfb' || ch
== '\xdb') res
~= "sh";
47 else if (ch
== '\xfd' || ch
== '\xdd') res
~= "sch";
48 else if (ch
== '\xff' || ch
== '\xdf') res
~= "x";
49 else if (ch
== '\xf9' || ch
== '\xd9') res
~= "y";
50 else if (ch
== '\xf8' || ch
== '\xd8') res
~= "w";
51 else if (ch
== '\xfc' || ch
== '\xdc') res
~= "e";
52 else if (ch
== '\xe0' || ch
== '\xc0') res
~= "hu";
53 else if (ch
== '\xf1' || ch
== '\xd1') res
~= "ja";
60 // ////////////////////////////////////////////////////////////////////////// //
61 public immutable char[256] koi8from866Table
= [
62 '\x00','\x01','\x02','\x03','\x04','\x05','\x06','\x07','\x08','\x09','\x0a','\x0b','\x0c','\x0d','\x0e','\x0f',
63 '\x10','\x11','\x12','\x13','\x14','\x15','\x16','\x17','\x18','\x19','\x1a','\x1b','\x1c','\x1d','\x1e','\x1f',
64 '\x20','\x21','\x22','\x23','\x24','\x25','\x26','\x27','\x28','\x29','\x2a','\x2b','\x2c','\x2d','\x2e','\x2f',
65 '\x30','\x31','\x32','\x33','\x34','\x35','\x36','\x37','\x38','\x39','\x3a','\x3b','\x3c','\x3d','\x3e','\x3f',
66 '\x40','\x41','\x42','\x43','\x44','\x45','\x46','\x47','\x48','\x49','\x4a','\x4b','\x4c','\x4d','\x4e','\x4f',
67 '\x50','\x51','\x52','\x53','\x54','\x55','\x56','\x57','\x58','\x59','\x5a','\x5b','\x5c','\x5d','\x5e','\x5f',
68 '\x60','\x61','\x62','\x63','\x64','\x65','\x66','\x67','\x68','\x69','\x6a','\x6b','\x6c','\x6d','\x6e','\x6f',
69 '\x70','\x71','\x72','\x73','\x74','\x75','\x76','\x77','\x78','\x79','\x7a','\x7b','\x7c','\x7d','\x7e','\x7f',
70 '\xe1','\xe2','\xf7','\xe7','\xe4','\xe5','\xf6','\xfa','\xe9','\xea','\xeb','\xec','\xed','\xee','\xef','\xf0',
71 '\xf2','\xf3','\xf4','\xf5','\xe6','\xe8','\xe3','\xfe','\xfb','\xfd','\xff','\xf9','\xf8','\xfc','\xe0','\xf1',
72 '\xc1','\xc2','\xd7','\xc7','\xc4','\xc5','\xd6','\xda','\xc9','\xca','\xcb','\xcc','\xcd','\xce','\xcf','\xd0',
73 '\x90','\x91','\x92','\x81','\x87','\xb2','\x3f','\x3f','\x3f','\xb5','\xa1','\xa8','\xae','\x3f','\xac','\x83',
74 '\x84','\x89','\x88','\x86','\x80','\x8a','\xaf','\xb0','\xab','\xa5','\xbb','\xb8','\xb1','\xa0','\xbe','\xb9',
75 '\xba','\x3f','\x3f','\xaa','\xa9','\xa2','\x3f','\x3f','\xbc','\x85','\x82','\x8d','\x8c','\x8e','\x8f','\x8b',
76 '\xd2','\xd3','\xd4','\xd5','\xc6','\xc8','\xc3','\xde','\xdb','\xdd','\xdf','\xd9','\xd8','\xdc','\xc0','\xd1',
77 '\xb3','\xa3','\xb4','\xa4','\xb7','\xa7','\x3f','\x3f','\x9c','\x95','\x9e','\x96','\x3f','\x3f','\x94','\x9a',
80 public immutable char[256] koi8from1251Table
= [
81 '\x00','\x01','\x02','\x03','\x04','\x05','\x06','\x07','\x08','\x09','\x0a','\x0b','\x0c','\x0d','\x0e','\x0f',
82 '\x10','\x11','\x12','\x13','\x14','\x15','\x16','\x17','\x18','\x19','\x1a','\x1b','\x1c','\x1d','\x1e','\x1f',
83 '\x20','\x21','\x22','\x23','\x24','\x25','\x26','\x27','\x28','\x29','\x2a','\x2b','\x2c','\x2d','\x2e','\x2f',
84 '\x30','\x31','\x32','\x33','\x34','\x35','\x36','\x37','\x38','\x39','\x3a','\x3b','\x3c','\x3d','\x3e','\x3f',
85 '\x40','\x41','\x42','\x43','\x44','\x45','\x46','\x47','\x48','\x49','\x4a','\x4b','\x4c','\x4d','\x4e','\x4f',
86 '\x50','\x51','\x52','\x53','\x54','\x55','\x56','\x57','\x58','\x59','\x5a','\x5b','\x5c','\x5d','\x5e','\x5f',
87 '\x60','\x61','\x62','\x63','\x64','\x65','\x66','\x67','\x68','\x69','\x6a','\x6b','\x6c','\x6d','\x6e','\x6f',
88 '\x70','\x71','\x72','\x73','\x74','\x75','\x76','\x77','\x78','\x79','\x7a','\x7b','\x7c','\x7d','\x7e','\x7f',
89 '\x3f','\x3f','\x3f','\x3f','\x3f','\x3f','\x3f','\x3f','\x3f','\x3f','\x3f','\x3f','\x3f','\x3f','\x3f','\x3f',
90 '\x3f','\x3f','\x3f','\x3f','\x3f','\x3f','\x3f','\x3f','\x3f','\x3f','\x3f','\x3f','\x3f','\x3f','\x3f','\x3f',
91 '\x9a','\x3f','\x3f','\x3f','\x3f','\xbd','\x3f','\x3f','\xb3','\xbf','\xb4','\x3f','\x3f','\x3f','\x3f','\xb7',
92 '\x9c','\x3f','\xb6','\xa6','\xad','\x3f','\x3f','\x9e','\xa3','\x3f','\xa4','\x3f','\x3f','\x3f','\x3f','\xa7',
93 '\xe1','\xe2','\xf7','\xe7','\xe4','\xe5','\xf6','\xfa','\xe9','\xea','\xeb','\xec','\xed','\xee','\xef','\xf0',
94 '\xf2','\xf3','\xf4','\xf5','\xe6','\xe8','\xe3','\xfe','\xfb','\xfd','\xff','\xf9','\xf8','\xfc','\xe0','\xf1',
95 '\xc1','\xc2','\xd7','\xc7','\xc4','\xc5','\xd6','\xda','\xc9','\xca','\xcb','\xcc','\xcd','\xce','\xcf','\xd0',
96 '\xd2','\xd3','\xd4','\xd5','\xc6','\xc8','\xc3','\xde','\xdb','\xdd','\xdf','\xd9','\xd8','\xdc','\xc0','\xd1',
99 // char toupper/tolower, koi8
100 public immutable char[256] koi8tolowerTable
= [
101 '\x00','\x01','\x02','\x03','\x04','\x05','\x06','\x07','\x08','\x09','\x0a','\x0b','\x0c','\x0d','\x0e','\x0f',
102 '\x10','\x11','\x12','\x13','\x14','\x15','\x16','\x17','\x18','\x19','\x1a','\x1b','\x1c','\x1d','\x1e','\x1f',
103 '\x20','\x21','\x22','\x23','\x24','\x25','\x26','\x27','\x28','\x29','\x2a','\x2b','\x2c','\x2d','\x2e','\x2f',
104 '\x30','\x31','\x32','\x33','\x34','\x35','\x36','\x37','\x38','\x39','\x3a','\x3b','\x3c','\x3d','\x3e','\x3f',
105 '\x40','\x61','\x62','\x63','\x64','\x65','\x66','\x67','\x68','\x69','\x6a','\x6b','\x6c','\x6d','\x6e','\x6f',
106 '\x70','\x71','\x72','\x73','\x74','\x75','\x76','\x77','\x78','\x79','\x7a','\x5b','\x5c','\x5d','\x5e','\x5f',
107 '\x60','\x61','\x62','\x63','\x64','\x65','\x66','\x67','\x68','\x69','\x6a','\x6b','\x6c','\x6d','\x6e','\x6f',
108 '\x70','\x71','\x72','\x73','\x74','\x75','\x76','\x77','\x78','\x79','\x7a','\x7b','\x7c','\x7d','\x7e','\x7f',
109 '\x80','\x81','\x82','\x83','\x84','\x85','\x86','\x87','\x88','\x89','\x8a','\x8b','\x8c','\x8d','\x8e','\x8f',
110 '\x90','\x91','\x92','\x93','\x94','\x95','\x96','\x97','\x98','\x99','\x9a','\x9b','\x9c','\x9d','\x9e','\x9f',
111 '\xa0','\xa1','\xa2','\xa3','\xa4','\xa5','\xa6','\xa7','\xa8','\xa9','\xaa','\xab','\xac','\xad','\xae','\xaf',
112 '\xb0','\xb1','\xb2','\xa3','\xa4','\xb5','\xa6','\xa7','\xb8','\xb9','\xba','\xbb','\xbc','\xad','\xbe','\xbf',
113 '\xc0','\xc1','\xc2','\xc3','\xc4','\xc5','\xc6','\xc7','\xc8','\xc9','\xca','\xcb','\xcc','\xcd','\xce','\xcf',
114 '\xd0','\xd1','\xd2','\xd3','\xd4','\xd5','\xd6','\xd7','\xd8','\xd9','\xda','\xdb','\xdc','\xdd','\xde','\xdf',
115 '\xc0','\xc1','\xc2','\xc3','\xc4','\xc5','\xc6','\xc7','\xc8','\xc9','\xca','\xcb','\xcc','\xcd','\xce','\xcf',
116 '\xd0','\xd1','\xd2','\xd3','\xd4','\xd5','\xd6','\xd7','\xd8','\xd9','\xda','\xdb','\xdc','\xdd','\xde','\xdf',
119 public immutable char[256] koi8toupperTable
= [
120 '\x00','\x01','\x02','\x03','\x04','\x05','\x06','\x07','\x08','\x09','\x0a','\x0b','\x0c','\x0d','\x0e','\x0f',
121 '\x10','\x11','\x12','\x13','\x14','\x15','\x16','\x17','\x18','\x19','\x1a','\x1b','\x1c','\x1d','\x1e','\x1f',
122 '\x20','\x21','\x22','\x23','\x24','\x25','\x26','\x27','\x28','\x29','\x2a','\x2b','\x2c','\x2d','\x2e','\x2f',
123 '\x30','\x31','\x32','\x33','\x34','\x35','\x36','\x37','\x38','\x39','\x3a','\x3b','\x3c','\x3d','\x3e','\x3f',
124 '\x40','\x41','\x42','\x43','\x44','\x45','\x46','\x47','\x48','\x49','\x4a','\x4b','\x4c','\x4d','\x4e','\x4f',
125 '\x50','\x51','\x52','\x53','\x54','\x55','\x56','\x57','\x58','\x59','\x5a','\x5b','\x5c','\x5d','\x5e','\x5f',
126 '\x60','\x41','\x42','\x43','\x44','\x45','\x46','\x47','\x48','\x49','\x4a','\x4b','\x4c','\x4d','\x4e','\x4f',
127 '\x50','\x51','\x52','\x53','\x54','\x55','\x56','\x57','\x58','\x59','\x5a','\x7b','\x7c','\x7d','\x7e','\x7f',
128 '\x80','\x81','\x82','\x83','\x84','\x85','\x86','\x87','\x88','\x89','\x8a','\x8b','\x8c','\x8d','\x8e','\x8f',
129 '\x90','\x91','\x92','\x93','\x94','\x95','\x96','\x97','\x98','\x99','\x9a','\x9b','\x9c','\x9d','\x9e','\x9f',
130 '\xa0','\xa1','\xa2','\xb3','\xb4','\xa5','\xb6','\xb7','\xa8','\xa9','\xaa','\xab','\xac','\xbd','\xae','\xaf',
131 '\xb0','\xb1','\xb2','\xb3','\xb4','\xb5','\xb6','\xb7','\xb8','\xb9','\xba','\xbb','\xbc','\xbd','\xbe','\xbf',
132 '\xe0','\xe1','\xe2','\xe3','\xe4','\xe5','\xe6','\xe7','\xe8','\xe9','\xea','\xeb','\xec','\xed','\xee','\xef',
133 '\xf0','\xf1','\xf2','\xf3','\xf4','\xf5','\xf6','\xf7','\xf8','\xf9','\xfa','\xfb','\xfc','\xfd','\xfe','\xff',
134 '\xe0','\xe1','\xe2','\xe3','\xe4','\xe5','\xe6','\xe7','\xe8','\xe9','\xea','\xeb','\xec','\xed','\xee','\xef',
135 '\xf0','\xf1','\xf2','\xf3','\xf4','\xf5','\xf6','\xf7','\xf8','\xf9','\xfa','\xfb','\xfc','\xfd','\xfe','\xff',
138 public immutable ubyte[32] koi8alphaTable
= [
139 0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0xfe,0xff,0xff,0x07,0xfe,0xff,0xff,0x07,
140 0x00,0x00,0x00,0x00,0xd8,0x20,0xd8,0x20,0xff,0xff,0xff,0xff,0xff,0xff,0xff,0xff,
143 public char dos2koi8 (char ch
) pure nothrow @trusted @nogc {
144 pragma(inline
, true);
145 return koi8from866Table
.ptr
[cast(int)ch
];
148 public char koi8lower (char ch
) pure nothrow @trusted @nogc {
149 pragma(inline
, true);
150 return koi8tolowerTable
.ptr
[cast(int)ch
];
153 public char koi8upper (char ch
) pure nothrow @trusted @nogc {
154 pragma(inline
, true);
155 return koi8toupperTable
.ptr
[cast(int)ch
];
158 public bool koi8isAlpha (char ch
) pure nothrow @trusted @nogc {
159 pragma(inline
, true);
160 return ((koi8alphaTable
.ptr
[ch
/8]&(1<<(ch
%8))) != 0);
164 // ////////////////////////////////////////////////////////////////////////// //
165 public __gshared WadArchive
[] wadList
;
166 __gshared core
.sync
.mutex
.Mutex glock
;
167 __gshared string dataPath
;
170 shared static this () {
171 glock
= new core
.sync
.mutex
.Mutex
;
175 public void setDataPath (const(char)[] path
) {
176 while (path
.length
> 1 && path
[0] == '/' && path
[1] == '/') path
= path
[1..$];
177 if (path
!= "/") while (path
.length
&& path
[$-1] == '/') path
= path
[0..$-1];
178 dataPath
= path
.idup
~"/";
182 public void addWad (string fname
) {
183 if (glock
!is null) glock
.lock();
184 scope(exit
) if (glock
!is null) glock
.unlock();
185 conwriteln("; adding '", fname
, "'...");
186 wadList
~= new WadArchive(fname
);
190 public File
openFile (string fname
) {
191 if (glock
!is null) glock
.lock();
192 scope(exit
) if (glock
!is null) glock
.unlock();
194 return File(dataPath
~fname
);
195 } catch (Exception
) {}
197 foreach_reverse (WadArchive wad
; wadList
) {
199 return wad
.fopen(fname
);
200 } catch (Exception
) {}
202 return File(fname
); // to throw a correct exception, lol
204 string ofname = fname;
206 fname = fname.baseName.setExtension("");
207 foreach_reverse (WadArchive wad; wadList) {
209 return wad.fopen(fname);
210 } catch (Exception) {}
212 return File(ofname); // to throw a correct exception, lol
217 public string
loadTextFile (string fname
) {
218 auto fl
= openFile(fname
);
220 if (sz
< 0 || sz
> 1024*1024) throw new Exception("invalid text file size: '"~fname
~"'");
221 if (sz
== 0) return null;
222 auto res
= new char[](cast(uint)sz
);
223 if (fl
.rawRead(res
[]).length
!= res
.length
) throw new Exception("error reading text file '"~fname
~"'");
224 import std
.exception
: assumeUnique
;
225 return res
.assumeUnique
;
229 // ////////////////////////////////////////////////////////////////////////// //
230 final class WadArchive
{
232 import std
.stdio
: File
;
235 static struct FileInfo
{
243 public static struct DirEntry
{
255 this (string fname
) {
256 import std
.stdio
: File
;
260 scope(failure
) { zfl
.close
; zfl
= zfl
.init
; }
263 // it now owns the file (if no exception was thrown)
267 scope(success
) zfl
= fl
;
270 @property auto files () {
271 static struct Range
{
277 this (WadArchive ame
, uint aidx
=0) { me
= ame
; curindex
= aidx
; }
280 @property bool empty () const { return (curindex
>= me
.dir
.length
); }
281 @property DirEntry
front () const {
283 (curindex
< me
.dir
.length ? me
.dir
[cast(usize
)curindex
].path
: null),
284 (curindex
< me
.dir
.length ? me
.dir
[cast(usize
)curindex
].name
: null),
285 (curindex
< me
.dir
.length ? me
.dir
[cast(usize
)curindex
].size
: 0));
287 @property Range
save () { return Range(me
, curindex
); }
288 void popFront () { if (curindex
< me
.dir
.length
) ++curindex
; }
289 @property uint length () const { return me
.dir
.length
; }
290 @property uint position () const { return curindex
; } // current position
291 @property void position (uint np
) { curindex
= np
; }
292 void rewind () { curindex
= 0; }
297 File
fopen (ref in DirEntry
de) {
298 foreach (immutable idx
, ref fi
; dir
) {
299 if (fi
.path
== de.path
&& fi
.name
== de.name
) return openDirEntry(idx
, fi
.name
);
301 throw new NamedException
!"WadArchive"("file not found");
304 File
fopen (const(char)[] fname
) {
306 auto pos
= fname
.length
;
307 while (pos
> 0 && fname
[pos
-1] != '/') --pos
;
309 de.path
= cast(string
)fname
[0..pos
]; // it's safe here
310 de.name
= cast(string
)fname
[pos
..$]; // it's safe here
311 //conwriteln("[", de.path, "] [", de.name, "]");
313 de.name
= cast(string
)fname
; // it's safe here
321 if (zfl
.isOpen
) zfl
.close
;
325 void open (File fl
) {
326 import std
.uni
: icmp
;
328 immutable string
[$] msn
= [
329 "SARG", "TROO", "POSS", "SPOS", "CYBR", "CPOS", "BOSS", "BOS2", "HEAD", "SKUL",
330 "PAIN", "SPID", "BSPI", "FATT", "SKEL", "VILE", "FISH", "BAR1", "ROBO", "PLAY"
335 string
fixName (const(char)[] name
) {
336 if (name
.length
>= 4 && name
[0..4] == "stcf") return name
.idup
~".vga";
337 if (name
.length
>= 4 && name
[0..4] == "stbf") return name
.idup
~".vga";
338 if (name
.length
>= 5 && name
[0..5] == "winum") return name
.idup
~".vga";
339 if (name
== "wicolon" || name
== "wiminus" || name
== "wipcnt") return name
.idup
~".vga";
340 if (name
.length
> 3 && name
[0..3] == "map") return name
.idup
~".d2m";
341 if (name
== "playpal") return "playpal.pal";
343 case "endoom": return "endoom.b80";
344 case "endanim": return "endanim.a8";
345 case "end2anim": return "end2anim.a8";
346 case "darts": return "darts.a8";
347 case "colormap": return "colormap.tbl";
348 case "mixmap": return "mixmap.tbl";
349 case "titlepic": return "titlepic.vga";
350 case "interpic": return "interpic.vga";
351 case "cd1pic": return "cd1pic.vga";
352 case "endpic": return "endpic.vraw";
353 case "m_therml": return "m_therml.vga";
354 case "m_thermm": return "m_thermm.vga";
355 case "m_thermo": return "m_thermo.vga";
356 case "m_thermr": return "m_thermr.vga";
357 case "m_lscntr": return "m_lscntr.vga";
358 case "m_lsleft": return "m_lsleft.vga";
359 case "m_lsrght": return "m_lsrght.vga";
363 import std
.algorithm
;
364 if (curpath
== "sounds/") return name
.idup
~".snd";
365 if (curpath
== "music/") return (name
.length
> 3 && name
[0..3] == "dmi" ? name
.idup
~".dmi" : name
.idup
~".dmm");
366 if (curpath
== "tilegfx/") return name
.idup
~".vga";
367 if (curpath
.startsWith("sprites/")) return name
.idup
~".vga";
371 string
fixPath (const(char)[] name
) {
384 return "sprites/sky/";
391 if (name
.length
>= 4 && name
[0..4] == "stcf") return "fonts/stcf/";
392 if (name
.length
>= 4 && name
[0..4] == "stbf") return "fonts/stbf/";
393 if (name
.length
>= 5 && name
[0..5] == "winum") return "fonts/winum/";
394 if (name
.length
>= 3 && name
[0..3] == "stt") return "fonts/stt/";
395 if (name
== "wicolon" || name
== "wiminus" || name
== "wipcnt") return "fonts/winum/";
396 if (name
.length
> 3 && name
[0..3] == "map") return "maps/";
397 if (name
== "d_start") curpath
= "sounds/";
398 if (name
== "m_start") curpath
= "music/";
399 if (name
== "w_start") curpath
= "tilegfx/";
400 if (name
== "s_start") curpath
= "sprites/";
401 if (curpath
== "sprites/" && name
.length
> 4) {
402 switch (name
[0..4]) {
403 case "sarg": return "sprites/monsters/demon/";
404 case "troo": return "sprites/monsters/imp/";
405 case "poss": return "sprites/monsters/zombie/";
406 case "spos": return "sprites/monsters/sergeant/";
407 case "cybr": return "sprites/monsters/cyberdemon/";
408 case "cpos": return "sprites/monsters/chaingunner/";
409 case "boss": return "sprites/monsters/baron/";
410 case "bos2": return "sprites/monsters/knight/";
411 case "head": return "sprites/monsters/cacodemon/";
412 case "skul": return "sprites/monsters/soul/";
413 case "pain": return "sprites/monsters/painel/";
414 case "spid": return "sprites/monsters/mastermind/";
415 case "bspi": return "sprites/monsters/arachnotron/";
416 case "fatt": return "sprites/monsters/mancubus/";
417 case "skel": return "sprites/monsters/revenant/";
418 case "vile": return "sprites/monsters/archvile/";
419 case "fish": return "sprites/monsters/fish/";
420 case "bar1": return "sprites/monsters/barrel/";
421 case "robo": return "sprites/monsters/robot/";
422 case "play": return "sprites/monsters/player/";
429 import core
.stdc
.stdio
: SEEK_CUR
, SEEK_END
;
430 scope(failure
) cleanup();
434 if (fl
.rawRead(data
[]).length
!= data
.length
) throw new NamedException
!"WadArchive"("reading error");
435 return cast(uint)(data
[0]+0x100*data
[1]+0x10000*data
[2]+0x1000000*data
[3]);
444 if (fl
.rawRead(lmpname
[0..4]).length
!= 4) throw new NamedException
!"WadArchive"("reading error");
445 if (lmpname
[0..4] != "PWAD" && lmpname
[0..4] != "IWAD") throw new NamedException
!"WadArchive"("not a WAD file");
446 auto count
= readU32();
447 auto dofs
= readU32();
448 if (count
== 0) return;
449 if (dofs
< 3*4 || count
== uint.max
) throw new NamedException
!"WadArchive"("invalid WAD file");
450 fl
.seek(dofs
-3*4, SEEK_CUR
);
451 while (count
-- > 0) {
454 if (fl
.rawRead(lmpname
[]).length
!= 8) throw new NamedException
!"WAD"("reading error");
456 while (pos
< 8 && lmpname
[pos
] != 0) {
457 if (lmpname
[pos
] >= 'A' && lmpname
[pos
] <= 'Z') lmpname
[pos
] += 32;
460 if (pos
== 0) continue;
461 auto nm
= lmpname
[0..pos
];
462 if (nm
.length
> 6 && nm
[$-6..$] == "_start") {
466 if (nm
.length
> 4 && nm
[$-4..$] == "_end") {
470 uint fidx
= uint.max
;
471 foreach (immutable idx
, ref de; dir
) if (de.name
== lmpname
[0..pos
]) { fidx
= cast(uint)idx
; break; }
472 if (fidx
>= dir
.length
) {
473 fidx
= cast(uint)dir
.length
;
476 dir
[fidx
].ofs
= lmpofs
;
477 dir
[fidx
].size
= lmpsize
;
478 //{ import std.stdio : stderr; stderr.writeln("[", nm, "]"); }
480 import std
.uni
: toLower
;
481 if (nm
.length
> 4 && nm
[0..5] == "stbf_") {
483 assert(nm
.length
== 6);
484 dir
[fidx
].name
= recodeToKOI8(recode(fixName(nm
), "utf-8", "cp866").toLower
, "utf-8");
485 dir
[fidx
].path
= recodeToKOI8(recode(fixPath(nm
), "utf-8", "cp866").toLower
, "utf-8");
486 import std
.string
: format
;
487 dir
[fidx
].name
= "stbf_x%02x.vga".format(cast(ubyte)(nm
[$-1]));
488 } else if (nm
.length
== 8 && nm
[0..5] == "stcfn") {
489 import std
.conv
: to
;
490 ubyte cc
= cast(ubyte)(koi8from866Table
[to
!ubyte(nm
[5..$], 10)]);
491 import std
.string
: format
;
492 dir
[fidx
].name
= "stcfnx%02x.vga".format(cc
);
493 dir
[fidx
].path
= recodeToKOI8(recode(fixPath(nm
), "utf-8", "cp866").toLower
, "utf-8");
495 dir
[fidx
].name
= koi8lotranslit(recodeToKOI8(recode(fixName(nm
), "utf-8", "cp866").toLower
, "utf-8"));
496 dir
[fidx
].path
= koi8lotranslit(recodeToKOI8(recode(fixPath(nm
), "utf-8", "cp866").toLower
, "utf-8"));
499 //debug conwriteln(dir[fidx].path, " : ", dir[fidx].name);
501 debug conwriteln(dir
.length
, " files found");
505 // ////////////////////////////////////////////////////////////////////// //
506 static import core
.sync
.mutex
;
508 core
.sync
.mutex
.Mutex
lock;
511 lock = new core
.sync
.mutex
.Mutex
;
514 auto openDirEntry (uint idx
, string filename
) {
515 import core
.sys
.linux
.stdio
: fopencookie
;
516 import core
.stdc
.stdio
: FILE
;
517 import core
.stdc
.stdio
: fopen
, fclose
;
518 import core
.stdc
.stdlib
: calloc
, free
;
520 import std
.internal
.cstring
: tempCString
;
521 import core
.memory
: GC
;
523 if (!zfl
.isOpen
) throw new NamedException
!"WadArchive"("archive wasn't opened");
524 if (idx
>= dir
.length
) throw new NamedException
!"WadArchive"("invalid dir index");
526 // create cookied `FILE*`
527 auto fc
= cast(InnerFileCookied
*)calloc(1, InnerFileCookied
.sizeof
);
528 scope(exit
) if (fc
!is null) free(fc
);
530 import core
.exception
: onOutOfMemoryErrorNoGC
;
531 onOutOfMemoryErrorNoGC();
533 (*fc
) = InnerFileCookied
.init
;
534 (*fc
).stpos
= flstpos
+dir
[idx
].ofs
;
535 (*fc
).size
= cast(uint)dir
[idx
].size
;
537 GC
.addRange(fc
, InnerFileCookied
.sizeof
);
539 // open `cooked` file
540 FILE
* fres
= fopencookie(cast(void*)fc
, "r", fcdatpkCallbacks
);
543 if ((*fc
).fl
!is null) fclose((*fc
).fl
);
544 try { (*fc
).xfl
.detach(); } catch (Exception
) {}
545 throw new NamedException
!"WadArchive"("can't open cookied file");
549 return File(fres
, filename
);
553 // ////////////////////////////////////////////////////////////////////// //
554 // "inner" file processor; processes both packed and unpacked files
555 // can be used as normal disk file processor too
556 static struct InnerFileCookied
{
557 private import core
.sys
.posix
.sys
.types
: ssize_t
, off64_t
= off_t
;
558 private import core
.stdc
.stdio
: FILE
;
562 core
.sync
.mutex
.Mutex
lock;
563 // note that either one of `fl` or `xfl` must be opened and operational
564 FILE
* fl
; // disk file, can be `null`
565 File xfl
; // disk file, can be closed
566 long stpos
; // starting position
567 uint size
; // unpacked size
568 uint pos
; // current file position
569 //uint prpos; // previous file position
570 //uint pkpos; // current position in DAT
571 //ubyte[] pkb; // packed data
574 @disable this (this);
577 ~this () { close(); }
579 @property bool isOpen () @safe /*@nogc*/ { return (fl
!is null || xfl
.isOpen
); }
582 import core
.memory
: GC
;
583 import core
.stdc
.stdlib
: free
;
585 if (lock !is null) lock.lock();
586 scope(exit
) if (lock !is null) lock.unlock();
588 import core
.stdc
.stdio
: fclose
;
592 try { xfl
.detach(); } catch (Exception
) {} // it's safe to detach closed File
597 ssize_t
read (void* buf
, size_t count
) {
598 if (buf
is null) return -1;
599 if (count
== 0 || size
== 0) return 0;
601 scope(exit
) lock.unlock();
602 if (!isOpen
) return -1; // read error
603 if (pos
>= size
) return 0; // EOF
605 import core
.stdc
.stdio
: ferror
, fread
;
606 import core
.sys
.posix
.stdio
: fseeko
;
607 if (size
-pos
< count
) count
= cast(size_t
)(size
-pos
);
610 if (fseeko(fl
, stpos
+pos
, 0) < 0) return -1;
611 auto rd
= fread(buf
, 1, count
, fl
);
612 if (rd
!= count
&& (rd
< 0 ||
ferror(fl
))) rd
= -1;
613 if (rd
> 0) pos
+= rd
;
618 xfl
.seek(stpos
+pos
, 0);
619 auto rd
= xfl
.rawRead(buf
[0..count
]);
621 return (rd
.length
== count ? rd
.length
: -1);
622 } catch (Exception
) {} //BAD DOGGY!
628 long seek (long ofs
, int whence
) {
630 scope(exit
) lock.unlock();
631 if (!isOpen
) return -1;
632 //TODO: overflow checks
640 if (ofs
> 0) ofs
= 0;
646 if (ofs
< 0) return -1;
647 if (ofs
> size
) ofs
= size
;
655 // ////////////////////////////////////////////////////////////////////// //
657 import core
.sys
.linux
.stdio
: cookie_io_functions_t
;
658 import core
.sys
.posix
.sys
.types
: ssize_t
, off64_t
= off_t
;
660 ssize_t
fcdatpkRead (void* cookie
, char* buf
, size_t count
) {
661 //conwriteln("reading ", count, " bytes");
662 import core
.stdc
.errno
;
663 auto fc
= cast(InnerFileCookied
*)cookie
;
664 auto res
= fc
.read(buf
, count
);
665 if (res
< 0) { errno
= EIO
; return -1; }
669 ssize_t
fcdatpkWrite (void* cookie
, const(char)* buf
, size_t count
) {
670 //conwriteln("writing ", count, " bytes");
671 import core
.stdc
.errno
;
672 errno
= EIO
; //FIXME: find better code
673 return 0; // error; write should not return `-1`
676 int fcdatpkSeek (void* cookie
, off64_t
* offset
, int whence
) {
677 //conwriteln("seeking ", *offset, " bytes, whence=", whence);
678 import core
.stdc
.errno
;
679 auto fc
= cast(InnerFileCookied
*)cookie
;
680 auto res
= fc
.seek(*offset
, whence
);
681 if (res
< 0) { errno
= EIO
; return -1; }
682 *offset
= cast(off64_t
)res
;
686 int fcdatpkClose (void* cookie
) {
687 import core
.memory
: GC
;
688 import core
.stdc
.stdlib
: free
;
689 //conwriteln("closing");
690 auto fc
= cast(InnerFileCookied
*)cookie
;
692 GC
.removeRange(cookie
);
693 try { fc
.__dtor(); } catch (Exception
) {}
694 // no need to run finalizers, we SHOULD NOT have any
695 //try { GC.runFinalizers(cookie[0..InnerFileCookied.sizeof]); } catch (Exception) {}
698 //conwriteln("closed");
703 __gshared cookie_io_functions_t fcdatpkCallbacks
= cookie_io_functions_t(
704 /*.read =*/ &fcdatpkRead
,
705 /*.write =*/ &fcdatpkWrite
,
706 /*.seek =*/ &fcdatpkSeek
,
707 /*.close =*/ &fcdatpkClose
,
711 T
[] xalloc(T
) (size_t len
) {
712 import core
.stdc
.stdlib
: malloc
;
713 if (len
< 1) return null;
714 auto res
= cast(T
*)malloc(len
*T
.sizeof
);
716 import core
.exception
: onOutOfMemoryErrorNoGC
;
717 onOutOfMemoryErrorNoGC();
719 res
[0..len
] = T
.init
;
723 void xfree(T
) (ref T
[] slc
) {
724 if (slc
.ptr
!is null) {
725 import core
.stdc
.stdlib
: free
;