The following routines implement a fast buffered file I/O system, which supports the reading and writing of compressed files using a ring buffer algorithm based on the LZSS compressor by Haruhiko Okumura. This does not achieve quite such good compression as programs like zip and lha, but unpacking is very fast and it does not require much memory. Packed files always begin with the 32 bit value F_PACK_MAGIC, and autodetect files with the value F_NOPACK_MAGIC.
char *fix_filename_case(char *path);
Converts a filename to a standardised case. On DOS platforms, they will
be entirely uppercase. Returns a copy of the path parameter.
char *fix_filename_slashes(char *path);
Converts all the directory separators in a filename to a standard
character. On DOS platforms, this is a backslash. Returns a copy of the
path parameter.
char *fix_filename_path(char *dest, char *path, int size);
Converts a partial filename into a full path, generating up to the
specified maximum number of characters. Returns a copy of the dest
parameter.
char *replace_filename(char *dest, char *path, char *filename, int size);
Replaces the specified path+filename with a new filename tail, generating
up to the specified maximum number of characters. Returns a copy of the
dest parameter.
char *replace_extension(char *dest, char *filename, char *ext, int size);
Replaces the specified filename+extension with a new extension tail,
generating up to the specified maximum number of characters. Returns a
copy of the dest parameter.
char *append_filename(char *dest, char *path, char *filename, int size);
Concatenates the specified filename onto the end of the specified path,
generating up to the specified maximum number of characters. Returns a
copy of the dest parameter.
char *get_filename(char *path);
When passed a completely specified file path, this returns a pointer to
the filename portion. Both '\' and '/' are recognized as directory
separators.
char *get_extension(char *filename);
When passed a complete filename (with or without path information) this
returns a pointer to the file extension.
void put_backslash(char *filename);
If the last character of the filename is not a '\', '/', or '#', this
routine will concatenate a '\' on to it.
int file_exists(char *filename, int attrib, int *aret);
Checks whether a file matching the given name and attributes exists,
returning non-zero if it does. The file attribute may contain any of the
FA_* constants from dir.h. If aret is not NULL, it will be set to the
attributes of the matching file. If an error occurs the system error code
will be stored in errno.
int exists(char *filename);
Shortcut version of file_exists(), which checks for normal files, which
may have the archive or read-only bits set, but are not hidden,
directories, system files, etc.
long file_size(char *filename);
Returns the size of a file, in bytes. If the file does not exist or an
error occurs, it will return zero and store the system error code in
errno.
long file_time(char *filename);
Returns the modification time of a file.
int delete_file(char *filename);
Removes a file from the disk.
int for_each_file(char *name, int attrib, void (*callback)(), int param);
Finds all the files on the disk which match the given wildcard
specification and file attributes, and executes callback() once for each.
callback() will be passed three arguments, the first a string which
contains the completed filename, the second being the attributes of the
file, and the third an int which is simply a copy of param (you can use
this for whatever you like). If an error occurs an error code will be
stored in errno, and callback() can cause for_each_file() to abort by
setting errno itself. Returns the number of successful calls made to
callback(). The file attribute may contain any of the FA_* flags from
dir.h.
void packfile_password(char *password);
Sets the encryption password to be used for all future read/writes of
compressed files. Files written with an encryption password cannot be
read unless the same password is selected, so be careful: if you forget
the key, I can't make your data come back again! Pass NULL or an empty
string to return to the normal, non-encrypted mode. If you are using this
function to prevent people getting access to your datafiles, be careful
not to store an obvious copy of the password in your executable: if there
are any strings like "I'm the password for the datafile", it would be
fairly easy to get access to your data :-)
PACKFILE *pack_fopen(char *filename, char *mode);
Opens a file according to mode, which may contain any of the flags:
The packfile functions also understand several "magic" filenames that are used for special purposes. These are:
int pack_fclose(PACKFILE *f);
int pack_fseek(PACKFILE *f, int offset);
int pack_feof(PACKFILE *f);
int pack_ferror(PACKFILE *f);
int pack_getc(PACKFILE *f);
int pack_putc(int c, PACKFILE *f);
int pack_igetw(PACKFILE *f);
long pack_igetl(PACKFILE *f);
int pack_iputw(int w, PACKFILE *f);
long pack_iputl(long l, PACKFILE *f);
int pack_mgetw(PACKFILE *f);
long pack_mgetl(PACKFILE *f);
int pack_mputw(int w, PACKFILE *f);
long pack_mputl(long l, PACKFILE *f);
long pack_fread(void *p, long n, PACKFILE *f);
long pack_fwrite(void *p, long n, PACKFILE *f);
char *pack_fgets(char *p, int max, PACKFILE *f);
int pack_fputs(char *p, PACKFILE *f);
These work like the equivalent stdio functions, except that pack_fread() and pack_fwrite() take a single size parameter instead of that silly size and num_elements system, seeking only supports forward movement relative to the current position, and the pack_fgets() function does not include a trailing carriage return in the returned string. The pack_i* and pack_m* routines read and write 16 and 32 bit values using the Intel and Motorola byte ordering systems (endianness) respectively. Note that seeking is very slow when reading compressed files, and so should be avoided unless you are sure that the file is not compressed.
PACKFILE *pack_fopen_chunk(PACKFILE *f, int pack);
Opens a sub-chunk of a file. Chunks are primarily intended for use by the
datafile code, but they may also be useful for your own file routines. A
chunk provides a logical view of part of a file, which can be compressed
as an individual entity and will automatically insert and check length
counts to prevent reading past the end of the chunk. To write a chunk to
the file f, use the code:
/* assumes f is a PACKFILE * which has been opened in write mode */ f = pack_fopen_chunk(f, pack); write some data to f f = pack_fclose_chunk(f);The data written to the chunk will be prefixed with two length counts (32 bit, big-endian). For uncompressed chunks these will both be set to the size of the data in the chunk. For compressed chunks (created by setting the pack flag), the first length will be the raw size of the chunk, and the second will be the negative size of the uncompressed data.
To read the chunk, use the code:
/* assumes f is a PACKFILE * which has been opened in read mode */ f = pack_fopen_chunk(f, FALSE); read data from f f = pack_fclose_chunk(f);This sequence will read the length counts created when the chunk was written, and automatically decompress the contents of the chunk if it was compressed. The length will also be used to prevent reading past the end of the chunk (Allegro will return EOF if you attempt this), and to automatically skip past any unread chunk data when you call pack_fclose_chunk().
Chunks can be nested inside each other by making repeated calls to pack_fopen_chunk(). When writing a file, the compression status is inherited from the parent file, so you only need to set the pack flag if the parent is not compressed but you want to pack the chunk data. If the parent file is already open in packed mode, setting the pack flag will result in data being compressed twice: once as it is written to the chunk, and again as the chunk passes it on to the parent file.
PACKFILE *pack_fclose_chunk(PACKFILE *f);
Closes a sub-chunk of a file, previously obtained by calling
pack_fopen_chunk().