6: File References

You deal with disk files using file references. Each fileref is an opaque C structure pointer; see section 1.6, "Opaque Objects".

A file reference contains platform-specific information about the name and location of the file, and possibly its type, if the platform has a notion of file type. It also includes a flag indication whether the file is a text file or binary file. [Note that this is different from the standard C I/O library, in which you specify text or binary mode when the file is opened.]

A fileref does not have to refer to a file which actually exists. You can create a fileref for a nonexistent file, and then open it in write mode to create a new file.

You always provide a usage argument when you create a fileref. The usage indicates the file type and the mode (text or binary.) It must be the logical-or of a file-type constant and a mode constant. These values are used when you create a new file, and also to filter file lists when the player is selecting a file to load. The constants are as follows:

In general, you should use text mode if the player expects to read the file with a platform-native text editor; you should use binary mode if the file is to be read back by your program, or if the data must be stored exactly. Text mode is appropriate for fileusage_Transcript; binary mode is appropriate for fileusage_SavedGame and probably for fileusage_InputRecord. fileusage_Data files may be text or binary, depending on what you use them for.

When a fileref is created via a user prompt (glk_fileref_create_by_prompt), it may include extra file type information. [For example, a prompt to write a transcript file might include a choice of text encodings, or even alternate formats such as RTF or HTML.] This player-selected information will override the default encoding rules noted above. When a fileref is created non-interactively (glk_fileref_create_by_name, glk_fileref_create_temp) the default rules always apply.

[See also the comments about encoding, section 5.6.3, "File Streams".]

6.1: The Types of File References

There are four different functions for creating a fileref, depending on how you wish to specify it. Remember that it is always possible that a fileref creation will fail and return NULL.

frefid_t glk_fileref_create_temp(glui32 usage, glui32 rock);

This creates a reference to a temporary file. It is always a new file (one which does not yet exist). The file (once created) will be somewhere out of the player's way. [This is why no name is specified; the player will never need to know it.]

A temporary file should not be used for long-term storage. It may be deleted automatically when the program exits, or at some later time, say when the machine is turned off or rebooted. You do not have to worry about deleting it yourself.

frefid_t glk_fileref_create_by_prompt(glui32 usage, glui32 fmode, glui32 rock);

This creates a reference to a file by asking the player to locate it. The library may simply prompt the player to type a name, or may use a platform-native file navigation tool. (The prompt, if any, is inferred from the usage argument.)

fmode must be one of these values:

The fmode argument should generally match the fmode which will be used to open the file.

[It is likely that the prompt or file tool will have a "cancel" option. If the player chooses this, glk_fileref_create_by_prompt() will return NULL. This is a major reason why you should make sure the return value is valid before you use it.]

The recommended file suffixes for files are ".glkdata" for fileusage_Data, ".glksave" for fileusage_SavedGame, ".txt" for fileusage_Transcript and fileusage_InputRecord.

The prompt may also include a choice of file formats. This will affect the format of the file (written or parsed), as noted earlier.

frefid_t glk_fileref_create_by_name(glui32 usage, char *name, glui32 rock);

This creates a reference to a file with a specific name. The file will be in a fixed location relevant to your program, and visible to the player. [This usually means "in the same directory as your program."]

Earlier versions of the Glk spec specified that the library may have to extend, truncate, or change your name argument in order to produce a legal native filename. This remains true. However, since Glk was originally proposed, the world has largely reached concensus about what a filename looks like. Therefore, it is worth including some recommended library behavior here. Libraries that share this behavior will more easily be able to exchange files, which may be valuable both to authors (distributing data files for games) and for players (moving data between different computers or different applications).

The library should take the given filename argument, and delete any characters illegal for a filename. This will include all of the following characters (and more, if the OS requires it): slash, backslash, angle brackets (less-than and greater-than), colon, double-quote, pipe (vertical bar), question-mark, asterisk. The library should also truncate the argument at the first period (delete the first period and any following characters). If the result is the empty string, change it to the string "null".

It should then append an appropriate suffix, depending on the usage: ".glkdata" for fileusage_Data, ".glksave" for fileusage_SavedGame, ".txt" for fileusage_Transcript and fileusage_InputRecord.

The above behavior is not a requirement of the Glk spec. Older implementations can continue doing what they do. Some programs (e.g. web-based interpreters) may not have access to a traditional filesystem at all, and to them these recommendations will be meaningless.

On the other side of the coin, the game file should not press these limitations. Best practice is for the game to pass a filename containing only letters and digits, beginning with a letters, and not mixing upper and lower case. Avoid overly-long filenames.

[The earlier Glk spec gave more stringent recommendations: "No more than 8 characters, consisting entirely of upper-case letters and numbers, starting with a letter". The DOS era is safely contained, if not over, so this has been relaxed. The I7 manual recommends "23 characters or fewer".]

[To address other complications:]

[Some filesystems are case-insensitive. If you create two filerefs with the names "File" and "FILE", they may wind up pointing to the same file, or they may not. Avoid doing this.]

[Some programs will look for all files in the same directory as the program itself (or, for interpreted games, in the same directory as the game file). Others may keep files in a data-specific directory appropriate for the user (e.g., ~/Library on MacOS).]

[If a game interpreter uses a data-specific directory, there is a question of whether to use a common location, or divide it into game-specific subdirectories. (Or to put it another way: should the namespace of named files be per-game or app-wide?) Since data files may be exchanged between games, they should be given an app-wide namespace. In contrast, saved games should be per-game, as they can never be exchanged. Transcripts and input records can go either way.]

[When updating an older library to follow these recommendations, consider backwards compatibility for games already installed. When opening an existing file (that is, not in a write-only mode) it may be worth looking under the older name (suffix) if the newer one does not already exist.]

[Game-save files are already stored with a variety of file suffixes, since that usage goes back to the oldest IF interpreters, long predating Glk. It is reasonable to treat them in some special way, while hewing closer to these recommendations for data files.]

[In case anyone cares, fileusage_Data (".glkdata") can be associated with the MIME type application/x-glkdata; fileusage_SavedGame (".glksave") with application/x-glksave. ".glksave" files are commonly Quetzal save data (but might be other formats for IF systems other than Z-code and Glulx). ".glkdata" can contain any data, obviously. So knowing the MIME type doesn't get you much, but we offer them anyway.]

frefid_t glk_fileref_create_from_fileref(glui32 usage, frefid_t fref, glui32 rock);

This copies an existing file reference, but changes the usage. (The original fileref is not modified.)

The use of this function can be tricky. If you change the type of the fileref (fileusage_Data, fileusage_SavedGame, etc), the new reference may or may not point to the same actual disk file. [Most platforms use suffixes to indicate file type, so it typically will not. See the earlier comments about recommended file suffixes.] If you do this, and open both file references for writing, the results are unpredictable. It is safest to change the type of a fileref only if it refers to a nonexistent file.

If you change the mode of a fileref (fileusage_TextMode, fileusage_BinaryMode), but leave the rest of the type unchanged, the new fileref will definitely point to the same disk file as the old one.

Obviously, if you write to a file in text mode and then read from it in binary mode, the results are platform-dependent.

6.2: Other File Reference Functions

void glk_fileref_destroy(frefid_t fref);

Destroys a fileref which you have created. This does not affect the disk file; it just reclaims the resources allocated by the glk_fileref_create... function.

It is legal to destroy a fileref after opening a file with it (while the file is still open.) The fileref is only used for the opening operation, not for accessing the file stream.

frefid_t glk_fileref_iterate(frefid_t fref, glui32 *rockptr);

This iterates through all the existing filerefs. See section 1.6.2, "Iterating Through Opaque Objects".

glui32 glk_fileref_get_rock(frefid_t fref);

This retrieves the fileref's rock value. See section 1.6.1, "Rocks".

void glk_fileref_delete_file(frefid_t fref);

This deletes the file referred to by fref. It does not destroy the fileref itself.

You should only call this with a fileref that refers to an existing file.

glui32 glk_fileref_does_file_exist(frefid_t fref);

This returns TRUE (1) if the fileref refers to an existing file, and FALSE (0) if not.

Up to top Previous chapter Next chapter