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
.arcz
/*is aliced*/;
20 import iv
.vfs
.types
: Seek
;
25 import iv
.vfs
.arc
.internal
;
28 // ////////////////////////////////////////////////////////////////////////// //
29 mixin(VFSSimpleArchiveDetectorMixin
!"ArcZ");
31 // use Balz compressor if available
32 static if (__traits(compiles
, { import iv
.balz
; })) enum iv_vfs_arcz_has_balz
= true; else enum iv_vfs_arcz_has_balz
= false;
33 static if (__traits(compiles
, { import iv
.dlzma
; })) enum iv_vfs_arcz_has_dlzma
= true; else enum iv_vfs_arcz_has_dlzma
= false;
34 static if (iv_vfs_arcz_has_balz
) import iv
.balz
;
35 static if (iv_vfs_arcz_has_dlzma
) import iv
.dlzma
;
38 // ////////////////////////////////////////////////////////////////////////// //
39 // ARZ archive accessor. Use this to open ARZ archives, and open packed files from ARZ archives.
40 private struct ArzArchive
{
41 private import core
.stdc
.stdio
: SEEK_SET
, SEEK_CUR
, SEEK_END
;
43 static assert(usize
.sizeof
>= (void*).sizeof
);
44 private import etc
.c
.zlib
;
52 static align(1) struct ChunkInfo
{
54 uint ofs
; // offset in file
55 uint pksize
; // packed chunk size (same as chunk size: chunk is unpacked)
58 static align(1) struct FileInfo
{
61 uint nameofs
; // in index
63 const(char)[] name (const(void)* index
) const pure nothrow @trusted @nogc { pragma(inline
, true); return (cast(const(char)*)index
)[nameofs
..nameofs
+namelen
]; }
64 // this is copied from index
66 uint chunkofs
; // offset of first file byte in unpacked chunk
67 uint size
; // unpacked file size
71 uint rc
= 1; // refcounter
72 ubyte* index
; // unpacked archive index
73 ChunkInfo
* chunks
; // in index, not allocated
74 FileInfo
* files
; // in index, not allocated
75 uint fcount
; // number of valid entries in files
76 uint ccount
; // number of valid entries in chunks
80 VFile afl
; // archive file, we'll keep it opened
82 @disable this (this); // no copies!
84 static void decRef (usize me
) {
86 auto nfo
= cast(Nfo
*)me
;
89 import core
.memory
: GC
;
90 import core
.stdc
.stdlib
: free
;
91 if (nfo
.afl
.isOpen
) nfo
.afl
.close();
92 if (nfo
.index
!is null) free(nfo
.index
);
93 //GC.removeRange(cast(void*)nfo/*, Nfo.sizeof*/);
95 debug(iv_vfs_arcz_rc
) { import core
.stdc
.stdio
: printf
; printf("Nfo %p freed\n", nfo
); }
101 usize nfop
; // hide it from GC
103 private @property Nfo
* nfo () { pragma(inline
, true); return cast(Nfo
*)nfop
; }
104 void decRef () { pragma(inline
, true); Nfo
.decRef(nfop
); nfop
= 0; }
106 static uint readUint (VFile fl
) {
107 if (!fl
.isOpen
) throw new Exception("cannot read from closed file");
109 fl
.rawReadExact((&v
)[0..1]);
111 import core
.bitop
: bswap;
113 } else version(LittleEndian
) {
116 static assert(0, "wtf?!");
121 static uint readUbyte (VFile fl
) {
122 if (!fl
.isOpen
) throw new Exception("cannot read from closed file");
124 fl
.rawReadExact((&v
)[0..1]);
128 static void readBuf (VFile fl
, void[] buf
) {
129 if (buf
.length
> 0) {
130 fl
.rawReadExact(buf
[]);
134 static T
* xalloc(T
, bool clear
=true) (uint mem
) if (T
.sizeof
> 0) {
135 import core
.exception
: onOutOfMemoryError
;
138 import core
.stdc
.stdlib
: calloc
;
139 auto res
= calloc(mem
, T
.sizeof
);
140 if (res
is null) onOutOfMemoryError();
141 static if (is(T
== struct)) {
142 import core
.stdc
.string
: memcpy
;
143 static immutable T i
= T
.init
;
144 foreach (immutable idx
; 0..mem
) memcpy(res
+idx
, &i
, T
.sizeof
);
146 debug(iv_vfs_arcz_alloc
) { import core
.stdc
.stdio
: printf
; printf("allocated %u bytes at %p\n", cast(uint)(mem
*T
.sizeof
), res
); }
149 import core
.stdc
.stdlib
: malloc
;
150 auto res
= malloc(mem
*T
.sizeof
);
151 if (res
is null) onOutOfMemoryError();
152 static if (is(T
== struct)) {
153 import core
.stdc
.string
: memcpy
;
154 static immutable T i
= T
.init
;
155 foreach (immutable idx
; 0..mem
) memcpy(res
+idx
, &i
, T
.sizeof
);
157 debug(iv_vfs_arcz_alloc
) { import core
.stdc
.stdio
: printf
; printf("allocated %u bytes at %p\n", cast(uint)(mem
*T
.sizeof
), res
); }
162 static void xfree(T
) (T
* ptr
) {
164 import core
.stdc
.stdlib
: free
;
165 debug(iv_vfs_arcz_alloc
) { import core
.stdc
.stdio
: printf
; printf("freing at %p\n", ptr
); }
170 static if (iv_vfs_arcz_has_balz
) static ubyte balzDictSize (uint blockSize
) {
171 foreach (ubyte bits
; Balz
.MinDictBits
..Balz
.MaxDictBits
+1) {
172 if ((1U<<bits
) >= blockSize
) return bits
;
174 return Balz
.MaxDictBits
;
177 // unpack exactly `destlen` bytes
178 static if (iv_vfs_arcz_has_balz
) static void unpackBlockBalz (void* dest
, uint destlen
, const(void)* src
, uint srclen
, uint blocksize
) {
180 bz
.reinit(balzDictSize(blocksize
));
182 auto dc
= bz
.decompress(
185 import core
.stdc
.string
: memcpy
;
186 if (ipos
>= srclen
) return 0;
187 uint rd
= destlen
-ipos
;
188 if (rd
> buf
.length
) rd
= cast(uint)buf
.length
;
189 memcpy(buf
.ptr
, src
+ipos
, rd
);
195 //if (opos+buf.length > destlen) throw new Exception("error unpacking archive");
196 uint wr
= destlen
-opos
;
197 if (wr
> buf
.length
) wr
= cast(uint)buf
.length
;
199 import core
.stdc
.string
: memcpy
;
200 memcpy(dest
+opos
, buf
.ptr
, wr
);
207 if (opos
!= destlen
) throw new Exception("error unpacking archive");
210 // unpack exactly `destlen` bytes
211 static if (iv_vfs_arcz_has_dlzma
) static void unpackBlockLzma (void* dest
, uint destlen
, const(void)* src
, uint srclen
, uint blocksize
) {
212 if (srclen
< LZMA_PROPS_SIZE
) throw new Exception("error unpacking LZMA data");
213 if (srclen
== LZMA_PROPS_SIZE
) {
214 if (destlen
== 0) return;
215 throw new Exception("error unpacking LZMA data");
217 const(ubyte)* srcBytes
= cast(const(ubyte)*)src
;
218 ubyte* destBytes
= cast(ubyte*)dest
;
220 LzmaDec_Init(&lzdec
);
221 if (LzmaDec_Allocate(&lzdec
, srcBytes
, LZMA_PROPS_SIZE
, &lzmaDefAllocator
) != SZ_OK
) {
222 throw new Exception("error allocating LZMA decoder");
224 scope(exit
) LzmaDec_Free(&lzdec
, &lzmaDefAllocator
);
225 srcBytes
+= LZMA_PROPS_SIZE
;
226 srclen
-= LZMA_PROPS_SIZE
;
228 usize dlen
= destlen
;
231 immutable dres
= LzmaDec_DecodeToBuf(&lzdec
, destBytes
, &dlen
, srcBytes
, &slen
, LZMA_FINISH_ANY
, &status
);
232 if (dres
!= SZ_OK || dlen
!= destlen
) throw new Exception("error unpacking LZMA data");
235 static void unpackBlockZLib (void* dest
, uint destlen
, const(void)* src
, uint srclen
, uint blocksize
) {
239 // initialize unpacker
240 if (inflateInit2(&zs
, 15) != Z_OK
) throw new Exception("can't initialize zlib");
241 scope(exit
) inflateEnd(&zs
);
242 zs
.next_in
= cast(typeof(zs
.next_in
))src
;
243 zs
.avail_in
= srclen
;
244 zs
.next_out
= cast(typeof(zs
.next_out
))dest
;
245 zs
.avail_out
= destlen
;
246 while (zs
.avail_out
> 0) {
247 auto err
= inflate(&zs
, Z_SYNC_FLUSH
);
248 if (err
!= Z_STREAM_END
&& err
!= Z_OK
) throw new Exception("error unpacking archive");
249 if (err
== Z_STREAM_END
) break;
251 if (zs
.avail_out
!= 0) throw new Exception("error unpacking archive");
254 static void unpackBlock (void* dest
, uint destlen
, const(void)* src
, uint srclen
, uint blocksize
, in Packer packer
) {
255 final switch (packer
) {
257 unpackBlockZLib(dest
, destlen
, src
, srclen
, blocksize
);
260 static if (iv_vfs_arcz_has_balz
) {
261 unpackBlockBalz(dest
, destlen
, src
, srclen
, blocksize
);
264 throw new Exception("no Balz support was compiled in ArcZ");
267 static if (iv_vfs_arcz_has_dlzma
) {
268 unpackBlockLzma(dest
, destlen
, src
, srclen
, blocksize
);
271 throw new Exception("no LZMA support was compiled in ArcZ");
277 this (in ArzArchive arc
) {
287 ~this () { close(); }
289 void opAssign (in ArzArchive arc
) {
291 auto n
= cast(Nfo
*)arc
.nfop
;
298 void close () { decRef(); }
300 //@property FileInfo[string] files () { return (nfop ? nfo.files : null); }
302 void openArchive (VFile fl
) {
303 debug/*(arcz)*/ import core
.stdc
.stdio
: printf
;
307 if (sign
!= "CZA2") throw new Exception("invalid arcz archive file");
308 switch (readUbyte(fl
)) {
309 case 0: packer
= Packer
.Zlib
; break;
310 case 1: packer
= Packer
.Balz
; break;
311 case 2: packer
= Packer
.Lzma
; break;
312 default: throw new Exception("invalid version of arcz archive file");
314 uint indexofs
= readUint(fl
); // index offset in file
315 uint pkidxsize
= readUint(fl
); // packed index size
316 uint idxsize
= readUint(fl
); // unpacked index size
317 if (pkidxsize
== 0 || idxsize
== 0 || indexofs
== 0) throw new Exception("invalid arcz archive file");
319 ubyte* idxbuf
= null;
320 scope(failure
) xfree(idxbuf
);
322 auto pib
= xalloc
!ubyte(pkidxsize
);
323 scope(exit
) xfree(pib
);
325 readBuf(fl
, pib
[0..pkidxsize
]);
326 idxbuf
= xalloc
!ubyte(idxsize
);
327 unpackBlock(idxbuf
, idxsize
, pib
, pkidxsize
, idxsize
, packer
);
330 // parse index and build structures
334 if (idxsize
-idxbufpos
< ubyte.sizeof
) throw new Exception("invalid index for arcz archive file");
335 return idxbuf
[idxbufpos
++];
339 if (idxsize
-idxbufpos
< uint.sizeof
) throw new Exception("invalid index for arcz archive file");
341 import core
.bitop
: bswap;
342 uint v
= *cast(uint*)(idxbuf
+idxbufpos
);
345 } else version(LittleEndian
) {
346 uint v
= *cast(uint*)(idxbuf
+idxbufpos
);
350 static assert(0, "wtf?!");
354 void getBuf (void[] buf
) {
355 if (buf
.length
> 0) {
356 import core
.stdc
.string
: memcpy
;
357 if (idxsize
-idxbufpos
< buf
.length
) throw new Exception("invalid index for arcz archive file");
358 memcpy(buf
.ptr
, idxbuf
+idxbufpos
, buf
.length
);
359 idxbufpos
+= buf
.length
;
363 void skipBuf (uint len
) {
365 if (idxsize
-idxbufpos
< len
) throw new Exception("invalid index for arcz archive file");
370 // allocate shared info struct
371 Nfo
* nfo
= xalloc
!Nfo(1);
373 debug(iv_vfs_arcz_rc
) { import core
.stdc
.stdio
: printf
; printf("Nfo %p allocated\n", nfo
); }
374 scope(failure
) decRef();
375 nfop
= cast(usize
)nfo
;
376 /* no need to, there is nothing that needs GC there
378 import core.memory : GC;
379 GC.addRange(nfo, Nfo.sizeof);
383 // read chunk info and data
385 nfo
.chunkSize
= getUint
;
386 auto ccount
= getUint
; // chunk count
387 nfo
.lastChunkSize
= getUint
;
388 debug(iv_vfs_arcz_dirread
) printf("chunk size: %u\nchunk count: %u\nlast chunk size:%u\n", nfo
.chunkSize
, ccount
, nfo
.lastChunkSize
);
389 if (ccount
== 0 || nfo
.chunkSize
< 1 || nfo
.lastChunkSize
< 1 || nfo
.lastChunkSize
> nfo
.chunkSize
) throw new Exception("invalid arcz archive file");
391 nfo.chunks.length = ccount;
392 // chunk offsets and sizes
393 foreach (ref ci; nfo.chunks) {
398 nfo
.chunks
= cast(ChunkInfo
*)(idxbuf
+idxbufpos
);
403 foreach (ref ChunkInfo ci
; nfo
.chunks
[0..ccount
]) {
404 import core
.bitop
: bswap;
405 ci
.ofs
= bswap(ci
.ofs
);
406 ci
.pksize
= bswap(ci
.pksize
);
409 // read file count and info
410 auto fcount
= getUint
;
411 if (fcount
== 0) throw new Exception("empty arcz archive");
412 debug(iv_vfs_arcz_dirread
) printf("file count: %u\n", fcount
);
413 if (fcount
>= uint.max
/(5*4)) throw new Exception("too many files in arcz archive");
414 if (fcount
*(5*4) > idxsize
-idxbufpos
) throw new Exception("invalid index in arcz archive");
415 nfo
.files
= cast(FileInfo
*)(idxbuf
+idxbufpos
);
417 skipBuf(fcount
*(5*4));
418 //immutable uint ubofs = idxbufpos;
419 //immutable uint ubsize = idxsize-ubofs;
420 foreach (ref FileInfo fi
; nfo
.files
[0..fcount
]) {
423 import core
.bitop
: bswap;
424 fi
.nameofs
= bswap(fi
.nameofs
);
425 fi
.namelen
= bswap(fi
.namelen
);
426 fi
.chunk
= bswap(fi
.chunk
);
427 fi
.chunkofs
= bswap(fi
.chunkofs
);
428 fi
.size
= bswap(fi
.size
);
430 if (fi
.namelen
> idxsize || fi
.nameofs
> idxsize || fi
.nameofs
+fi
.namelen
> idxsize
) throw new Exception("invalid index in arcz archive");
432 // transfer achive file ownership
437 //bool exists (const(char)[] name) { if (nfop) return ((name in nfo.files) !is null); else return false; }
439 AZFile
openByIndex (uint idx
) {
440 if (!nfop
) throw new Exception("can't open file from non-opened archive");
441 if (idx
>= nfo
.fcount
) throw new Exception("can't open non-existing file");
442 auto fi
= nfo
.files
+idx
;
443 auto zl
= xalloc
!LowLevelPackedRO(1);
444 scope(failure
) xfree(zl
);
445 debug(iv_vfs_arcz_rc
) { import core
.stdc
.stdio
: printf
; printf("Zl %p allocated\n", zl
); }
446 zl
.setup(nfo
, fi
.chunk
, fi
.chunkofs
, fi
.size
);
448 fl
.zlp
= cast(usize
)zl
;
453 static struct LowLevelPackedRO
{
454 private import etc
.c
.zlib
;
457 usize nfop
; // hide it from GC
459 private @property inout(Nfo
*) nfo () inout pure nothrow @trusted @nogc { pragma(inline
, true); return cast(typeof(return))nfop
; }
460 static void decRef (usize me
) {
462 auto zl
= cast(LowLevelPackedRO
*)me
;
465 import core
.stdc
.stdlib
: free
;
466 if (zl
.chunkData
!is null) free(zl
.chunkData
);
467 version(iv_vfs_arcz_use_more_memory
) if (zl
.pkdata
!is null) free(zl
.pkdata
);
470 debug(iv_vfs_arcz_rc
) { import core
.stdc
.stdio
: printf
; printf("Zl %p freed\n", zl
); }
472 //debug(iv_vfs_arcz_rc) { import core.stdc.stdio : printf; printf("Zl %p; rc after decRef is %u\n", zl, zl.rc); }
477 uint nextchunk
; // next chunk to read
478 uint curcpos
; // position in current chunk
479 uint curcsize
; // number of valid bytes in `chunkData`
480 uint stchunk
; // starting chunk
481 uint stofs
; // offset in starting chunk
482 uint totalsize
; // total file size
483 uint pos
; // current file position
484 uint lastrdpos
; // last actual read position
486 ubyte* chunkData
; // can be null
487 version(iv_vfs_arcz_use_more_memory
) {
492 @disable this (this);
494 void setup (Nfo
* anfo
, uint astchunk
, uint astofs
, uint asize
) {
495 assert(anfo
!is null);
497 nfop
= cast(usize
)anfo
;
499 nextchunk
= stchunk
= astchunk
;
505 @property bool eof () { pragma(inline
, true); return (pos
>= totalsize
); }
507 // return less than chunk size if our file fits in one non-full chunk completely
508 uint justEnoughMemory () pure const nothrow @safe @nogc {
509 pragma(inline
, true);
511 return nfo
.chunkSize
;
513 return (totalsize
< nfo
.chunkSize
&& stofs
+totalsize
< nfo
.chunkSize ? stofs
+totalsize
: nfo
.chunkSize
);
517 void unpackNextChunk () {
518 if (nfop
== 0) assert(0, "wtf?!");
519 //scope(failure) if (chunkData !is null) { xfree(chunkData); chunkData = null; }
520 debug(iv_vfs_arcz_unp
) { import core
.stdc
.stdio
: printf
; printf("unpacking chunk %u\n", nextchunk
); }
521 // allocate buffer for unpacked data
522 if (chunkData
is null) {
523 // optimize things a little: if our file fits in less then one chunk, allocate "just enough" memory
524 chunkData
= xalloc
!(ubyte, false)(justEnoughMemory
);
526 auto chunk
= &nfo
.chunks
[nextchunk
];
527 if (chunk
.pksize
== nfo
.chunkSize
) {
528 // unpacked chunk, just read it
529 debug(iv_vfs_arcz_unp
) { import core
.stdc
.stdio
: printf
; printf(" chunk is not packed\n"); }
530 nfo
.afl
.seek(chunk
.ofs
, Seek
.Set
);
531 nfo
.afl
.rawReadExact(chunkData
[0..nfo
.chunkSize
]);
532 curcsize
= nfo
.chunkSize
;
534 // packed chunk, unpack it
535 // allocate buffer for packed data
536 version(iv_vfs_arcz_use_more_memory
) {
537 import core
.stdc
.stdlib
: realloc
;
538 if (pkdatasize
< chunk
.pksize
) {
539 import core
.exception
: onOutOfMemoryError
;
540 auto newpk
= realloc(pkdata
, chunk
.pksize
);
541 if (newpk
is null) onOutOfMemoryError();
542 debug(iv_vfs_arcz_alloc
) { import core
.stdc
.stdio
: printf
; printf("reallocated from %u to %u bytes; %p -> %p\n", cast(uint)pkdatasize
, cast(uint)chunk
.pksize
, pkdata
, newpk
); }
543 pkdata
= cast(ubyte*)newpk
;
544 pkdatasize
= chunk
.pksize
;
548 auto pkd
= xalloc
!(ubyte, false)(chunk
.pksize
);
549 scope(exit
) xfree(pkd
);
551 nfo
.afl
.seek(chunk
.ofs
, Seek
.Set
);
552 nfo
.afl
.rawReadExact(pkd
[0..chunk
.pksize
]);
553 uint upsize
= (nextchunk
== nfo
.ccount
-1 ? nfo
.lastChunkSize
: nfo
.chunkSize
); // unpacked chunk size
554 immutable uint cksz
= upsize
;
555 immutable uint jem
= justEnoughMemory
;
556 if (upsize
> jem
) upsize
= jem
;
557 debug(iv_vfs_arcz_unp
) { import core
.stdc
.stdio
: printf
; printf(" unpacking %u bytes to %u bytes\n", chunk
.pksize
, upsize
); }
558 ArzArchive
.unpackBlock(chunkData
, upsize
, pkd
, chunk
.pksize
, cksz
, nfo
.packer
);
562 // fix first chunk offset if necessary
563 if (nextchunk
== stchunk
&& stofs
> 0) {
564 // it's easier to just memmove it
565 import core
.stdc
.string
: memmove
;
566 assert(stofs
< curcsize
);
567 memmove(chunkData
, chunkData
+stofs
, curcsize
-stofs
);
570 ++nextchunk
; // advance to next chunk
573 void syncReadPos () {
574 if (pos
>= totalsize || pos
== lastrdpos
) return;
575 immutable uint fcdata
= nfo
.chunkSize
-stofs
; // number of our bytes in the first chunk
576 // does our pos lie in the first chunk?
579 if (nextchunk
!= stchunk
+1) {
581 unpackNextChunk(); // we'll need it anyway
590 // find the chunk we want
591 uint npos
= pos
-fcdata
;
592 uint xblock
= stchunk
+1+npos
/nfo
.chunkSize
;
593 uint curcstart
= (xblock
-(stchunk
+1))*nfo
.chunkSize
+fcdata
;
594 if (xblock
!= nextchunk
-1) {
595 // read and unpack this chunk
602 assert(pos
>= curcstart
&& pos
< curcstart
+nfo
.chunkSize
);
603 uint skip
= pos
-curcstart
;
608 ssize
read (void* buf
, usize count
) {
609 if (buf
is null) return -1;
610 if (count
== 0 || totalsize
== 0) return 0;
611 if (totalsize
>= 0 && pos
>= totalsize
) return 0; // EOF
613 assert(lastrdpos
== pos
);
614 if (cast(long)pos
+count
> totalsize
) count
= totalsize
-pos
;
617 debug(iv_vfs_arcz_read
) { import core
.stdc
.stdio
: printf
; printf("reading %u bytes; pos=%u; lastrdpos=%u; curcpos=%u; curcsize=%u\n", count
, pos
, lastrdpos
, curcpos
, curcsize
); }
618 import core
.stdc
.string
: memcpy
;
619 if (curcpos
>= curcsize
) {
620 unpackNextChunk(); // we want next chunk!
621 debug(iv_vfs_arcz_read
) { import core
.stdc
.stdio
: printf
; printf(" *reading %u bytes; pos=%u; lastrdpos=%u; curcpos=%u; curcsize=%u\n", count
, pos
, lastrdpos
, curcpos
, curcsize
); }
623 assert(curcpos
< curcsize
&& curcsize
!= 0);
624 ssize rd
= (curcsize
-curcpos
>= count ? count
: curcsize
-curcpos
);
626 memcpy(buf
, chunkData
+curcpos
, rd
);
633 assert(pos
== lastrdpos
);
637 long lseek (long ofs
, int origin
) {
638 //TODO: overflow checks
640 case SEEK_SET
: break;
641 case SEEK_CUR
: ofs
+= pos
; break;
643 if (ofs
> 0) ofs
= 0;
644 if (-ofs
> totalsize
) ofs
= -cast(long)totalsize
;
650 if (ofs
< 0) return -1;
651 if (totalsize
>= 0 && ofs
> totalsize
) ofs
= totalsize
;
659 // ////////////////////////////////////////////////////////////////////////// //
661 private struct AZFile
{
662 private import core
.stdc
.stdio
: SEEK_SET
, SEEK_CUR
, SEEK_END
;
666 private @property inout(ArzArchive
.LowLevelPackedRO
)* zl () inout pure nothrow @trusted @nogc { pragma(inline
, true); return cast(typeof(return))zlp
; }
667 private void decRef () { pragma(inline
, true); ArzArchive
.LowLevelPackedRO
.decRef(zlp
); zlp
= 0; }
670 this (in AZFile afl
) {
680 ~this () { close(); }
682 void opAssign (in AZFile afl
) {
684 auto n
= cast(ArzArchive
.LowLevelPackedRO
*)afl
.zlp
;
691 void close () { decRef(); }
693 @property bool isOpen () const pure nothrow @safe @nogc { pragma(inline
, true); return (zlp
!= 0); }
694 @property uint size () const pure nothrow @safe @nogc { pragma(inline
, true); return (zlp ? zl
.totalsize
: 0); }
695 @property uint tell () const pure nothrow @safe @nogc { pragma(inline
, true); return (zlp ? zl
.pos
: 0); }
697 void seek (long ofs
, int origin
=SEEK_SET
) {
698 if (!zlp
) throw new Exception("can't seek in closed file");
699 auto res
= zl
.lseek(ofs
, origin
);
700 if (res
< 0) throw new Exception("seek error");
703 //TODO: overflow check
704 T
[] rawRead(T
) (T
[] buf
) if (!is(T
== const) && !is(T
== immutable)) {
705 if (!zlp
) throw new Exception("can't read from closed file");
706 if (buf
.length
> 0) {
707 auto res
= zl
.read(buf
.ptr
, buf
.length
*T
.sizeof
);
708 if (res
== -1 || res
%T
.sizeof
!= 0) throw new Exception("read error");
709 return buf
[0..res
/T
.sizeof
];
717 // ////////////////////////////////////////////////////////////////////////// //
718 public final class VFSDriverArcZ
: VFSDriver
{
719 mixin VFSSimpleArchiveDriverMixin
;
724 static struct FileInfo
{
726 string name
; // with path
730 VFile
wrap (usize idx
) { return wrapStream(arc
.openByIndex(dir
[idx
].aidx
), dir
[idx
].name
); }
732 void open (VFile fl
, const(char)[] prefixpath
) {
734 debug(iv_vfs_arcz_dirread
) { import core
.stdc
.stdio
; printf("files: %u\n", cast(uint)arc
.nfo
.fcount
); }
735 foreach (immutable aidx
, ref afi
; arc
.nfo
.files
[0..arc
.nfo
.fcount
]) {
736 auto xname
= afi
.name(arc
.nfo
.index
);
737 if (xname
.length
== 0) continue; // just in case
740 fi
.aidx
= cast(uint)aidx
;
741 auto name
= new char[](prefixpath
.length
+xname
.length
);
742 if (prefixpath
.length
) name
[0..prefixpath
.length
] = prefixpath
;
743 name
[prefixpath
.length
..$] = xname
[];
744 fi
.name
= cast(string
)name
; // it is safe to cast here
745 dir
.arrayAppendUnsafe(fi
);
751 // ////////////////////////////////////////////////////////////////////////// //
755 db 'CZA2' ; signature
756 db version ; 0: zlib; 1: balz
757 dd indexofs ; offset to packed index
758 dd pkindexsz ; size of packed index
759 dd upindexsz ; size of unpacked index
764 dd chunksize ; unpacked chunk size in bytes
765 dd chunkcount ; number of chunks in file
766 dd lastchunksz ; size of last chunk (it may be incomplete); 0: last chunk is completely used (all `chunksize` bytes)
768 then chunk offsets and sizes follows:
769 dd chunkofs ; from file start
770 dd pkchunksz ; size of (possibly packed) chunk data; if it equals to `chunksize`, this chunk is not packed
772 then file list follows:
773 dd filecount ; number of files in archive
775 then file info follows:
776 dd nameofs ; (in index)
777 dd namelen ; length of name (can't be 0)
778 dd firstchunk ; chunk where file starts
779 dd firstofs ; offset in first chunk (unpacked) where file starts
780 dd filesize ; unpacked file size
782 then name buffer follows -- just bytes