# File and compression routines

- al_ffblk_get_size — Get size of file returned by al_findfirst/al_findnext.
- al_findclose — Closes a previously opened search with al_findfirst().
- al_findfirst — Low-level function for searching files.
- al_findnext — Finds the next file in a search started by al_findfirst().
- append_filename — Concatenates a filename to a path.
- canonicalize_filename — Converts any filename into its canonical form.
- create_lzss_pack_data — Creates an LZSS structure for compression.
- create_lzss_unpack_data — Creates an LZSS structure for decompression.
- delete_file — Removes a file from the disk.
- exists — Shortcut version of file_exists() for normal files.
- file_exists — Tells if a file exists.
- file_size_ex — Returns the size of a file in bytes.
- file_time — Returns the modification time of a file.
- find_allegro_resource — Searches for a support file in many places.
- fix_filename_case — Converts a filename to a standardised case.
- fix_filename_slashes — Converts all the directory separators to a standard character.
- for_each_file_ex — Executes callback() for each file matching a wildcard.
- free_lzss_pack_data — Frees an LZSS structure.
- free_lzss_unpack_data — Frees an LZSS structure.
- get_executable_name — Obtains the full path to the current executable.
- get_extension — Returns a pointer to the extension of a filename.
- get_filename — Returns a pointer to the filename portion of a path.
- get_filename_encoding — Returns the encoding currently assumed for filenames.
- is_relative_filename — Returns TRUE if the filename is relative.
- lzss_read — Decompresses data using LZSS.
- lzss_write — Compresses data using LZSS.
- make_absolute_filename — Makes an absolute filename from a path and relative filename.
- make_relative_filename — Tries to make a relative filename from absolute path and filename.
- pack_fclose — Closes a stream previously opened.
- pack_fclose_chunk — Closes a previously opened sub-chunk.
- pack_feof — Returns nonzero as soon as you reach the end of the file.
- pack_ferror — Tells if an error occurred during an operation on the stream.
- pack_fgets — Reads a line from the stream.
- pack_fopen — Opens a file according to mode.
- pack_fopen_chunk — Opens a sub-chunk of a file.
- pack_fopen_vtable
- pack_fputs — Writes a string to the stream.
- pack_fread — Reads n bytes from the stream.
- pack_fseek — Seeks inside a stream.
- pack_fwrite — Writes n bytes to the stream.
- pack_getc — Returns the next character from a stream.
- pack_igetl — Like pack_getc(), but using 32-bit Intel byte ordering words.
- pack_igetw — Like pack_getc(), but using 16-bit Intel byte ordering words.
- pack_iputl — Like pack_putc(), but using 32-bit Intel byte ordering words.
- pack_iputw — Like pack_putc(), but using 16-bit Intel byte ordering words.
- pack_mgetl — Like pack_getc(), but using 32-bit Motorola byte ordering words.
- pack_mgetw — Like pack_getc(), but using 16-bit Motorola byte ordering words.
- pack_mputl — Like pack_putc(), but using 32-bit Motorola byte ordering words.
- pack_mputw — Like pack_putc(), but using 16-bit Motorola byte ordering words.
- pack_putc — Puts a character in the stream.
- pack_ungetc — Moves one single character back to the input buffer.
- packfile_password — Sets the global I/O encryption password.
- put_backslash — Puts a path separator at the end of a path if needed.
- replace_extension — Replaces filename+extension with a new extension tail.
- replace_filename — Replaces path+filename with a new filename tail.
- set_allegro_resource_path — Sets a specific resource search path.
- set_filename_encoding — Sets the encoding to use for filenames.

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_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

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).

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).

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 it tries to get the information from the ``/proc'` filesystem first, searching the directories specified in `$PATH' if necessary. If this fails too, it tries to find the executable name from the output of the ``ps'` command, using ``argv[0]'` only as a last resort if all other options fail. Example:

>           char name[200];
>           ...
>           get_executable_name(name, sizeof(name));
>           allegro_message("Running `%s'\n", name);

char *fix_filename_case(char *path);
Converts the filename stored in ``path'` to a standardised case. On DOS platforms, they will be entirely uppercase. On other platforms this function doesn't do anything. Example:

>           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 *fix_filename_slashes(char *path);
Converts all the directory separators in the filename stored in ``path'` to a standard character. On DOS and Windows platforms, this is a backslash. On most other platforms this is a slash. Example:

>           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.

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 and fixing incorrect forward/backward slashes for the current platform, storing at most ``size'` bytes into the ``dest'` buffer. You can use the same buffer both as input and output because Allegro internally works on a copy of the input before touching ``dest'`. Example:

>           char buf[256];
>           ...
>           canonicalize_filename(buf, "~/../s22/..\\t3st///hi.c",
>                                 sizeof(buf));
>           /* Running this under Unix would
>              return: /home/t3st/hi.c */

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").

Return value: 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. This is like calling replace_filename() and then canonicalize_filename(). Example:

>           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 *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. This function won't work if the paths are not canonical under the current platform (see canonicalize_filename()). Also, ``dest'` cannot be used as input value for ``path'` or ``filename'`. Example:

>           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.

int is_relative_filename(const char *filename);
Returns TRUE if the filename is relative or FALSE if it is absolute. Note that an absolute filename under DOS (with a device separator) will be considered as relative under Unix, because there absolute paths always start with a slash.

> 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. You can use the same buffer both as input and output because Allegro internally works on a copy of the input before touching ``dest'`. Example:

>           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.

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. If the filename doesn't have any extension at all, ``ext'` will be appended to it, adding a dot character if needed. You can use the same buffer both as input and output because Allegro internally works on a copy of the input before touching ``dest'`. Example:

>           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.

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. If ``path'` doesn't have a trailing path separator, the function will append one if needed. You can use the same buffer both as input and output because Allegro internally works on a copy of the input before touching ``dest'`. Example:

>           append_filename(buf, "/home/user",
>                           "prog.bin", sizeof(buf));

Return value: Returns a copy of the ``dest'` parameter.

> See also: replace_filename.

char *get_filename(const char *path);
Finds out the filename portion of a completely specified file path. Both ``\'` and ``/'` are recognized as directory separators under DOS and Windows. However, only ``/'` is recognized as directory separator under other platforms. Example:

>           get_executable_name(name, sizeof(name));
>           allegro_message("Running `%s'\n", get_filename(name));

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.

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.

char *get_extension(const char *filename);
Finds out the extension of the filename (with or without path information). Example:

>           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.

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.

void set_filename_encoding(int encoding)
Sets the encoding to use for filenames. By default, UTF-8 is assumed.

> See also: get_filename_encoding.

int get_filename_encoding(void)
Returns the encoding currently assumed for filenames.

> See also: set_filename_encoding.

int file_exists(const char *filename, int attrib, int *aret);
Checks whether a file matching the given name and attributes (see beginning of this chapter) exists. If ``aret'` is not NULL, it will be set to the attributes of the matching file. Example:

>           /* 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.

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.

Return value: Returns non-zero if the file exists, or zero if it doesn't.

> See also: file_exists, file_size_ex, file_time.

uint64_t file_size_ex(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.

> Examples using this: expackf.

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_ex.

int delete_file(const char *filename);
Removes a file from the disk. You can't delete directories, though.

Return value: Returns zero on success, non-zero on failure.

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. Basically, this is a convenient wrapper around al_findfirst(), al_findnext() and al_findclose(). ``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.

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.

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 al_ffblk structure looks like:

>           struct al_ffblk
>           {
>              int attrib;       - actual attributes of the file found
>              time_t time;      - modification time 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. Example:

>           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.

int al_findnext(struct al_ffblk *info);
This finds the next file in a search started by al_findfirst(). Example:

>           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.

void al_findclose(struct al_ffblk *info);
This closes a previously opened search with al_findfirst(). You need to call this on all successfully opened searches to avoid memory leaks in your program.

> See also: al_findfirst, al_findnext.

uint64_t al_ffblk_get_size(struct al_ffblk *info);
This returns the size of the file returned by al_findfirst or al_findnext.

> See also: al_findfirst, al_findnext, al_ffblk.

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 :-). Pass NULL for the parameters you are not using.

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.

int set_allegro_resource_path(int priority, const char *path);
Sometimes Allegro doesn't look in enough places to find a resource. For those special cases, you can call this function before loading your resource with additional paths to search for. You set up the priorities, higher numbers are searched for first. To modify an already setup path, call this function with the same priority and the new path. To remove an already setup path, call this function with the priority of the path and NULL as the path parameter. Example:

>           set_allegro_resource_path(10, "my_game/configs");
>           set_allegro_resource_path(0, "users/configs/");
>           set_allegro_resource_path(-45, "temp");

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.

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.

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, nobody can 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. The only exception to this is custom packfiles created with pack_fopen_vtable().

> See also: pack_fopen, load_datafile, pack_fopen_vtable.

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.

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.

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.

PACKFILE *pack_fopen_vtable(const PACKFILE_VTABLE *vtable, void *userdata);
Creates a new packfile structure that uses the functions specified in the vtable instead of the standard functions. The data pointer by ``vtable'` and ``userdata'` must remain available for the lifetime of the created packfile.

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.

int pack_fclose(PACKFILE *f);
Closes the stream ``f'` previously opened with pack_fopen() or pack_fopen_vtable(). 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, pack_fopen_vtable, packfile_password.

> Examples using this: expackf.

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. 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. Example:

>           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.

int pack_feof(PACKFILE *f);
Finds out if you have reached 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).

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.

int pack_ferror(PACKFILE *f);
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.

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.

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_ungetc(int c, PACKFILE *f);
Puts a character back to the file's input buffer. Like with ungetc from libc, only a single push back is guaranteed.

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.

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

Return value: Returns the character written 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'`. Example:

>           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.

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. However, 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.

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.

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. The PACKFILE parameter is a previously opened file, and ``pack'` is a boolean parameter which will turn compression on for the sub-chunk if it is non-zero. Example:

>           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);

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 following code:

>           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);

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.

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.

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

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.

LZSS_PACK_DATA *create_lzss_pack_data(void);
Creates an LZSS_PACK_DATA structure, which can be used for LZSS compression with PACKFILEs.

Return value: Returns a pointer to the structure, or NULL if there was an error.

> See also: free_lzss_pack_data.

void free_lzss_pack_data(LZSS_PACK_DATA *dat);
Frees an LZSS_PACK_DATA structure created with create_lzss_pack_data().

> See also: create_lzss_pack_data.

int lzss_write(PACKFILE *file, LZSS_PACK_DATA *dat, int size, unsigned char *buf, int last);
Packs ``size'` bytes from ``buf'`, using the pack information contained in ``dat'`. The compressed bytes will be stored in ``file'`.

Return value: Returns 0 on success, or EOF if there was an error.

> See also: create_lzss_pack_data, free_lzss_pack_data.

LZSS_UNPACK_DATA *create_lzss_unpack_data(void);
Creates an LZSS_UNPACK_DATA structure, which can be used for LZSS decompression reading PACKFILEs.

Return value: Returns a pointer to the structure, or NULL if there was an error.

> See also: free_lzss_unpack_data.

void free_lzss_unpack_data(LZSS_UNPACK_DATA *dat);
Frees an LZSS_UNPACK_DATA structure created with create_lzss_pack_data.

> See also: create_lzss_unpack_data.

int lzss_read(PACKFILE *file, LZSS_UNPACK_DATA *dat, int s, unsigned char *buf);
Unpacks from ``dat'` into ``buf'`, until either EOF is reached or ``s'` bytes have been extracted from ``file'`.

Return value: Returns the number of bytes added to the buffer ``buf'`.

> See also: free_lzss_unpack_data.

Back to contents