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.