<date>15. July 2002 </date>

## ZZIP-EXT/IO 
Customizing the file access

### 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](zziplib.html), but most of them were
  only of specific usage. After some discussions we came up with a
  model to customize the [zziplib library](zziplib.html)  
  calls in a number of ways - and adding two or three arguments to
  the zzip_* calls. The standard [zziplib library](zziplib.html)  
  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](zziplib.html) 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](zziplib.html) - 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](zziplib.html) circulated around
  [obfuscation](zzip-xor.html) or access to zip-files wrapped 
  in other data areas including encrpyted resources from other applications. 
  This has been addressed 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](zziplib.html) 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](zziplib.html) 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 library 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 existence. 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](zzip-xor.html) 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](zziplib.html) - 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 negligible (unlike
most commercial 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.
