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:
Do not use any other flags from DOS/Windows or your code will not compile on another platform. FA_RDONLY is for directory entries with read-only flag on DOS-like systems or unwritable by current user on Unix-like systems. Hidden files are directory entries that have the hidden flag set (DOS/Windows) or have names starting with '.' (UNIX, excluding '.' and '..'). Flags can be combined using '|' (binary OR operator).FA_NONE - Exclude files that have any attribute set FA_RDONLY - Directory entries that are unwritable for current user FA_HIDDEN - Hidden flag FA_DIREC - Directories FA_SYSTEM - Files with system flag set (DOS/Windows only) FA_LABEL - Files with volume label flag set (DOS/Windows only) FA_ARCH - Files with archive flag set (DOS/Windows only) FA_ALL - Match all attributes
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. In other words, you explicitly exclude the flags that you do not specify. 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. If FA_NONE is passed all attributes are excluded and only files with no attributes are returned. Conversely, if you pass FA_ALL, no attributes are excluded so all files are returned (which is what you would usually want).
Functions which accept wildcards as file names support the meta characters `*' (which means, zero or any quantity of characters) and `?' (which means any character, but only one).
char name[200]; ... get_executable_name(name, sizeof(name)); allegro_message("Running `%s'\n", name);
get_executable_name(name, sizeof(name)); fix_filename_case(name); allegro_message("Running `%s'\n", name);
Return value: Returns a copy of the `path' parameter.
See also: fix_filename_slashes, canonicalize_filename.
char buf[200] = "c:/dos\\backup/weirdo\\test"; ... fix_filename_slashes(buf); /* Under DOS we would have c:\dos\backup\weirdo\test. Under Unix we would have c:/dos/backup/weirdo/test. */
Return value: Returns a copy of the `path' parameter.
See also: fix_filename_case, canonicalize_filename.
Note that this function won't work as expected if the path to canonicalize comes from another platform (eg. a "c:\something" path will canonicalize into something really wrong under Unix: "/current/path/c:/something").char buf[256]; ... canonicalize_filename(buf, "~/../s22/..\\t3st///hi.c", sizeof(buf)); /* Running this under Unix would return: /home/t3st/hi.c */
Return value: Returns a copy of the `dest' parameter.
See also: fix_filename_case, fix_filename_slashes.
char buf[256]; ... make_absolute_filename(buf, "/usr/games/", "../temp.txt", sizeof(buf)); /* This would create /usr/temp.txt */
Return value: Returns a copy of the `dest' parameter.
See also: make_relative_filename, is_relative_filename, replace_filename, canonicalize_filename.
char base[] = "/long/absolute/path/program.exe"; char user_input[] = "/nice/and/short.txt"; ... make_relative_filename(buf, base, user_input, sizeof(buf)); /* Under Unix buf would contain: ../../../nice/and/short.txt */
Return value: Returns a copy of the `dest' parameter if it succeeds or NULL if it fails (eg. under DOS, one path starts with "C:\" and another with "A:\").
See also: make_absolute_filename, is_relative_filename, canonicalize_filename.
See also: make_absolute_filename, make_relative_filename.
char name[200]; ... get_executable_name(name, sizeof(name)); replace_filename(name, name, "sound.dat", sizeof(name));
Return value: Returns a copy of the `dest' parameter.
See also: get_filename, replace_extension, append_filename.
Examples using this: Available Allegro examples.
replace_extension(buf, "C:\\game\\prog.exe", "dat", sizeof(buf));
Return value: Returns a copy of the `dest' parameter.
See also: get_extension, replace_filename.
append_filename(buf, "/home/user", "prog.bin", sizeof(buf));
Return value: Returns a copy of the `dest' parameter.
See also: replace_filename.
Note that Allegro won't perform any IO operations during the verification. This means that if you have `/a/path/like/this/', which doesn't have a filename, the function will return a pointer to the trailing null character. However, if you have `/a/path/like/this', Allegro will return a pointer to `this', even if it is a valid directory.get_executable_name(name, sizeof(name)); allegro_message("Running `%s'\n", get_filename(name));
Return value: Returns a pointer to the portion of `path' where the filename starts, or the beginning of `path' if no valid filename is found (eg. you are processing a path with backslashes under Unix).
See also: get_extension, put_backslash, replace_filename.
Examples using this: exmidi.
get_executable_name(name, sizeof(name)); allegro_message("The binary has the extension `%s'\n", get_extension(name));
Return value: Returns a pointer to the portion of `filename' where the extension starts, or a pointer to the trailing null character if there is no filename or it doesn't have extension.
See also: get_filename, put_backslash, replace_extension.
See also: get_extension, get_filename.
/* Check for a normal file. */ if (file_exists("franken.dat", 0, NULL)) allegro_message("It is alive!\n");
Return value: Returns non-zero if the file exists, or zero if it doesn't or the specified attributes mask it out.
See also: exists, file_size_ex, file_time.
Return value: Returns non-zero if the file exists, or zero if it doesn't.
See also: file_exists, file_size_ex, file_time.
See also: file_exists, file_time.
Examples using this: expackf.
See also: file_exists, file_size_ex.
Return value: Returns zero on success, non-zero on failure.
The callback function will be passed three arguments: the first is a string which contains the completed filename (exactly the same string you passed to for_each_file_ex() but with meta characters), 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). The callback must return zero 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. Example:
int show_name(const char *filename, int attrib, void *param) { allegro_message("Caught `%s', attribs %d\n", filename, attrib); return 0; } ... count = for_each_file_ex("data/level*", FA_DIREC, 0, show_name, 0); allegro_message("%d game directories\n", count);
Return value: Returns the number of successful calls made to callback(), that is, the number of times callback() was called and returned 0.
See also: al_findfirst, al_findnext, al_findclose.
There is some other stuff in the structure as well, but it is there for internal use only. Example:struct al_ffblk { int attrib; - actual attributes of the file found time_t time; - modification time of file char name[512]; - name of file };
struct al_ffblk info; if (al_findfirst("*.pcx", &info, FA_ALL) != 0) { /* Tell user there are no PCX files. */ return; }
Return value: The function returns non-zero if no match is found or if an error occurred and, in the latter case, sets `errno' accordingly. It returns zero if a match is found, allocating some memory for internal use in the structure. Therefore you have to close your search when you are finished to avoid memory leaks in your program.
See also: al_findnext, al_findclose, al_ffblk_get_size.
if (al_findfirst("*.pcx", &info, 0) != 0) return; do { /* Do something useful here with info.name. */ } while (al_findnext(&info) == 0); al_findclose(&info);
Return value: Returns zero if a match is found, non-zero if none is found or if an error occurred and, in the latter case, sets errno accordingly.
See also: al_findfirst, al_findclose.
See also: al_findfirst, al_findnext.
See also: al_findfirst, al_findnext, al_ffblk.
Check the documentation chapter specific to your platform for information on additional paths this function might search for. Also, don't forget about set_allegro_resource_path() to extend the searches. Example:
char path[256]; int ret; ret = find_allegro_resource(path, "scores.cfg", NULL, NULL, NULL, NULL, NULL, sizeof(path)); if (ret == 0) { /* Found system wide scores file. */ } else { /* No previous scores, create our own file. */ }
Return value: Returns zero on success, and stores a full path to the file (at most size bytes) into the dest buffer.
See also: set_allegro_resource_path.
These custom paths will be valid until you call allegro_exit(). You can call this function before install_allegro(), but after set_uformat() if you want to use a text encoding format other than the default.set_allegro_resource_path(10, "my_game/configs"); set_allegro_resource_path(0, "users/configs/"); set_allegro_resource_path(-45, "temp");
Return value: Returns non-zero on success, zero if the path could not be added or you wanted to remove a path and the priority used didn't have any associated path. Modification of existing paths always succeeds.
See also: find_allegro_resource.
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. The only exception to this is custom packfiles created with pack_fopen_vtable().
See also: pack_fopen, load_datafile, pack_fopen_vtable.
The packfile functions also understand several "magic" filenames that are used for special purposes. These are:
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.
Example:
PACKFILE *input_file; input_file = pack_fopen("scores.dat", "rp"); if (!input_file) abort_on_error("Couldn't read `scores.dat'!");
Return value: On success, pack_fopen() returns a pointer to a PACKFILE 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.
See also: pack_fclose, pack_fopen_chunk, packfile_password, pack_fread, pack_getc, file_select_ex, pack_fopen_vtable.
Examples using this: expackf.
While the created packfile structure can be used with other Allegro functions, there are two limitations. First, opening chunks using pack_fopen_chunk() on top of the returned packfile is not possible at this time. And packfile_password() does not have any effect on packfiles opened with pack_fopen_vtable().
Return value: On success, it returns a pointer to a PACKFILE structure, and on error it returns NULL and stores an error code in `errno'.
See also: pack_fopen, pack_fopen_chunk, packfile_password.
Examples using this: expackf.
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, pack_fopen_vtable, packfile_password.
Examples using this: expackf.
input_file = pack_fopen("data.bin", "r"); if (!input_file) abort_on_error("Couldn't open binary data!"); /* Skip some useless header before reading data. */ pack_fseek(input_file, 32);
Return value: Returns zero on success or a negative number on error, storing the error code in `errno'.
See also: pack_fopen, pack_fopen_chunk.
Examples using this: expackf.
Return value: Returns non-zero if you are at the end of the file, zero otherwise.
See also: pack_fopen, pack_fopen_chunk, pack_ferror.
Return value: Returns nonzero if the error indicator for the stream is set, meaning that an error has occurred during a previous operation on the stream.
See also: pack_fopen, pack_fopen_chunk.
See also: pack_fopen, pack_fopen_chunk.
Note: pack_fgets internally uses pack_ungetc, so never use pack_ungetc directly after using pack_fgets on a PACKFILE.
Return value: Returns c on success, or EOF on error.
See also: pack_getc, pack_fgets.
Return value: Returns the character written on success, or EOF on error.
See also: pack_fopen, pack_fopen_chunk.
See also: pack_getc.
See also: pack_putc.
See also: pack_getc.
See also: pack_putc.
See also: pack_getc.
See also: pack_putc.
See also: pack_getc.
See also: pack_putc.
unsigned char buf[256]; ... if (pack_fread(buf, 256, input_file) != 256) abort_on_error("Truncated input file!");
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.
Examples using this: expackf.
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.
Note: This function internally may make calls to pack_ungetc, so you cannot use pack_ungetc directly afterwards.
Example:
char buf[256]; ... while (pack_fgets(buf, sizeof(buf), input_file)) { /* Process input line. */ } fclose(input_file);
Return value: Returns the pointer `p' on success, or NULL on error.
See also: pack_fopen, pack_fopen_chunk, pack_fread, pack_getc, pack_ungetc.
Return value: Returns zero on success or a negative number on error.
See also: pack_fopen, pack_fopen_chunk, pack_fwrite, pack_putc.
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.PACKFILE *output = pack_fopen("out.raw", "w!"); ... /* Create a sub-chunk with compression. */ output = pack_fopen(chunk(output, 1); if (!output) abort_on_error("Error saving data!"); /* Write some data to the sub-chunk. */ ... /* Close the sub-chunk, recovering parent file. */ output = pack_fclose_chunk(output);
To read the chunk, use the following code:
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().PACKFILE *input = pack_fopen("out.raw", "rp"); ... input = pack_fopen_chunk(input, 1); /* Read data from the sub-chunk and close it. */ ... input = pack_fclose_chunk(input);
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.
Return value: Returns a pointer to the sub-chunked PACKFILE, or NULL if there was some error (eg. you are using a custom PACKFILE vtable).
See also: pack_fclose_chunk, pack_fopen.
Return value: Returns a pointer to the parent of the sub-chunk you just closed. Returns NULL if there was some error (eg. you tried to close a PACKFILE which wasn't sub-chunked).
See also: pack_fopen_chunk.
Return value: Returns a pointer to the structure, or NULL if there was an error.
See also: free_lzss_pack_data.
See also: create_lzss_pack_data.
Return value: Returns 0 on success, or EOF if there was an error.
See also: create_lzss_pack_data, free_lzss_pack_data.
Return value: Returns a pointer to the structure, or NULL if there was an error.
See also: free_lzss_unpack_data.
See also: create_lzss_unpack_data.
Return value: Returns the number of bytes added to the buffer `buf'.
See also: free_lzss_unpack_data.