Difference between revisions of "HOWTO-Open Files"

Jump to navigation Jump to search
777 bytes removed ,  15:17, 25 October 2018
m
Text replacement - "</source>" to "</syntaxhighlight>"
m (Whitespace tweaks)
m (Text replacement - "</source>" to "</syntaxhighlight>")
 
(7 intermediate revisions by 5 users not shown)
Line 1: Line 1:
==Super Brief Quick Reference==
==Super Brief Quick Reference==
* Use File::open to open files (for reading) as before.
* Use File::open to open files (for reading) as before.
* However, do *not* use (absolute) paths with File::open()!
* However, do *not* use (absolute) paths with <code>File::open()</code>!
* an FSNode is kind of a "portable path". If you need to process a path (e.g. coming from the config file), first create an FSNode from it, then use that for whatever you need to (e.g. pass the FSNode to File::open)
* an FSNode is kind of a "portable path". If you need to process a path (e.g. coming from the config file), first create an FSNode from it, then use that for whatever you need to (e.g. pass the FSNode to <code>File::open()</code>)




Line 8: Line 8:
Here is just a quick guide on the various ways to open a file, without explaining much background.
Here is just a quick guide on the various ways to open a file, without explaining much background.


*Most of you can simply keep using class Common::File as before. The main changes in it:
The basic case: you want to open a file called "datafile.dat":
**You must not pass absolute paths to <tt>File::open()</tt>! If you must open a file using a path, the correct way is to first create an FSNode from the path, then pass that to File::open.
<syntaxhighlight lang="cpp">
**You can pass relative paths in a limited fashion; you must use the "/" character as separator. I am too lazy to provide details on this right now; but check out the doxygen docs of class FSDirectory.
#include "common/file.h"
**There are new File::open methods: You can pass an "Archive" subclass (e.g. a ZIPArchive) to it, and it will search for that file in the Archive)
*** In particular, the new file SearchManager (short: SearchMan) is such an Archive subclass, and can wrap arbitrary paths, ZIP archives, etc. By providing Archive subclasses, you can extend this arbitrarily.


If you have an FSNode and want to read from the corresponding file, you can use File::open(FSNode). Internally, this calls FSNode::openForReading(). Alternatively, you can use this method directly (but then you also must delete the ReadStream returned by it later on). This can be useful if you need to keep a pointer to the file stream anyway. E.g. assume you used to do this:
Common::File f;
<syntax type="C++">  Common::File *f = new Common::File;
if (!f.open("datafile.dat")) {
  if (f && f->open(node))
   // handle failure to find/open file
    return f;
}
   else {
// access f
    delete f;
</syntaxhighlight>
    return 0;
  }</syntax>
you can now do this instead:
<syntax type="C++">  return node.openForReading();</syntax>


Or, you want to open a file called "datafile.dat" in a subdirectory "data" of the game directory:
<syntaxhighlight lang="cpp">
#include "common/file.h"


You can also invoke the SearchManager directly to open a file with a specific name, but looking for it in various places (the game path, extrapath, DATADIR, current dir, etc.): SearchMan::openFile(filename). This is almost exactly what File::open(String) does, only that the later also tries to open the filename with a dot appened. I.e. if you do
Common::File f;
<syntax type="C++">  file.open("foo")</syntax>
if (!f.open("data/datafile.dat")) {
then if it can't find "foo", it also tries to open "foo." (this is to workaround problems on some systems, where an old DOS file without an extension incorrectly may get a dot appended when copying it). If you don't need this extra lookup, and if you may want to use the Stream directly, instead of wrapping it in a Common::File instance, you can use the SearchMan directly. To continue my example from above, instead of
   // handle failure to find/open file
<syntax type="C++">  Common::File *f = new Common::File;
}
  if (f && f->open(filename))
// access f
    return f;
</syntaxhighlight>
   else {
    delete f;
    return 0;
  }</syntax>
you could now do this instead (with almost identical meaning, except for the "trailing dot" hack):
<syntax type="C++">  return SearchMan.openFile(filename);</syntax>


This functions by searching through a default search path managed by the SearchManager (short: SearchMan).
The default search path contains:
* the game directory
* the global "extrapath" from the config file
* the game-specific "extrapath" from the config file
Also, by default it contains a number of system-specific paths, such as:
* on some platforms, a global data dir (e.g., /usr/share/scummvm )
* on Mac OS X, the Resource directory of the .app bundle
* the current directory [subject to change in the future]
Notes:
*A File is a SeekableReadStream. See the doxygen documentation of that class to see how to access the contents of a file.
*You must not pass absolute paths to <tt>File::open()</tt>! If you must open a file using a path, the correct way is to first create an FSNode from the path, then pass that to File::open.
*You can pass relative paths in a limited fashion; you must use the "/" character as separator. See the doxygen docs of class FSDirectory for details.
*There are new File::open methods: You can pass an "Archive" subclass (e.g. a ZIPArchive) to it, and it will search for that file in the Archive). In particular, the SearchMan is such an Archive subclass, and can wrap arbitrary paths, ZIP or ARJ archives, etc. By providing Archive subclasses, you can extend this arbitrarily.


==The Parts of the System==
==The Parts of the System==
Line 52: Line 59:
**by asking a directory FSNode for a child node
**by asking a directory FSNode for a child node


Finally, you can creat FSNodes from "paths". Caveat: You may not assume anything about the path format, like what the separator char is; in fact, there may not even exist the *concept* of a path separator on the target system. Hence, the only valid way to do that is to feed a "path" created by another FSNode to this FSNode. I.e. you can "serialize" an FSNode to a path, via the FSNode::getPath() method, then write that to a config file. Later, you read it back in, and create a new FSNode from it. That works fine, as long as you stay on the same OS / platform.
Finally, you can create FSNodes from "paths". Caveat: You may not assume anything about the path format, like what the separator char is; in fact, there may not even exist the *concept* of a path separator on the target system. Hence, the only valid way to do that is to feed a "path" created by another FSNode to this FSNode. I.e. you can "serialize" an FSNode to a path, via the FSNode::getPath() method, then write that to a config file. Later, you read it back in, and create a new FSNode from it. That works fine, as long as you stay on the same OS / platform.


Warning: a "path" as returned by FSNode::getPath should *not* be passed to File::open(). If you want to open a file at a specific path, first create an FSNode from it, then use that to open the file.
Warning: a "path" as returned by FSNode::getPath should *not* be passed to File::open(). If you want to open a file at a specific path, first create an FSNode from it, then use that to open the file by calling <code>node->openForReading();</code>.




Line 64: Line 71:


===Archive (from common/archive.h)===
===Archive (from common/archive.h)===
This class represents an accumulation of "files". It can be a filesystem directory (implemented by FSDirectory), and then "contains" all the files in that directory (and possibly also files contained some levels deep in that dir). It can be a ZIP archive (see common/unzip.h), and then the "files" in it are members of that ZIP file. It can even be an accumulation of multiple other archives, and then it contains all files contained in all of these dirs (see class SearchSet). The SearchManager is in fact just an instance of this class, too.
This class represents an accumulation of "files". It can be a filesystem directory (implemented by FSDirectory), and then "contains" all the files in that directory (and possibly also files contained some levels deep in that dir). It can be a ZIP or ARJ archive (see resp. common/unzip.h and common/unarj.h), and then the "files" in it are members of that compressed file. It can even be an accumulation of multiple other archives, and then it contains all files contained in all of these dirs (see class SearchSet). The SearchManager is in fact just an instance of this class, too.


This concept is simple yet powerful. By  using SearchSet, you can group together multiple directories and ZIP files, and then with a single call, search for files in all of them simultaneously.
This concept is simple yet powerful. By  using SearchSet, you can group together multiple directories and compressed files, and then with a single call, search for files in all of them simultaneously.


You can ask an Archive whether it contains a given file; open a file (either via the openFile() method, which returns a ReadStream pointer you have to delete later on; or via the new File::open(name, archive) method); get a list of all files in the archive; or get a list of
You can ask an Archive whether it contains a given file; open a file (either via the openFile() method, which returns a ReadStream pointer you have to delete later on; or via the new File::open(name, archive) method); get a list of all files in the archive; or get a list of
Line 84: Line 91:
Savestates are by default handled via FSNodes, too. If you need to handle savestates differently (like many console ports do), by providing a custom SavefileManager implementation (many ports do that already).
Savestates are by default handled via FSNodes, too. If you need to handle savestates differently (like many console ports do), by providing a custom SavefileManager implementation (many ports do that already).


If your port has the config file in a special location (be it in a special path, or you want to store it in the Windows registry, or in some special NVRAM, or whatever -- go nuts with ideas), you can overload OSystem::openConfigFileForReading() and OSystem::openConfigFileForWriting() methods (NOTE: Various ports already do it, but I'd wish the iPhone, PS2, PSP and PalmOS ports would finally clean out their cruft from common/system.cpp ;)
If your port has the config file in a special location (be it in a special path, or you want to store it in the Windows registry, or in some special NVRAM, or whatever -- go nuts with ideas), you can overload OSystem::openConfigFileForReading() and OSystem::openConfigFileForWriting() methods.


If you want ScummVM to look for file in additional dirs (like, on OSX, we store the engine-data files like sky.cpt and kyra.dat inside the application bundle), your port can overload the OSystem::addSysArchivesToSearchSet() method to hook these extra locations into the SearchMan -- this way, the location is checked whenever the default File::open method is used, for example. You can hook up arbitrary Archive classes here (even .zip files), not just directories, so it's quite flexible.
If you want ScummVM to look for file in additional dirs (like, on OSX, we store the engine-data files like sky.cpt and kyra.dat inside the application bundle), your port can overload the OSystem::addSysArchivesToSearchSet() method to hook these extra locations into the SearchMan -- this way, the location is checked whenever the default File::open method is used, for example. You can hook up arbitrary Archive classes here (even .ZIP or .ARJ files), not just directories, so it's quite flexible.
TrustedUser
2,147

edits

Navigation menu