2.3.14 File handling

Using files for storage of data is very important. Followingly there are
a number of functions available to aid you with this. However, let me
start with a little sermon on the subject of CPU usage:

Reading and writing to files is very CPU intensive, perhaps
not in the respect that the CPU actually has a lot to do while it
happens but that it is unable to do anything else at the same time. In
other words, reading and writing large portions of data often will slow
the game down significantly. To impose a small limit on excessive usage
of memory, disk and CPU, it's impossible to handle more than ca 50 kb of
data at one time. Files may be bigger, but you can't write or read
bigger chunks than that. This means you have to split up work on big
files into portions to be executed sequentially, preferrably with a
pause between each execution to give the rest of the game time to do
something. So, please keep in mind that this limit isn't there to annoy
you, to be sidestepped by nifty code, but as a reminder that you are
hogging the resources and should let others do something as well. Amen.

Let's start with the very basic conept of storing and restoring objects.
What you want to do usually is to store the global variables to file,
pending later restoration. For this purpose you use the efuns
save_object() and restore_object(). They both take a
filepath as argument and naturally have to specify a file which the
object in question is privileged to write or read, respectively. The
resulting savefile will have a name ending in '.o', and you must
remember to specify this extension to restore_object(). This is
optional with save_object() since it's added automatically if
you forget it. restore_object() returns the integer 1 on
successful reading of a file, and 0 otherwise. The contents of the saved
file are a list of all global variables with their contents on the same
line separaterd by a space. The storage format of the string is the
same as with val2str() mentioned earlier for the content of a
single variable. naturally save_object() will store the names
of the variables as well in front of the data it contains.

An important concept to remember is that data files stored with
save_object() are text files, and hence editable with the
internal ed() editor. However, the lines might become very long
if you store large arrays for exampe. ed() will then truncate the
lines at the maximum length, and if you then store the contents back to
file you will in fact destroy part of the data, making it impossible to
read back. This unfortunately is a very common mistakes with new
archwizards who want to hack the KEEPERSAVE.o file manually,
instead of going through the commands supplied for that purpose.

Mappings are the most convenient data type to be used with saving
variables. Just store the data you want in a mapping with a string
describing it as index, then store the mapping with the efun
save_map() for later restoration with restore_map().
The advantage with this method over save/restore_object() is
that you aren't limited to global non-static variables but can store
whatever you like. The drawback is that retrieving data is a bit more
complicated.

NB! Due to an inconsistency in the driver, indices can contain
space characters, but it's impossible to save any mapping containing
such an index with save_map(). The obvious solution (until we
can release a patch for the driver) is to avoid using space in indices
alltogether.

A fact to be remembered is that the save format used internally by
save_object() and save_map() is the same, which makes
it both possible and sometimes very useful to restore data from objects
that have saved their contents with save_object() by using
restore_map() and then just picking out the pieces you want from
the resulting mapping. Assume that you only would have been interested
in restoring the variable 'desc' in the example above, then you never
would have bothered with the other statements in the Method 2 restore.
Beware that using restore_object() on a savefile stored with
save_map() requires the indices used in the original mapping
to have the same name as the global variables intended to receive the
data, something that doesn't have to be true, as exemplified above.
Restoring the Method 2 savefile with Method 1 restore will not result
in an error, but it will fail to restore the variable 'data_map'.

Well, these are all methods for storing data in variables. Very often
you want to store free-form data however, and not just data in variables.
For this purpose you can use the efuns write_bytes() and
read_bytes(), or write_file() and read_file().
Basically both pairs of functions do the same thing, i.e. save or read a
string of certain length from file. The only difference is that
write_bytes() can be used to overwrite a portion of a file, while
write_file() only can append to a file. Also, read_bytes()
acts on exact bytes, while read_file() acts on lines separated
by newlines. Both write functions return 1 on success and 0 on failure.
Both read functions return a string with the result of the read
operation on success, on failure they return 0, so check the result with
stringp() to make sure it has succeeded.

You can get information about a file as well. You get the size of the
contents with the efun file_size(), but it can also be used to
check the type and existence of a file. If the returned number is positive,
it is a file and the number represents the size in bytes of the contents,
if the file doesn't exist, it returns -1 and if the file actually is a
directory it returns -2. To get the time of last modification you use
the efun file_time().

If you want to rename or move a file you can use the efun
rename(). Beware that this operation actually first copies the
file and then removes the old one. It can also be used to move
directories. If you wish to remove a file entirely, you use the efun
rm(). The efun rm() returns 1 on success and 0 on failure,
however beware that rename() works just the opposit way around,
it return 1 on failure and 0 if all is well.

The internal editor 'ed' is actually an efun that operates on a file.
You can use it for whatever purpose you like, but keep in mind that most
people don't really know how to use it. Also remember that the efun
ed() can be used to create new files and edit old as per the
privileges defined by the object. You can provide a function pointer
to a function that will be called on termination of the efun. If you
don't provide a filepath, the user will be expected to give the path
and name of the file from within the editor.