About TT's Zip Archive Package

Version: 1.3 (22 March 2007)

This is a collection of REALbasic Classes to extract and create ZIP archives (known as PKZIP and Info-ZIP formats).

Written March 2003 by Thomas Tempelmann

For updates see http://www.tempel.org/rb/#zip
For feedback and questions, write to: tt@tempel.org

This code is free for your own use, but you are encouraged to send me some money if you have a use for it (consider it shareware). See the end of this document for more information.

Current requirements: REALbasic 5.5.5 or later. The Einhugur e-CryptIt plugin is also required, which must be acquired separately.

It implements only a subset of the entire ZIP archive definition, though. Here are the known restrictions:

The only thing to be sure about is that files created by this class can be read by any modern ZIP tool, such as ZipIt (for Mac OS), OS X Tiger's Archives, WinZIP (for Windows), Stuffit Expander (Mac and Windows) and, of course, by itself.

A nice feature is that you can not only add items to an existing archive, but even remove them and then compact the archive to gain the space back.

Another cool thing I've added is the class ZipSnapshots: It allows you to make incremental backups of the same folder, in which unchanged files will not be stored multiple times. See the notes in the class and the code in the demo app to learn how to use it.

For more information about the ZIP archive format, search on www.google.com for "ZIP format appnote". The "appnote.txt" describes the format.

This code has been originally developed using RB 4.5.3 and has been tested to compile and run with RB 5.1. It has been tested by me (TT) on Mac OS 9.2.2 and on Windows 98, and a few other people have done more compatibility testing. In January 2007, it has been updated to compile with RB 2006r4 and 2007r1, and work on Intel based Macs as well.

Programming information

Note: To actually be able to create and read compressed items in and archive you will also need a plugin providing the so-called "ZLib" compression functions. The only known plugin to provide the necessary functionality is:

Quick Demo

Make sure you have the Einhugur "e-CryptIt Engine" plugin installed (plus its accompanying plugin "#TypeLib.rbx") into the Plugins folder inside your RB application folder - you may also use the Demo version if you do not want to buy the full version yet. Open the project called "TT's ZipArchiver.rb" and run it. The program should open a window into which you can drop files to compress or decompress instantly. It also allows you to choose options such as the encoding for the file names (unfortunately, different tools on the various computers use different encodings - though this will not be of relevance as long as you're using files names that contain only ASCII characters. Once you use accented chars, symbols or even non-Latin scripts in your file names, you need to make sure that the encoding used for compression matches the one used to later read the archive again.

Overview of the classes

The only classes you need to look at are:

or, if you only want to uncompress .zip files easily, look at:

The files inside the ZipSupport folder are not of much interest to you, while the files in the StreamSupport folder may be of general use for you in other projects, as they provide generalized read and write functions for both data and resource forks.

There is also a folder called "AddWhenNotUsingEinhugurPlugin". As its name suggests, you need to add its contents to your project if you do not have those plugins installed (you then also need to change the value of the constant "HaveEinhugurPlugin" to false and install the "TT's CRC-Plugin" which you can get from TT's web site, but which is not supporting the latest RB versions any more).

Creating an archive and adding files to it

First, create a new ZipArchive instance. Then you Open the archive by specifying a FolderItem along with a Boolean that is TRUE to signal that you want to write to the archive. If the FolderItem exists, it will opened as an archive (provided it's a valid archive file), otherwise a new archive file will be created.

 dim zar as ZipArchive

 zar = new ZipArchive
 if not zar.Open(theFile, true) then
 MsgBox "Error: " + zar.ErrorMessage
 return
 end

Next, you can either add single files or entire folders to the archive:

 dim result as Integer
 
 // add a file:
 result = zar.AddItemToRoot(aFile, zar.MacBinaryNever)
 if result <= 0 then
 MsgBox "Error: " + zar.ErrorMessage
 return
 end

 // add a folder:
 if not zar.AddFolderContents(aFolder, aFolder.Name, zar.MacBinaryNever, false) then
 MsgBox "Error: " + zar.ErrorMessage
 return
 end

Finally, close the archive:

 if not zar.Close() then
 MsgBox "Error: " + zar.ErrorMessage
 return
 end

MacBinary information

The MacBinary option allows you to preserve Macintosh-specific file information, mainly that's the resource forks (it does also preserve some minor attributes such as the File Creation Date, because standard Zip format only preserves the Modification Date).

A Mac file's Type and Creator codes will always be preserved, even if no MacBinary is used. You may want to pass MacBinarySmart or MacBinaryAlways instead of MacBinaryNever in order to better preserve Macintosh specific information (when running on Windows, they make no difference).

The "Smart" version will encode files only as MacBinary if the file contains a Resource Fork.

Note that many non-Macintosh Zip tools can not handle MacBinary encoding, which means that they would decode such files as a MacBinary file, hiding the data fork in them along with other information (some tools can decode this properly, though, like Stuffit Expander for Windows - they extract the data fork only, ignoring the resource fork).

Extracting all items from an archive

Create a new ZipArchive instance, then call Open with passing a FolderItem identifying the archive you want to extract from, and FALSE for reading from the archive (instead of TRUE for writing to it).

 dim zar as ZipArchive

 zar = new ZipArchive
 if not zar.Open(theFile, false) then
 MsgBox "Error: " + zar.ErrorMessage
 return
 end

You can now loop over all entries in the archive, get their "ZipEntry" instances, and then extract their files to a folder hierarchy of which you can provide the starting folder:

 dim f as FolderItem, e as ZipEntry, i as Integer
 
 for i = 1 to zar.EntryCount
 e = zar.Entry(i)
 f = e.MakeDestination(destFolder,false)
 if f.exists then
 // here you could ask the user whether to overwrite the
 // existing file (but it could be even a folder!) or
 // to skip the item or abort the entire process.
 f.Delete // this will not work if it's a non-empty folder, though!
 end
 if not e.Extract(f) then
 MsgBox "Extraction of """+e.RawPath+""" failed: "+e.ErrorMessage
 return
 end
 next

Finally, close the archive again:

 if not zar.Close() then
 MsgBox "Error: " + zar.ErrorMessage
 return
 end

More detailed programming information

There are many more options to store and retrieve items in a Zip archive:

To learn about all these, look up the functions in the classes (mostly ZipArchive and ZipEntry) and see the comments at the top of them. That's their documentation.

Note that the methods starting with "z_" are private functions, all other are open for general use.

If you find bugs or make enhancements to this software, please contact me about them (send me your changed version), and I'll see to incorporate them into my next release to have others benefit from it, too.

Known limitations and problems

Compatibility & interchangeability considerations

There are many different Zip archiver tools around, and many have interchange problems when it comes to these particularities:

Further reading

The "zip archive" standard is quite a loose one. There appear to be lots of zip programs around that do not follow the PKWARE-specs very well, and while I have tried to program this software as close to the specs as I could, you may run into compatibility problems when trying to open archives from or create archives for other zip implementations.

A universal, open-sourced, reference implementation that attempts to deal with all eventualities is the so-called info-zip code base, in C language, which is also used by Apple with OS X (even though Apple appears to use quite an outdated version of it). Here is the info-zip home page:

Info-Zip home

The original PKWARE specification for Zip archives is included with this package in the "Technical docs" folder, along with the MacBinary specs.

List of changes (Version History)

v1.0, 8 Apr 2003

v1.1, 25 Apr 2003

v1.1.1, 18 June 2003

v1.1.2, 17 July 2003

v1.1.3, 29 July 2004

v.1.2, 2004-2006 v1.3, March 2007

Copyrights and Acknowledgements

e-CryptIt Engine copyright Björn Eiríksson (www.einhugur.com)

e-CryptIt Engine uses zlib code, copyright © 1995-2002 Jean-Loup Gailly and Mark Adler.

Original zip format REALbasic code was written by Carsten Friehe for the Mieze program (http://carsten-friehe.de/).

RB code improved and reorganized by Thomas Tempelmann (http://www.tempel.org/rb/) for public release.

Some of the design and error messages was influenced by Java 1.1's ZipFile and related classes.

My thanks go to Leonard Rosenthol (author of Stuffit Zip support and maintainer of MacBinary format) and Tom Brown (author of ZipIt) for providing helpful information.

Terms of use

This RB code, written by Carsten Friehe and Thomas Tempelmann, is given to the Public Domain, which means you can do whatever you want with it. It is, however appreciated if you would "tip" me by sending me a few dollars for my work.

It took me more than two full weeks to develop this software for the public - I myself could have done with much less for my own needs, but I wanted to provide this as a clean and complete solution so that others won't have to deal with this not-trivial task. So, if you benefit from my work, please acknowledge it with a little financial support for me.

Please visit the following web address to find out how to tip me:

http://tip.tempel.org/

Enjoy!

8 April 2003

Thomas Tempelmann