dxx-rebirth/common/misc/physfsrwops.cpp

201 lines
6.1 KiB
C++

/*
* This file is part of the DXX-Rebirth project <https://www.dxx-rebirth.com/>.
* It is copyright by its individual contributors, as recorded in the
* project's Git history. See COPYING.txt at the top level for license
* terms and a link to the Git history.
*/
/*
* This code provides a glue layer between PhysicsFS and Simple Directmedia
* Layer's (SDL) RWops i/o abstraction.
*
* License: this code is public domain. I make no warranty that it is useful,
* correct, harmless, or environmentally safe.
*
* This particular file may be used however you like, including copying it
* verbatim into a closed-source project, exploiting it commercially, and
* removing any trace of my name from the source (although I hope you won't
* do that). I welcome enhancements and corrections to this file, but I do
* not require you to send me patches if you make changes. This code has
* NO WARRANTY.
*
* Unless otherwise stated, the rest of PhysicsFS falls under the zlib license.
* Please see LICENSE in the root of the source tree.
*
* SDL falls under the LGPL license. You can get SDL at https://www.libsdl.org/
*
* This file was written by Ryan C. Gordon. (icculus@clutteredmind.org).
*/
#include <stdio.h> /* used for SEEK_SET, SEEK_CUR, SEEK_END ... */
#include "physfsrwops.h"
#include "physfsx.h"
#if SDL_MAJOR_VERSION == 1
#define SDL_RWops_callback_seek_position int
#define SDL_RWops_callback_read_position int
#define SDL_RWops_callback_write_position int
#else
#define SDL_RWops_callback_seek_position Sint64
#define SDL_RWops_callback_read_position size_t
#define SDL_RWops_callback_write_position size_t
#endif
namespace {
static SDL_RWops_callback_seek_position physfsrwops_seek(SDL_RWops *rw, const SDL_RWops_callback_seek_position offset, const int whence)
{
PHYSFS_File *handle = reinterpret_cast<PHYSFS_File *>(rw->hidden.unknown.data1);
SDL_RWops_callback_seek_position pos = 0;
if (whence == SEEK_SET)
{
pos = offset;
} /* if */
else if (whence == SEEK_CUR)
{
PHYSFS_sint64 current = PHYSFS_tell(handle);
if (current == -1)
{
SDL_SetError("Can't find position in file: %s",
PHYSFS_getLastError());
return(-1);
} /* if */
#if SDL_MAJOR_VERSION == 1
pos = static_cast<int>(current);
if (static_cast<PHYSFS_sint64>(pos) != current)
{
SDL_SetError("Can't fit current file position in an int!");
return(-1);
} /* if */
#endif
if (offset == 0) /* this is a "tell" call. We're done. */
return(pos);
pos += offset;
} /* else if */
else if (whence == SEEK_END)
{
PHYSFS_sint64 len = PHYSFS_fileLength(handle);
if (len == -1)
{
SDL_SetError("Can't find end of file: %s", PHYSFS_getLastError());
return(-1);
} /* if */
#if SDL_MAJOR_VERSION == 1
pos = static_cast<int>(len);
if (static_cast<PHYSFS_sint64>(pos) != len)
{
SDL_SetError("Can't fit end-of-file position in an int!");
return(-1);
} /* if */
#endif
pos += offset;
} /* else if */
else
{
SDL_SetError("Invalid 'whence' parameter.");
return(-1);
} /* else */
if ( pos < 0 )
{
SDL_SetError("Attempt to seek past start of file.");
return(-1);
} /* if */
if (!PHYSFS_seek(handle, static_cast<PHYSFS_uint64>(pos)))
{
SDL_SetError("PhysicsFS error: %s", PHYSFS_getLastError());
return(-1);
} /* if */
return(pos);
} /* physfsrwops_seek */
static SDL_RWops_callback_read_position physfsrwops_read(SDL_RWops *const rw, void *const ptr, const SDL_RWops_callback_read_position size, const SDL_RWops_callback_read_position maxnum)
{
PHYSFS_File *handle = reinterpret_cast<PHYSFS_File *>(rw->hidden.unknown.data1);
PHYSFS_sint64 rc = (PHYSFS_read)(handle, ptr, size, maxnum);
if (rc != maxnum)
{
if (!PHYSFS_eof(handle)) /* not EOF? Must be an error. */
SDL_SetError("PhysicsFS error: %s", PHYSFS_getLastError());
} /* if */
return rc;
} /* physfsrwops_read */
static SDL_RWops_callback_write_position physfsrwops_write(SDL_RWops *const rw, const void *const ptr, const SDL_RWops_callback_write_position size, const SDL_RWops_callback_write_position num)
{
PHYSFS_File *handle = reinterpret_cast<PHYSFS_File *>(rw->hidden.unknown.data1);
PHYSFS_sint64 rc = (PHYSFS_write)(handle, reinterpret_cast<const uint8_t *>(ptr), size, num);
if (rc != num)
SDL_SetError("PhysicsFS error: %s", PHYSFS_getLastError());
return rc;
} /* physfsrwops_write */
static int physfsrwops_close(SDL_RWops *rw)
{
PHYSFS_File *handle = reinterpret_cast<PHYSFS_File *>(rw->hidden.unknown.data1);
if (!PHYSFS_close(handle))
{
SDL_SetError("PhysicsFS error: %s", PHYSFS_getLastError());
return(-1);
} /* if */
SDL_FreeRW(rw);
return(0);
} /* physfsrwops_close */
static std::pair<RWops_ptr, PHYSFS_ErrorCode> create_rwops(RAIIPHYSFS_File handle)
{
if (!handle)
{
const auto err = PHYSFS_getLastErrorCode();
SDL_SetError("PhysicsFS error: %s", PHYSFS_getErrorByCode(err));
return {nullptr, err};
}
else
{
RWops_ptr retval{SDL_AllocRW()};
if (retval)
{
retval->seek = physfsrwops_seek;
retval->read = physfsrwops_read;
retval->write = physfsrwops_write;
retval->close = physfsrwops_close;
retval->hidden.unknown.data1 = handle.release();
} /* if */
else
return {nullptr, PHYSFS_ERR_OTHER_ERROR};
return {std::move(retval), PHYSFS_ERR_OK};
} /* else */
} /* create_rwops */
}
std::pair<RWops_ptr, PHYSFS_ErrorCode> PHYSFSRWOPS_openRead(const char *fname)
{
return(create_rwops(RAIIPHYSFS_File{PHYSFS_openRead(fname)}));
} /* PHYSFSRWOPS_openRead */
std::pair<RWops_ptr, PHYSFS_ErrorCode> PHYSFSRWOPS_openReadBuffered(const char *fname, const PHYSFS_uint64 bufferSize)
{
RAIIPHYSFS_File fp{PHYSFS_openRead(fname)};
PHYSFS_setBuffer(fp, bufferSize);
return create_rwops(std::move(fp));
}
/* end of physfsrwops.c ... */