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.
The following FA_* flags are guaranteed to work: FA_RDONLY, FA_HIDDEN, FA_SYSTEM, FA_LABEL, FA_DIREC, FA_ARCH. Do not use any other flags from DOS/Windows or your code will not compile on another platform. Flags FA_SYSTEM, FA_LABEL and FA_ARCH are valuable only on DOS/Windows (entries with system flag, volume labels and archive flag). FA_RDONLY is for directory entries with read-only flag on DOS-like systems or unwritable by current user on Unix-like systems. FA_HIDDEN is for entries with hidden flag on DOS-like systems or starting with '.' on Unix (dotted files - excluding '.' and '..'). FA_DIREC represents directories. Flags can be combined using '|' (binary OR operator).
When passed to the functions as the 'attrib' parameter, these flags represent an upper set in which the actual flag set of a matching file must be included. That is, in order for a file to be matching, its attributes may contain any of the specified flags but must not contain any of the unspecified flags. Thus, if you pass 'FA_DIREC | FA_RDONLY', normal files and directories will be included as well as read-only files and directories, but not hidden files and directories. Similarly, if you pass 'FA_ARCH' then both archived and non-archived files will be included.
void get_executable_name(char *buf, int size);
Fills buf with the full path to the current executable, writing at most
size bytes. This generally comes from argv[0], but on Unix systems if
argv[0] does not specify the path, we search for our file in $PATH.
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, const char *path, int size);
Converts a partial filename into a full path, storing at most size bytes
into the dest buffer. Returns a copy of the dest parameter.
char *replace_filename(char *dest, const char *path,
const char *filename, int size);
Replaces the specified path+filename with a new filename tail, storing
at most size bytes into the dest buffer. Returns a copy of the dest
parameter.
char *replace_extension(char *dest, const char *filename,
const char *ext, int size);
Replaces the specified filename+extension with a new extension tail,
storing at most size bytes into the dest buffer. Returns a copy of the
dest parameter.
char *append_filename(char *dest, const char *path,
const char *filename, int size);
Concatenates the specified filename onto the end of the specified path,
storing at most size bytes into the dest buffer. Returns a copy of the
dest parameter.
char *get_filename(const 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(const 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 a device
separator (ie. ':' under DOS), this routine will concatenate either a '\'
or '/' on to it (depending on the platform). Note: ignore the function
name, it's out of date.
int file_exists(const char *filename, int attrib, int *aret);
Checks whether a file matching the given name and attributes (see above)
exists, returning non-zero if it does. 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(const 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(const 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.
time_t file_time(const char *filename);
Returns the modification time (number of seconds since 00:00:00 GMT
1/1/1970) of a file.
int delete_file(const char *filename);
Removes a file from the disk.
int for_each_file(const char *name, int attrib,
void (*callback)(const char *filename, int attrib, int param),
int param);
Finds all the files on the disk which match the given wildcard
specification and file attributes (see above), 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().
int al_findfirst(const char *pattern, struct al_ffblk *info, int attrib);
Low-level function for searching files. This function finds the first
file which matches the given wildcard specification and file attributes
(see above). The information about the file (if any) will be put in the
al_ffblk structure which you have to provide. The function returns zero
if a match is found, nonzero if none is found or if an error occured
and, in the latter case, sets errno accordingly. The al_ffblk structure
looks like:
struct al_ffblk { int attrib; - actual attributes of the file found time_t time; - modification time of file long size; - size of file char name[512]; - name of file };There is some other stuff in the structure as well, but it is there for internal use only.
int al_findnext(struct al_ffblk *info);
This finds the next file in a search started by al_findfirst(). Returns
zero if a match is found, nonzero if none is found or if an error
occured and, in the latter case, sets errno accordingly.
void al_findclose(struct al_ffblk *info);
This closes a previously opened search with al_findfirst().
int find_allegro_resource(char *dest, const char *resource,
const char *ext, const char *datafile,
const char *objectname, const char *envvar,
const char *subdir, int size);
Searches for a support file, eg. allegro.cfg or language.dat. Passed a
resource string describing what you are looking for, along with extra
optional information such as the default extension, what datafile to look
inside, what the datafile object name is likely to be, any special
environment variable to check, and any subdirectory that you would like
to check as well as the default location, this function looks in a hell
of a lot of different places :-) Returns zero on success, and stores a
full path to the file (at most size bytes) into the dest buffer.
void packfile_password(const char *password);
Sets the encryption password to be used for all read/write operations
on files opened in future using Allegro's packfile functions (whether
they are compressed or not), including all the save, load and config
routines. 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 :-)
Note #1: when writing a packfile, you can change the password to whatever you want after opening the file, without affecting the write operation. On the contrary, when writing a sub-chunk of a packfile, you must make sure that the password that was active at the time the sub-chunk was opened is still active before closing the sub-chunk. This is guaranteed to be true if you didn't call the packfile_password() routine in the meantime. Read operations, either on packfiles or sub-chunks, have no such restriction.
Note #2: as explained above, the password is used for all read/write operations on files, including for several functions of the library that operate on files without explicitly using packfiles, e.g load_bitmap(). The unencrypted mode is mandatory in order for those functions to work. Therefore remember to call packfile_password(NULL) before using them if you previously changed the password. As a rule of thumb, always call packfile_password(NULL) when you are done with operations on packfiles.
PACKFILE *pack_fopen(const char *filename, const 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(const void *p, long n, PACKFILE *f);
char *pack_fgets(char *p, int max, PACKFILE *f);
int pack_fputs(const char *p, PACKFILE *f);
These work like the equivalent stdio functions. There are some differences, however:
/* assumes f is a PACKFILE * which has been opened */ f = pack_fopen_chunk(f, pack); /* in write mode */ 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 */ f = pack_fopen_chunk(f, FALSE); */ in read mode */ 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().