The EXT/IO calls
There were quite some requests from game developers and graphics-apps
developers who wanted various extensions to be included into the
zziplib library, but most of them were
only of specific usage. After some discussions we came up with a
model to customize the zziplib library
calls in a number of ways - and adding two or three arguments to
the zzip_* calls. The standard zziplib library
will actually call these *_ext_io functions with these extra arguments
to be set to zero.
The EXT feature describes a way to customize the extensions used in
the magic wrapper to find a .ZIP file. It turned out that there are
quite a number of applications that did chose the zip file format as
their native document format and where just the file extension had
been changed. This includes file types like Quake3 ".PK3" files from
ID Software, the Java library files called ".JAR", and lately the
OpenOffice-6 (resp. StarOffice-6) documents which carry xml-files along.
Just build a zero-termined string-list of file-extensions and submit it
to the _ext_io calls to let the zziplib find
parts of those zip-documents automagically.
In quite some of these cases, it is very benefical to make use of the
o_modes functionality that allows to submit extra bit-options into
the zziplib - this includes options like
ZZIP_PREFERZIP or even ZZIP_ONLYZIP which
modifies the default behaviour of looking for real files first instead
of some within a zipped directory tree. Other bit-options include
ZZIP_CASELESS to imitate win32-like filematching for a
zipped filetree.
Other wishes on zziplib circulated around
obfuscation or access to zip-files wrapped
in other data areas including encrpyted resources from other applications.
This has been adressed with the IO-handlers that you can explicitly
submit to the *_ext_io functions - the default will be posix-IO
open/read/write and seek/tell. An application using
zziplib can divert these to its own set of
these calls - and it only needs to declare them on opening a zipped file.
The EXT stringlist
Declaring an EXT stringlist is very simple as it is simply a
list of strings, the zziplib provides
you with a double-const zzip_strings_t type to help
you move a global declaration into the writeonly segment of your
app - it turned out that about all developers wanted just some
extensions on the default and they were fine with having them
global-const for their application, nothing like dynamically
modifying them. Well, you are still allowed to make it fully
dynamic... if you find a use case for that.
Extending the magic zip-extensions is just done by adding the
additional extensions to be recognized - just remember to add
the uppercased variants too since those will be needed on
(unx-like) filesystems that are case-sensitive. In the internet
age, quite some downloaded will appear in uppercased format since
the other side declared it as that and that other end was happy
with it as being a (w32-like) case-insensitive server. Therefore,
it should look like
static zzip_strings_t my_ext[] = { ".zip", ".ZIP", ".jar", ".JAR", 0 };
There is one frequently asked question in this area - how to open
a zipped file as "test.zip/README" instead of "test/README". Other
than some people might expect, the library will not find it - if
you want to have that one needs a fileext list that contains the
empty string - not the zero string, an empty string that is. It
looks like
static zzip_strings_t my_ext[] = { ".zip", ".ZIP", "", 0 };
And last not least, people want to tell the libary to not try to
open a real file that lives side by side with the same path as the
file path that can be matched by the zziplib. Actually, the magic
wrappers were never meant to be used like - the developer should
have used zzip_dir_* functions to open a zip-file and the
zzip_file_* functions to read entries from that zip-file. However,
the magic-wrappers look rather more familiar, and so you will find
now a bit-option ZZIP_ONLYZIP that can be passed down to the _ext_io
variants of the magic-wrapper calls, and a real-file will never get
tested for existance. Actually, I would rather recommend that for
application data the option ZZIP_PREFERZIP, so that one can enter
debugging mode by unpacking the zip-file as a real directory tree
in the place of the original zip.
The IO handlers
While you will find the zzip_plugin_io_t declared in the zziplib
headers, you are not advised to make much assumptions about their
structure. Still we gone the path of simplicity, so you can use
a global static for this struct too just like one can do for the
EXT-list. This again mimics the internals of zziplib. There is
even a helper function zzip_init_io that will copy the zziplib
internal handlers to your own handlers-set. Actually, this is
barely needed since the zziplib library will not check for nulls
in the plugin_io structure, all handlers must be filled, and the
zziplib routines call them unconditionally - that's simply
because a conditional-call will be ten times slower than an
unconditional call which adds mostly just one or two cpu cycles
in the place so you won't ever notice zziplib to be anywhat
slower than before adding IO-handlers.
However, you better instantiate your handlers in your application
and call that zzip_init_io on that instance to have everything
filled, only then modify the entry you actually wish to have
modified. For obfuscation this
will mostly be just the read() routine. But one can
also use IO-handlers to wrap zip-files into another data part
for which one (also) wants to modify the open/close routines
as well.
Therefore, you can modify your normal stdio code to start using
zipped files by exchaning the fopen/fread/fclose calls by their
magic counterparts, i.e.
// FILE* file = fopen ("test/README", "rb");
ZZIP_FILE* file = zzip_fopen ("test/README", "rb");
// while (0 < fread (buffer, 1, buflen, file)))
while (0 < zzip_fread (buffer, 1, buflen, file)))
{ do something }
// fclose (file);
zzip_fclose (file);
and you then need to prefix this code with some additional
code to support your own EXT/IO set, so the code will finally
look like
/* use .DAT extension to find some files */
static zzip_strings_t ext[] = { ".dat", ".DAT", "", 0 };
/* add obfuscation routine - see zzxorcat.c examples */
static zzip_plugin_io_t io;
zzip_init_io (& io, 0);
io.read = xor_read;
/* and the rest of the code, just as above, but with ext/io */
ZZIP_FILE* file = zzip_open_ext_io ("test/README", O_RDONLY|O_BINARY,
ZZIP_ONLYZIP|ZZIP_CASELESS, ext, io);
while (0 < zzip_fread (buffer, 1, buflen, file)))
{ do something }
zzip_fclose (file);
Finally
What's more to it? Well, if you have some ideas then please mail me
about it - don't worry, I'll probably reject it to be part of the
standard zziplib dll, but perhaps it is worth to be added as a
configure option and can help others later, and even more perhaps
it can be somehow generalized just as the ext/io features have been
generalized now. In most respects, this ext/io did not add much
code to the zziplib - the posix-calls
in the implemenation, like "read(file)" were simply
exchanged with "zip->io->read(file)" , and the
old "zzip_open(name,mode)" call is split up - the old
entry still persists but directly calls
"zzip_open_ext_io(name,mode,0,0,0)" which has the
old implementation code with just one addition: when the ZIP_FILE
handle is created, it uses the transferred io-handlers (or the
default ones if io==0), and initialized the io-member of that
structure for usage within the zzip_read calls.
This adds just a few bytes to the libs and just consumes additional
cpu cycles that can be rightfully called to be negligable (unlike
most commerical vendors will tell you when they indeed want to
tell you that for soooo many new features you have to pay a price).
It makes for greater variability without adding fatness to the
core in the default case, this is truly efficient I'd say. Well,
call this a German desease :-)=) ... and again, if you have another
idea, write today... or next week.
|