从std::istream读取SDL_RWops

5

我很惊讶谷歌没有找到解决方案。我正在寻找一种解决方案,允许将SDL_RWops与std::istream一起使用。SDL_RWops是在SDL中读取/写入数据的替代机制。

有没有处理这个问题的网站链接?

一个明显的解决方案是预先读取足够的数据到内存中,然后使用SDL_RWFromMem。但是,这样做的缺点是我需要预先知道文件大小。

似乎可以通过“覆盖”SDL_RWops函数来解决该问题...


你的意思是想从 istream 创建一个 RWops 结构体吗? - Collin Dauphinee
那不是一个命中,我不明白你想做什么。 - Collin Dauphinee
我正在尝试从istream中读取SDL数据(例如从SDL_image中读取图像)。 - Kornel Kisielewicz
2个回答

7

我觉得回答自己的问题有点不好意思,但这个问题困扰了我一段时间,这是我想出的解决方案:

int istream_seek( struct SDL_RWops *context, int offset, int whence)
{
    std::istream* stream = (std::istream*) context->hidden.unknown.data1;

         if ( whence == SEEK_SET )
        stream->seekg ( offset, std::ios::beg );
    else if ( whence == SEEK_CUR )
        stream->seekg ( offset, std::ios::cur );
    else if ( whence == SEEK_END )
         stream->seekg ( offset, std::ios::end );

    return stream->fail() ? -1 : stream->tellg();
}


int istream_read(SDL_RWops *context, void *ptr, int size, int maxnum)
{
    if ( size == 0 ) return -1;
    std::istream* stream = (std::istream*) context->hidden.unknown.data1;
    stream->read( (char*)ptr, size * maxnum );

    return stream->bad() ? -1 : stream->gcount() / size;
}

int istream_close( SDL_RWops *context )
{
    if ( context ) {
        SDL_FreeRW( context );
    }
    return 0;
}


SDL_RWops *SDL_RWFromIStream( std::istream& stream )
{
    SDL_RWops *rwops;
    rwops = SDL_AllocRW();

    if ( rwops != NULL ) 
    {
        rwops->seek = istream_seek;
        rwops->read = istream_read;
        rwops->write = NULL;
        rwops->close = istream_close;
        rwops->hidden.unknown.data1 = &stream;
    }
    return rwops;
}

本程序假设SDL不会释放istream,并且它们会在操作中一直存在。目前仅支持istream,对于ostream需要编写单独的函数。我知道可以传递iostream,但这不允许将istream传递到转换函数:/。

欢迎提供任何错误或升级的建议。


4
使用 rwops->type 可以允许使用 istream、ostream 或 iostream。 - deft_code
3
使用NULL作为写入函数似乎很可怕。创建一个使用SDL_SetError并返回-1的函数可能更好。 - deft_code
@Caspin,谢谢,非常有用的提示!不过,发布的代码不起作用...我正在尝试调试它,但没有线索 :/ - Kornel Kisielewicz
好的,我的错,我没有将ios::binary传递给测试用例 :) - Kornel Kisielewicz

1
如果您正在尝试从istream获取一个SDL_RWops结构体,您可以通过将整个istream读入内存,然后使用SDL_RWFromMem来获取表示它的结构体来实现。
以下是一个快速示例;请注意,它是不安全的,因为没有进行任何健全性检查。例如,如果文件大小为0,则访问buffer[0]可能会在调试版本中引发异常或断言。
// Open a bitmap
std::ifstream bitmap("bitmap.bmp");

// Find the bitmap file's size
bitmap.seekg(0, std::ios_base::end);
std::istream::pos_tye fileSize = bitmap.tellg();
bitmap.seekg(0);

// Allocate a buffer to store the file in
std::vector<unsigned char> buffer(fileSize);

// Copy the istream into the buffer
std::copy(std::istreambuf_iterator<unsigned char>(bitmap), std::istreambuf_iterator<unsigned char>(), buffer.begin());

// Get an SDL_RWops struct for the file
SDL_RWops* rw = SDL_RWFromMem(&buffer[0], buffer.size());

// Do stuff with the SDL_RWops struct

我已经写过了,我不能这样做,因为我不想将整个文件预加载到内存中。如果我想要从文件中读取,我会使用IMG_Load :)。我想要从一个通过istream访问的自定义容器中加载。 - Kornel Kisielewicz
你只是提到这是一个问题,因为“你事先不知道文件的大小”。 - Collin Dauphinee
是的,这可以适用于任何istream,我只是在示例中使用了ifstream,因为这是创建istream最简单的方法。 - Collin Dauphinee
叹气...想象一下——我从一个文件存档库中获取了一个istream。没有大小,只有一个istream。此外,在那个zip文件中,有几个文件被存储。预加载文件将会加载1GB的图像。 - Kornel Kisielewicz

网页内容由stack overflow 提供, 点击上面的
可以查看英文原文,
原文链接