# File and compression routines

- al_findclose
- al_findfirst
- al_findnext
- append_filename
- canonicalize_filename
- delete_file
- exists
- file_exists
- file_size
- file_time
- find_allegro_resource
- fix_filename_case
- fix_filename_slashes
- for_each_file_ex
- get_executable_name
- get_extension
- get_filename
- is_relative_filename
- make_absolute_filename
- make_relative_filename
- pack_fclose
- pack_fclose_chunk
- pack_feof
- pack_ferror
- pack_fgets
- pack_fopen
- pack_fopen_chunk
- pack_fputs
- pack_fread
- pack_fseek
- pack_fwrite
- pack_getc
- pack_igetl
- pack_igetw
- pack_iputl
- pack_iputw
- pack_mgetl
- pack_mgetw
- pack_mputl
- pack_mputw
- pack_putc
- packfile_password
- put_backslash
- replace_extension
- replace_filename

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.

> See also: fix_filename_slashes, canonicalize_filename.

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.

> See also: fix_filename_case, canonicalize_filename.

char *canonicalize_filename(char *dest, const char *filename, int size);
Converts any filename into its canonical form, i.e. the minimal absolute filename describing the same file, storing at most size bytes into the dest buffer. Returns a copy of the dest parameter.

> See also: fix_filename_case, fix_filename_slashes.

char *make_absolute_filename(char *dest, const char *path, const char *filename, int size);
Makes an absolute filename from an absolute path and a relative filename, storing at most size bytes into the dest buffer. Returns a copy of the dest parameter.

> See also: make_relative_filename, is_relative_filename.

char *make_relative_filename(char *dest, const char *path, const char *filename, int size);
Attempts to make a relative filename from an absolute path and an absolute filename, storing at most size bytes into the dest buffer. Returns a copy of the dest parameter if it succeeds or NULL if it fails.

> See also: make_absolute_filename, is_relative_filename.

int is_relative_filename(const char *filename);
Returns TRUE if the filename is relative or FALSE if it is absolute.

> See also: make_absolute_filename, make_relative_filename.

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.

> See also: get_filename, replace_extension, append_filename.

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.

> See also: get_extension, replace_filename.

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.

> See also: replace_filename.

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.

> See also: get_extension, put_backslash, replace_filename.

char *get_extension(const char *filename);
When passed a complete filename (with or without path information) this returns a pointer to the file extension.

> See also: get_filename, put_backslash, replace_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.

> See also: get_extension, get_filename.

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.

> See also: exists, file_size, file_time.

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.

> See also: file_exists, file_size, file_time.

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.

> See also: file_exists, file_time.

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. If the file does not exist or an error occurs, it will return zero and store the system error code in errno.

> See also: file_exists, file_size.

int delete_file(const char *filename);
Removes a file from the disk.

int for_each_file_ex(const char *name, int in_attrib, int out_attrib, int (*callback)(const char *filename, int attrib, void *param), void *param);
Finds all the files on disk which match the given wildcard specification and file attributes, and executes callback() once for each. in_attrib is a bitmask specifying the attributes the files must carry, out_attrib is a bitmask specifying the attributes the files must not carry; attributes which are not specified in either bitmasks are not taken into account for deciding whether callback() is invoked or not. callback() will be passed three arguments: the first is a string which contains the completed filename, the second is the actual attributes of the file, and the third is a void pointer which is simply a copy of param (you can use this for whatever you like). It must return 0 to let the enumeration proceed, or any non-zero value to stop it. If an error occurs, the error code will be stored in errno but the enumeration won't stop. Returns the number of successful calls made to callback(), that is the number of times callback() was called and returned 0.

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.

> See also: al_findnext, al_findclose.

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.

> See also: al_findfirst, al_findclose.

void al_findclose(struct al_ffblk *info);
This closes a previously opened search with al_findfirst().

> See also: al_findfirst, al_findnext.

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.

> See also: pack_fopen, load_datafile.

PACKFILE *pack_fopen(const char *filename, const char *mode);
Opens a file according to mode, which may contain any of the flags:

- 'r' - open file for reading.
- 'w' - open file for writing, overwriting any existing data.
- 'p' - open file in packed mode. Data will be compressed as it is written to the file, and automatically uncompressed during read operations. Files created in this mode will produce garbage if they are read without this flag being set.
- '!' - open file for writing in normal, unpacked mode, but add the value F_NOPACK_MAGIC to the start of the file, so that it can later be opened in packed mode and Allegro will automatically detect that the data does not need to be decompressed.

Instead of these flags, one of the constants F_READ, F_WRITE, F_READ_PACKED, F_WRITE_PACKED or F_WRITE_NOPACK may be used as the mode parameter. On success, pack_fopen() returns a pointer to a file structure, and on error it returns NULL and stores an error code in errno. An attempt to read a normal file in packed mode will cause errno to be set to EDOM.

The packfile functions also understand several "magic" filenames that are used for special purposes. These are:

- "#" - read data that has been appended to your executable file with the exedat utility, as if it was a regular independent disk file.
- 'filename.dat#object_name' - open a specific object from a datafile, and read from it as if it was a regular file. You can treat nested datafiles exactly like a normal directory structure, for example you could open 'filename.dat#graphics/level1/mapdata'.
- '#object_name' - combination of the above, reading an object from a datafile that has been appended onto your executable.

With these special filenames, the contents of a datafile object or appended file can be read in an identical way to a normal disk file, so any of the file access functions in Allegro (eg. load_pcx() and set_config_file()) can be used to read from them. Note that you can't write to these special files, though: the fake file is read only. Also, you must save your datafile uncompressed or with per-object compression if you are planning on loading individual objects from it (otherwise there will be an excessive amount of seeking when it is read). Finally, be aware that the special Allegro object types aren't the same format as the files you import the data from. When you import data like bitmaps or samples into the grabber, they are converted into a special Allegro-specific format, but the '#' marker file syntax reads the objects as raw binary chunks. This means that if, for example, you want to use load_pcx to read an image from a datafile, you should import it as a binary block rather than as a BITMAP object.

> See also: pack_fclose, pack_fopen_chunk, packfile_password, pack_fread, pack_getc, file_select_ex.

int pack_fclose(PACKFILE *f);
Closes the stream f previously opened with pack_fopen(). After you have closed the stream, performing operations on it will yield errors in your application (e.g. crash it) or even block your OS.

Return value: Returns zero on success. On error, returns an error code which is also stored in errno. This function can fail only when writing to files: if the file was opened in read mode, it will always succeed.

> See also: pack_fopen, packfile_password.

int pack_fseek(PACKFILE *f, int offset);
Moves the position indicator of the stream f. Unlike the standard fseek() function, this only supports forward movements relative to the current position and in read-only streams, so don't use negative offsets.

Return value: Returns zero on success or a negative number on error, storing the error code in errno.

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.

> See also: pack_fopen, pack_fopen_chunk.

int pack_feof(PACKFILE *f);
pack_feof() returns nonzero as soon as you reach the end of the file. It does not wait for you to attempt to read beyond the end of the file, contrary to the ISO C feof() function. The only way to know whether you have read beyond the end of the file is to check the return value of the read operation you use (and be wary of pack_*getl() as EOF is also a valid return value with these functions).

> See also: pack_fopen, pack_fopen_chunk, pack_ferror.

int pack_ferror(PACKFILE *f);

Return value: Returns nonzero if the error indicator for the stream is set, indicating that an error has occurred during a previous operation on the stream.

Since EOF is used to report errors by some functions, it's often better to use the pack_feof() function to check explicitly for end of file and pack_ferror() to check for errors. Both functions check indicators that are part of the internal state of the stream to detect correctly the different situations.

> See also: pack_fopen, pack_fopen_chunk.

int pack_getc(PACKFILE *f);
Returns the next character from the stream f, or EOF if the end of the file has been reached.

> See also: pack_fopen, pack_fopen_chunk.

int pack_putc(int c, PACKFILE *f);
Puts a character in the stream f.

Return value: Returns zero on success, or EOF on error.

> See also: pack_fopen, pack_fopen_chunk.

int pack_igetw(PACKFILE *f);
Like pack_getc, but reads a 16-bit word from a file, using Intel byte ordering (least significant byte first, a.k.a. little-endian).

> See also: pack_getc.

int pack_iputw(int c, PACKFILE *f);
Like pack_putc, but writes a 16-bit word to a file, using Intel byte ordering (least significant byte first, a.k.a. little-endian).

> See also: pack_putc.

long pack_igetl(PACKFILE *f);
Like pack_getc, but reads a 32-bit long from a file, using Intel byte ordering (least significant byte first, a.k.a. little-endian).

> See also: pack_getc.

long pack_iputl(long c, PACKFILE *f);
Like pack_putc, but writes a 32-bit long to a file, using Intel byte ordering (least significant byte first, a.k.a. little-endian).

> See also: pack_putc.

int pack_mgetw(PACKFILE *f);
Like pack_getc, but reads a 16-bit word from a file, using Motorola byte ordering (most significant byte first, a.k.a. big-endian).

> See also: pack_getc.

int pack_mputw(int c, PACKFILE *f);
Like pack_putc, but writes a 16-bit word to a file, using Motorola byte ordering (most significant byte first, a.k.a. big-endian).

> See also: pack_putc.

long pack_mgetl(PACKFILE *f);
Like pack_getc, but reads a 32-bit long from a file, using Motorola byte ordering (most significant byte first, a.k.a. big-endian).

> See also: pack_getc.

long pack_mputl(long c, PACKFILE *f);
Like pack_putc, but writes a 32-bit long to a file, using Motorola byte ordering (most significant byte first, a.k.a. big-endian).

> See also: pack_putc.

long pack_fread(void *p, long n, PACKFILE *f);
Reads n bytes from the stream f, storing them at the memory location pointed to by p.

Return value: Returns the number of bytes read, which will be less than n if EOF is reached or an error occurs. Error codes are stored in errno.

> See also: pack_fopen, pack_fopen_chunk, pack_feof.

long pack_fwrite(const void *p, long n, PACKFILE *f);
Writes n bytes to the stream f from memory location pointed to by p.

Return value: Returns the number of bytes written, which will be less than n if an error occurs. Error codes are stored in errno.

> See also: pack_fopen, pack_fopen_chunk, pack_feof.

char *pack_fgets(char *p, int max, PACKFILE *f);
Reads a line from the stream f, storing it at location pointed to by p. Stops when a linefeed is encountered, or 'max' bytes have been read. The end of line is handled by detecting the right combination of characters for the platform. This supports CR-LF (DOS/Windows), LF (Unix), and CR (Mac) formats.

Return value: Returns the pointer p on success, or NULL on error.

Note, however, that the trailing carriage return is not included in the returned string, in order to provide easy code portability across platforms. If you need the carriage return, use pack_fread() and/or pack_getc() instead.

> See also: pack_fopen, pack_fopen_chunk, pack_fread, pack_getc.

int pack_fputs(const char *p, PACKFILE *f);
Writes a string to the stream f. The input string is converted from the current text encoding format to UTF-8 before writing. Newline characters are written as \r\n on DOS and Windows platforms. If you don't want this behaviour, use pack_fwrite() and/or pack_putc() instead.

Return value: Returns zero on success or a negative number on error.

> See also: pack_fopen, pack_fopen_chunk, pack_fwrite, pack_putc.

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 */
>           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, a.k.a. 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.

> See also: pack_fclose_chunk, pack_fopen.

PACKFILE *pack_fclose_chunk(PACKFILE *f);
Closes a sub-chunk of a file, previously obtained by calling pack_fopen_chunk().

> See also: pack_fopen_chunk.

Back to contents