Site under construction!
User Data Streams
Introduction
DSFML has several resource classes: images, fonts, sounds, etc. In
most programs, these resources will be loaded from files, with the
help of their loadFromFile()
method. In a
few other situations, resources will be packed directly into the
executable or in a big data file, and loaded from memory with
loadFromMemory()
. These methods cover almost
all the possible use cases -- but they are not without their limits.
Sometimes you want to load files from unusual places, such as a
compressed/encrypted archive, or a remote network location for
example. For these special situations, DSFML provides a third loading
function: loadFromStream()
. This function
reads data using an abstract InputStream
interface, which allows you to provide your own implementation of a
stream class that works with DSFML.
In this tutorial you'll learn how to write and use your own derived input stream.
InputStream
The InputStream interface declares four methods:
interface InputStream
{
long read(void[] data);
long seek(long position);
long tell();
long getSize();
}
read()
must extract size bytes of data from
the stream, and copy them to the supplied data address; it returns
the number of bytes read, or -1 on error.
seek()
must change the current reading
position in the stream; its position argument is the absolute byte
offset to jump to (so it is relative to the beginning of the data,
not to the current position); it returns the new position, or -1 on
error.
tell()
must return the current reading
position (in bytes) in the stream, or -1 on error.
getSize()
must return the total size (in
bytes) of the data which is contained in the stream, or -1 on error.
To create your own working stream, you must implement all of these four methods according to their requirements.
Using an InputStream
Using a custom stream class is straight-forward: instantiate it, and pass it to the loadFromStream (or openFromStream) function of the object that you want to load.
auto stream = new FileStream();
stream.open("image.png");
auto texture = new Texture();;
texture.loadFromStream(stream);
Examples
If you need a demonstration that helps you focus on how the code works, and not get lost in implementation details, you could take a look at the unit tests for InputStream, which includes a simple file based stream implementation.
Don't forget to check SFML's forum and wiki. Chances are that another user already wrote an sf::InputStream class that suits your needs, and it should be easily translatable to D. And if you write a new one and feel like it could be useful to other people as well, don't hesitate to share!
Common mistakes
Some resource classes are not loaded completely after loadFromStream
has been called. Instead, they continue to read from their data
source as long as they are used. This is the case for Music,
which streams audio samples as they are played, and for Font,
which loads glyphs on the fly depending on the text that is
displayed.
As a consequence, the stream instance that you used to load a music or a font, as well as its data source, must remain alive as long as the resource uses it. If it is destroyed while still being used, it results in undefined behavior (can be a crash, corrupt data, or nothing visible). This is usually not something to worry about when using the GC, but it is something to keep in mind.
Another common mistake is to return whatever the internal
functions return directly, but sometimes it doesn't match what
SFML expects. For example, when writing a FILE*
based implementation one might be tempted to write the seek
function as follows:
long seek(long position)
{
return fseek(m_file, position, SEEK_SET);
}
This code is wrong, because fseek
returns zero on success, whereas SFML expects the new position
to be returned.