1
0
forked from 0ad/0ad

add higher-level dox, fix some bugs, made case-insensitive

This was SVN commit r973.
This commit is contained in:
janwas 2004-08-11 20:21:42 +00:00
parent e67ed4acc0
commit c5701c8299
2 changed files with 177 additions and 135 deletions

View File

@ -16,6 +16,22 @@
// Jan.Wassenberg@stud.uni-karlsruhe.de // Jan.Wassenberg@stud.uni-karlsruhe.de
// http://www.stud.uni-karlsruhe.de/~urkt/ // http://www.stud.uni-karlsruhe.de/~urkt/
// components:
// - z_*: Zip-specific code
// passes the list of files in an archive to lookup.
// - lookup_*: file lookup
// per archive: return file info (e.g. offset, size), given filename.
// - ZArchive_*: Handle-based container for archive info
// owns archive file and its lookup mechanism.
// - inf_*: in-memory inflate routines (zlib wrapper)
// decompresses blocks from file_io callback.
// - zip_*: file from Zip archive
// uses lookup to get file information; holds inflate state.
// - sync and async I/O
// uses file_* and inf_*.
// - file mapping
#include "precompiled.h" #include "precompiled.h"
#include "lib.h" #include "lib.h"
@ -89,8 +105,8 @@ static inline int z_validate(const void* const file, const size_t size)
if(size < 22) if(size < 22)
return -1; return -1;
// check "header" (first CDFH) signature // check "header" (first LFH) signature
return (*(u32*)file == *(u32*)&cdfh_id)? 0 : -1; return (*(u32*)file == *(u32*)&lfh_id)? 0 : -1;
} }
@ -199,7 +215,7 @@ static int z_read_cdfh(const u8*& cdfh, const char*& fn, size_t& fn_len, ZLoc* c
debug_warn("warning: unknown compression method"); debug_warn("warning: unknown compression method");
goto skip_file; goto skip_file;
} }
// tell is_compressed that the file is uncompressed, // tell zfile_compressed that the file is uncompressed,
// by setting csize_ to 0. // by setting csize_ to 0.
if(method == 0) if(method == 0)
csize_ = 0; csize_ = 0;
@ -301,7 +317,7 @@ static int z_enum_files(const void* const file, const size_t size, const CDFH_CB
/////////////////////////////////////////////////////////////////////////////// ///////////////////////////////////////////////////////////////////////////////
// //
// lookup_*: file lookup // lookup_*: file lookup
// per archive: retrieve file information (e.g. offset, size), given filename. // per archive: return file info (e.g. offset, size), given filename.
// //
/////////////////////////////////////////////////////////////////////////////// ///////////////////////////////////////////////////////////////////////////////
@ -359,6 +375,22 @@ struct LookupInfo
}; };
// support for case-insensitive filenames: the FNV hash of each
// filename string is saved in lookup_add_file_cb and searched for by
// lookup_get_file_info. in both cases, we convert a temporary to
// lowercase before hashing it.
static void strcpy_lower(char* dst, const char* src)
{
int c;
do
{
c = *src++;
*dst++ = tolower(c);
}
while(c != '\0');
}
// add file <fn> to the lookup data structure. // add file <fn> to the lookup data structure.
// called from z_enum_files in order (0 <= idx < num_files). // called from z_enum_files in order (0 <= idx < num_files).
// the first call notifies us of # files, so we can allocate memory. // the first call notifies us of # files, so we can allocate memory.
@ -392,7 +424,9 @@ static int lookup_add_file_cb(const uintptr_t user, const i32 idx, const char* c
ZEnt* ent = li->ents + idx; ZEnt* ent = li->ents + idx;
FnHash fn_hash = fnv_hash(fn, fn_len); char lc_fn[PATH_MAX];
strcpy_lower(lc_fn, fn);
FnHash fn_hash = fnv_hash(lc_fn, fn_len);
(*li->idx)[fn_hash] = idx; (*li->idx)[fn_hash] = idx;
li->fn_hashes[idx] = fn_hash; li->fn_hashes[idx] = fn_hash;
@ -400,6 +434,7 @@ static int lookup_add_file_cb(const uintptr_t user, const i32 idx, const char* c
// valid file - write out its info. // valid file - write out its info.
if(loc) if(loc)
{ {
// copy filename, so we can 0-terminate it
ent->fn = (const char*)malloc(fn_len+1); ent->fn = (const char*)malloc(fn_len+1);
if(!ent->fn) if(!ent->fn)
return ERR_NO_MEM; return ERR_NO_MEM;
@ -473,9 +508,11 @@ static int lookup_free(LookupInfo* const li)
// return file information of file <fn>. // return file information of file <fn>.
static int lookup_get_file_info(LookupInfo* const li, const char* fn, ZLoc* const loc) static int lookup_get_file_info(LookupInfo* const li, const char* fn, ZLoc* const loc)
{ {
const FnHash fn_hash = fnv_hash(fn); char lc_fn[PATH_MAX];
const FnHash* fn_hashes = li->fn_hashes; strcpy_lower(lc_fn, fn);
const FnHash fn_hash = fnv_hash(lc_fn);
const FnHash* fn_hashes = li->fn_hashes;
const i32 num_files = li->num_files; const i32 num_files = li->num_files;
i32 i = li->next_file; i32 i = li->next_file;
@ -501,19 +538,17 @@ static int lookup_get_file_info(LookupInfo* const li, const char* fn, ZLoc* cons
} }
typedef ZipFileCB LookupFileCB; static int lookup_enum_files(LookupInfo* const li, FileCB cb, uintptr_t user)
static int lookup_enum_files(LookupInfo* const li, LookupFileCB cb, uintptr_t user)
{ {
const ZEnt* ent = li->ents; const ZEnt* ent = li->ents;
for(i32 i = 0; i < li->num_files; i++, ent++) for(i32 i = 0; i < li->num_files; i++, ent++)
{ {
// is this entry a directory? ssize_t size = (ssize_t)ent->loc.ucsize;
int flags = 0; if(size == 0) // it's a directory
if(ent->loc.csize == 0 && ent->loc.ucsize == 0) size = -1;
flags |= LOC_DIR;
CHECK_ERR(cb(ent->fn, flags, (ssize_t)ent->loc.ucsize, user)); CHECK_ERR(cb(ent->fn, size, user));
// pass in complete path (see file_enum rationale).
} }
return 0; return 0;
@ -522,7 +557,7 @@ static int lookup_enum_files(LookupInfo* const li, LookupFileCB cb, uintptr_t us
/////////////////////////////////////////////////////////////////////////////// ///////////////////////////////////////////////////////////////////////////////
// //
// container with handle for archive info // ZArchive_*: Handle-based container for archive info
// owns archive file and its lookup mechanism. // owns archive file and its lookup mechanism.
// //
/////////////////////////////////////////////////////////////////////////////// ///////////////////////////////////////////////////////////////////////////////
@ -534,9 +569,17 @@ struct ZArchive
LookupInfo li; LookupInfo li;
// hack: on first open, file is invalid (fn_hash isn't set), // problem:
// and file validate in file_close fails. // if ZArchive_reload aborts due to file_open failing, ZArchive_dtor
// workaround: only close if open. // is called by h_alloc, and file_close complains the File is
// invalid (wasn't open). this happens if e.g. vfs_mount blindly
// tries to open a directory as an archive.
// workaround:
// only free the above if ZArchive_reload succeeds, i.e. is_open.
// note:
// if lookup_init fails after file_open opened the file,
// we wouldn't file_close in the dtor,
// but it's taken care of by ZArchive_reload.
bool is_open; bool is_open;
}; };
@ -569,7 +612,9 @@ static int ZArchive_reload(ZArchive* za, const char* fn, Handle h)
int err; int err;
err = file_open(fn, 0, &za->f); err = file_open(fn, 0, &za->f);
if(err < 0) // don't CHECK_ERR; this happens often. if(err < 0)
// don't complain - this happens when vfs_mount blindly
// zip_archive_opens a dir.
return err; return err;
// map // map
@ -594,26 +639,29 @@ exit_unmap_close:
file_unmap(&za->f); file_unmap(&za->f);
exit_close: exit_close:
file_close(&za->f); file_close(&za->f);
// don't complain here either; this happens when vfs_mount
// zip_archive_opens an invalid file that's in a mount point dir.
return err; return err;
} }
// open and return a handle to the zip archive indicated by <fn> // open and return a handle to the zip archive indicated by <fn>
Handle zip_archive_open(const char* const fn) inline Handle zip_archive_open(const char* const fn)
{ {
return h_alloc(H_ZArchive, fn); return h_alloc(H_ZArchive, fn);
} }
// close the archive <ha> and set ha to 0 // close the archive <ha> and set ha to 0
int zip_archive_close(Handle& ha) inline int zip_archive_close(Handle& ha)
{ {
return h_free(ha, H_ZArchive); return h_free(ha, H_ZArchive);
} }
// see lookup rationale for not passing along a key. // call <cb>, passing <user>, for all files in archive <ha>
int zip_enum(const Handle ha, const ZipFileCB cb, const uintptr_t user) int zip_enum(const Handle ha, const FileCB cb, const uintptr_t user)
{ {
H_DEREF(ha, ZArchive, za); H_DEREF(ha, ZArchive, za);
@ -623,7 +671,8 @@ int zip_enum(const Handle ha, const ZipFileCB cb, const uintptr_t user)
/////////////////////////////////////////////////////////////////////////////// ///////////////////////////////////////////////////////////////////////////////
// //
// in-memory inflate routines (zlib wrapper) // inf_*: in-memory inflate routines (zlib wrapper)
// decompresses blocks from file_io callback.
// //
/////////////////////////////////////////////////////////////////////////////// ///////////////////////////////////////////////////////////////////////////////
@ -749,8 +798,8 @@ int inf_free_ctx(uintptr_t ctx)
/////////////////////////////////////////////////////////////////////////////// ///////////////////////////////////////////////////////////////////////////////
// //
// file from Zip archive // zip_*: file from Zip archive
// on top of inflate and lookup // uses lookup to get file information; holds inflate state.
// //
/////////////////////////////////////////////////////////////////////////////// ///////////////////////////////////////////////////////////////////////////////
@ -758,7 +807,8 @@ int inf_free_ctx(uintptr_t ctx)
enum ZFileFlags enum ZFileFlags
{ {
// the ZFile has been successfully zip_map-ped. // the ZFile has been successfully zip_map-ped.
// we store this so that the archive mapping refcount remains balanced. // used to make sure the archive's mmap refcount remains balanced,
// i.e. no one double-frees the mapping.
ZF_HAS_MAPPING = 0x4000 ZF_HAS_MAPPING = 0x4000
}; };
@ -788,7 +838,7 @@ static int zfile_validate(uint line, ZFile* zf)
#endif #endif
else if(!zf->ucsize) else if(!zf->ucsize)
msg = "ucsize = 0"; msg = "ucsize = 0";
else if(!zf->read_ctx) else if(!zf->inf_ctx)
msg = "read context invalid"; msg = "read context invalid";
// everything is OK // everything is OK
else else
@ -811,6 +861,35 @@ do\
while(0); while(0);
// convenience function, allows implementation change in ZFile.
// note that size == ucsize isn't foolproof, and adding a flag to
// ofs or size is ugly and error-prone.
// no error checking - always called from functions that check zf.
static inline bool zfile_compressed(ZFile* zf)
{
return zf->csize != 0;
}
// return file information for <fn> in archive <ha>
int zip_stat(Handle ha, const char* fn, struct stat* s)
{
// zero output param in case we fail below.
memset(s, 0, sizeof(struct stat));
H_DEREF(ha, ZArchive, za);
LookupInfo* li = &za->li;
ZLoc loc;
CHECK_ERR(lookup_get_file_info(li, fn, &loc));
s->st_size = loc.ucsize;
return 0;
}
int zip_open(const Handle ha, const char* fn, ZFile* zf) int zip_open(const Handle ha, const char* fn, ZFile* zf)
{ {
H_DEREF(ha, ZArchive, za); H_DEREF(ha, ZArchive, za);
@ -842,7 +921,7 @@ int zip_open(const Handle ha, const char* fn, ZFile* zf)
zf->csize = loc.csize; zf->csize = loc.csize;
zf->ha = ha; zf->ha = ha;
zf->read_ctx = inf_init_ctx(); zf->inf_ctx = inf_init_ctx();
} }
invalid_zf: invalid_zf:
@ -857,41 +936,14 @@ int zip_close(ZFile* zf)
CHECK_ZFILE(zf); CHECK_ZFILE(zf);
// remaining ZFile fields don't need to be freed/cleared // remaining ZFile fields don't need to be freed/cleared
return inf_free_ctx(zf->read_ctx); return inf_free_ctx(zf->inf_ctx);
}
// return file information for <fn> in archive <ha>
int zip_stat(Handle ha, const char* fn, struct stat* s)
{
// zero output param in case we fail below.
memset(s, 0, sizeof(struct stat));
H_DEREF(ha, ZArchive, za);
LookupInfo* li = &za->li;
ZLoc loc;
CHECK_ERR(lookup_get_file_info(li, fn, &loc));
s->st_size = loc.ucsize;
return 0;
}
// convenience function, allows implementation change in ZFile.
// note that size == ucsize isn't foolproof, and adding a flag to
// ofs or size is ugly and error-prone.
// no error checking - always called from functions that check zf.
static inline bool is_compressed(ZFile* zf)
{
return zf->csize != 0;
} }
/////////////////////////////////////////////////////////////////////////////// ///////////////////////////////////////////////////////////////////////////////
// //
// synchronous I/O // sync and async I/O
// uses file_* and inf_*.
// //
/////////////////////////////////////////////////////////////////////////////// ///////////////////////////////////////////////////////////////////////////////
@ -913,7 +965,7 @@ ssize_t zip_read(ZFile* zf, off_t raw_ofs, size_t size, void** p)
// not compressed - just pass it on to file_io // not compressed - just pass it on to file_io
// (avoid the Zip inflate start/finish stuff below) // (avoid the Zip inflate start/finish stuff below)
if(!is_compressed(zf)) if(!zfile_compressed(zf))
return file_io(&za->f, ofs, size, p); return file_io(&za->f, ofs, size, p);
// no need to set last_raw_ofs - only checked if compressed. // no need to set last_raw_ofs - only checked if compressed.
@ -947,7 +999,7 @@ ssize_t zip_read(ZFile* zf, off_t raw_ofs, size_t size, void** p)
*p = buf; *p = buf;
} }
err = (ssize_t)inf_start_read(zf->read_ctx, buf, size); err = (ssize_t)inf_start_read(zf->inf_ctx, buf, size);
if(err < 0) if(err < 0)
{ {
fail: fail:
@ -964,11 +1016,11 @@ fail:
// zip_inflate, until all compressed data has been read, or it indicates // zip_inflate, until all compressed data has been read, or it indicates
// the desired output amount has been reached. // the desired output amount has been reached.
const size_t raw_size = zf->csize; const size_t raw_size = zf->csize;
raw_bytes_read = file_io(&za->f, ofs, raw_size, (void**)0, inf_inflate, zf->read_ctx); raw_bytes_read = file_io(&za->f, ofs, raw_size, (void**)0, inf_inflate, zf->inf_ctx);
zf->last_raw_ofs = raw_ofs + (off_t)raw_bytes_read; zf->last_raw_ofs = raw_ofs + (off_t)raw_bytes_read;
err = inf_finish_read(zf->read_ctx); err = inf_finish_read(zf->inf_ctx);
if(err < 0) if(err < 0)
goto fail; goto fail;
@ -982,6 +1034,47 @@ fail:
} }
///////////////////////////////////////////////////////////////////////////////
// rationale for not supporting aio for compressed files:
// would complicate things considerably (could no longer just
// return the file I/O handle, since we have to decompress in wait_io),
// yet it isn't really useful - aio is used to stream music,
// which is already compressed.
// begin transferring <size> bytes, starting at <ofs>. get result
// with zip_wait_io; when no longer needed, free via zip_discard_io.
Handle zip_start_io(ZFile* const zf, off_t ofs, size_t size, void* buf)
{
CHECK_ZFILE(zf);
if(zfile_compressed(zf))
{
debug_warn("Zip aio doesn't currently support compressed files (see rationale above)");
return -1;
}
H_DEREF(zf->ha, ZArchive, za);
return file_start_io(&za->f, zf->ofs+ofs, size, buf);
}
// wait until the transfer <hio> completes, and return its buffer.
// output parameters are zeroed on error.
inline int zip_wait_io(Handle hio, void*& p, size_t& size)
{
return file_wait_io(hio, p, size);
}
// finished with transfer <hio> - free its buffer (returned by vfs_wait_io)
inline int zip_discard_io(Handle& hio)
{
return file_discard_io(hio);
}
/////////////////////////////////////////////////////////////////////////////// ///////////////////////////////////////////////////////////////////////////////
// //
// file mapping // file mapping
@ -1005,14 +1098,22 @@ int zip_map(ZFile* const zf, void*& p, size_t& size)
// mapping compressed files doesn't make sense because the // mapping compressed files doesn't make sense because the
// compression algorithm is unspecified - disallow it. // compression algorithm is unspecified - disallow it.
if(is_compressed(zf)) if(zfile_compressed(zf))
{ {
debug_warn("zip_map: file is compressed"); debug_warn("zip_map: file is compressed");
return -1; return -1;
} }
H_DEREF(zf->ha, ZArchive, za) // note: we mapped the archive in zip_archive_open, but unmapped it
CHECK_ERR(file_map(&za->f, p, size)); // in the meantime to save memory in case it wasn't going to be mapped.
// now we do so again; it's unmapped in zip_unmap (refcounted).
H_DEREF(zf->ha, ZArchive, za);
void* archive_p;
size_t archive_size;
CHECK_ERR(file_map(&za->f, archive_p, archive_size));
p = (char*)archive_p + zf->ofs;
size = zf->ucsize;
zf->flags |= ZF_HAS_MAPPING; zf->flags |= ZF_HAS_MAPPING;
return 0; return 0;
@ -1034,51 +1135,6 @@ int zip_unmap(ZFile* const zf)
return -1; return -1;
zf->flags &= ~ZF_HAS_MAPPING; zf->flags &= ~ZF_HAS_MAPPING;
H_DEREF(zf->ha, ZArchive, za) H_DEREF(zf->ha, ZArchive, za);
return file_unmap(&za->f); return file_unmap(&za->f);
} }
///////////////////////////////////////////////////////////////////////////////
//
// asynchronous I/O
//
///////////////////////////////////////////////////////////////////////////////
// rationale for not supporting aio for compressed files:
// would complicate things considerably (could no longer just
// return the file I/O handle, since we have to decompress in wait_io),
// yet it isn't really useful - aio is used to stream music,
// which is already compressed.
// begin transferring <size> bytes, starting at <ofs>. get result
// with zip_wait_io; when no longer needed, free via zip_discard_io.
Handle zip_start_io(ZFile* const zf, off_t ofs, size_t size, void* buf)
{
CHECK_ZFILE(zf);
if(is_compressed(zf))
{
debug_warn("Zip aio doesn't currently support compressed files (see rationale above)");
return -1;
}
H_DEREF(zf->ha, ZArchive, za);
return file_start_io(&za->f, zf->ofs+ofs, size, buf);
}
// wait until the transfer <hio> completes, and return its buffer.
// output parameters are zeroed on error.
inline int zip_wait_io(Handle hio, void*& p, size_t& size)
{
return file_wait_io(hio, p, size);
}
// finished with transfer <hio> - free its buffer (returned by vfs_wait_io)
inline int zip_discard_io(Handle& hio)
{
return file_discard_io(hio);
}

View File

@ -20,23 +20,10 @@
#define ZIP_H__ #define ZIP_H__
#include "h_mgr.h" #include "h_mgr.h"
#include "lib.h" #include "file.h" // FileCB for zip_enum
#include "file.h"
// // note: filenames are case-insensitive.
// in-memory inflate routines (zlib wrapper)
//
extern uintptr_t inf_init_ctx();
extern int inf_start_read(uintptr_t ctx, void* out, size_t out_size);
extern ssize_t inf_inflate(uintptr_t ctx, void* in, size_t in_size);
extern int inf_finish_read(uintptr_t ctx);
extern int inf_free_ctx(uintptr_t ctx);
// //
@ -49,10 +36,10 @@ extern Handle zip_archive_open(const char* fn);
// close the archive <ha> and set ha to 0 // close the archive <ha> and set ha to 0
extern int zip_archive_close(Handle& ha); extern int zip_archive_close(Handle& ha);
// for all files in archive <ha>: call <cb>, passing <user>
// and the entries's complete path.
extern int zip_enum(const Handle ha, const FileCB cb, const uintptr_t user);
// all files in archive!
typedef int(*ZipFileCB)(const char* const fn, const uint flags, const ssize_t size, const uintptr_t user);
extern int zip_enum(const Handle ha, const ZipFileCB cb, const uintptr_t user);
// //
// file // file
@ -64,7 +51,7 @@ struct ZFile
u32 magic; u32 magic;
#endif #endif
// keep offset of flags and size members in sync with struct ZFile! // keep offset of flags and size members in sync with struct File!
// it is accessed by VFS and must be the same for both (union). // it is accessed by VFS and must be the same for both (union).
// dirty, but necessary because VFile is pushing the HDATA size limit. // dirty, but necessary because VFile is pushing the HDATA size limit.
uint flags; uint flags;
@ -76,10 +63,10 @@ struct ZFile
off_t last_raw_ofs; off_t last_raw_ofs;
Handle ha; Handle ha;
uintptr_t read_ctx; uintptr_t inf_ctx;
}; };
// return file information for <fn> (may include path) in archive <ha> // return file information for <fn> in archive <ha>
extern int zip_stat(Handle ha, const char* fn, struct stat* s); extern int zip_stat(Handle ha, const char* fn, struct stat* s);
// open the file <fn> in archive <ha>, and fill *zf with information about it. // open the file <fn> in archive <ha>, and fill *zf with information about it.
@ -125,7 +112,6 @@ extern Handle zip_start_io(ZFile* const zf, off_t ofs, size_t size, void* buf);
// output parameters are zeroed on error. // output parameters are zeroed on error.
extern int zip_wait_io(Handle hio, void*& p, size_t& size); extern int zip_wait_io(Handle hio, void*& p, size_t& size);
// finished with transfer <hio> - free its buffer (returned by vfs_wait_io) // finished with transfer <hio> - free its buffer (returned by vfs_wait_io)
extern int zip_discard_io(Handle& hio); extern int zip_discard_io(Handle& hio);