Delegate PCX loading to SDL_image

This adds a new dependency, but most systems likely already have
SDL_image installed.  Use of SDL_image can be disabled, but this is
discouraged, because various in-game interfaces assume the use of the
original background.

The old implementation automatically corrected for filename case.  The
new implementation expects that the supplied filename can be passed to
PYHSFS_openRead as-is.  All known uses in-game have been corrected to
satisfy this requirement.  If the new stricter match requirement becomes
a problem, a variant of PHYSFSRWOPS_openRead that adjusts filename case
could be created for use here.

- Update install instructions
- Update ebuild
- Update Arch PKGBUILD
This commit is contained in:
Kp 2020-05-17 23:35:26 +00:00
parent 71ca1a7553
commit 3114874713
12 changed files with 206 additions and 145 deletions

View File

@ -23,6 +23,7 @@ PhysFS 3.x is recommended.
Optional, but recommended:
* [SDL\_image 1.2](https://www.libsdl.org/projects/SDL_image/).
* [SDL\_mixer 1.2](https://www.libsdl.org/projects/SDL_mixer/).
* [libpng](http://www.libpng.org/).
* C++ compiler with support for selected C++14 features. One of:
@ -79,6 +80,7 @@ Install the listed prerequisites through your system package manager.
base-devel
scons
sdl
sdl\_image
sdl\_mixer
physfs**
@ -87,6 +89,7 @@ Install the listed prerequisites through your system package manager.
gcc-c++
scons
SDL-devel
SDL\_image-devel
SDL\_mixer-devel
physfs-devel**
@ -94,6 +97,7 @@ Install the listed prerequisites through your system package manager.
* **emerge --ask --verbose --noreplace
dev-util/scons
media-libs/libsdl
media-libs/sdl-image
media-libs/sdl-mixer
dev-games/physfs**
@ -102,6 +106,7 @@ Install the listed prerequisites through your system package manager.
build-essential
scons
libsdl1.2-dev
libsdl-image1.2-dev
libsdl-mixer1.2-dev
libphysfs-dev**
@ -125,6 +130,7 @@ DXX-Rebirth can be built from the Terminal (via SCons) without Xcode; to build u
gcc5
scons
sdl
sdl\_image
sdl\_mixer
physfs**

View File

@ -1390,17 +1390,22 @@ static void terminate_handler()
def _check_SDL(self,context):
if self.user_settings.sdl2:
check_libSDL = self.check_libSDL2
check_SDL_image = self.check_SDL2_image
check_SDL_mixer = self.check_SDL2_mixer
else:
check_libSDL = self.check_libSDL
check_SDL_image = self.check_SDL_image
check_SDL_mixer = self.check_SDL_mixer
check_libSDL(context)
check_SDL_image(context)
check_SDL_mixer(context)
@_implicit_test
def check_libSDL(self,context,_guess_flags={
'LIBS' : ['SDL'] if sys.platform != 'darwin' else [],
}):
self._check_libSDL(context, '', _guess_flags)
@_implicit_test
def check_libSDL2(self,context,_guess_flags={
'LIBS' : ['SDL2'] if sys.platform != 'darwin' else [],
@ -1408,6 +1413,7 @@ static void terminate_handler()
if not self.user_settings.opengl:
raise SCons.Errors.StopError('Rebirth does not support SDL2 without OpenGL. Set opengl=1 or sdl2=0.')
self._check_libSDL(context, '2', _guess_flags)
def _check_libSDL(self,context,sdl2,guess_flags):
user_settings = self.user_settings
successflags = self.pkgconfig.merge(context, self.msgprefix, user_settings, 'sdl%s' % sdl2, 'SDL%s' % sdl2, guess_flags).copy()
@ -1475,40 +1481,62 @@ static void terminate_handler()
if not e2 and e[0] == 1:
e = (None, error_text_opengl_mismatch)
raise SCons.Errors.StopError(e[1])
@_implicit_test
def check_SDL_image(self,context):
self._check_SDL_image(context, '')
@_implicit_test
def check_SDL2_image(self,context):
self._check_SDL_image(context, '2')
def _check_SDL_image(self,context,sdl2):
self._check_SDL_addon_library(context, sdl2, 'SDL%s_image', 'DXX_USE_SDLIMAGE', self.user_settings.sdlimage, '''
IMG_Init(0);
SDL_RWops *rw = reinterpret_cast<SDL_RWops *>(argv);
SDL_Surface *s = IMG_LoadPCX_RW(rw);
(void)s;
IMG_Quit();
''')
# SDL_mixer/SDL2_mixer use the same -I line as SDL/SDL2
@_implicit_test
def check_SDL_mixer(self,context,_guess_flags={
'LIBS' : ['SDL_mixer'] if sys.platform != 'darwin' else [],
}):
self._check_SDL_mixer(context, '', _guess_flags)
def check_SDL_mixer(self,context):
self._check_SDL_mixer(context, '')
@_implicit_test
def check_SDL2_mixer(self,context,_guess_flags={
'LIBS' : ['SDL2_mixer'] if sys.platform != 'darwin' else [],
}):
self._check_SDL_mixer(context, '2', _guess_flags)
def _check_SDL_mixer(self,context,sdl2,guess_flags):
mixer = 'SDL%s_mixer' % sdl2
user_settings = self.user_settings
context.Display('%s: checking whether to use %s...%s\n' % (self.msgprefix, mixer, 'yes' if user_settings.sdlmixer else 'no'))
# SDL_mixer support?
use_sdlmixer = user_settings.sdlmixer
self._define_macro(context, 'DXX_USE_SDLMIXER', int(use_sdlmixer))
if not use_sdlmixer:
return
successflags = self.pkgconfig.merge(context, self.msgprefix, user_settings, mixer, mixer, guess_flags)
if user_settings.host_platform == 'darwin':
successflags = successflags.copy()
successflags['FRAMEWORKS'] = [mixer]
relative_headers = 'Library/Frameworks/%s.framework/Headers' % mixer
successflags['CPPPATH'] = [os.path.join(os.getenv("HOME"), relative_headers), '/%s' % relative_headers]
self._check_system_library(context,header=['SDL_mixer.h'],main='''
def check_SDL2_mixer(self,context):
self._check_SDL_mixer(context, '2')
def _check_SDL_mixer(self,context,sdl2):
self._check_SDL_addon_library(context, sdl2, 'SDL%s_mixer', 'DXX_USE_SDLMIXER', self.user_settings.sdlmixer, '''
int i = Mix_Init(MIX_INIT_FLAC | MIX_INIT_OGG);
(void)i;
Mix_Pause(0);
Mix_ResumeMusic();
Mix_Quit();
''',
lib=mixer, successflags=successflags)
''')
def _check_SDL_addon_library(self,context,sdl2,library_format_name,macro_name,use_addon,main):
library_name = library_format_name % sdl2
self._define_macro(context, macro_name, int(use_addon))
context.Display('%s: checking whether to use %s...%s\n' % (self.msgprefix, library_name, 'yes' if use_addon else 'no'))
if not use_addon:
return
user_settings = self.user_settings
guess_flags = {
'LIBS' : [library_name] if sys.platform != 'darwin' else [],
}
successflags = self.pkgconfig.merge(context, self.msgprefix, user_settings, library_name, library_name, guess_flags)
if user_settings.host_platform == 'darwin':
successflags = successflags.copy()
successflags['FRAMEWORKS'] = [library_name]
relative_headers = 'Library/Frameworks/%s.framework/Headers' % library_name
successflags['CPPPATH'] = [h for h in (os.path.join(os.getenv("HOME"), relative_headers), '/%s' % relative_headers) if os.path.isdir(h)]
# SDL2 headers still use SDL_*.h for their filename, not
# SDL2_*.h, so expanded library_format_name with an explicitly
# blank insert, regardless of whether building for SDL1 or SDL2.
self._check_system_library(context, header=['%s.h' % (library_format_name % '')], main=main, lib=library_name, successflags=successflags)
@_custom_test
def check_compiler_missing_field_initializers(self,context,
@ -3728,6 +3756,11 @@ class DXXCommon(LazyObjectConstructor):
('opengles', self.default_opengles, 'build with OpenGL ES support'),
('editor', False, 'include editor into build (!EXPERIMENTAL!)'),
('sdl2', self.default_sdl2, 'use libSDL2+SDL2_mixer (!EXPERIMENTAL!)'),
# Build with SDL_Image support for PCX file support
# Currently undocumented because the user experience
# without PCX support is ugly, so this should always
# be left enabled.
('sdlimage', True, None),
('sdlmixer', True, 'build with SDL_Mixer support for sound and music (includes external music support)'),
('ipv6', False, 'enable UDP/IPv6 for multiplayer'),
('use_udp', True, 'enable UDP support'),
@ -4490,6 +4523,7 @@ class DXXArchive(DXXCommon):
'common/misc/hash.cpp',
'common/misc/hmp.cpp',
'common/misc/ignorecase.cpp',
'common/misc/physfsrwops.cpp',
'common/misc/strutil.cpp',
'common/misc/vgrphys.cpp',
'common/misc/vgwphys.cpp',
@ -5202,7 +5236,6 @@ class D2XProgram(DXXProgram):
'd2x-rebirth/main/escort.cpp',
'd2x-rebirth/main/gamepal.cpp',
'd2x-rebirth/main/movie.cpp',
'd2x-rebirth/misc/physfsrwops.cpp',
))):
value = __get_dxx_objects_common(self)
value.extend(__get_dsx_objects_common(self))

View File

@ -52,7 +52,6 @@ struct palette_array_t : public std::array<rgb_t, 256> {};
#endif
void copy_bound_palette(palette_array_t &d, const palette_array_t &s);
void copy_diminish_palette(palette_array_t &palette, const ubyte *p);
void diminish_palette(palette_array_t &palette);
extern void gr_palette_set_gamma( int gamma );
extern int gr_palette_get_gamma();
@ -66,6 +65,12 @@ extern ubyte gr_palette_gamma;
extern palette_array_t gr_current_pal;
}
#ifdef DXX_BUILD_DESCENT_I
namespace dsx {
void copy_diminish_palette(palette_array_t &palette, const uint8_t *p);
}
#endif
#endif
#endif

View File

@ -9,7 +9,7 @@ pkgdesc="An enhanced engine to play with Descent1 data."
arch=('x86_64')
url="https://www.dxx-rebirth.com/"
license=('custom:D1x' 'LGPL' 'custom:as-is')
depends=('sdl' 'sdl_mixer' 'mesa' 'physfs')
depends=('sdl' 'sdl_image' 'sdl_mixer' 'mesa' 'physfs')
makedepends=('scons' 'unzip')
install="$pkgname.install"
source=("https://www.dxx-rebirth.com/download/dxx/rebirth/dxx-rebirth_$pkgver-src.tar.xz"

View File

@ -9,7 +9,7 @@ pkgdesc="An enhanced engine to play with Descent2 data."
arch=('x86_64')
url="https://www.dxx-rebirth.com/"
license=('custom:D2x' 'LGPL' 'custom:as-is')
depends=('sdl' 'sdl_mixer' 'mesa' 'physfs')
depends=('sdl' 'sdl_image' 'sdl_mixer' 'mesa' 'physfs')
makedepends=('scons' 'unzip')
install="$pkgname.install"
source=("https://www.dxx-rebirth.com/download/dxx/rebirth/dxx-rebirth_$pkgver-src.tar.xz"

View File

@ -111,6 +111,7 @@ DXX_RDEPEND_ENGINE_FRAGMENT='
DXX_DEPEND_USE_SDL_VERSION_FRAGMENT='
media-libs/lib${SDL_version}[joystick?,opengl?,sound,video]
media-libs/sdl${SDL_version}-image
music? ( media-libs/${SDL_version}-mixer )
'
DXX_RDEPEND_USE_SDL_VERSION_FRAGMENT='

View File

@ -76,16 +76,6 @@ void copy_bound_palette(palette_array_t &d, const palette_array_t &s)
std::transform(s.begin(), s.end(), d.begin(), a);
}
void copy_diminish_palette(palette_array_t &palette, const ubyte *p)
{
range_for (auto &i, palette)
{
i.r = *p++ >> 2;
i.g = *p++ >> 2;
i.b = *p++ >> 2;
}
}
}
namespace dcx {
@ -124,6 +114,18 @@ int gr_palette_get_gamma()
namespace dsx {
#if defined(DXX_BUILD_DESCENT_I)
void copy_diminish_palette(palette_array_t &palette, const uint8_t *p)
{
range_for (auto &i, palette)
{
i.r = *p++ >> 2;
i.g = *p++ >> 2;
i.b = *p++ >> 2;
}
}
#endif
#if defined(DXX_BUILD_DESCENT_II)
void gr_copy_palette(palette_array_t &gr_palette, const palette_array_t &pal)
{

View File

@ -36,12 +36,41 @@ COPYRIGHT 1993-1998 PARALLAX SOFTWARE CORPORATION. ALL RIGHTS RESERVED.
#include "dxxsconf.h"
#include "dsx-ns.h"
#include "compiler-lengthof.h"
#include "compiler-poison.h"
#include "compiler-range_for.h"
#include "d_range.h"
#include "partial_range.h"
#include "console.h"
#if DXX_USE_SDLIMAGE
#include "physfsrwops.h"
#include <SDL_image.h>
#endif
namespace dcx {
namespace {
#if DXX_USE_SDLIMAGE
struct RAII_SDL_Surface
{
struct deleter
{
void operator()(SDL_Surface *s) const
{
SDL_FreeSurface(s);
}
};
std::unique_ptr<SDL_Surface, deleter> surface;
explicit RAII_SDL_Surface(SDL_Surface *const s) :
surface(s)
{
}
};
#endif
}
#if !DXX_USE_OGL && DXX_USE_SCREENSHOT_FORMAT_LEGACY
static int pcx_encode_byte(ubyte byt, ubyte cnt, PHYSFS_File *fid);
static int pcx_encode_line(const uint8_t *inBuff, uint_fast32_t inLen, PHYSFS_File *fp);
@ -69,32 +98,68 @@ struct PCXHeader
#define PCXHEADER_SIZE 128
/*
* reads n PCXHeader structs from a PHYSFS_File
*/
static int PCXHeader_read_n(PCXHeader *ph, int n, PHYSFS_File *fp)
#if DXX_USE_SDLIMAGE
static pcx_result pcx_read_bitmap(const char *const filename, grs_main_bitmap &bmp, palette_array_t &palette, RWops_ptr rw)
{
int i;
for (i = 0; i < n; i++) {
ph->Manufacturer = PHYSFSX_readByte(fp);
ph->Version = PHYSFSX_readByte(fp);
ph->Encoding = PHYSFSX_readByte(fp);
ph->BitsPerPixel = PHYSFSX_readByte(fp);
ph->Xmin = PHYSFSX_readShort(fp);
ph->Ymin = PHYSFSX_readShort(fp);
ph->Xmax = PHYSFSX_readShort(fp);
ph->Ymax = PHYSFSX_readShort(fp);
ph->Hdpi = PHYSFSX_readShort(fp);
ph->Vdpi = PHYSFSX_readShort(fp);
PHYSFS_read(fp, &ph->ColorMap, 16*3, 1);
ph->Reserved = PHYSFSX_readByte(fp);
ph->Nplanes = PHYSFSX_readByte(fp);
ph->BytesPerLine = PHYSFSX_readShort(fp);
PHYSFS_read(fp, &ph->filler, 60, 1);
RAII_SDL_Surface surface(IMG_LoadPCX_RW(rw.get()));
if (!surface.surface)
{
con_printf(CON_NORMAL, "%s:%u: failed to create surface from \"%s\"", __FILE__, __LINE__, filename);
return pcx_result::ERROR_OPENING;
}
return i;
const auto &s = *surface.surface.get();
const auto fmt = s.format;
if (!fmt || fmt->BitsPerPixel != 8)
return pcx_result::ERROR_WRONG_VERSION;
const auto fpal = fmt->palette;
if (!fpal || fpal->ncolors != palette.size())
return pcx_result::ERROR_NO_PALETTE;
const unsigned xsize = s.w;
const unsigned ysize = s.h;
if (xsize > 3840)
return pcx_result::ERROR_MEMORY;
if (ysize > 2400)
return pcx_result::ERROR_MEMORY;
DXX_CHECK_MEM_IS_DEFINED(s.pixels, xsize * ysize);
gr_init_bitmap_alloc(bmp, bm_mode::linear, 0, 0, xsize, ysize, xsize);
std::copy_n(reinterpret_cast<const uint8_t *>(s.pixels), xsize * ysize, &bmp.get_bitmap_data()[0]);
{
const auto a = [](const SDL_Color &c) {
return rgb_t{
static_cast<uint8_t>(c.r >> 2),
static_cast<uint8_t>(c.g >> 2),
static_cast<uint8_t>(c.b >> 2)
};
};
std::transform(fpal->colors, fpal->colors + palette.size(), palette.begin(), a);
}
return pcx_result::SUCCESS;
}
#else
static pcx_result pcx_read_blank(const char *const filename, grs_main_bitmap &bmp, palette_array_t &palette)
{
con_printf(CON_NORMAL, "%s:%u: PCX support disabled at compile time; cannot read file \"%s\"", __FILE__, __LINE__, filename);
constexpr unsigned xsize = 640;
constexpr unsigned ysize = 480;
gr_init_bitmap_alloc(bmp, bm_mode::linear, 0, 0, xsize, ysize, xsize);
auto &bitmap_data = *reinterpret_cast<uint8_t (*)[ysize][xsize]>(bmp.get_bitmap_data());
constexpr uint8_t border = 1;
constexpr uint8_t body = 0;
std::fill(&bitmap_data[0][0], &bitmap_data[2][0], border);
std::fill(&bitmap_data[2][0], &bitmap_data[ysize - 2][0], body);
std::fill(&bitmap_data[ysize - 2][0], &bitmap_data[ysize][0], border);
for (auto &&y : xrange(2u, ysize - 2))
{
bitmap_data[y][0] = border;
bitmap_data[y][1] = border;
bitmap_data[y][xsize - 2] = border;
bitmap_data[y][xsize - 1] = border;
}
palette = {};
palette[border] = {63, 63, 63};
return pcx_result::SUCCESS;
}
#endif
}
@ -192,92 +257,19 @@ pcx_result bald_guy_load(const char *const filename, grs_bitmap *const bmp, pale
namespace dcx {
struct PCX_PHYSFS_file
{
RAIIPHYSFS_File PCXfile;
};
static pcx_result pcx_read_bitmap_file(struct PCX_PHYSFS_file *const pcxphysfs, grs_main_bitmap &bmp, palette_array_t &palette);
pcx_result pcx_read_bitmap(const char *const filename, grs_main_bitmap &bmp, palette_array_t &palette)
{
PCX_PHYSFS_file pcxphysfs{PHYSFSX_openReadBuffered(filename)};
if (!pcxphysfs.PCXfile)
#if DXX_USE_SDLIMAGE
auto rw = PHYSFSRWOPS_openRead(filename);
if (!rw)
{
con_printf(CON_NORMAL, "%s:%u: failed to open \"%s\"", __FILE__, __LINE__, filename);
return pcx_result::ERROR_OPENING;
return pcx_read_bitmap_file(&pcxphysfs, bmp, palette);
}
static int PCX_PHYSFS_read(struct PCX_PHYSFS_file *pcxphysfs, ubyte *data, unsigned size)
{
return PHYSFS_read(pcxphysfs->PCXfile, data, size, sizeof(*data));
}
static pcx_result pcx_read_bitmap_file(struct PCX_PHYSFS_file *const pcxphysfs, grs_main_bitmap &bmp, palette_array_t &palette)
{
PCXHeader header;
// read 128 char PCX header
if (PCXHeader_read_n( &header, 1, pcxphysfs->PCXfile )!=1) {
return pcx_result::ERROR_NO_HEADER;
}
// Is it a 256 color PCX file?
if ((header.Manufacturer != 10)||(header.Encoding != 1)||(header.Nplanes != 1)||(header.BitsPerPixel != 8)||(header.Version != 5)) {
return pcx_result::ERROR_WRONG_VERSION;
}
// Find the size of the image
const unsigned xsize = header.Xmax - header.Xmin + 1;
if (xsize > 3840)
return pcx_result::ERROR_MEMORY;
const unsigned ysize = header.Ymax - header.Ymin + 1;
if (ysize > 2400)
return pcx_result::ERROR_MEMORY;
gr_init_bitmap_alloc(bmp, bm_mode::linear, 0, 0, xsize, ysize, xsize);
{
range_for (const unsigned row, xrange(ysize))
{
auto pixdata = &bmp.get_bitmap_data()[bmp.bm_rowsize*row];
for (unsigned col = 0; col < xsize;)
{
uint8_t data;
if (PCX_PHYSFS_read(pcxphysfs, &data, 1) != 1) {
return pcx_result::ERROR_READING;
}
if ((data & 0xC0) == 0xC0) {
const unsigned count = std::min(data & 0x3Fu, xsize - col);
if (PCX_PHYSFS_read(pcxphysfs, &data, 1) != 1) {
return pcx_result::ERROR_READING;
}
memset( pixdata, data, count );
pixdata += count;
col += count;
} else {
*pixdata++ = data;
col++;
}
}
}
}
// Read the extended palette at the end of PCX file
// Read in a character which should be 12 to be extended palette file
{
uint8_t data;
if (PCX_PHYSFS_read(pcxphysfs, &data, 1) == 1) {
if ( data == 12 ) {
if (PCX_PHYSFS_read(pcxphysfs, reinterpret_cast<ubyte *>(&palette[0]), palette.size() * sizeof(palette[0])) != 1) {
return pcx_result::ERROR_READING;
}
diminish_palette(palette);
}
} else {
return pcx_result::ERROR_NO_PALETTE;
}
}
return pcx_result::SUCCESS;
return pcx_read_bitmap(filename, bmp, palette, std::move(rw));
#else
return pcx_read_blank(filename, bmp, palette);
#endif
}
#if !DXX_USE_OGL && DXX_USE_SCREENSHOT_FORMAT_LEGACY

View File

@ -17,6 +17,11 @@
#include "text.h"
#include "args.h"
#include "window.h"
#include "dxxsconf.h"
#if DXX_USE_SDLIMAGE
#include <SDL_image.h>
#endif
namespace dsx {
@ -36,6 +41,9 @@ static void arch_close(void)
{
digi_close();
}
#if DXX_USE_SDLIMAGE
IMG_Quit();
#endif
SDL_Quit();
}
@ -50,6 +58,9 @@ arch_atexit arch_init()
if (SDL_Init(SDL_INIT_VIDEO) < 0)
Error("SDL library initialisation failed: %s.",SDL_GetError());
#if DXX_USE_SDLIMAGE
IMG_Init(0);
#endif
#if SDL_MAJOR_VERSION == 2
/* In SDL1, grabbing input grabbed both the keyboard and the mouse.
* Many game management keys assume a keyboard grab.

View File

@ -111,6 +111,9 @@ char copyright[] = "DESCENT II COPYRIGHT (C) 1994-1996 PARALLAX SOFTWARE CORPOR
#include "dsx-ns.h"
#include "compiler-begin.h"
#if DXX_USE_SDLIMAGE
#include <SDL_image.h>
#endif
#if DXX_USE_SDLMIXER
#include <SDL_mixer.h>
#endif
@ -547,6 +550,14 @@ static int main(int argc, char *argv[])
#endif
con_printf(CON_VERBOSE, "D" DXX_NAME_NUMBER "X-Rebirth built with libSDL %u.%u.%u; loaded with libSDL %u.%u.%u", vc.major, vc.minor, vc.patch, vl->major, vl->minor, vl->patch);
}
#if DXX_USE_SDLIMAGE
{
SDL_version vc;
SDL_IMAGE_VERSION(&vc);
const auto vl = IMG_Linked_Version();
con_printf(CON_VERBOSE, "D" DXX_NAME_NUMBER "X-Rebirth built with SDL_image %u.%u.%u; loaded with SDL_image %u.%u.%u", vc.major, vc.minor, vc.patch, vl->major, vl->minor, vl->patch);
}
#endif
#if DXX_USE_SDLMIXER
{
SDL_version vc;