Initial commit

This commit is contained in:
2026-02-02 04:50:13 +01:00
commit 5b11698731
22592 changed files with 7677434 additions and 0 deletions

View File

@@ -0,0 +1,282 @@
/* ScummVM - Graphic Adventure Engine
*
* ScummVM is the legal property of its developers, whose names
* are too numerous to list here. Please refer to the COPYRIGHT
* file distributed with this source distribution.
*
* This program is free software: you can redistribute it and/or modify
* it under the terms of the GNU General Public License as published by
* the Free Software Foundation, either version 3 of the License, or
* (at your option) any later version.
*
* This program is distributed in the hope that it will be useful,
* but WITHOUT ANY WARRANTY; without even the implied warranty of
* MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
* GNU General Public License for more details.
*
* You should have received a copy of the GNU General Public License
* along with this program. If not, see <http://www.gnu.org/licenses/>.
*
*/
#include "ultima/ultima8/misc/debugger.h"
#include "ultima/ultima8/gfx/anim_dat.h"
#include "ultima/ultima8/world/actors/actor_anim.h"
#include "ultima/ultima8/world/actors/actor.h"
#include "ultima/ultima8/world/get_object.h"
#include "ultima/ultima8/ultima8.h"
namespace Ultima {
namespace Ultima8 {
AnimDat::AnimDat() {
}
AnimDat::~AnimDat() {
for (unsigned int i = 0; i < _anims.size(); i++)
delete _anims[i];
}
const ActorAnim *AnimDat::getAnim(uint32 shape) const {
if (shape >= _anims.size())
return nullptr;
return _anims[shape];
}
const AnimAction *AnimDat::getAnim(uint32 shape, uint32 action) const {
if (shape >= _anims.size())
return nullptr;
if (_anims[shape] == 0)
return nullptr;
return _anims[shape]->getAction(action);
}
uint32 AnimDat::getActionNumberForSequence(Animation::Sequence action, const Actor *actor) {
if (GAME_IS_U8) {
return static_cast<uint32>(action);
} else {
bool smallwpn = true;
bool altfire = false;
bool isavatar = (actor && actor->getShape() == 1);
if (isavatar && actor->getActiveWeapon()) {
const Item *wpn = getItem(actor->getActiveWeapon());
const ShapeInfo *shapeinfo = (wpn ? wpn->getShapeInfo() : nullptr);
const WeaponInfo *wpninfo = (shapeinfo ? shapeinfo->_weaponInfo : nullptr);
smallwpn = (wpninfo && wpninfo->_small);
altfire = (wpninfo && (wpninfo->_overlayShape == 0x36e || wpninfo->_overlayShape == 0x33b));
}
//
// For crusader the actions have different IDs. Rather than
// rewrite everything, we just translate them here for all the ones
// we want to use programmatically. There are more, but they are
// called from usecode so don't need translation.
//
// We also translate based on weapon. See the function at 1128:2104
//
// First, if the animation includes the Animation::crusaderAbsoluteAnimFlag
// bitmask then it's from the usecode - use directly and don't translate.
//
const uint32 action_int = static_cast<uint32>(action);
if (action_int & Animation::crusaderAbsoluteAnimFlag)
return action_int - Animation::crusaderAbsoluteAnimFlag;
switch (action) {
case Animation::stand:
return Animation::standCru;
case Animation::step:
return Animation::walkCru; // Same as walk in crusader.
case Animation::walk:
return Animation::walkCru;
case Animation::retreat:
return (smallwpn ? Animation::retreatSmallWeapon : Animation::retreatLargeWeapon);
case Animation::reloadSmallWeapon:
return (smallwpn ? Animation::reloadSmallWeapon : Animation::reloadLargeWeapon);
case Animation::run:
return Animation::runCru;
case Animation::combatRunSmallWeapon:
return (smallwpn ? Animation::combatRunSmallWeapon : Animation::combatRunLargeWeapon);
case Animation::combatStand:
return (smallwpn ? Animation::combatStandSmallWeapon : Animation::combatStandLargeWeapon);
case Animation::unreadyWeapon:
return (smallwpn ? Animation::unreadySmallWeapon : Animation::unreadyLargeWeapon);
case Animation::readyWeapon:
return (smallwpn ? Animation::readySmallWeapon : Animation::readyLargeWeapon);
case Animation::attack: {
if (smallwpn)
return Animation::fireSmallWeapon;
return (altfire ? Animation::brightFireLargeWpn : Animation::fireLargeWeapon);
}
// Note: 14, 17, 21, 22, 29 == nothing for avatar
case Animation::fallBackwards:
return Animation::fallBackwardsCru;
case Animation::die:
return Animation::fallBackwardsCru; // by default fall over backwards.
case Animation::advance:
return (smallwpn ? Animation::advanceSmallWeapon : Animation::advanceLargeWeapon);
// Kneel start/end never used in code - mover process uses correct ones.
/*case Animation::startKneeling:
return Animation::kneelStartCru;
case Animation::stopKneeling:
return Animation::kneelEndCru;*/
case Animation::kneel:
return (smallwpn ? Animation::kneelingWithSmallWeapon : Animation::kneelingWithLargeWeapon);
case Animation::kneelAndFire: {
if (smallwpn)
return Animation::kneelAndFireSmallWeapon;
return (altfire ? Animation::brightKneelAndFireLargeWeapon : Animation::kneelAndFireLargeWeapon);
}
case Animation::lookLeft:
return 0;
case Animation::lookRight:
return 0;
case Animation::jump:
return Animation::quickJumpCru;
//case Animation::startRunLargeWeapon:
// return (smallwpn ? Animation::startRunSmallWeapon : Animation::startRunLargeWeapon); // FIXME: overlaps with kneel
case Animation::stopRunningAndDrawSmallWeapon:
return (smallwpn ? Animation::stopRunningAndDrawSmallWeapon : Animation::stopRunningAndDrawLargeWeapon);
default:
return action_int;
}
}
}
void AnimDat::load(Common::SeekableReadStream *rs) {
AnimFrame f;
// CONSTANT !
_anims.resize(2048);
unsigned int actioncount = 64;
if (GAME_IS_CRUSADER) {
actioncount = 256;
}
for (unsigned int shape = 0; shape < _anims.size(); shape++) {
rs->seek(4 * shape);
uint32 offset = rs->readUint32LE();
if (offset == 0) {
_anims[shape] = nullptr;
continue;
}
ActorAnim *a = new ActorAnim();
// CONSTANT !
a->_actions.resize(actioncount);
for (unsigned int action = 0; action < actioncount; action++) {
rs->seek(offset + action * 4);
uint32 actionoffset = rs->readUint32LE();
if (actionoffset == 0) {
a->_actions[action] = 0;
continue;
}
a->_actions[action] = new AnimAction();
a->_actions[action]->_shapeNum = shape;
a->_actions[action]->_action = action;
rs->seek(actionoffset);
// byte 0: action size
uint32 actionsize = rs->readByte();
a->_actions[action]->_size = actionsize;
// byte 1: flags low byte
uint32 rawflags = rs->readByte();
// byte 2: frame repeat and rotated flag
byte repeatAndRotateFlag = rs->readByte();
a->_actions[action]->_frameRepeat = repeatAndRotateFlag & 0xf;
if (GAME_IS_U8 && (repeatAndRotateFlag & 0xf0)) {
// This should never happen..
error("Anim data: frame repeat byte should never be > 0xf");
} else if (GAME_IS_CRUSADER) {
// WORKAROUND: In Crusader, the process wait semantics changed so
// wait of 1 frame was the same as no wait. The frame repeat
// is implemented as a process wait, so this effectively reduced
// all frame repeats by 1.
if (a->_actions[action]->_frameRepeat)
a->_actions[action]->_frameRepeat--;
}
// byte 3: flags high byte
rawflags |= rs->readByte() << 8;
// Only one flag in this byte in crusader.. the "rotate" flag.
rawflags |= (repeatAndRotateFlag & 0xf0) << 12;
a->_actions[action]->_flags = AnimAction::loadAnimActionFlags(rawflags);
unsigned int dirCount = 8;
if (a->_actions[action]->hasFlags(AnimAction::AAF_16DIRS)) {
dirCount = 16;
}
/*
if (a->_actions[action]->_flags & AnimAction::AAF_UNKFLAGS) {
warning("AnimFlags: shape %d action %d has unknown flags %04X", shape, action,
a->_actions[action]->_flags & AnimAction::AAF_UNKFLAGS);
}
*/
a->_actions[action]->_dirCount = dirCount;
for (unsigned int dir = 0; dir < dirCount; dir++) {
a->_actions[action]->_frames[dir].clear();
for (unsigned int j = 0; j < actionsize; j++) {
if (GAME_IS_U8) {
f._frame = rs->readByte(); // & 0x7FF;
uint8 x = rs->readByte();
f._frame += (x & 0x7) << 8;
f._deltaZ = rs->readSByte();
f._sfx = rs->readByte();
f._deltaDir = rs->readSByte();
f._flags = rs->readByte();
f._flags += (x & 0xF8) << 8;
} else if (GAME_IS_CRUSADER) {
// byte 0: low byte of frame
f._frame = rs->readByte();
// byte 1: low nibble is high part of frame, high nibble is flags (used later)
const uint8 x = rs->readByte();
f._frame += (x & 0xF) << 8;
// byte 2: delta z
f._deltaZ = rs->readSByte();
// byte 3: sfx
f._sfx = rs->readByte();
// byte 4: deltadir (signed) - convert to pixels
f._deltaDir = rs->readSByte();
// byte 5: flags. The lowest bit is actually a sign bit for
// deltaDir, which is technically 9 bits long. There are no
// animations in the game exceeding 8-bit signed, the 9th bit
// is there so that multiple frames can be stacked in the same
// struct by the original game when it's skipping frames.
// We have more memory and don't frame-skip, so just ignore it.
f._flags = rs->readByte() & 0xFE;
f._flags += (x & 0xF0) << 8;
// bytes 6, 7: more flags
f._flags += rs->readUint16LE() << 16;
/*if (f._flags & AnimFrame::AFF_UNKNOWN) {
warning("AnimFlags: shape %d action %d dir %d frame %d has unknown flags %08X", shape, action, dir, j,
f._flags & AnimFrame::AFF_UNKNOWN);
}*/
}
a->_actions[action]->_frames[dir].push_back(f);
}
}
}
_anims[shape] = a;
}
}
} // End of namespace Ultima8
} // End of namespace Ultima

View File

@@ -0,0 +1,54 @@
/* ScummVM - Graphic Adventure Engine
*
* ScummVM is the legal property of its developers, whose names
* are too numerous to list here. Please refer to the COPYRIGHT
* file distributed with this source distribution.
*
* This program is free software: you can redistribute it and/or modify
* it under the terms of the GNU General Public License as published by
* the Free Software Foundation, either version 3 of the License, or
* (at your option) any later version.
*
* This program is distributed in the hope that it will be useful,
* but WITHOUT ANY WARRANTY; without even the implied warranty of
* MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
* GNU General Public License for more details.
*
* You should have received a copy of the GNU General Public License
* along with this program. If not, see <http://www.gnu.org/licenses/>.
*
*/
#ifndef ULTIMA8_GFX_ANIMDAT_H
#define ULTIMA8_GFX_ANIMDAT_H
#include "ultima/shared/std/containers.h"
#include "ultima/ultima8/world/actors/animation.h"
namespace Ultima {
namespace Ultima8 {
class AnimAction;
class ActorAnim;
class Actor;
class AnimDat {
public:
AnimDat();
~AnimDat();
void load(Common::SeekableReadStream *rs);
const ActorAnim *getAnim(uint32 shape) const;
const AnimAction *getAnim(uint32 shape, uint32 action) const;
//! Return the action number for a given animation sequence on the given actor
static uint32 getActionNumberForSequence(Animation::Sequence action, const Actor *actor);
private:
Std::vector<ActorAnim *> _anims;
};
} // End of namespace Ultima8
} // End of namespace Ultima
#endif

View File

@@ -0,0 +1,142 @@
/* ScummVM - Graphic Adventure Engine
*
* ScummVM is the legal property of its developers, whose names
* are too numerous to list here. Please refer to the COPYRIGHT
* file distributed with this source distribution.
*
* This program is free software: you can redistribute it and/or modify
* it under the terms of the GNU General Public License as published by
* the Free Software Foundation, either version 3 of the License, or
* (at your option) any later version.
*
* This program is distributed in the hope that it will be useful,
* but WITHOUT ANY WARRANTY; without even the implied warranty of
* MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
* GNU General Public License for more details.
*
* You should have received a copy of the GNU General Public License
* along with this program. If not, see <http://www.gnu.org/licenses/>.
*
*/
#include "ultima/ultima8/audio/music_process.h"
#include "ultima/ultima8/gfx/avi_player.h"
#include "ultima/ultima8/gfx/render_surface.h"
#include "ultima/ultima8/gfx/texture.h"
#include "video/avi_decoder.h"
namespace Ultima {
namespace Ultima8 {
AVIPlayer::AVIPlayer(Common::SeekableReadStream *rs, int width, int height, const byte *overridePal, bool noScale)
: MoviePlayer(), _playing(false), _width(width), _height(height),
_doubleSize(false), _pausedMusic(false), _overridePal(overridePal) {
_decoder = new Video::AVIDecoder();
_decoder->loadStream(rs);
uint32 vidWidth = _decoder->getWidth();
uint32 vidHeight = _decoder->getHeight();
if (vidWidth <= _width / 2 && vidHeight <= _height / 2 && !noScale) {
_doubleSize = true;
vidHeight *= 2;
vidWidth *= 2;
}
_xoff = _width / 2 - (vidWidth / 2);
_yoff = _height / 2 - (vidHeight / 2);
_currentFrame.create(vidWidth, vidHeight, _decoder->getPixelFormat());
_currentFrame.fillRect(Common::Rect(0, 0, vidWidth, vidHeight),
_decoder->getPixelFormat().RGBToColor(0, 0, 0));
if (_currentFrame.format.bytesPerPixel == 1)
_currentFrame.setTransparentColor(0);
}
AVIPlayer::~AVIPlayer() {
delete _decoder;
}
void AVIPlayer::start() {
MusicProcess *music = MusicProcess::get_instance();
if (music && music->isPlaying()) {
music->pauseMusic();
_pausedMusic = true;
}
_playing = true;
_decoder->start();
}
void AVIPlayer::stop() {
MusicProcess *music = MusicProcess::get_instance();
if (music && _pausedMusic) {
music->unpauseMusic();
_pausedMusic = false;
}
_playing = false;
_decoder->stop();
}
void AVIPlayer::paint(RenderSurface *surf, int /*lerp*/) {
if (_decoder->endOfVideo()) {
_playing = false;
return;
}
if (_decoder->needsUpdate())
{
const Graphics::Surface *frame = _decoder->decodeNextFrame();
if (!frame || _decoder->getCurFrame() < 0) {
// Some sort of decoding error?
_playing = false;
return;
}
if (frame->format.bytesPerPixel == 1) {
const byte *pal;
if (_overridePal)
pal = _overridePal;
else
pal = _decoder->getPalette();
_currentFrame.setPalette(pal, 0, 256);
}
if (_doubleSize) {
// TODO: Add support for multiple bytes per pixel
assert(_currentFrame.w == frame->w * 2 && _currentFrame.h == frame->h * 2);
const int bpp = frame->format.bytesPerPixel;
for (int y = 0; y < frame->h; y++) {
const uint8 *srcPixel = static_cast<const uint8 *>(frame->getPixels()) + frame->pitch * y;
uint8 *dstPixels = static_cast<uint8 *>(_currentFrame.getPixels()) + _currentFrame.pitch * y * 2;
for (int x = 0; x < frame->w; x++) {
for (int i = 0; i < bpp; i++) {
dstPixels[x * 2 * bpp + i] = *srcPixel;
dstPixels[x * 2 * bpp + i + bpp] = *srcPixel;
srcPixel++;
}
}
}
} else {
_currentFrame.blitFrom(*frame);
}
}
uint32 color = TEX32_PACK_RGB(0, 0, 0);
surf->fill32(color, _xoff, _yoff, _currentFrame.w, _currentFrame.h);
Common::Rect srcRect(_currentFrame.w, _currentFrame.h);
surf->Blit(_currentFrame, srcRect, _xoff, _yoff);
}
void AVIPlayer::run() {
if (_decoder->endOfVideo()) {
_playing = false;
}
}
int AVIPlayer::getFrameNo() const {
return _decoder->getCurFrame();
}
void AVIPlayer::setOffset(int xoff, int yoff) {
_xoff = xoff;
_yoff = yoff;
}
} // End of namespace Ultima8
} // End of namespace Ultima

View File

@@ -0,0 +1,80 @@
/* ScummVM - Graphic Adventure Engine
*
* ScummVM is the legal property of its developers, whose names
* are too numerous to list here. Please refer to the COPYRIGHT
* file distributed with this source distribution.
*
* This program is free software: you can redistribute it and/or modify
* it under the terms of the GNU General Public License as published by
* the Free Software Foundation, either version 3 of the License, or
* (at your option) any later version.
*
* This program is distributed in the hope that it will be useful,
* but WITHOUT ANY WARRANTY; without even the implied warranty of
* MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
* GNU General Public License for more details.
*
* You should have received a copy of the GNU General Public License
* along with this program. If not, see <http://www.gnu.org/licenses/>.
*
*/
#ifndef ULTIMA8_GFX_AVIPLAYER_H
#define ULTIMA8_GFX_AVIPLAYER_H
#include "ultima/ultima8/gfx/movie_player.h"
#include "graphics/managed_surface.h"
namespace Video {
class AVIDecoder;
}
namespace Ultima {
namespace Ultima8 {
class AVIPlayer : public MoviePlayer {
public:
//!
//! Create a new player for the given stream at the given size. Playback is
//! automatically scaled up using the line-skip style from Crusader, unless
//! noScale flag is set.
//! If overridePal is set, use that as the palette instead of the AVI's one.
//!
AVIPlayer(Common::SeekableReadStream *rs, int width, int height, const byte *overridePal, bool noScale);
~AVIPlayer();
void run() override;
void paint(RenderSurface *surf, int lerp) override;
void start() override;
void stop() override;
bool isPlaying() const override {
return _playing;
}
int getFrameNo() const;
// Adjust the offsets by the given values
void setOffset(int xoff, int yoff) override;
private:
bool _playing;
Video::AVIDecoder *_decoder;
Graphics::ManagedSurface _currentFrame;
// Width and height of the area we've been given to play back in
uint32 _width;
uint32 _height;
// Xoff and Yoff into that playback area
uint32 _xoff;
uint32 _yoff;
bool _doubleSize;
const byte *_overridePal;
bool _pausedMusic;
};
} // End of namespace Ultima8
} // End of namespace Ultima
#endif

View File

@@ -0,0 +1,144 @@
/* ScummVM - Graphic Adventure Engine
*
* ScummVM is the legal property of its developers, whose names
* are too numerous to list here. Please refer to the COPYRIGHT
* file distributed with this source distribution.
*
* This program is free software: you can redistribute it and/or modify
* it under the terms of the GNU General Public License as published by
* the Free Software Foundation, either version 3 of the License, or
* (at your option) any later version.
*
* This program is distributed in the hope that it will be useful,
* but WITHOUT ANY WARRANTY; without even the implied warranty of
* MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
* GNU General Public License for more details.
*
* You should have received a copy of the GNU General Public License
* along with this program. If not, see <http://www.gnu.org/licenses/>.
*
*/
#include "ultima/ultima8/gfx/cycle_process.h"
#include "ultima/ultima8/gfx/palette.h"
#include "ultima/ultima8/ultima8.h"
namespace Ultima {
namespace Ultima8 {
CycleProcess *CycleProcess::_instance = nullptr;
// Which color to cycle in each of the palette colors 8~14
static const bool CYCLE_COL_FLAGS[7][3] = {
{ 1, 0, 0 },
{ 0, 0, 1 },
{ 1, 0, 0 },
{ 0, 0, 1 },
{ 1, 1, 0 },
{ 1, 1, 1 },
{ 0, 1, 0 }
};
static const uint8 CYCLE_INIT_COLS[7][3] = {{ 0, 0, 0 }, { 0, 0, 0 }, { 124, 0, 0 }, { 0, 0, 124 }, { 0, 0, 0 }, { 0, 0, 0 }, { 0, 0, 0 }};
static const bool CYCLE_RANDOMIZE[7] = {
false, false, false, false, false, false, true
};
// p_dynamic_class stuff
DEFINE_RUNTIME_CLASSTYPE_CODE(CycleProcess)
static inline void copyColor(uint8 *dst, const uint8 *src) {
dst[0] = src[0];
dst[1] = src[1];
dst[2] = src[2];
}
CycleProcess::CycleProcess() : Process(), _running(1) {
_instance = this;
_ticksPerRun = 2;
_type = 1; // persistent
for (int i = 0; i < 7; i++) {
copyColor(_cycleColData[i], CYCLE_INIT_COLS[i]);
}
}
CycleProcess::~CycleProcess(void) {
if (_instance == this)
_instance = nullptr;
}
CycleProcess *CycleProcess::get_instance() {
return _instance;
}
static bool cycleColor(uint8 *col, const bool flags[3]) {
bool wrapped = false;
for (int i = 0; i < 3; i++) {
if (flags[i]) {
col[i] += 8;
}
if (col[i] > 252) {
col[i] = 0;
wrapped = true;
}
}
return wrapped;
}
void CycleProcess::run() {
if (!_running)
return;
PaletteManager *pm = PaletteManager::get_instance();
Palette *pal = pm->getPalette(PaletteManager::Pal_Game);
// Step 1: Rotate 7 colors (1~7)
byte r1, g1, b1;
// tmp copy of color 1
pal->get(1, r1, g1, b1);
for (int i = 1; i < 7; i++) {
byte r, g, b;
pal->get(i + 1, r, g, b);
pal->set(i, r, g, b);
}
// move color 1 -> color 7
pal->set(7, r1, g1, b1);
Common::RandomSource &rs = Ultima8Engine::get_instance()->getRandomSource();
// Step 2: Cycle 7 other colors 8~14 by increasing their value
// until they hit max, then reset.
for (int i = 0; i < 7; i++) {
bool wrapped = cycleColor(_cycleColData[i], CYCLE_COL_FLAGS[i]);
if (CYCLE_RANDOMIZE[i] && wrapped) {
_cycleColData[i][0] += rs.getRandomNumber(9);
_cycleColData[i][1] += rs.getRandomNumber(9);
_cycleColData[i][2] += rs.getRandomNumber(9);
}
pal->set(i + 8, _cycleColData[i][0], _cycleColData[i][1], _cycleColData[i][2]);
}
// Update the cached palette.
pm->updatedPalette(PaletteManager::Pal_Game, 16);
}
void CycleProcess::saveData(Common::WriteStream *ws) {
Process::saveData(ws);
ws->writeByte(_running);
}
bool CycleProcess::loadData(Common::ReadStream *rs, uint32 version) {
if (!Process::loadData(rs, version)) return false;
_running = rs->readByte();
_instance = this; //static
_type = 1; // should be persistent but older savegames may not know that.
return true;
}
} // End of namespace Ultima8
} // End of namespace Ultima

View File

@@ -0,0 +1,63 @@
/* ScummVM - Graphic Adventure Engine
*
* ScummVM is the legal property of its developers, whose names
* are too numerous to list here. Please refer to the COPYRIGHT
* file distributed with this source distribution.
*
* This program is free software: you can redistribute it and/or modify
* it under the terms of the GNU General Public License as published by
* the Free Software Foundation, either version 3 of the License, or
* (at your option) any later version.
*
* This program is distributed in the hope that it will be useful,
* but WITHOUT ANY WARRANTY; without even the implied warranty of
* MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
* GNU General Public License for more details.
*
* You should have received a copy of the GNU General Public License
* along with this program. If not, see <http://www.gnu.org/licenses/>.
*
*/
#ifndef ULTIMA8_GFX_CYCLEPROCESS_H
#define ULTIMA8_GFX_CYCLEPROCESS_H
#include "ultima/ultima8/kernel/process.h"
#include "ultima/ultima8/gfx/palette_manager.h"
#include "ultima/ultima8/misc/classtype.h"
namespace Ultima {
namespace Ultima8 {
/** The process to cycle some palette colors in Crusader */
class CycleProcess : public Process {
uint8 _running;
uint8 _cycleColData[7][3];
public:
static CycleProcess *_instance;
// p_dynamic_class stuff
ENABLE_RUNTIME_CLASSTYPE()
CycleProcess();
~CycleProcess() override;
void run() override;
static CycleProcess *get_instance();
void pauseCycle() {
_running = 0;
}
void resumeCycle() {
_running = 1;
}
bool loadData(Common::ReadStream *rs, uint32 version);
void saveData(Common::WriteStream *ws) override;
};
} // End of namespace Ultima8
} // End of namespace Ultima
#endif

View File

@@ -0,0 +1,98 @@
/* ScummVM - Graphic Adventure Engine
*
* ScummVM is the legal property of its developers, whose names
* are too numerous to list here. Please refer to the COPYRIGHT
* file distributed with this source distribution.
*
* This program is free software: you can redistribute it and/or modify
* it under the terms of the GNU General Public License as published by
* the Free Software Foundation, either version 3 of the License, or
* (at your option) any later version.
*
* This program is distributed in the hope that it will be useful,
* but WITHOUT ANY WARRANTY; without even the implied warranty of
* MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
* GNU General Public License for more details.
*
* You should have received a copy of the GNU General Public License
* along with this program. If not, see <http://www.gnu.org/licenses/>.
*
*/
#include "ultima/ultima8/gfx/palette_fader_process.h"
#include "ultima/ultima8/gfx/fade_to_modal_process.h"
#include "ultima/ultima8/gfx/texture.h"
#include "ultima/ultima8/kernel/kernel.h"
#include "ultima/ultima8/gumps/gump_notify_process.h"
namespace Ultima {
namespace Ultima8 {
// p_dynamic_class stuff
DEFINE_RUNTIME_CLASSTYPE_CODE(FadeToModalProcess)
FadeToModalProcess::FadeToModalProcess(ModalGump *modal)
: _modal(modal), _nextState(FS_OpenFadeOut), _fader(nullptr)
{
setRunPaused();
}
FadeToModalProcess::~FadeToModalProcess(void) {
}
void FadeToModalProcess::onWakeUp() {
if (_nextState == FS_CloseFadeIn) {
// Jump in now and make sure the fade in is started (ie, we go to black)
// before the modal is closed, otherwise a single frame of the thing
// behind it will be shown first.
_fader = new PaletteFaderProcess(TEX32_PACK_RGBA(0x00, 0x00, 0x00, 0x00), true, 0x7FFF, 30, false);
_fader->run();
}
}
void FadeToModalProcess::run() {
switch (_nextState) {
case FS_OpenFadeOut:
{
_fader = new PaletteFaderProcess(TEX32_PACK_RGBA(0x00, 0x00, 0x00, 0x00), false, 0x7FFF, 30, true);
Kernel::get_instance()->addProcess(_fader);
_fader->setRunPaused();
_nextState = FS_ShowGump;
waitFor(_fader);
break;
}
case FS_ShowGump:
{
// kernel will delete the fader object for us
_fader = nullptr;
_modal->InitGump(0);
_modal->setRelativePosition(Gump::CENTER);
_modal->CreateNotifier();
// Reset the palette before showing the modal
PaletteManager::get_instance()->untransformPalette(PaletteManager::Pal_Game);
_nextState = FS_CloseFadeIn;
waitFor(_modal->GetNotifyProcess());
break;
}
case FS_CloseFadeIn:
{
// Already created a new fader in onWakeUp..
Kernel::get_instance()->addProcess(_fader);
_fader->setRunPaused();
_nextState = FS_Finshed;
waitFor(_fader);
break;
}
case FS_Finshed:
{
// kernel will delete the fader object for us
_fader = nullptr;
terminate();
break;
}
}
}
} // End of namespace Ultima8
} // End of namespace Ultima

View File

@@ -0,0 +1,62 @@
/* ScummVM - Graphic Adventure Engine
*
* ScummVM is the legal property of its developers, whose names
* are too numerous to list here. Please refer to the COPYRIGHT
* file distributed with this source distribution.
*
* This program is free software: you can redistribute it and/or modify
* it under the terms of the GNU General Public License as published by
* the Free Software Foundation, either version 3 of the License, or
* (at your option) any later version.
*
* This program is distributed in the hope that it will be useful,
* but WITHOUT ANY WARRANTY; without even the implied warranty of
* MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
* GNU General Public License for more details.
*
* You should have received a copy of the GNU General Public License
* along with this program. If not, see <http://www.gnu.org/licenses/>.
*
*/
#ifndef ULTIMA8_GFX_FADETOMODALPROCESS_H
#define ULTIMA8_GFX_FADETOMODALPROCESS_H
#include "ultima/ultima8/kernel/process.h"
#include "ultima/ultima8/gumps/modal_gump.h"
#include "ultima/ultima8/misc/classtype.h"
namespace Ultima {
namespace Ultima8 {
class PaletteFaderProcess;
/** A process to fade out the screen and open a modal */
class FadeToModalProcess : public Process {
enum FadeToModalState {
FS_OpenFadeOut,
FS_ShowGump,
FS_CloseFadeIn,
FS_Finshed
} _nextState;
ModalGump * _modal;
PaletteFaderProcess * _fader;
public:
// p_dynamic_class stuff
ENABLE_RUNTIME_CLASSTYPE()
FadeToModalProcess(ModalGump *modal);
~FadeToModalProcess(void) override;
void onWakeUp() override;
void run() override;
};
} // End of namespace Ultima8
} // End of namespace Ultima
#endif

View File

@@ -0,0 +1,383 @@
/* ScummVM - Graphic Adventure Engine
*
* ScummVM is the legal property of its developers, whose names
* are too numerous to list here. Please refer to the COPYRIGHT
* file distributed with this source distribution.
*
* This program is free software: you can redistribute it and/or modify
* it under the terms of the GNU General Public License as published by
* the Free Software Foundation, either version 3 of the License, or
* (at your option) any later version.
*
* This program is distributed in the hope that it will be useful,
* but WITHOUT ANY WARRANTY; without even the implied warranty of
* MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
* GNU General Public License for more details.
*
* You should have received a copy of the GNU General Public License
* along with this program. If not, see <http://www.gnu.org/licenses/>.
*
*/
#include "ultima/ultima.h"
#include "ultima/ultima8/misc/debugger.h"
#include "ultima/ultima8/misc/common_types.h"
#include "ultima/ultima8/gfx/fonts/font.h"
namespace Ultima {
namespace Ultima8 {
Font::Font() : _highRes(false) {
}
Font::~Font() {
}
void Font::getTextSize(const Std::string &text,
int32 &resultwidth, int32 &resultheight,
unsigned int &remaining,
int32 width, int32 height, TextAlign align,
bool u8specials, bool pagebreaks) {
Std::list<PositionedText> tmp;
tmp = typesetText<Traits>(this, text, remaining,
width, height, align, u8specials, pagebreaks,
resultwidth, resultheight);
}
//static
bool Font::Traits::canBreakAfter(Std::string::const_iterator &i) {
// It's not really relevant what we do here, because this probably will
// not be used at normal font sizes.
return true;
}
//static
bool Font::SJISTraits::canBreakAfter(Std::string::const_iterator &i) {
Std::string::const_iterator j = i;
uint32 u1 = unicode(j);
// See: https://wiki.wesnoth.org/index.php?title=JapaneseTranslation&oldid=23480#Word-Wrapping
// and: https://ja.wikipedia.org/wiki/%E7%A6%81%E5%89%87
switch (u1) {
case 0xff08:
case 0x3014:
case 0xff3b:
case 0xff5b:
case 0x3008:
case 0x300a:
case 0x300c:
case 0x300e:
case 0x3010:
case 0x2018:
case 0x201c:
return false;
default:
break;
}
uint32 u2 = unicode(j);
switch (u2) {
case 0x3001:
case 0x3002:
case 0xff0c:
case 0xff0e:
case 0xff09:
case 0x3015:
case 0xff3d:
case 0xff5d:
case 0x3009:
case 0x300b:
case 0x300d:
case 0x300f:
case 0x3011:
case 0x2019:
case 0x201d:
case 0x309d:
case 0x309e:
case 0x30fd:
case 0x30fe:
case 0x3005:
case 0xff1f:
case 0xff01:
case 0xff1a:
case 0xff1b:
case 0x3041:
case 0x3043:
case 0x3045:
case 0x3047:
case 0x3049:
case 0x3083:
case 0x3085:
case 0x3087:
case 0x308e:
case 0x30a1:
case 0x30a3:
case 0x30a5:
case 0x30a7:
case 0x30a9:
case 0x30e3:
case 0x30e5:
case 0x30e7:
case 0x30ee:
case 0x3063:
case 0x30f5:
case 0x30c3:
case 0x30f6:
case 0x30fb:
case 0x2026:
case 0x30fc:
return false;
default:
break;
}
// Also don't allow breaking between roman characters
if (((u1 >= 'A' && u1 <= 'Z') || (u1 >= 'a' && u1 <= 'z')) &&
((u2 >= 'A' && u2 <= 'Z') || (u2 >= 'a' && u2 <= 'z'))) {
return false;
}
return true;
}
template<class T>
static void findWordEnd(const Std::string &text,
Std::string::const_iterator &iter, bool u8specials) {
while (iter != text.end()) {
if (T::isSpace(iter, u8specials)) return;
T::advance(iter);
}
}
template<class T>
static void passSpace(const Std::string &text,
Std::string::const_iterator &iter, bool u8specials) {
while (iter != text.end()) {
if (!T::isSpace(iter, u8specials)) return;
T::advance(iter);
}
return;
}
/*
Special characters in U8:
@ = bullet for conversation options
~ = line break
% = tab
* = line break on graves and plaques, possibly page break in books
CHECKME: any others? (page breaks for books?)
*/
template<class T>
Std::list<PositionedText> typesetText(Font *font,
const Std::string &text, unsigned int &remaining, int32 width, int32 height,
Font::TextAlign align, bool u8specials, bool pagebreaks, int32 &resultwidth,
int32 &resultheight, Std::string::size_type cursor) {
debugC(kDebugGraphics, "typeset (%d, %d) %s", width, height, text.c_str());
// be optimistic and assume everything will fit
remaining = text.size();
Std::string curline;
int totalwidth = 0;
int totalheight = 0;
Std::list<PositionedText> lines;
PositionedText line;
Std::string::const_iterator iter = text.begin();
Std::string::const_iterator cursoriter = text.begin();
if (cursor != Std::string::npos) cursoriter += cursor;
Std::string::const_iterator curlinestart = text.begin();
bool breakhere = false;
while (true) {
if (iter == text.end() || breakhere || T::isBreak(iter, u8specials)) {
// break here
bool atpagebreak = pagebreaks && T::isPageBreak(iter, u8specials);
int32 stringwidth = 0, stringheight = 0;
font->getStringSize(curline, stringwidth, stringheight);
line._dims.left = 0;
line._dims.top = totalheight;
line._dims.setWidth(stringwidth);
line._dims.setHeight(stringheight);
line._text = curline;
line._cursor = Std::string::npos;
if (cursor != Std::string::npos && cursoriter >= curlinestart &&
(cursoriter < iter || (!breakhere && cursoriter == iter))) {
line._cursor = cursoriter - curlinestart;
if (line._dims.width() == 0) {
stringwidth = 2;
line._dims.setWidth(stringwidth);
}
}
lines.push_back(line);
if (stringwidth > totalwidth) totalwidth = stringwidth;
totalheight += font->getBaselineSkip();
curline = "";
if (iter == text.end())
break; // done
if (breakhere) {
breakhere = false;
curlinestart = iter;
} else {
T::advance(iter);
curlinestart = iter;
}
if (atpagebreak || (height != 0 && totalheight + font->getHeight() > height)) {
// next line won't fit
remaining = curlinestart - text.begin();
break;
}
} else {
// see if next word still fits on the current line
Std::string::const_iterator nextword = iter;
passSpace<T>(text, nextword, u8specials);
// process spaces
bool foundLF = false;
Std::string spaces;
for (; iter < nextword; T::advance(iter)) {
if (T::isBreak(iter, u8specials)) {
foundLF = true;
break;
} else if (T::isTab(iter, u8specials)) {
// ignore tabs at beginning of line when centered
if (!(curline.empty() && align == Font::TEXT_CENTER))
spaces.append(" ");
} else if (!curline.empty()) {
spaces.append(" ");
}
}
// no next word?
if (foundLF || nextword == text.end()) continue;
// process word
Std::string::const_iterator endofnextword = iter;
findWordEnd<T>(text, endofnextword, u8specials);
int32 stringwidth = 0, stringheight = 0;
Std::string newline = curline + spaces +
text.substr(nextword - text.begin(), endofnextword - nextword);
font->getStringSize(newline, stringwidth, stringheight);
// if not, break line before this word
if (width != 0 && stringwidth > width) {
if (!curline.empty()) {
iter = nextword;
} else {
// word is longer than the line; have to break in mid-word
// FIXME: this is rather inefficient; binary search?
// FIXME: clean up...
iter = nextword;
Std::string::const_iterator saveiter = nextword; // Dummy initialization
Std::string::const_iterator saveiter_fail;
Std::string curline_fail;
newline = spaces;
bool breakok = true;
int breakcount = -1;
do {
if (breakok) {
curline = newline;
saveiter = iter;
breakcount++;
}
curline_fail = newline;
saveiter_fail = iter;
if (iter == text.end()) break;
breakok = T::canBreakAfter(iter);
// try next character
T::advance(iter);
newline = spaces + text.substr(nextword - text.begin(),
iter - nextword);
font->getStringSize(newline, stringwidth, stringheight);
} while (stringwidth <= width);
if (breakcount > 0) {
iter = saveiter;
} else {
iter = saveiter_fail;
curline = curline_fail;
}
}
breakhere = true;
continue;
} else {
// copy next word into curline
curline = newline;
iter = endofnextword;
}
}
}
if (lines.size() == 1 && align == Font::TEXT_LEFT) {
// only one line, so use the actual text width
width = totalwidth;
}
if (width != 0) totalwidth = width;
// adjust total height
totalheight -= font->getBaselineSkip();
totalheight += font->getHeight();
// fixup x coordinates of lines
for (auto &l : lines) {
switch (align) {
case Font::TEXT_LEFT:
break;
case Font::TEXT_RIGHT:
l._dims.moveTo(totalwidth - l._dims.width(), l._dims.top);
break;
case Font::TEXT_CENTER:
l._dims.moveTo((totalwidth - l._dims.width()) / 2, l._dims.top);
break;
}
debugC(kDebugGraphics, "%d, %d, %d, %d: %s", l._dims.left, l._dims.top,
l._dims.width(), l._dims.height(), l._text.c_str());
}
resultwidth = totalwidth;
resultheight = totalheight;
return lines;
}
// explicit instantiations
template
Std::list<PositionedText> typesetText<Font::Traits>
(Font *font, const Std::string &text,
unsigned int &remaining, int32 width, int32 height,
Font::TextAlign align, bool u8specials, bool pagebreaks,
int32 &resultwidth, int32 &resultheight, Std::string::size_type cursor);
template
Std::list<PositionedText> typesetText<Font::SJISTraits>
(Font *font, const Std::string &text,
unsigned int &remaining, int32 width, int32 height,
Font::TextAlign align, bool u8specials, bool pagebreaks,
int32 &resultwidth, int32 &resultheight, Std::string::size_type cursor);
} // End of namespace Ultima8
} // End of namespace Ultima

View File

@@ -0,0 +1,180 @@
/* ScummVM - Graphic Adventure Engine
*
* ScummVM is the legal property of its developers, whose names
* are too numerous to list here. Please refer to the COPYRIGHT
* file distributed with this source distribution.
*
* This program is free software: you can redistribute it and/or modify
* it under the terms of the GNU General Public License as published by
* the Free Software Foundation, either version 3 of the License, or
* (at your option) any later version.
*
* This program is distributed in the hope that it will be useful,
* but WITHOUT ANY WARRANTY; without even the implied warranty of
* MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
* GNU General Public License for more details.
*
* You should have received a copy of the GNU General Public License
* along with this program. If not, see <http://www.gnu.org/licenses/>.
*
*/
#ifndef ULTIMA8_GFX_FONTS_FONT_H
#define ULTIMA8_GFX_FONTS_FONT_H
#include "common/rect.h"
#include "ultima/shared/std/containers.h"
#include "ultima/shared/std/string.h"
#include "ultima/ultima8/misc/encoding.h"
namespace Ultima {
namespace Ultima8 {
class RenderedText;
struct PositionedText {
Std::string _text;
Common::Rect32 _dims;
Std::string::size_type _cursor;
};
class Font {
public:
Font();
virtual ~Font();
enum TextAlign {
TEXT_LEFT,
TEXT_CENTER,
TEXT_RIGHT
};
//! get the height of the font
virtual int getHeight() = 0;
//! get the baseline of the font (relative from the top)
virtual int getBaseline() = 0;
//! get the baselineskip of the font (distance between two baselines)
virtual int getBaselineSkip() = 0;
//! get the dimensions of a string (not containing any newlines)
//! \param text The string
//! \param width Returns the width
//! \param height Returns the height
virtual void getStringSize(const Std::string &text,
int32 &width, int32 &height) = 0;
//! render a string
//! \param text The text
//! \param remaining Returns index of the first character not printed
//! \param width The width of the target rectangle, or 0 for unlimited
//! \param height The height of the target rectangle, or 0 for unlimited
//! \param align Alignment of the text (left, right, center)
//! \param u8specials If true, interpret the special characters U8 uses
//! \param pagebreaks If true (and u8specials too), stop at U8 pagebreaks
//! \return the rendered text in a RenderedText object
virtual RenderedText *renderText(const Std::string &text,
unsigned int &remaining, int32 width = 0, int32 height = 0,
TextAlign align = TEXT_LEFT, bool u8specials = false,
bool pagebreaks = false,
Std::string::size_type cursor = Std::string::npos) = 0;
//! get the dimensions of a rendered string
//! \param text The text
//! \param resultwidth Returns the resulting width
//! \param resultheight Returns the resulting height
//! \param remaining Returns index of the first character not printed
//! \param width The width of the target rectangle, or 0 for unlimited
//! \param height The height of the target rectangle, or 0 for unlimited
//! \param align Alignment of the text (left, right, center)
//! \param u8specials If true, interpret the special characters U8 uses
//! \param pagebreaks If true (and u8specials too), stop at U8 pagebreaks
virtual void getTextSize(const Std::string &text,
int32 &resultwidth, int32 &resultheight, unsigned int &remaining,
int32 width = 0, int32 height = 0, TextAlign align = TEXT_LEFT,
bool u8specials = false, bool pagebreaks = false);
void setHighRes(bool hr) {
_highRes = hr;
}
bool isHighRes() const {
return _highRes;
}
protected:
bool _highRes;
struct Traits {
static bool isSpace(Std::string::const_iterator &i, bool u8specials) {
char c = *i;
return (c == ' ' || c == '\t' || c == '\n' || c == '\r' ||
(u8specials && (c == '%' || c == '~' || c == '*' || c == '^')));
}
static bool isTab(Std::string::const_iterator &i, bool u8specials) {
char c = *i;
return (c == '\t' ||
(u8specials && (c == '\t' || c == '%')));
}
static bool isBreak(Std::string::const_iterator &i, bool u8specials) {
char c = *i;
return (c == '\n' ||
(u8specials && (c == '\n' || c == '~' || c == '*')));
}
static bool isPageBreak(Std::string::const_iterator &i, bool u8specials) {
char c = *i;
return (u8specials && c == '*');
}
static bool canBreakAfter(Std::string::const_iterator &i);
static void advance(Std::string::const_iterator &i) {
++i;
}
static Std::string::size_type length(const Std::string &t) {
return t.size();
}
static uint32 unicode(Std::string::const_iterator &i) {
return encoding[static_cast<uint8>(*i++)];
}
};
struct SJISTraits : public Traits {
static bool canBreakAfter(Std::string::const_iterator &i);
static void advance(Std::string::const_iterator &i) {
// FIXME: this can advance past the end of a malformed string
uint8 c = *i;
i++;
if (c >= 0x80) i++;
}
static Std::string::size_type length(const Std::string &t) {
Std::string::size_type l = 0;
Std::string::const_iterator iter = t.begin();
while (iter != t.end()) {
advance(iter);
l++;
}
return l;
}
static uint32 unicode(Std::string::const_iterator &i) {
uint16 s = static_cast<uint8>(*i);
i++;
if (s >= 0x80) {
uint16 t = static_cast<uint8>(*i++);
s |= (t << 8);
}
return shiftjis_to_unicode(s);
}
};
};
template<class T>
Std::list<PositionedText> typesetText(Font *font,
const Std::string &text, unsigned int &remaining,
int32 width, int32 height, Font::TextAlign align,
bool u8specials, bool pagebreaks,
int32 &resultwidth, int32 &resultheight,
Std::string::size_type cursor = Std::string::npos);
} // End of namespace Ultima8
} // End of namespace Ultima
#endif

View File

@@ -0,0 +1,212 @@
/* ScummVM - Graphic Adventure Engine
*
* ScummVM is the legal property of its developers, whose names
* are too numerous to list here. Please refer to the COPYRIGHT
* file distributed with this source distribution.
*
* This program is free software: you can redistribute it and/or modify
* it under the terms of the GNU General Public License as published by
* the Free Software Foundation, either version 3 of the License, or
* (at your option) any later version.
*
* This program is distributed in the hope that it will be useful,
* but WITHOUT ANY WARRANTY; without even the implied warranty of
* MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
* GNU General Public License for more details.
*
* You should have received a copy of the GNU General Public License
* along with this program. If not, see <http://www.gnu.org/licenses/>.
*
*/
#include "common/config-manager.h"
#include "common/file.h"
#include "ultima/ultima.h"
#include "ultima/ultima8/misc/common_types.h"
#include "ultima/ultima8/gfx/fonts/font_manager.h"
#include "ultima/ultima8/games/game_data.h"
#include "ultima/ultima8/gfx/fonts/shape_font.h"
#include "ultima/ultima8/gfx/fonts/font_shape_archive.h"
#include "ultima/ultima8/gfx/fonts/tt_font.h"
#include "ultima/ultima8/gfx/fonts/jp_font.h"
#include "ultima/ultima8/gfx/palette.h"
#include "ultima/ultima8/gfx/palette_manager.h"
#include "graphics/fonts/ttf.h"
namespace Ultima {
namespace Ultima8 {
FontManager *FontManager::_fontManager = nullptr;
FontManager::FontManager() {
debug(1, "Creating Font Manager...");
_fontManager = this;
ConfMan.registerDefault("font_highres", true);
}
FontManager::~FontManager() {
debug(1, "Destroying Font Manager...");
resetGameFonts();
assert(_fontManager == this);
_fontManager = nullptr;
}
// Reset the font manager
void FontManager::resetGameFonts() {
for (unsigned int i = 0; i < _overrides.size(); ++i)
delete _overrides[i];
_overrides.clear();
for (unsigned int i = 0; i < _ttFonts.size(); ++i)
delete _ttFonts[i];
_ttFonts.clear();
for (auto &i : _ttfFonts)
delete i._value;
_ttfFonts.clear();}
Font *FontManager::getGameFont(unsigned int fontnum,
bool allowOverride) {
if (allowOverride && fontnum < _overrides.size() && _overrides[fontnum])
return _overrides[fontnum];
return GameData::get_instance()->getFonts()->getFont(fontnum);
}
Font *FontManager::getTTFont(unsigned int fontnum) {
if (fontnum >= _ttFonts.size())
return nullptr;
return _ttFonts[fontnum];
}
Graphics::Font *FontManager::getTTF_Font(const Common::Path &filename, int pointsize, bool antialiasing) {
#ifdef USE_FREETYPE2
TTFId id;
id._filename = filename;
id._pointSize = pointsize;
TTFFonts::iterator iter;
iter = _ttfFonts.find(id);
if (iter != _ttfFonts.end())
return iter->_value;
Common::File* fontids = new Common::File();
if (!fontids->open(filename)) {
warning("Failed to open TTF: %s", filename.toString().c_str());
delete fontids;
return nullptr;
}
// open font using ScummVM TTF API
// Note: The RWops and ReadStream will be deleted by the TTF_Font
Graphics::TTFRenderMode mode = antialiasing ? Graphics::kTTFRenderModeNormal : Graphics::kTTFRenderModeMonochrome;
Graphics::Font *font = Graphics::loadTTFFont(fontids, DisposeAfterUse::YES, pointsize, Graphics::kTTFSizeModeCharacter, 0, 0, mode, 0, false);
if (!font) {
warning("Failed to open TTF: %s", filename.toString().c_str());
delete fontids;
return nullptr;
}
_ttfFonts[id] = font;
debugC(kDebugGraphics, "Opened TTF: %s.", filename.toString().c_str());
return font;
#else // !USE_FREETYPE2
return nullptr;
#endif
}
void FontManager::setOverride(unsigned int fontnum, Font *newFont) {
if (fontnum >= _overrides.size())
_overrides.resize(fontnum + 1);
if (_overrides[fontnum])
delete _overrides[fontnum];
_overrides[fontnum] = newFont;
}
bool FontManager::addTTFOverride(unsigned int fontnum, const Common::Path &filename,
int pointsize, uint32 rgb, int bordersize,
bool SJIS) {
bool antialiasing = ConfMan.getBool("font_antialiasing");
Graphics::Font *f = getTTF_Font(filename, pointsize, antialiasing);
if (!f)
return false;
TTFont *font = new TTFont(f, rgb, bordersize, antialiasing, SJIS);
bool highres = ConfMan.getBool("font_highres");
font->setHighRes(highres);
setOverride(fontnum, font);
debugC(kDebugGraphics, "Added TTF override for font %u", fontnum);
return true;
}
bool FontManager::addJPOverride(unsigned int fontnum,
unsigned int jpfont, uint32 rgb) {
ShapeFont *jf = dynamic_cast<ShapeFont *>(GameData::get_instance()->getFonts()->getFont(jpfont));
if (!jf)
return false;
JPFont *font = new JPFont(jf, fontnum);
setOverride(fontnum, font);
PaletteManager *palman = PaletteManager::get_instance();
PaletteManager::PalIndex fontpal = static_cast<PaletteManager::PalIndex>
(PaletteManager::Pal_JPFontStart + fontnum);
palman->duplicate(PaletteManager::Pal_Game, fontpal);
Palette *pal = palman->getPalette(fontpal);
// TODO: maybe a small gradient
// the main text uses index 3
// indices 1,2 and 3 are in use for the bullets for conversation options
for (int i = 1; i < 4; ++i) {
pal->set(i, (rgb >> 16) & 0xFF, (rgb >> 8) & 0xFF, (rgb) & 0xFF);
}
palman->updatedPalette(fontpal);
debugC(kDebugGraphics, "Added JP override for font %u", fontnum);
return true;
}
bool FontManager::loadTTFont(unsigned int fontnum, const Common::Path &filename,
int pointsize, uint32 rgb, int bordersize) {
bool antialiasing = ConfMan.getBool("font_antialiasing");
Graphics::Font *f = getTTF_Font(filename, pointsize, antialiasing);
if (!f)
return false;
TTFont *font = new TTFont(f, rgb, bordersize, antialiasing, false);
// TODO: check if this is indeed what we want for non-gamefonts
bool highres = ConfMan.getBool("font_highres");
font->setHighRes(highres);
if (fontnum >= _ttFonts.size())
_ttFonts.resize(fontnum + 1);
if (_ttFonts[fontnum])
delete _ttFonts[fontnum];
_ttFonts[fontnum] = font;
return true;
}
} // End of namespace Ultima8
} // End of namespace Ultima

View File

@@ -0,0 +1,126 @@
/* ScummVM - Graphic Adventure Engine
*
* ScummVM is the legal property of its developers, whose names
* are too numerous to list here. Please refer to the COPYRIGHT
* file distributed with this source distribution.
*
* This program is free software: you can redistribute it and/or modify
* it under the terms of the GNU General Public License as published by
* the Free Software Foundation, either version 3 of the License, or
* (at your option) any later version.
*
* This program is distributed in the hope that it will be useful,
* but WITHOUT ANY WARRANTY; without even the implied warranty of
* MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
* GNU General Public License for more details.
*
* You should have received a copy of the GNU General Public License
* along with this program. If not, see <http://www.gnu.org/licenses/>.
*
*/
#ifndef ULTIMA8_GFX_FONTS_FONTMANAGER_H
#define ULTIMA8_GFX_FONTS_FONTMANAGER_H
#include "ultima/shared/std/containers.h"
#include "ultima/shared/std/string.h"
#include "common/path.h"
#include "graphics/font.h"
namespace Ultima {
namespace Ultima8 {
class Font;
// This is TTF_Font struct
typedef struct _TTF_Font TTF_Font;
class TTFont;
class FontManager {
private:
struct TTFId {
Common::Path _filename;
int _pointSize;
bool operator<(const TTFId &other) const {
return (_pointSize < other._pointSize ||
(_pointSize == other._pointSize &&
_filename < other._filename));
}
};
struct TTFHash {
uint operator()(const TTFId &x) const {
// TODO: See if something better can be used as a hash key
int64 val = (int64)&x;
return (uint)val;
}
};
struct TTFEqual {
bool operator()(const TTFId &x, const TTFId &y) const {
return x._filename == y._filename && x._pointSize == y._pointSize;
}
};
typedef Common::HashMap<TTFId, Graphics::Font *, TTFHash, TTFEqual> TTFFonts;
TTFFonts _ttfFonts;
//! Get a (possibly cached) TTF_Font structure for filename/pointsize,
//! loading it if necessary.
Graphics::Font *getTTF_Font(const Common::Path &filename, int pointsize, bool antialiasing);
//! Override fontnum with specified font
void setOverride(unsigned int fontnum, Font *newFont);
Std::vector<Font *> _overrides;
Std::vector<Font *> _ttFonts;
static FontManager *_fontManager;
public:
FontManager();
~FontManager();
static FontManager *get_instance() {
return _fontManager;
}
//! get a Font by fontnum (for game fonts)
//! \param fontnum the number of the font
//! \param allowOverride if true, allow an override font to be used
Font *getGameFont(unsigned int fontnum,
bool allowOverride = false);
//! get a TTF font (for non-game fonts)
Font *getTTFont(unsigned int ttfnum);
//! override a game font with a TTF.
//! \param fontnum the font to override
//! \param filename the filename of the TTF
//! \param pointsize the pointsize to use
//! \param rgb the color to use for the font
//! \param bordersize the size of the black border to add
//! \param SJIS true for a Japanese game font
bool addTTFOverride(unsigned int fontnum, const Common::Path &filename,
int pointsize, uint32 rgb, int bordersize,
bool SJIS = false);
//! override a game font with a Japanese font.
//! \param fontnum the font to override
//! \param jpfont the fontnum of the Japanese font to use
//! \param rgb the color to use
bool addJPOverride(unsigned int fontnum, unsigned int jpfont, uint32 rgb);
//! load a TTF (for non-game fonts)
bool loadTTFont(unsigned int ttfnum, const Common::Path &filename,
int pointsize, uint32 rgb, int bordersize);
// Reset the game fonts
void resetGameFonts();
};
} // End of namespace Ultima8
} // End of namespace Ultima // End of namespace Ultima8
#endif

View File

@@ -0,0 +1,90 @@
/* ScummVM - Graphic Adventure Engine
*
* ScummVM is the legal property of its developers, whose names
* are too numerous to list here. Please refer to the COPYRIGHT
* file distributed with this source distribution.
*
* This program is free software: you can redistribute it and/or modify
* it under the terms of the GNU General Public License as published by
* the Free Software Foundation, either version 3 of the License, or
* (at your option) any later version.
*
* This program is distributed in the hope that it will be useful,
* but WITHOUT ANY WARRANTY; without even the implied warranty of
* MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
* GNU General Public License for more details.
*
* You should have received a copy of the GNU General Public License
* along with this program. If not, see <http://www.gnu.org/licenses/>.
*
*/
#include "ultima/ultima8/misc/debugger.h"
#include "ultima/ultima8/gfx/fonts/font_shape_archive.h"
#include "ultima/ultima8/misc/util.h"
#include "ultima/ultima8/gfx/fonts/shape_font.h"
#include "ultima/ultima8/conf/config_file_manager.h"
namespace Ultima {
namespace Ultima8 {
ShapeFont *FontShapeArchive::getFont(uint32 fontnum) {
return dynamic_cast<ShapeFont *>(getShape(fontnum));
}
void FontShapeArchive::cache(uint32 shapenum) {
if (shapenum >= _count) return;
if (_shapes.empty()) _shapes.resize(_count);
if (_shapes[shapenum]) return;
uint32 shpsize;
uint8 *data = getRawObject(shapenum, &shpsize);
if (!data || shpsize == 0) return;
// Auto detect format
if (!_format)
_format = Shape::DetectShapeFormat(data, shpsize);
if (!_format) {
delete [] data;
warning("Unable to detect shape format for flex.");
return;
}
Shape *shape = new ShapeFont(data, shpsize, _format, _id, shapenum);
if (_palette) shape->setPalette(_palette);
_shapes[shapenum] = shape;
}
void FontShapeArchive::setHVLeads() {
ConfigFileManager *config = ConfigFileManager::get_instance();
KeyMap leadkeyvals = config->listKeyValues("game", "fontleads");
for (const auto &i : leadkeyvals) {
int fontnum = atoi(i._key.c_str());
Std::string leaddesc = i._value;
Std::vector<Std::string> vals;
SplitString(leaddesc, ',', vals);
if (vals.size() != 2) {
warning("Invalid hlead/vlead description: %s", leaddesc.c_str());
continue;
}
int hlead = atoi(vals[0].c_str());
int vlead = atoi(vals[1].c_str());
ShapeFont *font = getFont(fontnum);
if (font) {
font->setHLead(hlead);
font->setVLead(vlead);
}
}
}
} // End of namespace Ultima8
} // End of namespace Ultima

View File

@@ -0,0 +1,54 @@
/* ScummVM - Graphic Adventure Engine
*
* ScummVM is the legal property of its developers, whose names
* are too numerous to list here. Please refer to the COPYRIGHT
* file distributed with this source distribution.
*
* This program is free software: you can redistribute it and/or modify
* it under the terms of the GNU General Public License as published by
* the Free Software Foundation, either version 3 of the License, or
* (at your option) any later version.
*
* This program is distributed in the hope that it will be useful,
* but WITHOUT ANY WARRANTY; without even the implied warranty of
* MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
* GNU General Public License for more details.
*
* You should have received a copy of the GNU General Public License
* along with this program. If not, see <http://www.gnu.org/licenses/>.
*
*/
#ifndef ULTIMA8_GFX_FONTS_FONTSHAPEARCHIVE_H
#define ULTIMA8_GFX_FONTS_FONTSHAPEARCHIVE_H
#include "ultima/ultima8/gfx/shape_archive.h"
namespace Ultima {
namespace Ultima8 {
class ShapeFont;
class FontShapeArchive : public ShapeArchive {
public:
FontShapeArchive(uint16 id, Palette *pal = 0,
const ConvertShapeFormat *format = 0)
: ShapeArchive(id, pal, format) { }
FontShapeArchive(Common::SeekableReadStream *rs, uint16 id, Palette *pal = 0,
const ConvertShapeFormat *format = 0)
: ShapeArchive(rs, id, pal, format) { }
~FontShapeArchive() override { }
//! load HVLeads from u8.ini
void setHVLeads();
ShapeFont *getFont(uint32 fontnum);
void cache(uint32 fontnum) override;
};
} // End of namespace Ultima8
} // End of namespace Ultima
#endif

View File

@@ -0,0 +1,105 @@
/* ScummVM - Graphic Adventure Engine
*
* ScummVM is the legal property of its developers, whose names
* are too numerous to list here. Please refer to the COPYRIGHT
* file distributed with this source distribution.
*
* This program is free software: you can redistribute it and/or modify
* it under the terms of the GNU General Public License as published by
* the Free Software Foundation, either version 3 of the License, or
* (at your option) any later version.
*
* This program is distributed in the hope that it will be useful,
* but WITHOUT ANY WARRANTY; without even the implied warranty of
* MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
* GNU General Public License for more details.
*
* You should have received a copy of the GNU General Public License
* along with this program. If not, see <http://www.gnu.org/licenses/>.
*
*/
#include "ultima/ultima8/misc/debugger.h"
#include "ultima/ultima8/misc/common_types.h"
#include "ultima/ultima8/gfx/fonts/jp_font.h"
#include "ultima/ultima8/gfx/fonts/shape_font.h"
#include "ultima/ultima8/gfx/shape_frame.h"
#include "ultima/ultima8/gfx/fonts/jp_rendered_text.h"
namespace Ultima {
namespace Ultima8 {
JPFont::JPFont(ShapeFont *jpfont, unsigned int fontnum)
: _fontNum(fontnum), _shapeFont(jpfont) {
assert(_shapeFont->frameCount() > 256);
}
JPFont::~JPFont() {
}
int JPFont::getWidth(int c) {
return _shapeFont->getFrame(c)->_width;
}
int JPFont::getHeight() {
return _shapeFont->getHeight();
}
int JPFont::getBaseline() {
return _shapeFont->getBaseline();
}
int JPFont::getBaselineSkip() {
return _shapeFont->getBaselineSkip();
}
void JPFont::getStringSize(const Std::string &text, int32 &width, int32 &height) {
int hlead = _shapeFont->getHlead();
width = hlead;
height = getHeight();
for (unsigned int i = 0; i < text.size(); ++i) {
if (text[i] == '\n' || text[i] == '\r') {
// ignore
} else {
uint16 sjis = text[i] & 0xFF;
if (sjis >= 0x80) {
uint16 t = text[++i] & 0xFF;
sjis += (t << 8);
}
width += getWidth(shiftjis_to_ultima8(sjis)) - hlead;
}
}
}
void JPFont::getTextSize(const Std::string &text,
int32 &resultwidth, int32 &resultheight,
unsigned int &remaining,
int32 width, int32 height, TextAlign align,
bool u8specials, bool pagebreaks) {
Std::list<PositionedText> tmp;
tmp = typesetText<SJISTraits>(this, text, remaining,
width, height, align, u8specials, pagebreaks,
resultwidth, resultheight);
}
RenderedText *JPFont::renderText(const Std::string &text,
unsigned int &remaining,
int32 width, int32 height, TextAlign align,
bool u8specials, bool pagebreaks,
Std::string::size_type cursor) {
int32 resultwidth, resultheight;
Std::list<PositionedText> lines;
lines = typesetText<SJISTraits>(this, text, remaining,
width, height, align, u8specials, pagebreaks,
resultwidth, resultheight,
cursor);
return new JPRenderedText(lines, resultwidth, resultheight,
_shapeFont->getVlead(), _shapeFont, _fontNum);
}
} // End of namespace Ultima8
} // End of namespace Ultima

View File

@@ -0,0 +1,65 @@
/* ScummVM - Graphic Adventure Engine
*
* ScummVM is the legal property of its developers, whose names
* are too numerous to list here. Please refer to the COPYRIGHT
* file distributed with this source distribution.
*
* This program is free software: you can redistribute it and/or modify
* it under the terms of the GNU General Public License as published by
* the Free Software Foundation, either version 3 of the License, or
* (at your option) any later version.
*
* This program is distributed in the hope that it will be useful,
* but WITHOUT ANY WARRANTY; without even the implied warranty of
* MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
* GNU General Public License for more details.
*
* You should have received a copy of the GNU General Public License
* along with this program. If not, see <http://www.gnu.org/licenses/>.
*
*/
#ifndef ULTIMA8_GFX_FONTS_JPFONT_H
#define ULTIMA8_GFX_FONTS_JPFONT_H
#include "ultima/ultima8/gfx/fonts/font.h"
namespace Ultima {
namespace Ultima8 {
class ShapeFont;
class JPFont : public Font {
public:
JPFont(ShapeFont *jpfont, unsigned int fontnum);
~JPFont() override;
int getWidth(int c);
int getHeight() override;
int getBaseline() override;
int getBaselineSkip() override;
void getStringSize(const Std::string &text,
int32 &width, int32 &height) override;
void getTextSize(const Std::string &text, int32 &resultwidth,
int32 &resultheight, unsigned int &remaining, int32 width = 0,
int32 height = 0, TextAlign align = TEXT_LEFT,
bool u8specials = false, bool pagebreaks = false) override;
RenderedText *renderText(const Std::string &text,
unsigned int &remaining, int32 width = 0, int32 height = 0,
TextAlign align = TEXT_LEFT, bool u8specials = false,
bool pagebreaks = false,
Std::string::size_type cursor = Std::string::npos) override;
protected:
unsigned int _fontNum;
ShapeFont *_shapeFont;
};
} // End of namespace Ultima8
} // End of namespace Ultima
#endif

View File

@@ -0,0 +1,127 @@
/* ScummVM - Graphic Adventure Engine
*
* ScummVM is the legal property of its developers, whose names
* are too numerous to list here. Please refer to the COPYRIGHT
* file distributed with this source distribution.
*
* This program is free software: you can redistribute it and/or modify
* it under the terms of the GNU General Public License as published by
* the Free Software Foundation, either version 3 of the License, or
* (at your option) any later version.
*
* This program is distributed in the hope that it will be useful,
* but WITHOUT ANY WARRANTY; without even the implied warranty of
* MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
* GNU General Public License for more details.
*
* You should have received a copy of the GNU General Public License
* along with this program. If not, see <http://www.gnu.org/licenses/>.
*
*/
#include "ultima/ultima8/misc/debugger.h"
#include "ultima/ultima8/misc/common_types.h"
#include "ultima/ultima8/gfx/fonts/jp_rendered_text.h"
#include "ultima/ultima8/gfx/fonts/shape_font.h"
#include "ultima/ultima8/gfx/render_surface.h"
#include "ultima/ultima8/gfx/shape_frame.h"
#include "ultima/ultima8/gfx/palette_manager.h"
#include "ultima/ultima8/gfx/texture.h"
namespace Ultima {
namespace Ultima8 {
JPRenderedText::JPRenderedText(Std::list<PositionedText> &lines, int width, int height,
int vLead, ShapeFont *font, unsigned int fontNum)
: _lines(lines), _font(font), _fontNum(fontNum) {
_width = width;
_height = height;
_vLead = vLead;
}
JPRenderedText::~JPRenderedText() {
}
void JPRenderedText::draw(RenderSurface *surface, int x, int y, bool /*destmasked*/) {
// TODO support masking here??
PaletteManager *palman = PaletteManager::get_instance();
PaletteManager::PalIndex fontpal = static_cast<PaletteManager::PalIndex>
(PaletteManager::Pal_JPFontStart + _fontNum);
Palette *pal = palman->getPalette(fontpal);
const Palette *savepal = _font->getPalette();
_font->setPalette(pal);
uint32 color = TEX32_PACK_RGB(0, 0, 0);
for (const auto &line : _lines) {
int line_x = x + line._dims.left;
int line_y = y + line._dims.top;
size_t textsize = line._text.size();
for (size_t i = 0; i < textsize; ++i) {
uint16 sjis = line._text[i] & 0xFF;
if (sjis >= 0x80) {
uint16 t = line._text[++i] & 0xFF;
sjis += (t << 8);
}
uint16 u8char = shiftjis_to_ultima8(sjis);
surface->Paint(_font, u8char, line_x, line_y);
if (i == line._cursor) {
surface->fill32(color, line_x, line_y - _font->getBaseline(),
1, line._dims.height());
}
line_x += (_font->getFrame(u8char))->_width - _font->getHlead();
}
if (line._cursor == textsize) {
surface->fill32(color, line_x, line_y - _font->getBaseline(),
1, line._dims.height());
}
}
_font->setPalette(savepal);
}
void JPRenderedText::drawBlended(RenderSurface *surface, int x, int y,
uint32 col, bool /*destmasked*/) {
// TODO Support masking here??
PaletteManager *palman = PaletteManager::get_instance();
PaletteManager::PalIndex fontpal = static_cast<PaletteManager::PalIndex>
(PaletteManager::Pal_JPFontStart + _fontNum);
Palette *pal = palman->getPalette(fontpal);
const Palette *savepal = _font->getPalette();
_font->setPalette(pal);
Std::list<PositionedText>::const_iterator iter;
for (const auto &line : _lines) {
int line_x = x + line._dims.left;
int line_y = y + line._dims.top;
size_t textsize = line._text.size();
for (size_t i = 0; i < textsize; ++i) {
uint16 sjis = line._text[i] & 0xFF;
if (sjis >= 0x80) {
uint16 t = line._text[++i] & 0xFF;
sjis += (t << 8);
}
uint16 u8char = shiftjis_to_ultima8(sjis);
surface->PaintHighlight(_font, u8char, line_x, line_y,
false, false, col);
line_x += (_font->getFrame(u8char))->_width - _font->getHlead();
}
}
_font->setPalette(savepal);
}
} // End of namespace Ultima8
} // End of namespace Ultima

View File

@@ -0,0 +1,52 @@
/* ScummVM - Graphic Adventure Engine
*
* ScummVM is the legal property of its developers, whose names
* are too numerous to list here. Please refer to the COPYRIGHT
* file distributed with this source distribution.
*
* This program is free software: you can redistribute it and/or modify
* it under the terms of the GNU General Public License as published by
* the Free Software Foundation, either version 3 of the License, or
* (at your option) any later version.
*
* This program is distributed in the hope that it will be useful,
* but WITHOUT ANY WARRANTY; without even the implied warranty of
* MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
* GNU General Public License for more details.
*
* You should have received a copy of the GNU General Public License
* along with this program. If not, see <http://www.gnu.org/licenses/>.
*
*/
#ifndef ULTIMA8_GFX_FONTS_JPRENDEREDTEXT_H
#define ULTIMA8_GFX_FONTS_JPRENDEREDTEXT_H
#include "ultima/ultima8/gfx/fonts/rendered_text.h"
#include "ultima/ultima8/gfx/fonts/font.h"
namespace Ultima {
namespace Ultima8 {
class ShapeFont;
class JPRenderedText : public RenderedText {
public:
JPRenderedText(Std::list<PositionedText> &lines,
int width, int height, int vlead, ShapeFont *font,
unsigned int fontnum);
~JPRenderedText() override;
void draw(RenderSurface *surface, int x, int y, bool destmasked = false) override;
void drawBlended(RenderSurface *surface, int x, int y, uint32 col, bool destmasked = false) override;
protected:
Std::list<PositionedText> _lines;
ShapeFont *_font;
unsigned int _fontNum;
};
} // End of namespace Ultima8
} // End of namespace Ultima
#endif

View File

@@ -0,0 +1,37 @@
/* ScummVM - Graphic Adventure Engine
*
* ScummVM is the legal property of its developers, whose names
* are too numerous to list here. Please refer to the COPYRIGHT
* file distributed with this source distribution.
*
* This program is free software: you can redistribute it and/or modify
* it under the terms of the GNU General Public License as published by
* the Free Software Foundation, either version 3 of the License, or
* (at your option) any later version.
*
* This program is distributed in the hope that it will be useful,
* but WITHOUT ANY WARRANTY; without even the implied warranty of
* MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
* GNU General Public License for more details.
*
* You should have received a copy of the GNU General Public License
* along with this program. If not, see <http://www.gnu.org/licenses/>.
*
*/
#include "ultima/ultima8/misc/common_types.h"
#include "ultima/ultima8/gfx/fonts/rendered_text.h"
namespace Ultima {
namespace Ultima8 {
RenderedText::RenderedText()
: _width(-1), _height(-1), _vLead(0) {
}
RenderedText::~RenderedText() {
}
} // End of namespace Ultima8
} // End of namespace Ultima

View File

@@ -0,0 +1,65 @@
/* ScummVM - Graphic Adventure Engine
*
* ScummVM is the legal property of its developers, whose names
* are too numerous to list here. Please refer to the COPYRIGHT
* file distributed with this source distribution.
*
* This program is free software: you can redistribute it and/or modify
* it under the terms of the GNU General Public License as published by
* the Free Software Foundation, either version 3 of the License, or
* (at your option) any later version.
*
* This program is distributed in the hope that it will be useful,
* but WITHOUT ANY WARRANTY; without even the implied warranty of
* MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
* GNU General Public License for more details.
*
* You should have received a copy of the GNU General Public License
* along with this program. If not, see <http://www.gnu.org/licenses/>.
*
*/
#ifndef ULTIMA8_GFX_FONTS_RENDEREDTEXT_H
#define ULTIMA8_GFX_FONTS_RENDEREDTEXT_H
namespace Ultima {
namespace Ultima8 {
class RenderSurface;
class RenderedText {
public:
RenderedText();
virtual ~RenderedText();
//! Draw self to a rendersurface.
//! \param surface The surface to draw to
//! \param x X coordinate of target
//! \param y Y coordinate of target. This will be the top baseline.
virtual void draw(RenderSurface *surface, int x, int y, bool destmasked = false) = 0;
//! Draw self to a rendersurface blended (0xAABBGGRR, alpha is blend level)
virtual void drawBlended(RenderSurface *surface, int x, int y, uint32 col, bool destmasked = false) = 0;
//! Get dimensions.
//! \param x Returns the width
//! \param y Returns the height
virtual void getSize(int &x, int &y) const {
x = _width;
y = _height;
}
//! Get vlead
virtual int getVlead() {
return _vLead;
}
protected:
int _width, _height;
int _vLead;
};
} // End of namespace Ultima8
} // End of namespace Ultima
#endif

View File

@@ -0,0 +1,137 @@
/* ScummVM - Graphic Adventure Engine
*
* ScummVM is the legal property of its developers, whose names
* are too numerous to list here. Please refer to the COPYRIGHT
* file distributed with this source distribution.
*
* This program is free software: you can redistribute it and/or modify
* it under the terms of the GNU General Public License as published by
* the Free Software Foundation, either version 3 of the License, or
* (at your option) any later version.
*
* This program is distributed in the hope that it will be useful,
* but WITHOUT ANY WARRANTY; without even the implied warranty of
* MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
* GNU General Public License for more details.
*
* You should have received a copy of the GNU General Public License
* along with this program. If not, see <http://www.gnu.org/licenses/>.
*
*/
#include "ultima/ultima8/misc/common_types.h"
#include "ultima/ultima8/ultima8.h"
#include "ultima/ultima8/gfx/fonts/shape_font.h"
#include "ultima/ultima8/gfx/shape_frame.h"
#include "ultima/ultima8/gfx/fonts/shape_rendered_text.h"
namespace Ultima {
namespace Ultima8 {
ShapeFont::ShapeFont(const uint8 *data, uint32 size,
const ConvertShapeFormat *format,
const uint16 flexId, const uint32 shapeNum)
: Font(), Shape(data, size, format, flexId, shapeNum),
_height(0), _baseLine(0), _vLead(-1), _hLead(0) {
_crusaderCharMap = GAME_IS_CRUSADER && shapeNum == 1;
}
ShapeFont::~ShapeFont() {
}
int ShapeFont::getWidth(char c) {
const ShapeFrame *frame = getFrame(charToFrameNum(c));
if (frame)
return frame->_width;
else
return 7; // small space..
}
int ShapeFont::getHeight() {
if (_height == 0) {
for (uint32 i = 0; i < frameCount(); i++) {
const ShapeFrame *frame = getFrame(i);
if (!frame)
continue;
int h = frame->_height;
if (h > _height) _height = h;
}
}
return _height;
}
int ShapeFont::getBaseline() {
if (_baseLine == 0) {
for (uint32 i = 0; i < frameCount(); i++) {
int b = getFrame(i)->_yoff;
if (b > _baseLine) _baseLine = b;
}
}
return _baseLine;
}
int ShapeFont::getBaselineSkip() {
return getHeight() + getVlead();
}
void ShapeFont::getStringSize(const Std::string &text, int32 &width, int32 &height) {
width = _hLead;
height = getHeight();
for (unsigned int i = 0; i < text.size(); ++i) {
if (text[i] == '\n' || text[i] == '\r') {
// ignore
} else {
width += getWidth(text[i]) - _hLead;
}
}
}
int ShapeFont::charToFrameNum(char c) const {
if (_crusaderCharMap) {
if (c < 41)
// ( and ) are combined into a single shape
return c;
// weirdly X and Y are swapped in both upper and lowercase
else if (c == 'X')
return 'X';
else if (c == 'Y')
return 'W';
else if (c < 96)
return c - 1;
else if (c == 96)
// no backquote char
return charToFrameNum('\'');
else if (c == 'x')
return 'w';
else if (c == 'y')
return 'v';
else
return c - 2;
} else {
return static_cast<unsigned char>(c);
}
}
RenderedText *ShapeFont::renderText(const Std::string &text,
unsigned int &remaining,
int32 width, int32 height, TextAlign align,
bool u8specials, bool pagebreaks,
Std::string::size_type cursor) {
int32 resultwidth, resultheight;
Std::list<PositionedText> lines;
lines = typesetText<Traits>(this, text, remaining,
width, height, align, u8specials, pagebreaks,
resultwidth, resultheight, cursor);
return new ShapeRenderedText(lines, resultwidth, resultheight,
getVlead(), this);
}
} // End of namespace Ultima8
} // End of namespace Ultima

View File

@@ -0,0 +1,77 @@
/* ScummVM - Graphic Adventure Engine
*
* ScummVM is the legal property of its developers, whose names
* are too numerous to list here. Please refer to the COPYRIGHT
* file distributed with this source distribution.
*
* This program is free software: you can redistribute it and/or modify
* it under the terms of the GNU General Public License as published by
* the Free Software Foundation, either version 3 of the License, or
* (at your option) any later version.
*
* This program is distributed in the hope that it will be useful,
* but WITHOUT ANY WARRANTY; without even the implied warranty of
* MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
* GNU General Public License for more details.
*
* You should have received a copy of the GNU General Public License
* along with this program. If not, see <http://www.gnu.org/licenses/>.
*
*/
#ifndef ULTIMA8_GFX_FONTS_SHAPEFONT_H
#define ULTIMA8_GFX_FONTS_SHAPEFONT_H
#include "ultima/ultima8/gfx/fonts/font.h"
#include "ultima/ultima8/gfx/shape.h"
namespace Ultima {
namespace Ultima8 {
class ShapeFont : public Font, public Shape {
int _height;
int _baseLine;
int _vLead;
int _hLead;
bool _crusaderCharMap;
public:
ShapeFont(const uint8 *data, uint32 size, const ConvertShapeFormat *format,
const uint16 flexId, const uint32 shapenum);
~ShapeFont() override;
int getHeight() override;
int getBaseline() override;
int getBaselineSkip() override;
int getWidth(char c);
int getVlead() const {
return _vLead;
}
int getHlead() const {
return _hLead;
}
void setVLead(int vl) {
_vLead = vl;
}
void setHLead(int hl) {
_hLead = hl;
}
int charToFrameNum(char c) const;
void getStringSize(const Std::string &text,
int32 &width, int32 &height) override;
RenderedText *renderText(const Std::string &text,
unsigned int &remaining, int32 width = 0, int32 height = 0,
TextAlign align = TEXT_LEFT, bool u8specials = false,
bool pagebreaks = false,
Std::string::size_type cursor = Std::string::npos) override;
};
} // End of namespace Ultima8
} // End of namespace Ultima
#endif

View File

@@ -0,0 +1,102 @@
/* ScummVM - Graphic Adventure Engine
*
* ScummVM is the legal property of its developers, whose names
* are too numerous to list here. Please refer to the COPYRIGHT
* file distributed with this source distribution.
*
* This program is free software: you can redistribute it and/or modify
* it under the terms of the GNU General Public License as published by
* the Free Software Foundation, either version 3 of the License, or
* (at your option) any later version.
*
* This program is distributed in the hope that it will be useful,
* but WITHOUT ANY WARRANTY; without even the implied warranty of
* MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
* GNU General Public License for more details.
*
* You should have received a copy of the GNU General Public License
* along with this program. If not, see <http://www.gnu.org/licenses/>.
*
*/
#include "ultima/ultima8/misc/debugger.h"
#include "ultima/ultima8/gfx/fonts/shape_rendered_text.h"
#include "ultima/ultima8/gfx/fonts/shape_font.h"
#include "ultima/ultima8/gfx/render_surface.h"
#include "ultima/ultima8/gfx/texture.h"
namespace Ultima {
namespace Ultima8 {
ShapeRenderedText::ShapeRenderedText(const Std::list<PositionedText> &lines,
int width, int height, int vLead,
ShapeFont *font)
: _lines(lines), _font(font) {
_width = width;
_height = height;
_vLead = vLead;
}
ShapeRenderedText::~ShapeRenderedText() {
}
void ShapeRenderedText::draw(RenderSurface *surface, int x, int y, bool /*destmasked*/) {
// TODO support masking here???
uint32 color = TEX32_PACK_RGB(0, 0, 0);
Std::list<PositionedText>::const_iterator iter;
surface->BeginPainting();
for (const auto &line : _lines) {
int line_x = x + line._dims.left;
int line_y = y + line._dims.top;
size_t textsize = line._text.size();
for (size_t i = 0; i < textsize; ++i) {
surface->Paint(_font, _font->charToFrameNum(line._text[i]),
line_x, line_y);
if (i == line._cursor) {
surface->fill32(color, line_x, line_y - _font->getBaseline(),
1, line._dims.height());
}
line_x += _font->getWidth(line._text[i]) - _font->getHlead();
}
if (line._cursor == textsize) {
surface->fill32(color, line_x, line_y - _font->getBaseline(),
1, line._dims.height());
}
}
surface->EndPainting();
}
void ShapeRenderedText::drawBlended(RenderSurface *surface, int x, int y,
uint32 col, bool /*destmasked*/) {
// TODO Support masking here ????
Std::list<PositionedText>::const_iterator iter;
for (const auto &line : _lines) {
int line_x = x + line._dims.left;
int line_y = y + line._dims.top;
size_t textsize = line._text.size();
for (size_t i = 0; i < textsize; ++i) {
surface->PaintHighlight(_font,
static_cast<unsigned char>(line._text[i]),
line_x, line_y, false, false, col);
line_x += _font->getWidth(line._text[i]) - _font->getHlead();
}
}
}
} // End of namespace Ultima8
} // End of namespace Ultima

View File

@@ -0,0 +1,50 @@
/* ScummVM - Graphic Adventure Engine
*
* ScummVM is the legal property of its developers, whose names
* are too numerous to list here. Please refer to the COPYRIGHT
* file distributed with this source distribution.
*
* This program is free software: you can redistribute it and/or modify
* it under the terms of the GNU General Public License as published by
* the Free Software Foundation, either version 3 of the License, or
* (at your option) any later version.
*
* This program is distributed in the hope that it will be useful,
* but WITHOUT ANY WARRANTY; without even the implied warranty of
* MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
* GNU General Public License for more details.
*
* You should have received a copy of the GNU General Public License
* along with this program. If not, see <http://www.gnu.org/licenses/>.
*
*/
#ifndef ULTIMA8_GFX_FONTS_SHAPERENDEREDTEXT_H
#define ULTIMA8_GFX_FONTS_SHAPERENDEREDTEXT_H
#include "ultima/ultima8/gfx/fonts/rendered_text.h"
#include "ultima/ultima8/gfx/fonts/font.h"
namespace Ultima {
namespace Ultima8 {
class ShapeFont;
class ShapeRenderedText : public RenderedText {
public:
ShapeRenderedText(const Std::list<PositionedText> &lines,
int width, int height, int vlead, ShapeFont *font);
~ShapeRenderedText() override;
void draw(RenderSurface *surface, int x, int y, bool destmasked = false) override;
void drawBlended(RenderSurface *surface, int x, int y, uint32 col, bool destmasked = false) override;
protected:
Std::list<PositionedText> _lines;
ShapeFont *_font;
};
} // End of namespace Ultima8
} // End of namespace Ultima
#endif

View File

@@ -0,0 +1,299 @@
/* ScummVM - Graphic Adventure Engine
*
* ScummVM is the legal property of its developers, whose names
* are too numerous to list here. Please refer to the COPYRIGHT
* file distributed with this source distribution.
*
* This program is free software: you can redistribute it and/or modify
* it under the terms of the GNU General Public License as published by
* the Free Software Foundation, either version 3 of the License, or
* (at your option) any later version.
*
* This program is distributed in the hope that it will be useful,
* but WITHOUT ANY WARRANTY; without even the implied warranty of
* MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
* GNU General Public License for more details.
*
* You should have received a copy of the GNU General Public License
* along with this program. If not, see <http://www.gnu.org/licenses/>.
*
*/
#include "ultima/ultima8/misc/common_types.h"
#include "ultima/ultima8/gfx/fonts/tt_font.h"
#include "ultima/ultima8/gfx/fonts/ttf_rendered_text.h"
#include "ultima/ultima8/gfx/texture.h"
//include iomanip
namespace Ultima {
namespace Ultima8 {
// various unicode characters which look like small black circles
static const uint16 BULLETS[] = { 0x2022, 0x30FB, 0x25CF, 0 };
TTFont::TTFont(Graphics::Font *font, uint32 rgb, int borderSize,
bool antiAliased, bool SJIS) :
_borderSize(borderSize), _ttfFont(font), _antiAliased(antiAliased), _SJIS(SJIS),
_PF_RGBA(Graphics::PixelFormat(4, 8, 8, 8, 8, 24, 16, 8, 0)) {
_color = _PF_RGBA.RGBToColor((rgb >> 16) & 0xFF, (rgb >> 8) & 0xff, rgb & 0xff);
_bullet = 0;
// scan for a character to use as a conversation option _bullet
for (int i = 0; BULLETS[i]; ++i) {
Common::Rect box = font->getBoundingBox(BULLETS[i]);
if (!box.isEmpty()) {
_bullet = BULLETS[i];
break;
}
}
if (_bullet == 0) {
_bullet = '*';
}
}
TTFont::~TTFont() {
}
int TTFont::getHeight() {
return _ttfFont->getFontHeight() + 2 * _borderSize; // constant (border)
}
int TTFont::getBaseline() {
Common::Rect box = _ttfFont->getBoundingBox('W');
return box.bottom;
}
int TTFont::getBaselineSkip() {
// TODO: Come up with something more generic than just hardcoding 2 pixel line separation
return getHeight() + 2;
}
template<class T>
static Common::U32String toUnicode(const Std::string &text, uint16 bullet) {
Std::string::size_type len = T::length(text);
Common::U32String result = Common::U32String(text.c_str(), len);
Std::string::const_iterator iter = text.begin();
for (uint idx = 0; idx < len; ++idx) {
uint32 u = T::unicode(iter);
if (u == '@') {
result.setChar(bullet, idx);
} else {
result.setChar(u, idx);
}
}
return result;
}
void TTFont::getStringSize(const Std::string &text, int32 &width, int32 &height) {
// convert to unicode
Common::U32String unicodeText;
if (!_SJIS)
unicodeText = toUnicode<Traits>(text, _bullet);
else
unicodeText = toUnicode<SJISTraits>(text, _bullet);
width = _ttfFont->getStringWidth(unicodeText);
height = _ttfFont->getFontHeight();
width += 2 * _borderSize;
height += 2 * _borderSize;
}
void TTFont::getTextSize(const Std::string &text,
int32 &resultWidth, int32 &resultHeight,
unsigned int &remaining,
int32 width, int32 height, TextAlign align,
bool u8specials, bool pagebreaks) {
Std::list<PositionedText> tmp;
if (!_SJIS)
tmp = typesetText<Traits>(this, text, remaining,
width, height, align, u8specials, pagebreaks,
resultWidth, resultHeight);
else
tmp = typesetText<SJISTraits>(this, text, remaining,
width, height, align, u8specials, pagebreaks,
resultWidth, resultHeight);
}
void TTFont::addTextBorder(Graphics::ManagedSurface &textSurf, uint32 *texBuf, const Common::Rect32 &dims, int32 resultWidth, int32 resultHeight, uint32 borderColor) {
uint8 bA, bR, bG, bB;
_PF_RGBA.colorToARGB(borderColor, bA, bR, bG, bB);
int sqrSize = _borderSize * _borderSize;
int sqrEdge = (_borderSize + 1) * (_borderSize + 1);
for (int y = 0; y < textSurf.h; y++) {
const byte* surfrow = (const byte*)textSurf.getBasePtr(0, y);
for (int x = 0; x < textSurf.w; x++) {
if (_antiAliased) {
uint32 sColor = *((const uint32 *)(surfrow + x * 4));
uint8 sR, sG, sB, sA;
_PF_RGBA.colorToARGB(sColor, sA, sR, sG, sB);
if (sA == 0x00)
continue;
for (int dx = -_borderSize; dx <= _borderSize; dx++) {
for (int dy = -_borderSize; dy <= _borderSize; dy++) {
int tx = dims.left + x + _borderSize + dx;
int ty = dims.top + y + _borderSize + dy;
if (tx >= 0 && tx < resultWidth && ty >= 0 && ty < resultHeight) {
uint32 dColor = texBuf[ty * resultWidth + tx];
if (borderColor != dColor) {
int sqrDist = (dx * dx) + (dy * dy);
if (sqrDist < sqrSize) {
texBuf[ty * resultWidth + tx] = borderColor;
}
else if (sqrDist < sqrEdge) {
// Blend border color at source intensity with destination
uint8 dA, dR, dG, dB;
_PF_RGBA.colorToARGB(dColor, dA, dR, dG, dB);
double bAlpha = (double)bA / 255.0;
double sAlpha = (double)sA / 255.0;
double dAlpha = (double)dA / 255.0;
dAlpha *= (1.0 - sAlpha);
dR = static_cast<uint8>((bR * sAlpha + dR * dAlpha) / (sAlpha + dAlpha));
dG = static_cast<uint8>((bG * sAlpha + dG * dAlpha) / (sAlpha + dAlpha));
dB = static_cast<uint8>((bB * sAlpha + dB * dAlpha) / (sAlpha + dAlpha));
dA = static_cast<uint8>(255. * bAlpha * (sAlpha + dAlpha));
texBuf[ty * resultWidth + tx] = _PF_RGBA.ARGBToColor(dA, dR, dG, dB);
}
}
}
}
}
}
else if (surfrow[x] == 1) {
for (int dx = -_borderSize; dx <= _borderSize; dx++) {
for (int dy = -_borderSize; dy <= _borderSize; dy++) {
int tx = dims.left + x + _borderSize + dx;
int ty = dims.top + y + _borderSize + dy;
if (tx >= 0 && tx < resultWidth && ty >= 0 && ty < resultHeight) {
int sqrDist = (dx * dx) + (dy * dy);
if (sqrDist < sqrEdge) {
texBuf[ty * resultWidth + tx] = borderColor;
}
}
}
}
}
}
}
}
RenderedText *TTFont::renderText(const Std::string &text, unsigned int &remaining,
int32 width, int32 height, TextAlign align, bool u8specials, bool pagebreaks,
Std::string::size_type cursor) {
int32 resultWidth, resultHeight, lineHeight;
Std::list<PositionedText> lines;
if (!_SJIS)
lines = typesetText<Traits>(this, text, remaining, width, height, align, u8specials, pagebreaks,
resultWidth, resultHeight, cursor);
else
lines = typesetText<SJISTraits>(this, text, remaining, width, height, align, u8specials, pagebreaks,
resultWidth, resultHeight, cursor);
lineHeight = _ttfFont->getFontHeight();
uint32 borderColor = _PF_RGBA.ARGBToColor(0xFF, 0x00, 0x00, 0x00);
Graphics::ManagedSurface *texture = new Graphics::ManagedSurface(resultWidth, resultHeight, _PF_RGBA);
uint32 *texBuf = (uint32 *)texture->getPixels();
for (const auto &line : lines) {
// convert to unicode
Common::U32String unicodeText;
if (!_SJIS)
unicodeText = toUnicode<Traits>(line._text, _bullet);
else
unicodeText = toUnicode<SJISTraits>(line._text, _bullet);
// Create a surface and render the text
Graphics::ManagedSurface textSurf;
if (!_antiAliased) {
// When not in antialiased mode, use a paletted surface where '1' is
// used for pixels of the text
textSurf.create(resultWidth, lineHeight, Graphics::PixelFormat::createFormatCLUT8());
_ttfFont->drawString(&textSurf, unicodeText, 0, 0, resultWidth, 1);
} else {
// Use a high color surface with the specified _color color for text
textSurf.create(resultWidth, lineHeight, _PF_RGBA);
_ttfFont->drawString(&textSurf, unicodeText, 0, 0, resultWidth, _color);
};
// Add border within radius. Pixels on the edge are alpha blended if antialiased
if (_borderSize > 0) {
addTextBorder(textSurf, texBuf, line._dims, resultWidth, resultHeight, borderColor);
}
// render the text surface into our texture buffer
for (int y = 0; y < textSurf.h; y++) {
const byte *surfrow = (const byte *)textSurf.getBasePtr(0, y);
int ty = line._dims.top + y + _borderSize;
for (int x = 0; x < textSurf.w; x++) {
int tx = line._dims.left + x + _borderSize;
if (_antiAliased) {
uint32 sColor = *((const uint32 *)(surfrow + x * 4));
uint8 sR, sG, sB, sA;
_PF_RGBA.colorToARGB(sColor, sA, sR, sG, sB);
if (sA == 0xFF) {
texBuf[ty * resultWidth + tx] = sColor;
}
else if (sA != 0x00) {
// Blend color with destination
int32 dColor = texBuf[ty * resultWidth + tx];
uint8 dA, dR, dG, dB;
_PF_RGBA.colorToARGB(dColor, dA, dR, dG, dB);
double sAlpha = (double)sA / 255.0;
double dAlpha = (double)dA / 255.0;
dAlpha *= (1.0 - sAlpha);
dR = static_cast<uint8>((sR * sAlpha + dR * dAlpha) / (sAlpha + dAlpha));
dG = static_cast<uint8>((sG * sAlpha + dG * dAlpha) / (sAlpha + dAlpha));
dB = static_cast<uint8>((sB * sAlpha + dB * dAlpha) / (sAlpha + dAlpha));
dA = static_cast<uint8>(255. * (sAlpha + dAlpha));
texBuf[ty * resultWidth + tx] = _PF_RGBA.ARGBToColor(dA, dR, dG, dB);
}
}
else if (surfrow[x] == 1) {
texBuf[ty * resultWidth + tx] = _color;
}
}
}
if (line._cursor != Std::string::npos) {
assert(line._cursor <= line._text.size());
unicodeText = unicodeText.substr(0, line._cursor);
int w = _ttfFont->getStringWidth(unicodeText);
for (int y = 0; y < line._dims.height(); y++) {
int tx = line._dims.left + w + _borderSize;
int ty = line._dims.top + y;
texBuf[ty * resultWidth + tx] = borderColor;
}
}
}
return new TTFRenderedText(texture, resultWidth, resultHeight,
getBaselineSkip() - getHeight(), getBaseline(), isAntialiased());
}
} // End of namespace Ultima8
} // End of namespace Ultima

View File

@@ -0,0 +1,76 @@
/* ScummVM - Graphic Adventure Engine
*
* ScummVM is the legal property of its developers, whose names
* are too numerous to list here. Please refer to the COPYRIGHT
* file distributed with this source distribution.
*
* This program is free software: you can redistribute it and/or modify
* it under the terms of the GNU General Public License as published by
* the Free Software Foundation, either version 3 of the License, or
* (at your option) any later version.
*
* This program is distributed in the hope that it will be useful,
* but WITHOUT ANY WARRANTY; without even the implied warranty of
* MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
* GNU General Public License for more details.
*
* You should have received a copy of the GNU General Public License
* along with this program. If not, see <http://www.gnu.org/licenses/>.
*
*/
#ifndef ULTIMA8_GFX_FONTS_TTFONT_H
#define ULTIMA8_GFX_FONTS_TTFONT_H
#include "ultima/ultima8/gfx/fonts/font.h"
#include "graphics/font.h"
#include "graphics/pixelformat.h"
namespace Ultima {
namespace Ultima8 {
class TTFont : public Font {
public:
TTFont(Graphics::Font *font, uint32 rgb, int bordersize,
bool antiAliased, bool SJIS);
~TTFont() override;
int getHeight() override;
int getBaseline() override;
int getBaselineSkip() override;
bool isAntialiased() {
return _antiAliased;
}
void getStringSize(const Std::string &text,
int32 &width, int32 &height) override;
void getTextSize(const Std::string &text,
int32 &resultwidth, int32 &resultheight, unsigned int &remaining,
int32 width = 0, int32 height = 0, TextAlign align = TEXT_LEFT,
bool u8specials = false, bool pagebreaks = false) override;
RenderedText *renderText(const Std::string &text,
unsigned int &remaining, int32 width = 0, int32 height = 0,
TextAlign align = TEXT_LEFT, bool u8specials = false,
bool pagebreaks = false,
Std::string::size_type cursor = Std::string::npos) override;
protected:
Graphics::Font *_ttfFont;
uint32 _color;
int _borderSize;
bool _antiAliased;
bool _SJIS;
Graphics::PixelFormat _PF_RGBA;
uint16 _bullet;
void addTextBorder(Graphics::ManagedSurface &textSurf, uint32 *texBuf, const Common::Rect32 &dims, int32 resultWidth, int32 resultHeight, uint32 borderColor);
};
} // End of namespace Ultima8
} // End of namespace Ultima
#endif

View File

@@ -0,0 +1,61 @@
/* ScummVM - Graphic Adventure Engine
*
* ScummVM is the legal property of its developers, whose names
* are too numerous to list here. Please refer to the COPYRIGHT
* file distributed with this source distribution.
*
* This program is free software: you can redistribute it and/or modify
* it under the terms of the GNU General Public License as published by
* the Free Software Foundation, either version 3 of the License, or
* (at your option) any later version.
*
* This program is distributed in the hope that it will be useful,
* but WITHOUT ANY WARRANTY; without even the implied warranty of
* MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
* GNU General Public License for more details.
*
* You should have received a copy of the GNU General Public License
* along with this program. If not, see <http://www.gnu.org/licenses/>.
*
*/
#include "ultima/ultima8/misc/common_types.h"
#include "ultima/ultima8/gfx/fonts/ttf_rendered_text.h"
#include "ultima/ultima8/gfx/fonts/tt_font.h"
#include "ultima/ultima8/gfx/render_surface.h"
namespace Ultima {
namespace Ultima8 {
TTFRenderedText::TTFRenderedText(Graphics::ManagedSurface *texture, int width, int height,
int vLead, int baseline, bool antiAliased) : _texture(texture), _baseline(baseline), _antiAliased(antiAliased) {
_width = width;
_height = height;
_vLead = vLead;
}
TTFRenderedText::~TTFRenderedText() {
delete _texture;
}
void TTFRenderedText::draw(RenderSurface *surface, int x, int y, bool destmasked) {
if (!_width)
return;
Common::Rect srcRect(_width, _height);
if (!destmasked)
surface->Blit(*_texture, srcRect, x, y - _baseline, _antiAliased);
else
surface->MaskedBlit(*_texture, srcRect, x, y - _baseline, 0, _antiAliased);
}
void TTFRenderedText::drawBlended(RenderSurface *surface, int x, int y,
uint32 col, bool destmasked) {
Common::Rect srcRect(_width, _height);
if (!destmasked)
surface->FadedBlit(*_texture, srcRect, x, y - _baseline, col, _antiAliased);
else
surface->MaskedBlit(*_texture, srcRect, x, y - _baseline, col, _antiAliased);
}
} // End of namespace Ultima8
} // End of namespace Ultima

View File

@@ -0,0 +1,55 @@
/* ScummVM - Graphic Adventure Engine
*
* ScummVM is the legal property of its developers, whose names
* are too numerous to list here. Please refer to the COPYRIGHT
* file distributed with this source distribution.
*
* This program is free software: you can redistribute it and/or modify
* it under the terms of the GNU General Public License as published by
* the Free Software Foundation, either version 3 of the License, or
* (at your option) any later version.
*
* This program is distributed in the hope that it will be useful,
* but WITHOUT ANY WARRANTY; without even the implied warranty of
* MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
* GNU General Public License for more details.
*
* You should have received a copy of the GNU General Public License
* along with this program. If not, see <http://www.gnu.org/licenses/>.
*
*/
#ifndef ULTIMA8_GFX_FONTS_SHAPERENDEREDTEXT_H
#define ULTIMA8_GFX_FONTS_SHAPERENDEREDTEXT_H
#include "ultima/ultima8/gfx/fonts/rendered_text.h"
#include "ultima/ultima8/gfx/fonts/font.h"
#include "graphics/managed_surface.h"
namespace Ultima {
namespace Ultima8 {
class TTFont;
class Texture;
class TTFRenderedText : public RenderedText {
public:
TTFRenderedText(Graphics::ManagedSurface *texture, int width, int height, int vlead,
int baseline, bool antiAliased);
~TTFRenderedText() override;
void draw(RenderSurface *surface, int x, int y,
bool destmasked = false) override;
void drawBlended(RenderSurface *surface, int x, int y, uint32 col,
bool destmasked = false) override;
protected:
Graphics::ManagedSurface *_texture;
int _baseline;
bool _antiAliased;
};
} // End of namespace Ultima8
} // End of namespace Ultima
#endif

View File

@@ -0,0 +1,41 @@
/* ScummVM - Graphic Adventure Engine
*
* ScummVM is the legal property of its developers, whose names
* are too numerous to list here. Please refer to the COPYRIGHT
* file distributed with this source distribution.
*
* This program is free software: you can redistribute it and/or modify
* it under the terms of the GNU General Public License as published by
* the Free Software Foundation, either version 3 of the License, or
* (at your option) any later version.
*
* This program is distributed in the hope that it will be useful,
* but WITHOUT ANY WARRANTY; without even the implied warranty of
* MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
* GNU General Public License for more details.
*
* You should have received a copy of the GNU General Public License
* along with this program. If not, see <http://www.gnu.org/licenses/>.
*
*/
#include "ultima/ultima8/gfx/frame_id.h"
namespace Ultima {
namespace Ultima8 {
void FrameID::save(Common::WriteStream *ws) {
ws->writeUint16LE(_flexId);
ws->writeUint32LE(_shapeNum);
ws->writeUint32LE(_frameNum);
}
bool FrameID::load(Common::ReadStream *rs) {
_flexId = rs->readUint16LE();
_shapeNum = rs->readUint32LE();
_frameNum = rs->readUint32LE();
return true;
}
} // End of namespace Ultima8
} // End of namespace Ultima

View File

@@ -0,0 +1,47 @@
/* ScummVM - Graphic Adventure Engine
*
* ScummVM is the legal property of its developers, whose names
* are too numerous to list here. Please refer to the COPYRIGHT
* file distributed with this source distribution.
*
* This program is free software: you can redistribute it and/or modify
* it under the terms of the GNU General Public License as published by
* the Free Software Foundation, either version 3 of the License, or
* (at your option) any later version.
*
* This program is distributed in the hope that it will be useful,
* but WITHOUT ANY WARRANTY; without even the implied warranty of
* MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
* GNU General Public License for more details.
*
* You should have received a copy of the GNU General Public License
* along with this program. If not, see <http://www.gnu.org/licenses/>.
*
*/
#ifndef ULTIMA8_GFX_FRAMEID_H
#define ULTIMA8_GFX_FRAMEID_H
#include "common/stream.h"
namespace Ultima {
namespace Ultima8 {
struct FrameID {
uint16 _flexId;
uint32 _shapeNum;
uint32 _frameNum;
FrameID() : _flexId(0), _shapeNum(0), _frameNum(0) { }
FrameID(uint16 flex, uint32 shape, uint32 frame)
: _flexId(flex), _shapeNum(shape), _frameNum(frame) {
}
void save(Common::WriteStream *ws);
bool load(Common::ReadStream *rs);
};
} // End of namespace Ultima8
} // End of namespace Ultima
#endif

View File

@@ -0,0 +1,53 @@
/* ScummVM - Graphic Adventure Engine
*
* ScummVM is the legal property of its developers, whose names
* are too numerous to list here. Please refer to the COPYRIGHT
* file distributed with this source distribution.
*
* This program is free software: you can redistribute it and/or modify
* it under the terms of the GNU General Public License as published by
* the Free Software Foundation, either version 3 of the License, or
* (at your option) any later version.
*
* This program is distributed in the hope that it will be useful,
* but WITHOUT ANY WARRANTY; without even the implied warranty of
* MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
* GNU General Public License for more details.
*
* You should have received a copy of the GNU General Public License
* along with this program. If not, see <http://www.gnu.org/licenses/>.
*
*/
#include "ultima/ultima8/gfx/gump_shape_archive.h"
#include "common/stream.h"
namespace Ultima {
namespace Ultima8 {
GumpShapeArchive::~GumpShapeArchive() {
for (unsigned int i = 0; i < _gumpItemArea.size(); ++i)
delete _gumpItemArea[i];
}
void GumpShapeArchive::loadGumpage(Common::SeekableReadStream *rs) {
unsigned int total = rs->size() / 8;
_gumpItemArea.resize(total + 1);
for (unsigned int i = 1; i <= total; ++i) {
int x1, y1, x2, y2;
x1 = static_cast<int16>(rs->readUint16LE());
y1 = static_cast<int16>(rs->readUint16LE());
x2 = static_cast<int16>(rs->readUint16LE());
y2 = static_cast<int16>(rs->readUint16LE());
_gumpItemArea[i] = new Common::Rect32(x1, y1, x2, y2);
}
}
Common::Rect32 *GumpShapeArchive::getGumpItemArea(uint32 shapenum) {
if (shapenum >= _gumpItemArea.size())
return nullptr;
return _gumpItemArea[shapenum];
}
} // End of namespace Ultima8
} // End of namespace Ultima

View File

@@ -0,0 +1,52 @@
/* ScummVM - Graphic Adventure Engine
*
* ScummVM is the legal property of its developers, whose names
* are too numerous to list here. Please refer to the COPYRIGHT
* file distributed with this source distribution.
*
* This program is free software: you can redistribute it and/or modify
* it under the terms of the GNU General Public License as published by
* the Free Software Foundation, either version 3 of the License, or
* (at your option) any later version.
*
* This program is distributed in the hope that it will be useful,
* but WITHOUT ANY WARRANTY; without even the implied warranty of
* MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
* GNU General Public License for more details.
*
* You should have received a copy of the GNU General Public License
* along with this program. If not, see <http://www.gnu.org/licenses/>.
*
*/
#ifndef ULTIMA8_GFX_GUMPSHAPEARCHIVE_H
#define ULTIMA8_GFX_GUMPSHAPEARCHIVE_H
#include "ultima/ultima8/gfx/shape_archive.h"
#include "common/rect.h"
namespace Ultima {
namespace Ultima8 {
class GumpShapeArchive : public ShapeArchive {
public:
GumpShapeArchive(uint16 id, Palette *pal = 0,
const ConvertShapeFormat *format = 0)
: ShapeArchive(id, pal, format) { }
GumpShapeArchive(Common::SeekableReadStream *rs, uint16 id, Palette *pal = 0,
const ConvertShapeFormat *format = 0)
: ShapeArchive(rs, id, pal, format) { }
~GumpShapeArchive() override;
void loadGumpage(Common::SeekableReadStream *rs);
Common::Rect32 *getGumpItemArea(uint32 shapenum);
protected:
Std::vector<Common::Rect32 *> _gumpItemArea;
};
} // End of namespace Ultima8
} // End of namespace Ultima
#endif

View File

@@ -0,0 +1,113 @@
/* ScummVM - Graphic Adventure Engine
*
* ScummVM is the legal property of its developers, whose names
* are too numerous to list here. Please refer to the COPYRIGHT
* file distributed with this source distribution.
*
* This program is free software: you can redistribute it and/or modify
* it under the terms of the GNU General Public License as published by
* the Free Software Foundation, either version 3 of the License, or
* (at your option) any later version.
*
* This program is distributed in the hope that it will be useful,
* but WITHOUT ANY WARRANTY; without even the implied warranty of
* MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
* GNU General Public License for more details.
*
* You should have received a copy of the GNU General Public License
* along with this program. If not, see <http://www.gnu.org/licenses/>.
*
*/
#include "ultima/ultima8/gfx/inverter_process.h"
#include "ultima/ultima8/kernel/kernel.h"
#include "ultima/ultima8/ultima8.h"
namespace Ultima {
namespace Ultima8 {
static unsigned int states[] = { 0, 8, 63, 211, 493, 945, 1594, 2459, 3552,
4870, 6406, 8139, 10042, 12078, 14207, 16384,
18561, 20690, 22726, 24629, 26362, 27898,
29216, 30308, 31174, 31823, 32274, 32556,
32704, 32760, 32768,
32775, 32831, 32979, 33261, 33713, 34362,
35227, 36320, 37638, 39174, 40907, 42810,
44846, 46975, 49152, 51328, 53457, 55494,
57396, 59129, 60665, 61984, 63076, 63942,
64591, 65042, 65324, 65472, 65528, 65536
};
InverterProcess *InverterProcess::_inverter = nullptr;
// p_dynamic_class stuff
DEFINE_RUNTIME_CLASSTYPE_CODE(InverterProcess)
InverterProcess::InverterProcess()
: Process(), _targetState(0) {
}
InverterProcess::InverterProcess(unsigned int target)
: Process(), _targetState(target) {
}
InverterProcess::~InverterProcess(void) {
if (_inverter == this)
_inverter = nullptr;
}
void InverterProcess::run() {
Ultima8Engine *app = Ultima8Engine::get_instance();
unsigned int state = app->getInversion();
if (state == _targetState) {
terminate();
} else {
unsigned int i = 0;
while (states[i] <= state) i++;
app->setInversion(states[i]);
}
}
void InverterProcess::saveData(Common::WriteStream *ws) {
Process::saveData(ws);
ws->writeUint16LE(static_cast<uint16>(_targetState));
}
bool InverterProcess::loadData(Common::ReadStream *rs, uint32 version) {
if (!Process::loadData(rs, version)) return false;
_targetState = rs->readUint16LE();
_inverter = this; //static
return true;
}
// static
ProcId InverterProcess::invertScreen() {
if (_inverter) {
if (_inverter->_targetState == 0)
_inverter->setTarget(0x8000);
else
_inverter->setTarget(0);
return _inverter->getPid();
} else {
unsigned int target = 0x8000;
if (Ultima8Engine::get_instance()->isInverted()) target = 0;
_inverter = new InverterProcess(target);
return Kernel::get_instance()->addProcess(_inverter);
}
}
uint32 InverterProcess::I_invertScreen(const uint8 *args,
unsigned int /*argsize*/) {
return invertScreen();
}
} // End of namespace Ultima8
} // End of namespace Ultima

View File

@@ -0,0 +1,63 @@
/* ScummVM - Graphic Adventure Engine
*
* ScummVM is the legal property of its developers, whose names
* are too numerous to list here. Please refer to the COPYRIGHT
* file distributed with this source distribution.
*
* This program is free software: you can redistribute it and/or modify
* it under the terms of the GNU General Public License as published by
* the Free Software Foundation, either version 3 of the License, or
* (at your option) any later version.
*
* This program is distributed in the hope that it will be useful,
* but WITHOUT ANY WARRANTY; without even the implied warranty of
* MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
* GNU General Public License for more details.
*
* You should have received a copy of the GNU General Public License
* along with this program. If not, see <http://www.gnu.org/licenses/>.
*
*/
#ifndef ULTIMA8_GFX_INVERTERPROCESS_H
#define ULTIMA8_GFX_INVERTERPROCESS_H
#include "ultima/ultima8/kernel/process.h"
#include "ultima/ultima8/usecode/intrinsics.h"
#include "ultima/ultima8/misc/classtype.h"
namespace Ultima {
namespace Ultima8 {
/** The process to tear and flip the screen, triggered by some game events in U8 */
class InverterProcess : public Process {
public:
InverterProcess();
InverterProcess(unsigned int targetstate);
~InverterProcess() override;
// p_dynamic_class stuff
ENABLE_RUNTIME_CLASSTYPE()
void setTarget(unsigned int target) {
_targetState = target;
}
void run() override;
static ProcId invertScreen();
INTRINSIC(I_invertScreen);
bool loadData(Common::ReadStream *rs, uint32 version);
void saveData(Common::WriteStream *ws) override;
protected:
static InverterProcess *_inverter;
unsigned int _targetState;
};
} // End of namespace Ultima8
} // End of namespace Ultima
#endif

View File

@@ -0,0 +1,84 @@
/* ScummVM - Graphic Adventure Engine
*
* ScummVM is the legal property of its developers, whose names
* are too numerous to list here. Please refer to the COPYRIGHT
* file distributed with this source distribution.
*
* This program is free software: you can redistribute it and/or modify
* it under the terms of the GNU General Public License as published by
* the Free Software Foundation, either version 3 of the License, or
* (at your option) any later version.
*
* This program is distributed in the hope that it will be useful,
* but WITHOUT ANY WARRANTY; without even the implied warranty of
* MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
* GNU General Public License for more details.
*
* You should have received a copy of the GNU General Public License
* along with this program. If not, see <http://www.gnu.org/licenses/>.
*
*/
#include "ultima/ultima8/misc/debugger.h"
#include "ultima/ultima8/gfx/main_shape_archive.h"
#include "ultima/ultima8/gfx/type_flags.h"
#include "ultima/ultima8/gfx/anim_dat.h"
namespace Ultima {
namespace Ultima8 {
MainShapeArchive::~MainShapeArchive() {
if (_typeFlags) {
delete _typeFlags;
}
if (_animDat) {
delete _animDat;
}
}
void MainShapeArchive::loadTypeFlags(Common::SeekableReadStream *rs) {
if (_typeFlags) {
delete _typeFlags;
_typeFlags = nullptr;
}
_typeFlags = new TypeFlags;
_typeFlags->load(rs);
}
void MainShapeArchive::loadDamageDat(Common::SeekableReadStream *rs) {
assert(_typeFlags);
_typeFlags->loadDamageDat(rs);
}
const ShapeInfo *MainShapeArchive::getShapeInfo(uint32 shapenum) {
assert(_typeFlags);
return _typeFlags->getShapeInfo(shapenum);
}
void MainShapeArchive::loadAnimDat(Common::SeekableReadStream *rs) {
if (_animDat) {
delete _animDat;
_animDat = nullptr;
}
_animDat = new AnimDat;
_animDat->load(rs);
}
const ActorAnim *MainShapeArchive::getAnim(uint32 shape) const {
assert(_animDat);
return _animDat->getAnim(shape);
}
const AnimAction *MainShapeArchive::getAnim(uint32 shape, uint32 action) const {
assert(_animDat);
return _animDat->getAnim(shape, action);
}
} // End of namespace Ultima8
} // End of namespace Ultima

View File

@@ -0,0 +1,63 @@
/* ScummVM - Graphic Adventure Engine
*
* ScummVM is the legal property of its developers, whose names
* are too numerous to list here. Please refer to the COPYRIGHT
* file distributed with this source distribution.
*
* This program is free software: you can redistribute it and/or modify
* it under the terms of the GNU General Public License as published by
* the Free Software Foundation, either version 3 of the License, or
* (at your option) any later version.
*
* This program is distributed in the hope that it will be useful,
* but WITHOUT ANY WARRANTY; without even the implied warranty of
* MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
* GNU General Public License for more details.
*
* You should have received a copy of the GNU General Public License
* along with this program. If not, see <http://www.gnu.org/licenses/>.
*
*/
#ifndef ULTIMA8_GFX_MAINSHAPEARCHIVE_H
#define ULTIMA8_GFX_MAINSHAPEARCHIVE_H
#include "ultima/ultima8/gfx/shape_archive.h"
namespace Ultima {
namespace Ultima8 {
class TypeFlags;
class ShapeInfo;
class AnimDat;
class ActorAnim;
class AnimAction;
class MainShapeArchive : public ShapeArchive {
public:
MainShapeArchive(uint16 id, const Palette *pal = 0,
const ConvertShapeFormat *format = 0)
: ShapeArchive(id, pal, format), _typeFlags(0), _animDat(0) { }
MainShapeArchive(Common::SeekableReadStream *rs, uint16 id, const Palette *pal = 0,
const ConvertShapeFormat *format = 0)
: ShapeArchive(rs, id, pal, format), _typeFlags(0), _animDat(0) { }
~MainShapeArchive() override;
void loadTypeFlags(Common::SeekableReadStream *rs);
void loadDamageDat(Common::SeekableReadStream *rs);
const ShapeInfo *getShapeInfo(uint32 shapenum);
void loadAnimDat(Common::SeekableReadStream *rs);
const ActorAnim *getAnim(uint32 shape) const;
const AnimAction *getAnim(uint32 shape, uint32 action) const;
protected:
TypeFlags *_typeFlags;
AnimDat *_animDat;
};
} // End of namespace Ultima8
} // End of namespace Ultima
#endif

View File

@@ -0,0 +1,56 @@
/* ScummVM - Graphic Adventure Engine
*
* ScummVM is the legal property of its developers, whose names
* are too numerous to list here. Please refer to the COPYRIGHT
* file distributed with this source distribution.
*
* This program is free software: you can redistribute it and/or modify
* it under the terms of the GNU General Public License as published by
* the Free Software Foundation, either version 3 of the License, or
* (at your option) any later version.
*
* This program is distributed in the hope that it will be useful,
* but WITHOUT ANY WARRANTY; without even the implied warranty of
* MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
* GNU General Public License for more details.
*
* You should have received a copy of the GNU General Public License
* along with this program. If not, see <http://www.gnu.org/licenses/>.
*
*/
#ifndef ULTIMA8_GFX_MOUSESHAPEARCHIVE_H
#define ULTIMA8_GFX_MOUSESHAPEARCHIVE_H
#include "ultima/ultima8/gfx/shape_archive.h"
namespace Ultima {
namespace Ultima8 {
// Fake archive class for mouse shape in the shape viewer
// TODO - consider using this in GameData and owning the shape
class MouseShapeArchive : public ShapeArchive {
public:
MouseShapeArchive(uint16 id, Palette *pal = 0,
const ConvertShapeFormat *format = 0)
: ShapeArchive(id, pal, format) {}
MouseShapeArchive(Shape *shape, uint16 id, Palette *pal = 0,
const ConvertShapeFormat *format = 0)
: ShapeArchive(id, pal, format) {
_shapes.push_back(shape);
_count = 1;
}
~MouseShapeArchive() override {
_shapes.clear();
}
void cache(uint32 shapenum) override {}
void uncache(uint32 shapenum) override {}
bool isCached(uint32 shapenum) const override { return true; }
};
} // End of namespace Ultima8
} // End of namespace Ultima
#endif

View File

@@ -0,0 +1,48 @@
/* ScummVM - Graphic Adventure Engine
*
* ScummVM is the legal property of its developers, whose names
* are too numerous to list here. Please refer to the COPYRIGHT
* file distributed with this source distribution.
*
* This program is free software: you can redistribute it and/or modify
* it under the terms of the GNU General Public License as published by
* the Free Software Foundation, either version 3 of the License, or
* (at your option) any later version.
*
* This program is distributed in the hope that it will be useful,
* but WITHOUT ANY WARRANTY; without even the implied warranty of
* MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
* GNU General Public License for more details.
*
* You should have received a copy of the GNU General Public License
* along with this program. If not, see <http://www.gnu.org/licenses/>.
*
*/
#ifndef ULTIMA8_GFX_MOVIEPLAYER_H
#define ULTIMA8_GFX_MOVIEPLAYER_H
namespace Ultima {
namespace Ultima8 {
class RenderSurface;
class MoviePlayer {
public:
MoviePlayer() {};
virtual ~MoviePlayer() {};
virtual void run() = 0;
virtual void paint(RenderSurface *surf, int lerp) = 0;
virtual void start() = 0;
virtual void stop() = 0;
virtual bool isPlaying() const = 0;
virtual void setOffset(int x, int y) {};
};
} // End of namespace Ultima8
} // End of namespace Ultima
#endif

View File

@@ -0,0 +1,62 @@
/* ScummVM - Graphic Adventure Engine
*
* ScummVM is the legal property of its developers, whose names
* are too numerous to list here. Please refer to the COPYRIGHT
* file distributed with this source distribution.
*
* This program is free software: you can redistribute it and/or modify
* it under the terms of the GNU General Public License as published by
* the Free Software Foundation, either version 3 of the License, or
* (at your option) any later version.
*
* This program is distributed in the hope that it will be useful,
* but WITHOUT ANY WARRANTY; without even the implied warranty of
* MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
* GNU General Public License for more details.
*
* You should have received a copy of the GNU General Public License
* along with this program. If not, see <http://www.gnu.org/licenses/>.
*
*/
#ifndef ULTIMA8_GFX_PAL_TRANSFORMS_H
#define ULTIMA8_GFX_PAL_TRANSFORMS_H
namespace Ultima {
namespace Ultima8 {
enum PalTransforms {
// Normal untransformed palette
Transform_None = 0,
// O[i] = I[r]*0.375 + I[g]*0.5 + I[b]*0.125;
Transform_Greyscale = 1,
// O[r] = 0;
Transform_NoRed = 2,
// O[i] = (I[i] + Grey)*0.25 + 0.1875;
Transform_RainStorm = 3,
// O[r] = (I[r] + Grey)*0.5 + 0.1875;
// O[g] = I[g]*0.5 + Grey*0.25;
// O[b] = I[b]*0.5;
Transform_FireStorm = 4,
// O[i] = I[i]*2 -Grey;
Transform_Saturate = 5,
// O[g] = I[r]; O[b] = I[g]; O[r] = I[b];
Transform_GBR = 6,
// O[b] = I[r]; O[r] = I[g]; O[g] = I[b];
Transform_BRG = 7,
// Any value beyond this is invalid in savegames.
Transform_Invalid = 8
};
} // End of namespace Ultima8
} // End of namespace Ultima
#endif

View File

@@ -0,0 +1,179 @@
/* ScummVM - Graphic Adventure Engine
*
* ScummVM is the legal property of its developers, whose names
* are too numerous to list here. Please refer to the COPYRIGHT
* file distributed with this source distribution.
*
* This program is free software: you can redistribute it and/or modify
* it under the terms of the GNU General Public License as published by
* the Free Software Foundation, either version 3 of the License, or
* (at your option) any later version.
*
* This program is distributed in the hope that it will be useful,
* but WITHOUT ANY WARRANTY; without even the implied warranty of
* MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
* GNU General Public License for more details.
*
* You should have received a copy of the GNU General Public License
* along with this program. If not, see <http://www.gnu.org/licenses/>.
*
*/
#include "ultima/ultima8/gfx/palette.h"
#include "ultima/ultima8/gfx/texture.h"
#include "ultima/ultima8/misc/debugger.h"
namespace Ultima {
namespace Ultima8 {
void Palette::load(Common::ReadStream &rs, Common::ReadStream &xformrs) {
load(rs);
for (int i = 0; i < 256; i++)
_xform_untransformed[i] = xformrs.readUint32LE();
}
void Palette::load(Common::ReadStream &rs) {
int i;
byte raw[768];
rs.read(raw, 768);
// convert from 0-63 to 0-255 _palette
for (i = 0; i < 256; i++) {
set(i, PALETTE_6BIT_TO_8BIT(raw[i * 3]), PALETTE_6BIT_TO_8BIT(raw[i * 3 + 1]), PALETTE_6BIT_TO_8BIT(raw[i * 3 + 2]));
}
for (i = 0; i < 256; i++)
_xform_untransformed[i] = 0;
// Setup the transformation _matrix
_matrix[0] = 0x800;
_matrix[1] = 0;
_matrix[2] = 0;
_matrix[3] = 0;
_matrix[4] = 0;
_matrix[5] = 0x800;
_matrix[6] = 0;
_matrix[7] = 0;
_matrix[8] = 0;
_matrix[9] = 0;
_matrix[10] = 0x800;
_matrix[11] = 0;
_transform = Transform_None;
}
void Palette::transformRGB(int &r_, int &g_, int &b_) const {
const int r = r_;
const int g = g_;
const int b = b_;
r_ = (r * _matrix[0] + g * _matrix[1] + b * _matrix[2] + 255 * _matrix[3]) / 2048;
if (r_ < 0) r_ = 0;
if (r_ > 0xFF) r_ = 0xFF;
g_ = (r * _matrix[4] + g * _matrix[5] + b * _matrix[6] + 255 * _matrix[7]) / 2048;
if (g_ < 0) g_ = 0;
if (g_ > 0xFF) g_ = 0xFF;
b_ = (r * _matrix[8] + g * _matrix[9] + b * _matrix[10] + 255 * _matrix[11]) / 2048;
if (b_ < 0) b_ = 0;
if (b_ > 0xFF) b_ = 0xFF;
}
void Palette::updateNativeMap(const Graphics::PixelFormat &format, int maxindex) {
if (maxindex == 0)
maxindex = size();
for (int i = 0; i < maxindex; i++) {
int32 r, g, b;
byte sr, sg, sb;
// Normal palette
get(i, sr, sg, sb);
if (format.isCLUT8()) {
_native_untransformed[i] = i;
} else {
_native_untransformed[i] = format.RGBToColor(sr, sg, sb);
}
r = _matrix[0] * sr +
_matrix[1] * sg +
_matrix[2] * sb +
_matrix[3] * 255;
if (r < 0)
r = 0;
if (r > 0x7F800)
r = 0x7F800;
r = r >> 11;
g = _matrix[4] * sr +
_matrix[5] * sg +
_matrix[6] * sb +
_matrix[7] * 255;
if (g < 0)
g = 0;
if (g > 0x7F800)
g = 0x7F800;
g = g >> 11;
b = _matrix[8] * sr +
_matrix[9] * sg +
_matrix[10] * sb +
_matrix[11] * 255;
if (b < 0)
b = 0;
if (b > 0x7F800)
b = 0x7F800;
b = b >> 11;
// Transformed normal palette
if (format.isCLUT8()) {
_native[i] = findBestColor(static_cast<uint8>(r),
static_cast<uint8>(g),
static_cast<uint8>(b));
} else {
_native[i] = format.RGBToColor(static_cast<uint8>(r),
static_cast<uint8>(g),
static_cast<uint8>(b));
}
// Transformed XFORM palette (Uses the TEX32 format)
if (TEX32_A(_xform_untransformed[i])) {
r = _matrix[0] * TEX32_R(_xform_untransformed[i]) +
_matrix[1] * TEX32_G(_xform_untransformed[i]) +
_matrix[2] * TEX32_B(_xform_untransformed[i]) +
_matrix[3] * 255;
if (r < 0)
r = 0;
if (r > 0x7F800)
r = 0x7F800;
g = _matrix[4] * TEX32_R(_xform_untransformed[i]) +
_matrix[5] * TEX32_G(_xform_untransformed[i]) +
_matrix[6] * TEX32_B(_xform_untransformed[i]) +
_matrix[7] * 255;
if (g < 0)
g = 0;
if (g > 0x7F800)
g = 0x7F800;
b = _matrix[8] * TEX32_R(_xform_untransformed[i]) +
_matrix[9] * TEX32_G(_xform_untransformed[i]) +
_matrix[10] * TEX32_B(_xform_untransformed[i]) +
_matrix[11] * 255;
if (b < 0)
b = 0;
if (b > 0x7F800)
b = 0x7F800;
_xform[i] = TEX32_PACK_RGBA(static_cast<uint8>(r >> 11),
static_cast<uint8>(g >> 11),
static_cast<uint8>(b >> 11),
TEX32_A(_xform_untransformed[i]));
} else
_xform[i] = 0;
}
}
} // End of namespace Ultima8
} // End of namespace Ultima

View File

@@ -0,0 +1,77 @@
/* ScummVM - Graphic Adventure Engine
*
* ScummVM is the legal property of its developers, whose names
* are too numerous to list here. Please refer to the COPYRIGHT
* file distributed with this source distribution.
*
* This program is free software: you can redistribute it and/or modify
* it under the terms of the GNU General Public License as published by
* the Free Software Foundation, either version 3 of the License, or
* (at your option) any later version.
*
* This program is distributed in the hope that it will be useful,
* but WITHOUT ANY WARRANTY; without even the implied warranty of
* MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
* GNU General Public License for more details.
*
* You should have received a copy of the GNU General Public License
* along with this program. If not, see <http://www.gnu.org/licenses/>.
*
*/
#ifndef ULTIMA8_GFX_PALETTE_H
#define ULTIMA8_GFX_PALETTE_H
#include "graphics/palette.h"
#include "graphics/pixelformat.h"
#include "ultima/ultima8/gfx/pal_transforms.h"
namespace Common {
class ReadStream;
}
namespace Ultima {
namespace Ultima8 {
class Palette: public Graphics::Palette {
public:
Palette() : Graphics::Palette(256) {}
void load(Common::ReadStream &rs, Common::ReadStream &xformrs);
void load(Common::ReadStream &rs);
// Transform a single set of rgb values based on the current matrix.
// Not designed for speed - just useful for one-offs.
void transformRGB(int &r, int &g, int &b) const;
// Update the palette maps based on the pixel format and the current transformation matrix
void updateNativeMap(const Graphics::PixelFormat &format, int maxindex = 0);
// Untransformed pixel format palette map
uint32 _native_untransformed[256];
// Transformed pixel format palette map
uint32 _native[256];
// Untransformed XFORM ARGB palette map
uint32 _xform_untransformed[256];
// Transformed XFORM ARGB palette map
uint32 _xform[256];
// Colour transformation matrix (for fades, hue shifts)
// Applied by the RenderSurface (fixed -4.11)
// R = R*matrix[0] + G*matrix[1] + B*matrix[2] + matrix[3];
// G = R*matrix[4] + G*matrix[5] + B*matrix[6] + matrix[7];
// B = R*matrix[8] + G*matrix[9] + B*matrix[10] + matrix[11];
// A = A;
int16 _matrix[12];
// The current palette transform
PalTransforms _transform;
};
} // End of namespace Ultima8
} // End of namespace Ultima
#endif

View File

@@ -0,0 +1,360 @@
/* ScummVM - Graphic Adventure Engine
*
* ScummVM is the legal property of its developers, whose names
* are too numerous to list here. Please refer to the COPYRIGHT
* file distributed with this source distribution.
*
* This program is free software: you can redistribute it and/or modify
* it under the terms of the GNU General Public License as published by
* the Free Software Foundation, either version 3 of the License, or
* (at your option) any later version.
*
* This program is distributed in the hope that it will be useful,
* but WITHOUT ANY WARRANTY; without even the implied warranty of
* MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
* GNU General Public License for more details.
*
* You should have received a copy of the GNU General Public License
* along with this program. If not, see <http://www.gnu.org/licenses/>.
*
*/
#include "ultima/ultima8/gfx/palette_fader_process.h"
#include "ultima/ultima8/gfx/palette.h"
#include "ultima/ultima8/gfx/texture.h"
#include "ultima/ultima8/kernel/kernel.h"
#include "ultima/ultima8/ultima8.h"
namespace Ultima {
namespace Ultima8 {
PaletteFaderProcess *PaletteFaderProcess::_fader = nullptr;
// p_dynamic_class stuff
DEFINE_RUNTIME_CLASSTYPE_CODE(PaletteFaderProcess)
PaletteFaderProcess::PaletteFaderProcess() : Process(), _priority(0),
_counter(0), _maxCounter(0) {
}
PaletteFaderProcess::PaletteFaderProcess(PalTransforms trans,
int priority, int frames) : _priority(priority),
_counter(frames), _maxCounter(frames) {
PaletteManager *pm = PaletteManager::get_instance();
Palette *pal = pm->getPalette(PaletteManager::Pal_Game);
for (int i = 0; i < 12; i++) _oldMatrix[i] = pal->_matrix[i];
pm->getTransformMatrix(_newMatrix, trans);
pal->_transform = trans;
}
PaletteFaderProcess::PaletteFaderProcess(uint32 col32, bool from,
int priority, int frames, bool current) : _priority(priority),
_counter(frames), _maxCounter(frames) {
PaletteManager *pm = PaletteManager::get_instance();
Palette *pal = pm->getPalette(PaletteManager::Pal_Game);
if (!from) {
if (current)
for (int i = 0; i < 12; i++) _oldMatrix[i] = pal->_matrix[i];
else
pm->getTransformMatrix(_oldMatrix, pal->_transform);
pm->getTransformMatrix(_newMatrix, col32);
} else {
pm->getTransformMatrix(_oldMatrix, col32);
if (current)
for (int i = 0; i < 12; i++) _newMatrix[i] = pal->_matrix[i];
else
pm->getTransformMatrix(_newMatrix, pal->_transform);
}
}
PaletteFaderProcess::PaletteFaderProcess(const int16 from[12], const int16 to[12],
int priority, int frames) : _priority(priority),
_counter(frames), _maxCounter(frames) {
int i;
for (i = 0; i < 12; i++) _oldMatrix[i] = from[i];
for (i = 0; i < 12; i++) _newMatrix[i] = to[i];
}
PaletteFaderProcess::~PaletteFaderProcess(void) {
if (_fader == this)
_fader = nullptr;
}
void PaletteFaderProcess::run() {
int16 matrix[12];
for (int i = 0; i < 12; i++) {
int32 o = _oldMatrix[i] * _counter;
int32 n = _newMatrix[i] * (_maxCounter - _counter);
matrix[i] = static_cast<int16>((o + n) / _maxCounter);
}
PaletteManager::get_instance()->transformPalette(
PaletteManager::Pal_Game,
matrix);
if (!_counter--) terminate();
}
void PaletteFaderProcess::saveData(Common::WriteStream *ws) {
Process::saveData(ws);
ws->writeUint32LE(static_cast<uint32>(_priority));
ws->writeUint32LE(static_cast<uint32>(_counter));
ws->writeUint32LE(static_cast<uint32>(_maxCounter));
unsigned int i;
for (i = 0; i < 12; ++i)
ws->writeUint16LE(_oldMatrix[i]);
for (i = 0; i < 12; ++i)
ws->writeUint16LE(_newMatrix[i]);
}
bool PaletteFaderProcess::loadData(Common::ReadStream *rs, uint32 version) {
if (!Process::loadData(rs, version)) return false;
_priority = static_cast<int>(rs->readUint32LE());
_counter = static_cast<int>(rs->readUint32LE());
_maxCounter = static_cast<int>(rs->readUint32LE());
unsigned int i;
for (i = 0; i < 12; ++i)
_oldMatrix[i] = rs->readUint16LE();
for (i = 0; i < 12; ++i)
_newMatrix[i] = rs->readUint16LE();
_fader = this; //static
return true;
}
uint32 PaletteFaderProcess::I_fadeToPaletteTransform(const uint8 *args,
unsigned int /*argsize*/) {
ARG_UINT16(transform);
ARG_UINT16(priority);
// If current _fader has higher _priority, we do nothing
if (_fader && _fader->_priority > priority)
return 0;
else if (_fader && !_fader->is_terminated())
_fader->terminate();
_fader = new PaletteFaderProcess(static_cast<PalTransforms>(transform),
priority, 45);
return Kernel::get_instance()->addProcess(_fader);
}
uint32 PaletteFaderProcess::I_fadeToBlack(const uint8 *args,
unsigned int argsize) {
if (_fader && _fader->_priority > 0x7FFF)
return 0;
else if (_fader && !_fader->is_terminated())
_fader->terminate();
int nsteps = (GAME_IS_U8 ? 30 : 40);
if (argsize > 0) {
ARG_UINT16(n);
nsteps = n;
if (argsize > 2) {
ARG_UINT16(unk);
warning("PaletteFaderProcess::I_fadeToBlackWithParam: Ignoring param %d", unk);
}
}
_fader = new PaletteFaderProcess(TEX32_PACK_RGBA(0x00, 0x00, 0x00, 0x00), false, 0x7FFF, nsteps, true);
return Kernel::get_instance()->addProcess(_fader);
}
uint32 PaletteFaderProcess::I_fadeFromBlack(const uint8 *args,
unsigned int argsize) {
if (_fader && _fader->_priority > 0x7FFF)
return 0;
else if (_fader && !_fader->is_terminated())
_fader->terminate();
int nsteps = (GAME_IS_U8 ? 30 : 40);
if (argsize > 0) {
ARG_UINT16(n);
nsteps = n;
if (argsize > 2) {
ARG_UINT16(unk);
warning("PaletteFaderProcess::I_fadeFromBlackWithParam: Ignoring param %d", unk);
}
}
_fader = new PaletteFaderProcess(TEX32_PACK_RGBA(0x00, 0x00, 0x00, 0x00), true, 0x7FFF, nsteps, false);
return Kernel::get_instance()->addProcess(_fader);
}
uint32 PaletteFaderProcess::I_fadeToWhite(const uint8 * /*args*/,
unsigned int /*argsize*/) {
if (_fader && _fader->_priority > 0x7FFF)
return 0;
else if (_fader && !_fader->is_terminated())
_fader->terminate();
_fader = new PaletteFaderProcess(TEX32_PACK_RGBA(0xFF, 0xFF, 0xFF, 0x00), false, 0x7FFF, 30, true);
return Kernel::get_instance()->addProcess(_fader);
}
uint32 PaletteFaderProcess::I_fadeFromWhite(const uint8 * /*args*/,
unsigned int /*argsize*/) {
if (_fader && _fader->_priority > 0x7FFF)
return 0;
else if (_fader && !_fader->is_terminated())
_fader->terminate();
_fader = new PaletteFaderProcess(TEX32_PACK_RGBA(0xFF, 0xFF, 0xFF, 0x00), true, 0x7FFF, 30, false);
return Kernel::get_instance()->addProcess(_fader);
}
uint32 PaletteFaderProcess::I_lightningBolt(const uint8 * /*args*/,
unsigned int /*argsize*/) {
if (_fader && _fader->_priority > -1)
return 0;
else if (_fader && !_fader->is_terminated())
_fader->terminate();
_fader = new PaletteFaderProcess(TEX32_PACK_RGBA(0xCF, 0xCF, 0xCF, 0x3F), true, -1, 10, false);
return Kernel::get_instance()->addProcess(_fader);
}
static const int16 NoFadeMatrix[] = {0x800, 0, 0, 0,
0, 0x800, 0, 0,
0, 0, 0x800, 0
};
// Transform used in Crusader is Yib. We only care about Y:
// Y = (r * 0.299 + g * 0.587 + b * 0.114)
static const int16 GreyFadeMatrix[] = {612, 1202, 233, 0,
612, 1202, 233, 0,
612, 1202, 233, 0
};
static const int16 AllWhiteMatrix[] = {0, 0, 0, 0x7ff,
0, 0, 0, 0x7ff,
0, 0, 0, 0x7ff
};
static const int16 AllBlackMatrix[] = {0, 0, 0, 0,
0, 0, 0, 0,
0, 0, 0, 0
};
uint32 PaletteFaderProcess::I_fadeToGreyScale(const uint8 * /*args*/,
unsigned int /*argsize*/) {
if (_fader && _fader->_priority > 0x7FFF) return 0;
else if (_fader) _fader->terminate();
_fader = new PaletteFaderProcess(NoFadeMatrix, GreyFadeMatrix, 0x7FFF, 1);
return Kernel::get_instance()->addProcess(_fader);
}
uint32 PaletteFaderProcess::I_fadeToGivenColor(const uint8 *args,
unsigned int /*argsize*/) {
if (_fader && _fader->_priority > 0x7FFF) return 0;
else if (_fader) _fader->terminate();
// TODO: guessing that color order should be same as other one below?
ARG_UINT8(r);
ARG_UINT8(g);
ARG_UINT8(b);
ARG_UINT16(nsteps);
ARG_UINT16(unk);
uint32 target = (r << 16) | (g << 8) | (b << 0);
warning("PaletteFaderProcess::I_fadeToGivenColor: Ignoring param %d", unk);
_fader = new PaletteFaderProcess(target, true, 0x7FFF, nsteps, false);
return Kernel::get_instance()->addProcess(_fader);
}
uint32 PaletteFaderProcess::I_fadeToGamePal(const uint8 *args,
unsigned int argsize) {
if (_fader && _fader->_priority > 0x7FFF)
return 0;
else if (_fader && !_fader->is_terminated())
_fader->terminate();
int nsteps = (GAME_IS_U8 ? 30 : 20);
if (argsize > 0) {
ARG_UINT16(n);
nsteps = n;
if (argsize > 2) {
ARG_UINT16(unk);
warning("PaletteFaderProcess::I_fadeToGamePalWithParam: Ignoring param %d", unk);
}
}
int16 curmatrix[12];
PaletteManager *pm = PaletteManager::get_instance();
pm->getTransformMatrix(curmatrix, PaletteManager::Pal_Game);
_fader = new PaletteFaderProcess(curmatrix, NoFadeMatrix, 0x7FFF, nsteps);
return Kernel::get_instance()->addProcess(_fader);
}
uint32 PaletteFaderProcess::I_jumpToGreyScale(const uint8 * /*args*/,
unsigned int /*argsize*/) {
if (_fader && _fader->_priority > 0x7FFF) return 0;
else if (_fader) _fader->terminate();
PaletteManager::get_instance()->transformPalette(PaletteManager::Pal_Game,
GreyFadeMatrix);
return 0;
}
uint32 PaletteFaderProcess::I_jumpToAllBlack(const uint8 * /*args*/,
unsigned int /*argsize*/) {
if (_fader && _fader->_priority > 0x7FFF) return 0;
else if (_fader) _fader->terminate();
PaletteManager::get_instance()->transformPalette(PaletteManager::Pal_Game,
AllBlackMatrix);
return 0;
}
uint32 PaletteFaderProcess::I_jumpToAllWhite(const uint8 * /*args*/,
unsigned int /*argsize*/) {
if (_fader && _fader->_priority > 0x7FFF) return 0;
else if (_fader) _fader->terminate();
PaletteManager::get_instance()->transformPalette(PaletteManager::Pal_Game,
AllWhiteMatrix);
return 0;
}
uint32 PaletteFaderProcess::I_jumpToAllGivenColor(const uint8 *args,
unsigned int /*argsize*/) {
if (_fader && _fader->_priority > 0x7FFF) return 0;
else if (_fader) _fader->terminate();
ARG_UINT8(r);
ARG_UINT8(g);
ARG_UINT8(b);
// Transform matrix goes 0~2048, scale 0-63 vals from input
const int16 r16 = static_cast<int16>(r) * 32;
const int16 g16 = static_cast<int16>(g) * 32;
const int16 b16 = static_cast<int16>(b) * 32;
const int16 color_matrix[] = {0, 0, 0, r16,
0, 0, 0, g16,
0, 0, 0, b16
};
PaletteManager::get_instance()->transformPalette(PaletteManager::Pal_Game,
color_matrix);
return 0;
}
uint32 PaletteFaderProcess::I_jumpToNormalPalette(const uint8 * /*args*/,
unsigned int /*argsize*/) {
if (_fader && _fader->_priority > 0x7FFF) return 0;
else if (_fader) _fader->terminate();
PaletteManager::get_instance()->transformPalette(PaletteManager::Pal_Game,
NoFadeMatrix);
return 0;
}
} // End of namespace Ultima8
} // End of namespace Ultima

View File

@@ -0,0 +1,75 @@
/* ScummVM - Graphic Adventure Engine
*
* ScummVM is the legal property of its developers, whose names
* are too numerous to list here. Please refer to the COPYRIGHT
* file distributed with this source distribution.
*
* This program is free software: you can redistribute it and/or modify
* it under the terms of the GNU General Public License as published by
* the Free Software Foundation, either version 3 of the License, or
* (at your option) any later version.
*
* This program is distributed in the hope that it will be useful,
* but WITHOUT ANY WARRANTY; without even the implied warranty of
* MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
* GNU General Public License for more details.
*
* You should have received a copy of the GNU General Public License
* along with this program. If not, see <http://www.gnu.org/licenses/>.
*
*/
#ifndef ULTIMA8_GFX_PALETTEFADERPROCESS_H
#define ULTIMA8_GFX_PALETTEFADERPROCESS_H
#include "ultima/ultima8/kernel/process.h"
#include "ultima/ultima8/gfx/palette_manager.h"
#include "ultima/ultima8/usecode/intrinsics.h"
#include "ultima/ultima8/misc/classtype.h"
namespace Ultima {
namespace Ultima8 {
/** A process to fade the palette from one transform matrix to another */
class PaletteFaderProcess : public Process {
int _priority;
int32 _counter;
int32 _maxCounter;
int16 _oldMatrix[12]; // Fixed point -4.11
int16 _newMatrix[12];
public:
static PaletteFaderProcess *_fader;
// p_dynamic_class stuff
ENABLE_RUNTIME_CLASSTYPE()
PaletteFaderProcess();
PaletteFaderProcess(PalTransforms trans, int priority, int frames);
PaletteFaderProcess(uint32 rgba, bool from, int priority, int frames, bool current);
PaletteFaderProcess(const int16 from[12], const int16 to[12], int priority, int frames);
~PaletteFaderProcess(void) override;
void run() override;
INTRINSIC(I_fadeToPaletteTransform);
INTRINSIC(I_fadeToBlack);
INTRINSIC(I_fadeFromWhite);
INTRINSIC(I_fadeToWhite);
INTRINSIC(I_fadeFromBlack);
INTRINSIC(I_lightningBolt);
INTRINSIC(I_fadeToGreyScale);
INTRINSIC(I_fadeToGivenColor);
INTRINSIC(I_fadeToGamePal);
INTRINSIC(I_jumpToGreyScale);
INTRINSIC(I_jumpToAllBlack);
INTRINSIC(I_jumpToAllWhite);
INTRINSIC(I_jumpToAllGivenColor);
INTRINSIC(I_jumpToNormalPalette);
bool loadData(Common::ReadStream *rs, uint32 version);
void saveData(Common::WriteStream *ws) override;
};
} // End of namespace Ultima8
} // End of namespace Ultima
#endif

View File

@@ -0,0 +1,358 @@
/* ScummVM - Graphic Adventure Engine
*
* ScummVM is the legal property of its developers, whose names
* are too numerous to list here. Please refer to the COPYRIGHT
* file distributed with this source distribution.
*
* This program is free software: you can redistribute it and/or modify
* it under the terms of the GNU General Public License as published by
* the Free Software Foundation, either version 3 of the License, or
* (at your option) any later version.
*
* This program is distributed in the hope that it will be useful,
* but WITHOUT ANY WARRANTY; without even the implied warranty of
* MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
* GNU General Public License for more details.
*
* You should have received a copy of the GNU General Public License
* along with this program. If not, see <http://www.gnu.org/licenses/>.
*
*/
#include "ultima/ultima8/misc/debugger.h"
#include "ultima/ultima8/gfx/palette_manager.h"
#include "ultima/ultima8/gfx/palette.h"
#include "ultima/ultima8/gfx/texture.h"
namespace Ultima {
namespace Ultima8 {
PaletteManager *PaletteManager::_paletteManager = nullptr;
PaletteManager::PaletteManager(const Graphics::PixelFormat &format) : _format(format) {
debug(1, "Creating PaletteManager...");
_paletteManager = this;
}
PaletteManager::~PaletteManager() {
reset();
debug(1, "Destroying PaletteManager...");
_paletteManager = nullptr;
}
// Reset the Palette Manager
void PaletteManager::reset() {
debug(1, "Resetting PaletteManager...");
for (unsigned int i = 0; i < _palettes.size(); ++i)
delete _palettes[i];
_palettes.clear();
}
void PaletteManager::updatedPalette(PalIndex index, int maxindex) {
Palette *pal = getPalette(index);
if (pal)
pal->updateNativeMap(_format, maxindex);
}
// Reset all the transforms back to default
void PaletteManager::resetTransforms() {
debug(1, "Resetting Palette Transforms...");
int16 matrix[12];
getTransformMatrix(matrix, Transform_None);
for (unsigned int i = 0; i < _palettes.size(); ++i) {
Palette *pal = _palettes[i];
if (!pal) continue;
pal->_transform = Transform_None;
for (int j = 0; j < 12; j++)
pal->_matrix[j] = matrix[j];
pal->updateNativeMap(_format);
}
}
bool PaletteManager::loadTransforms(Common::ReadStream& rs) {
int16 matrix[12];
for (int i = 0; i < 12; i++)
matrix[i] = rs.readUint16LE();
PaletteManager::get_instance()->transformPalette(PaletteManager::Pal_Game, matrix);
Palette *pal = getPalette(PaletteManager::Pal_Game);
pal->_transform = static_cast<PalTransforms>(rs.readUint16LE());
if (pal->_transform >= Transform_Invalid) {
warning("Invalid palette transform %d. Corrupt savegame?", static_cast<int>(pal->_transform));
return false;
}
return true;
}
void PaletteManager::saveTransforms(Common::WriteStream& ws) {
Palette *pal = getPalette(PaletteManager::Pal_Game);
for (int i = 0; i < 12; i++)
ws.writeUint16LE(pal->_matrix[i]);
ws.writeUint16LE(pal->_transform);
}
void PaletteManager::PixelFormatChanged(const Graphics::PixelFormat &format) {
_format = format;
// Create native _palettes for all currently loaded _palettes
for (unsigned int i = 0; i < _palettes.size(); ++i)
if (_palettes[i])
_palettes[i]->updateNativeMap(_format);
}
void PaletteManager::load(PalIndex index, Common::ReadStream &rs, Common::ReadStream &xformrs) {
if (_palettes.size() <= static_cast<unsigned int>(index))
_palettes.resize(index + 1);
if (_palettes[index])
delete _palettes[index];
Palette *pal = new Palette;
pal->load(rs, xformrs);
pal->updateNativeMap(_format);
_palettes[index] = pal;
}
void PaletteManager::load(PalIndex index, Common::ReadStream &rs) {
if (_palettes.size() <= static_cast<unsigned int>(index))
_palettes.resize(index + 1);
if (_palettes[index])
delete _palettes[index];
Palette *pal = new Palette;
pal->load(rs);
pal->updateNativeMap(_format);
_palettes[index] = pal;
}
void PaletteManager::duplicate(PalIndex src, PalIndex dest) {
Palette *newpal = getPalette(dest);
if (!newpal)
newpal = new Palette;
Palette *srcpal = getPalette(src);
if (srcpal)
*newpal = *srcpal;
newpal->updateNativeMap(_format);
if (_palettes.size() <= static_cast<unsigned int>(dest))
_palettes.resize(dest + 1);
_palettes[dest] = newpal;
}
Palette *PaletteManager::getPalette(PalIndex index) {
if (static_cast<unsigned int>(index) >= _palettes.size())
return nullptr;
return _palettes[index];
}
void PaletteManager::transformPalette(PalIndex index, const int16 matrix[12]) {
Palette *pal = getPalette(index);
if (pal) {
for (int i = 0; i < 12; i++)
pal->_matrix[i] = matrix[i];
pal->updateNativeMap(_format);
}
}
void PaletteManager::untransformPalette(PalIndex index) {
Palette *pal = getPalette(index);
if (!pal) return;
pal->_transform = Transform_None;
int16 matrix[12];
getTransformMatrix(matrix, Transform_None);
transformPalette(index, matrix);
}
bool PaletteManager::getTransformMatrix(int16 matrix[12], PalIndex index) {
Palette *pal = getPalette(index);
if (!pal) return false;
for (int i = 0; i < 12; i++)
matrix[i] = pal->_matrix[i];
return true;
}
void PaletteManager::getTransformMatrix(int16 matrix[12], PalTransforms trans) {
switch (trans) {
// Normal untransformed palette
case Transform_None: {
matrix[0] = 0x800;
matrix[1] = 0;
matrix[2] = 0;
matrix[3] = 0;
matrix[4] = 0;
matrix[5] = 0x800;
matrix[6] = 0;
matrix[7] = 0;
matrix[8] = 0;
matrix[9] = 0;
matrix[10] = 0x800;
matrix[11] = 0;
}
break;
// O[i] = I[r]*0.375 + I[g]*0.5 + I[b]*0.125;
case Transform_Greyscale: {
for (int i = 0; i < 3; i++) {
matrix[i * 4 + 0] = 0x0300;
matrix[i * 4 + 1] = 0x0400;
matrix[i * 4 + 2] = 0x0100;
matrix[i * 4 + 3] = 0;
}
}
break;
// O[r] = 0;
case Transform_NoRed: {
matrix[0] = 0;
matrix[1] = 0;
matrix[2] = 0;
matrix[3] = 0;
matrix[4] = 0;
matrix[5] = 0x800;
matrix[6] = 0;
matrix[7] = 0;
matrix[8] = 0;
matrix[9] = 0;
matrix[10] = 0x800;
matrix[11] = 0;
}
break;
// O[i] = (I[i] + Grey)*0.25 + 0.1875;
case Transform_RainStorm: {
for (int i = 0; i < 3; i++) {
matrix[i * 4 + 0] = (0x0300 * 0x0200) >> 11;
matrix[i * 4 + 1] = (0x0400 * 0x0200) >> 11;
matrix[i * 4 + 2] = (0x0100 * 0x0200) >> 11;
matrix[i * 4 + i] += 0x0200;
matrix[i * 4 + 3] = 0x0180;
}
}
break;
// O[r] = I[r]*0.5 + Grey*0.5 + 0.1875;
// O[g] = I[g]*0.5 + Grey*0.25;
// O[b] = I[b]*0.5;
case Transform_FireStorm: {
// O[r] = I[r]*0.5 + Grey*0.5 + 0.1875;
matrix[0] = ((0x0300 * 0x0400) >> 11) + 0x0400;
matrix[1] = (0x0400 * 0x0400) >> 11;
matrix[2] = (0x0100 * 0x0400) >> 11;
matrix[3] = 0x0180;
// O[g] = I[g]*0.5 + Grey*0.25;
matrix[4] = (0x0300 * 0x0200) >> 11;
matrix[5] = ((0x0400 * 0x0200) >> 11) + 0x0400;
matrix[6] = (0x0100 * 0x0200) >> 11;
matrix[7] = 0;
// O[b] = I[b]*0.5;
matrix[8] = 0;
matrix[9] = 0;
matrix[10] = 0x0400;
matrix[11] = 0;
}
break;
// O[i] = I[i]*2 -Grey;
case Transform_Saturate: {
for (int i = 0; i < 3; i++) {
matrix[i * 4 + 0] = -0x0300;
matrix[i * 4 + 1] = -0x0400;
matrix[i * 4 + 2] = -0x0100;
matrix[i * 4 + 3] = 0;
matrix[i * 4 + i] += 0x1000;
}
}
break;
// O[b] = I[r]; O[r] = I[g]; O[g] = I[b];
case Transform_BRG: {
matrix[0] = 0;
matrix[1] = 0x800;
matrix[2] = 0;
matrix[3] = 0;
matrix[4] = 0;
matrix[5] = 0;
matrix[6] = 0x800;
matrix[7] = 0;
matrix[8] = 0x800;
matrix[9] = 0;
matrix[10] = 0;
matrix[11] = 0;
}
break;
// O[g] = I[r]; O[b] = I[g]; O[r] = I[b];
case Transform_GBR: {
matrix[0] = 0;
matrix[1] = 0;
matrix[2] = 0x800;
matrix[3] = 0;
matrix[4] = 0x800;
matrix[5] = 0;
matrix[6] = 0;
matrix[7] = 0;
matrix[8] = 0;
matrix[9] = 0x800;
matrix[10] = 0;
matrix[11] = 0;
}
break;
// Unknown
default: {
warning("Unknown Palette Transformation: %d", trans);
matrix[0] = 0x800;
matrix[1] = 0;
matrix[2] = 0;
matrix[3] = 0;
matrix[4] = 0;
matrix[5] = 0x800;
matrix[6] = 0;
matrix[7] = 0;
matrix[8] = 0;
matrix[9] = 0;
matrix[10] = 0x800;
matrix[11] = 0;
}
break;
}
}
void PaletteManager::getTransformMatrix(int16 matrix[12], uint32 col32) {
matrix[0] = (static_cast<int32>(TEX32_A(col32)) * 0x800) / 255;
matrix[1] = 0;
matrix[2] = 0;
matrix[3] = (static_cast<int32>(TEX32_R(col32)) * 0x800) / 255;
matrix[4] = 0;
matrix[5] = (static_cast<int32>(TEX32_A(col32)) * 0x800) / 255;
matrix[6] = 0;
matrix[7] = (static_cast<int32>(TEX32_G(col32)) * 0x800) / 255;
matrix[8] = 0;
matrix[9] = 0;
matrix[10] = (static_cast<int32>(TEX32_A(col32)) * 0x800) / 255;
matrix[11] = (static_cast<int32>(TEX32_B(col32)) * 0x800) / 255;
}
} // End of namespace Ultima8
} // End of namespace Ultima

View File

@@ -0,0 +1,103 @@
/* ScummVM - Graphic Adventure Engine
*
* ScummVM is the legal property of its developers, whose names
* are too numerous to list here. Please refer to the COPYRIGHT
* file distributed with this source distribution.
*
* This program is free software: you can redistribute it and/or modify
* it under the terms of the GNU General Public License as published by
* the Free Software Foundation, either version 3 of the License, or
* (at your option) any later version.
*
* This program is distributed in the hope that it will be useful,
* but WITHOUT ANY WARRANTY; without even the implied warranty of
* MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
* GNU General Public License for more details.
*
* You should have received a copy of the GNU General Public License
* along with this program. If not, see <http://www.gnu.org/licenses/>.
*
*/
#ifndef ULTIMA8_GFX_PALETTEMANAGER_H
#define ULTIMA8_GFX_PALETTEMANAGER_H
#include "ultima/shared/std/containers.h"
#include "graphics/pixelformat.h"
#include "ultima/ultima8/gfx/pal_transforms.h"
namespace Ultima {
namespace Ultima8 {
class Palette;
class PaletteManager {
public:
explicit PaletteManager(const Graphics::PixelFormat &format);
~PaletteManager();
static PaletteManager *get_instance() {
return _paletteManager;
}
enum PalIndex {
Pal_Game = 0,
Pal_Movie = 1,
Pal_Diff = 2, // Crusaders only - difficulty screen??
Pal_Misc = 3, // Crusaders only - game menu
Pal_Misc2 = 4, // Crusaders only - ??
Pal_Star = 5, // Crusaders only - ??
Pal_Cred = 6, // Crusader: No regret only (but mentioned in the no remorse exe!)
Pal_JPFontStart = 16
};
void load(PalIndex index, Common::ReadStream &rs, Common::ReadStream &xformrs);
void load(PalIndex index, Common::ReadStream &rs);
Palette *getPalette(PalIndex index);
uint getNumPalettes() const { return _palettes.size(); }
void duplicate(PalIndex src, PalIndex dest);
//! Re-convert a palette to native format after modifying it. If maxindex is set,
//! only recalculate color indexes up to that value.
void updatedPalette(PalIndex index, int maxindex = 0);
//! Apply a transform matrix to a palette (-4.11 fixed)
void transformPalette(PalIndex index, const int16 matrix[12]);
//! reset the transformation matrix of a palette
void untransformPalette(PalIndex index);
//! Get the current TransformMatrix for the given index
bool getTransformMatrix(int16 matrix[12], PalIndex index);
// Get a TransformMatrix from a PalTransforms value (-4.11 fixed)
static void getTransformMatrix(int16 matrix[12],
PalTransforms trans);
// Create a custom Transform Matrix from RGBA col32. (-4.11 fixed)
// Alpha will set how much of original palette to keep. 0 = keep none
static void getTransformMatrix(int16 matrix[12], uint32 col32);
//! Change the pixel format used by the PaletteManager
void PixelFormatChanged(const Graphics::PixelFormat &format);
//! Reset the Palette Manager
void reset();
//! Reset all the transforms back to default
void resetTransforms();
bool loadTransforms(Common::ReadStream &rs);
void saveTransforms(Common::WriteStream &ws);
private:
Std::vector<Palette *> _palettes;
Graphics::PixelFormat _format;
static PaletteManager *_paletteManager;
};
} // End of namespace Ultima8
} // End of namespace Ultima
#endif

View File

@@ -0,0 +1,174 @@
/* ScummVM - Graphic Adventure Engine
*
* ScummVM is the legal property of its developers, whose names
* are too numerous to list here. Please refer to the COPYRIGHT
* file distributed with this source distribution.
*
* This program is free software: you can redistribute it and/or modify
* it under the terms of the GNU General Public License as published by
* the Free Software Foundation, either version 3 of the License, or
* (at your option) any later version.
*
* This program is distributed in the hope that it will be useful,
* but WITHOUT ANY WARRANTY; without even the implied warranty of
* MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
* GNU General Public License for more details.
*
* You should have received a copy of the GNU General Public License
* along with this program. If not, see <http://www.gnu.org/licenses/>.
*
*/
#include "ultima/ultima8/misc/debugger.h"
#include "ultima/ultima8/gfx/raw_shape_frame.h"
#include "ultima/ultima8/convert/u8/convert_shape_u8.h"
#include "ultima/ultima8/misc/stream_util.h"
#include "common/memstream.h"
namespace Ultima {
namespace Ultima8 {
/*
parse data and fill class
*/
RawShapeFrame::RawShapeFrame(const uint8 *data, uint32 size, const ConvertShapeFormat *format,
const uint8 special[256], ConvertShapeFrame *prev) : _line_offsets(0),
_rle_data(nullptr) {
// Load it as u8
if (!format || format == &U8ShapeFormat || format == &U82DShapeFormat)
loadU8Format(data, size);
else if (format == &PentagramShapeFormat)
loadPentagramFormat(data, size);
else if (format == &U8CMPShapeFormat)
loadU8CMPFormat(data, size, format, special, prev);
else
loadGenericFormat(data, size, format);
}
RawShapeFrame::~RawShapeFrame() {
delete [] _line_offsets;
}
// This will load a u8 style shape 'optimized'.
void RawShapeFrame::loadU8Format(const uint8 *data, uint32 size) {
Common::MemoryReadStream stream(data, size + 8);
stream.skip(8); // skip header
_compressed = stream.readByte();
stream.skip(1);
_width = stream.readSint16LE();
_height = stream.readSint16LE();
_xoff = stream.readSint16LE();
_yoff = stream.readSint16LE();
if (_height == 0)
return;
_line_offsets = new uint32[_height];
for (int32 i = 0; i < _height; i++) {
_line_offsets[i] = stream.readUint16LE() - ((_height - i) * 2);
}
_rle_data = data + stream.pos();
}
// This will load a pentagram style shape 'optimized'.
void RawShapeFrame::loadPentagramFormat(const uint8 *data, uint32 size) {
Common::MemoryReadStream stream(data, size);
_compressed = stream.readByte();
stream.skip(3);
_width = stream.readSint32LE();
_height = stream.readSint32LE();
_xoff = stream.readSint32LE();
_yoff = stream.readSint32LE();
if (_height == 0)
return;
_line_offsets = new uint32[_height];
for (int32 i = 0; i < _height; i++) {
_line_offsets[i] = stream.readSint32LE();
}
_rle_data = data + stream.pos();
}
// This will load any sort of shape via a ConvertShapeFormat struct
void RawShapeFrame::loadGenericFormat(const uint8 *data, uint32 size, const ConvertShapeFormat *format) {
Common::MemoryReadStream ds(data + format->_bytes_frame_unknown, size);
_compressed = readX(ds, format->_bytes_frame_compression);
_width = readXS(ds, format->_bytes_frame_width);
_height = readXS(ds, format->_bytes_frame_height);
_xoff = readXS(ds, format->_bytes_frame_xoff);
_yoff = readXS(ds, format->_bytes_frame_yoff);
if (_height == 0)
return;
// Fairly arbitrary sanity check
if (_height < 0 || _height > 4096 || _width < 0 || _width > 4096 || _xoff > 4096 || _yoff > 4096) {
warning("got some invalid data loading shape");
_width = _height = _xoff = _yoff = 0;
return;
}
_line_offsets = new uint32[_height];
for (int32 i = 0; i < _height; i++) {
if (format->_line_offset_absolute) {
_line_offsets[i] = readX(ds, format->_bytes_line_offset);
} else {
if (ds.size() - ds.pos() < (int32)format->_bytes_line_offset) {
warning("going off end of %d buffer at %d reading %d",
(int)ds.size(), (int)ds.pos(), format->_bytes_line_offset);
}
_line_offsets[i] = readX(ds, format->_bytes_line_offset) - ((_height - i) * format->_bytes_line_offset);
}
}
_rle_data = data + format->_len_frameheader2 + _height * format->_bytes_line_offset;
}
// This will load an U8-compressed shape
void RawShapeFrame::loadU8CMPFormat(const uint8 *data, uint32 size, const ConvertShapeFormat *format, const uint8 special[256], ConvertShapeFrame *prev) {
Common::MemoryReadStream ds(data, size);
ConvertShapeFrame f;
f.ReadCmpFrame(ds, format, special, prev);
uint32 to_alloc = f._height + (f._bytes_rle + 3) / 4;
_line_offsets = new uint32[to_alloc];
_rle_data = reinterpret_cast<uint8 *>(_line_offsets + f._height);
_compressed = f._compression;
_height = f._height;
_width = f._width;
_xoff = f._xoff;
_yoff = f._yoff;
memcpy(_line_offsets, f._line_offsets, f._height * 4);
memcpy(const_cast<uint8 *>(_rle_data), f._rle_data, f._bytes_rle);
f.Free();
}
void RawShapeFrame::getConvertShapeFrame(ConvertShapeFrame &csf) {
csf._compression = _compressed;
csf._width = _width;
csf._height = _height;
csf._xoff = _xoff;
csf._yoff = _yoff;
csf._line_offsets = _line_offsets;
csf._bytes_rle = 0;
csf._rle_data = const_cast<uint8 *>(_rle_data);
}
} // End of namespace Ultima8
} // End of namespace Ultima

View File

@@ -0,0 +1,78 @@
/* ScummVM - Graphic Adventure Engine
*
* ScummVM is the legal property of its developers, whose names
* are too numerous to list here. Please refer to the COPYRIGHT
* file distributed with this source distribution.
*
* This program is free software: you can redistribute it and/or modify
* it under the terms of the GNU General Public License as published by
* the Free Software Foundation, either version 3 of the License, or
* (at your option) any later version.
*
* This program is distributed in the hope that it will be useful,
* but WITHOUT ANY WARRANTY; without even the implied warranty of
* MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
* GNU General Public License for more details.
*
* You should have received a copy of the GNU General Public License
* along with this program. If not, see <http://www.gnu.org/licenses/>.
*
*/
#ifndef ULTIMA8_GFX_RAW_SHAPEFRAME_H
#define ULTIMA8_GFX_RAW_SHAPEFRAME_H
namespace Ultima {
namespace Ultima8 {
struct ConvertShapeFormat;
struct ConvertShapeFrame;
/**
* A raw shape frame just contains references into the (possibly compressed)
* data for the shape and is used as an intermediate step in the loading
* process.
*/
class RawShapeFrame {
friend class ShapeFrame;
public:
// parse data.
//
// You will find this is quite similar to the ConvertShapeFrame except
// all the unknown crap is removed. It's designed to allow for painting
// only, and for speed when loading.
RawShapeFrame(const uint8 *data, uint32 size, const ConvertShapeFormat *format = 0,
const uint8 special[256] = 0, ConvertShapeFrame *prev = 0);
~RawShapeFrame();
void getConvertShapeFrame(ConvertShapeFrame &csf);
private:
uint32 _compressed;
int32 _width, _height;
int32 _xoff, _yoff;
uint32 *_line_offsets; // Note these are offsets into rle_data
const uint8 *_rle_data;
// This will load a u8 style shape 'optimized'.
void loadU8Format(const uint8 *data, uint32 size);
// This will load a pentagram style shape 'optimized'.
void loadPentagramFormat(const uint8 *data, uint32 size);
// This will load any sort of shape via a ConvertShapeFormat struct
// Crusader shapes must be loaded this way
void loadGenericFormat(const uint8 *data, uint32 size, const ConvertShapeFormat *format);
// This will load a u8-compressed shape
void loadU8CMPFormat(const uint8 *data, uint32 size, const ConvertShapeFormat *format, const uint8 special[256], ConvertShapeFrame *prev);
};
} // End of namespace Ultima8
} // End of namespace Ultima
#endif

View File

@@ -0,0 +1,993 @@
/* ScummVM - Graphic Adventure Engine
*
* ScummVM is the legal property of its developers, whose names
* are too numerous to list here. Please refer to the COPYRIGHT
* file distributed with this source distribution.
*
* This program is free software: you can redistribute it and/or modify
* it under the terms of the GNU General Public License as published by
* the Free Software Foundation, either version 3 of the License, or
* (at your option) any later version.
*
* This program is distributed in the hope that it will be useful,
* but WITHOUT ANY WARRANTY; without even the implied warranty of
* MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
* GNU General Public License for more details.
*
* You should have received a copy of the GNU General Public License
* along with this program. If not, see <http://www.gnu.org/licenses/>.
*
*/
#include "ultima/ultima8/gfx/render_surface.h"
#include "ultima/ultima8/gfx/palette.h"
#include "ultima/ultima8/gfx/shape.h"
#include "ultima/ultima8/gfx/shape_frame.h"
#include "ultima/ultima8/gfx/texture.h"
#include "graphics/blit.h"
namespace Ultima {
namespace Ultima8 {
RenderSurface::RenderSurface(int width, int height, const Graphics::PixelFormat &format) :
_pixels(nullptr), _ox(0), _oy(0), _pitch(0),
_flipped(false), _clipWindow(0, 0, 0, 0), _lockCount(0),
_disposeAfterUse(DisposeAfterUse::YES) {
_surface = new Graphics::ManagedSurface(width, height, format);
_clipWindow.setWidth(_surface->w);
_clipWindow.setHeight(_surface->h);
SetPixelsPointer();
}
RenderSurface::RenderSurface(Graphics::ManagedSurface *s, DisposeAfterUse::Flag disposeAfterUse) :
_pixels(nullptr), _ox(0), _oy(0), _pitch(0),
_flipped(false), _clipWindow(0, 0, 0, 0), _lockCount(0),
_surface(s), _disposeAfterUse(disposeAfterUse) {
_clipWindow.setWidth(_surface->w);
_clipWindow.setHeight(_surface->h);
SetPixelsPointer();
}
//
// RenderSurface::~RenderSurface()
//
// Desc: Destructor
//
RenderSurface::~RenderSurface() {
if (_disposeAfterUse == DisposeAfterUse::YES)
delete _surface;
}
void RenderSurface::SetPixelsPointer()
{
_pixels = static_cast<uint8 *>(_surface->getBasePtr(_ox, _oy));
_pitch = _surface->pitch;
if (_flipped) {
_pixels = static_cast<uint8 *>(_surface->getBasePtr(_ox, _surface->h - 1 - _oy));
_pitch = -_pitch;
}
}
//
// RenderSurface::BeginPainting()
//
// Desc: Prepare the surface for drawing this frame (in effect lock it for drawing)
// Returns: Non Zero on error
//
bool RenderSurface::BeginPainting() {
if (!_lockCount) {
_surface->markAllDirty();
}
_lockCount++;
// Origin offset pointers
SetPixelsPointer();
// No error
return true;
}
//
// RenderSurface::EndPainting()
//
// Desc: Prepare the surface for drawing this frame (in effect lock it for drawing)
// Returns: Non Zero on error
//
bool RenderSurface::EndPainting() {
// Already Unlocked
if (!_lockCount) {
error("Error: BeginPainting()/EndPainting() Mismatch!");
return false;
}
// Decrement counter
--_lockCount;
if (!_lockCount) {
// Clear pointers
_pixels = nullptr;
}
// No error
return true;
}
//
// Common::Rect32 RenderSurface::GetSurfaceDims()
//
// Desc: Get the Surface Dimensions (and logical origin)
// r: Rect object to fill
//
Common::Rect32 RenderSurface::getSurfaceDims() const {
Common::Rect32 r;
r.moveTo(_ox, _oy);
r.setWidth(_surface->w);
r.setHeight(_surface->h);
return r;
}
//
// void RenderSurface::SetOrigin(int32 x, int32 y)
//
// Desc: Set the Phyiscal Pixel to be the logical origin
//
void RenderSurface::SetOrigin(int32 x, int32 y) {
// Adjust the clipping window
_clipWindow.translate(_ox - x, _oy - y);
// Set the origin
_ox = x;
_oy = y;
// The new pointers
SetPixelsPointer();
}
//
// void RenderSurface::GetOrigin(int32 &x, int32 &y)
//
// Desc: Get the Phyiscal Pixel that is the logical origin
//
void RenderSurface::GetOrigin(int32 &x, int32 &y) const {
// Set the origin
x = _ox;
y = _oy;
}
//
// Common::Rect32 RenderSurface::getClippingRect()
//
// Desc: Get the Clipping Rectangle
// r: Rect object to fill
//
Common::Rect32 RenderSurface::getClippingRect() const {
return Common::Rect32(_clipWindow.left, _clipWindow.top, _clipWindow.right, _clipWindow.bottom);
}
//
// void RenderSurface::setClippingRect(const Common::Rect32 &r)
//
// Desc: Set the Clipping Rectangle
// r: Rect object that contains new Clipping Rectangle
//
void RenderSurface::setClippingRect(const Common::Rect32 &r) {
// What we need to do is to clip the clipping rect to the phyiscal screen
_clipWindow = Common::Rect(r.left, r.top, r.right, r.bottom);
_clipWindow.clip(Common::Rect(-_ox, -_oy, -_ox + _surface->w, -_oy + _surface->h));
}
//
// void RenderSurface::SetFlipped(bool _flipped)
//
// Desc: Flip the surface
//
void RenderSurface::SetFlipped(bool wantFlipped) {
_flipped = wantFlipped;
SetPixelsPointer();
}
//
// bool RenderSurface::IsFlipped() const
//
// Desc: Has the render surface been _flipped?
//
bool RenderSurface::IsFlipped() const {
return _flipped;
}
void RenderSurface::fillRect(const Common::Rect32 &r, uint32 color) {
Common::Rect rect(r.left, r.top, r.right, r.bottom);
rect.clip(_clipWindow);
rect.translate(_ox, _oy);
_surface->fillRect(rect, color);
}
void RenderSurface::frameRect(const Common::Rect32& r, uint32 color) {
Common::Rect rect(r.left, r.top, r.right, r.bottom);
rect.clip(_clipWindow);
rect.translate(_ox, _oy);
_surface->frameRect(rect, color);
}
void RenderSurface::drawLine(int32 sx, int32 sy, int32 ex, int32 ey, uint32 color) {
_surface->drawLine(sx + _ox, sy + _oy, ex + _ox, ey + _oy, color);
}
void RenderSurface::fill32(uint32 rgb, const Common::Rect32 &r) {
Common::Rect rect(r.left, r.top, r.right, r.bottom);
rect.clip(_clipWindow);
rect.translate(_ox, _oy);
rgb = _surface->format.RGBToColor(TEX32_R(rgb), TEX32_G(rgb), TEX32_B(rgb));
_surface->fillRect(rect, rgb);
}
namespace {
template<typename uintX>
void inline fillBlendedLogic(uint8 *pixels, int32 pitch, uint32 rgba, const Common::Rect &rect, const Graphics::PixelFormat &format) {
int32 w = rect.width();
int32 h = rect.height();
if (!w || !h)
return;
uint32 sa = TEX32_A(rgba);
uint32 sr = TEX32_R(rgba);
uint32 sg = TEX32_G(rgba);
uint32 sb = TEX32_B(rgba);
uint32 ia = 256 - TEX32_A(rgba);
uint8 *pixel = pixels + rect.top * pitch + rect.left * format.bytesPerPixel;
int diff = pitch - w * format.bytesPerPixel;
for (int y = 0; y < h; ++y) {
for (int x = 0; x < w; ++x) {
uintX *dest = reinterpret_cast<uintX *>(pixel);
uint8 dr, dg, db, da;
format.colorToARGB(*dest, da, dr, dg, db);
if (da) {
dr = (dr * ia + sr * sa) >> 8;
dg = (dg * ia + sg * sa) >> 8;
db = (db * ia + sb * sa) >> 8;
*dest = format.ARGBToColor(da, dr, dg, db);
}
pixel += format.bytesPerPixel;
}
pixel += diff;
}
}
} // End of anonymous namespace
void RenderSurface::fillBlended(uint32 rgba, const Common::Rect32 &r) {
int alpha = TEX32_A(rgba);
if (alpha == 0xFF) {
fill32(rgba, r);
return;
} else if (!alpha) {
return;
}
Common::Rect rect(r.left, r.top, r.right, r.bottom);
rect.clip(_clipWindow);
if (_surface->format.bytesPerPixel == 4)
fillBlendedLogic<uint32>(_pixels, _pitch, rgba, rect, _surface->format);
else if (_surface->format.bytesPerPixel == 2)
fillBlendedLogic<uint16>(_pixels, _pitch, rgba, rect, _surface->format);
}
void RenderSurface::frameRect32(uint32 rgb, const Common::Rect32 &r) {
Common::Rect rect(r.left, r.top, r.right, r.bottom);
rect.clip(_clipWindow);
rect.translate(_ox, _oy);
rgb = _surface->format.RGBToColor(TEX32_R(rgb), TEX32_G(rgb), TEX32_B(rgb));
_surface->frameRect(rect, rgb);
}
void RenderSurface::drawLine32(uint32 rgb, int32 sx, int32 sy, int32 ex, int32 ey) {
rgb = _surface->format.RGBToColor(TEX32_R(rgb), TEX32_G(rgb), TEX32_B(rgb));
_surface->drawLine(sx + _ox, sy + _oy, ex + _ox, ey + _oy, rgb);
}
//
// RenderSurface::Blit(Graphics::ManagedSurface &src, const Common::Rect &srcRect, int32 dx, int32 dy, bool alpha_blend)
//
// Desc: Blit a region from a Texture (Alpha == 0 -> skipped)
//
void RenderSurface::Blit(const Graphics::ManagedSurface &src, const Common::Rect &srcRect, int32 dx, int32 dy, bool alpha_blend) {
Common::Point dpoint = Common::Point(_ox + dx, _oy + dy);
if (alpha_blend) {
_surface->transBlitFrom(src, srcRect, dpoint);
} else {
_surface->blitFrom(src, srcRect, dpoint);
}
}
void RenderSurface::CrossKeyBlitMap(const Graphics::Surface& src, const Common::Rect& srcRect, int32 dx, int32 dy, const uint32* map, const uint32 key) {
byte *dstPixels = reinterpret_cast<byte *>(_surface->getBasePtr(_ox + dx, _oy + dy));
const byte *srcPixels = reinterpret_cast<const byte *>(src.getBasePtr(srcRect.left, srcRect.top));
Graphics::crossKeyBlitMap(dstPixels, srcPixels, _surface->pitch, src.pitch, srcRect.width(), srcRect.height(), _surface->format.bytesPerPixel, map, key);
}
namespace {
template<typename uintDst, typename uintSrc>
void inline fadedBlitLogic(uint8 *pixels, int32 pitch,
const Common::Rect &clipWindow,
const Graphics::PixelFormat &format,
const Graphics::ManagedSurface &src,
const Common::Rect &srcRect, int32 dx, int32 dy, uint32 col32, bool alpha_blend) {
int32 w = srcRect.width();
int32 h = srcRect.height();
// Clamp or wrap or return?
if (w > static_cast<int32>(src.w))
return;
// Clamp or wrap or return?
if (h > static_cast<int32>(src.h))
return;
// Clip to window
int px = dx, py = dy;
Common::Rect rect(dx, dy, dx + w, dy + h);
rect.clip(clipWindow);
dx = rect.left;
dy = rect.top;
w = rect.width();
h = rect.height();
if (!w || !h)
return;
int32 sx = srcRect.left;
int32 sy = srcRect.top;
// Adjust source x and y
if (px != dx)
sx += dx - px;
if (py != dy)
sy += dy - py;
uint32 a = TEX32_A(col32);
uint32 ia = 256 - a;
uint32 r = (TEX32_R(col32) * a);
uint32 g = (TEX32_G(col32) * a);
uint32 b = (TEX32_B(col32) * a);
uint8 *dstPixels = pixels + dy * pitch + dx * sizeof(uintDst);
int dstStep = sizeof(uintDst);
int dstDelta = pitch - w * sizeof(uintDst);
const uint8 *srcPixels = reinterpret_cast<const uint8 *>(src.getBasePtr(sx, sy));
int srcStep = sizeof(uintSrc);
int srcDelta = src.pitch - w * sizeof(uintSrc);
byte palette[768];
src.grabPalette(palette, 0, 256);
for (int y = 0; y < h; ++y) {
for (int x = 0; x < w; ++x) {
uint8 sa, sr, sg, sb;
const uint32 color = *(reinterpret_cast<const uintSrc *>(srcPixels));
if (src.format.isCLUT8()) {
sa = 0xff;
sr = palette[color * 3 + 0];
sg = palette[color * 3 + 1];
sb = palette[color * 3 + 2];
} else {
src.format.colorToARGB(color, sa, sr, sg, sb);
}
if (sa == 0xFF || (sa && !alpha_blend)) {
uintDst *dest = reinterpret_cast<uintDst *>(dstPixels);
*dest = format.RGBToColor((sr * ia + r) >> 8,
(sg * ia + g) >> 8,
(sb * ia + b) >> 8);
} else if (sa) {
uintDst *dest = reinterpret_cast<uintDst *>(dstPixels);
uint8 r2, g2, b2;
format.colorToRGB(*dest, r2, g2, b2);
uint32 dr = r2 * (256 - sa);
uint32 dg = g2 * (256 - sa);
uint32 db = b2 * (256 - sa);
dr += sr * ia + ((r * sa) >> 8);
dg += sg * ia + ((g * sa) >> 8);
db += sb * ia + ((b * sa) >> 8);
*dest = format.RGBToColor(dr >> 8, dg >> 8, db >> 8);
}
srcPixels += srcStep;
dstPixels += dstStep;
}
srcPixels += srcDelta;
dstPixels += dstDelta;
}
}
} // End of anonymous namespace
//
// void RenderSurface::FadedBlit(Graphics::ManagedSurface &src, const Common::Rect &srcRect, int32 dx, int32 dy, uint32 col32)
//
// Desc: Blit a region from a Texture (Alpha == 0 -> skipped)
//
void RenderSurface::FadedBlit(const Graphics::ManagedSurface &src, const Common::Rect &srcRect, int32 dx, int32 dy, uint32 col32, bool alpha_blend) {
if (_surface->format.bytesPerPixel == 4) {
if (src.format.bytesPerPixel == 4) {
fadedBlitLogic<uint32, uint32>(_pixels, _pitch, _clipWindow, _surface->format, src, srcRect, dx, dy, col32, alpha_blend);
}
else if (src.format.bytesPerPixel == 2) {
fadedBlitLogic<uint32, uint16>(_pixels, _pitch, _clipWindow, _surface->format, src, srcRect, dx, dy, col32, alpha_blend);
}
else if (src.format.isCLUT8()) {
fadedBlitLogic<uint32, uint8>(_pixels, _pitch, _clipWindow, _surface->format, src, srcRect, dx, dy, col32, alpha_blend);
}
else {
error("FadedBlit not supported from %s to %s", src.format.toString().c_str(), _surface->format.toString().c_str());
}
} else if (_surface->format.bytesPerPixel == 2) {
if (src.format.bytesPerPixel == 4) {
fadedBlitLogic<uint16, uint32>(_pixels, _pitch, _clipWindow, _surface->format, src, srcRect, dx, dy, col32, alpha_blend);
}
else if (src.format.bytesPerPixel == 2) {
fadedBlitLogic<uint16, uint16>(_pixels, _pitch, _clipWindow, _surface->format, src, srcRect, dx, dy, col32, alpha_blend);
}
else if (src.format.isCLUT8()) {
fadedBlitLogic<uint16, uint8>(_pixels, _pitch, _clipWindow, _surface->format, src, srcRect, dx, dy, col32, alpha_blend);
}
else {
error("FadedBlit not supported from %s to %s", src.format.toString().c_str(), _surface->format.toString().c_str());
}
}
else {
error("FadedBlit not supported from %s to %s", src.format.toString().c_str(), _surface->format.toString().c_str());
}
}
namespace {
template<typename uintDst, typename uintSrc>
void inline maskedBlitLogic(uint8 *pixels, int32 pitch,
const Common::Rect &clipWindow,
const Graphics::PixelFormat &format,
const Graphics::ManagedSurface &src,
const Common::Rect &srcRect, int32 dx, int32 dy, uint32 col32, bool alpha_blend) {
int32 w = srcRect.width();
int32 h = srcRect.height();
// Clamp or wrap or return?
if (w > static_cast<int32>(src.w))
return;
// Clamp or wrap or return?
if (h > static_cast<int32>(src.h))
return;
// Clip to window
int px = dx, py = dy;
Common::Rect rect(dx, dy, dx + w, dy + h);
rect.clip(clipWindow);
dx = rect.left;
dy = rect.top;
w = rect.width();
h = rect.height();
if (!w || !h)
return;
int32 sx = srcRect.left;
int32 sy = srcRect.top;
// Adjust source x and y
if (px != dx)
sx += dx - px;
if (py != dy)
sy += dy - py;
uint32 a = TEX32_A(col32);
uint32 ia = 256 - a;
uint32 r = (TEX32_R(col32) * a);
uint32 g = (TEX32_G(col32) * a);
uint32 b = (TEX32_B(col32) * a);
uint32 aMask = format.aMax() << format.aShift;
uint8 *dstPixels = pixels + dy * pitch + dx * sizeof(uintDst);
int dstStep = sizeof(uintDst);
int dstDelta = pitch - w * sizeof(uintDst);
const uint8 *srcPixels = reinterpret_cast<const uint8 *>(src.getBasePtr(sx, sy));
int srcStep = sizeof(uintSrc);
int srcDelta = src.pitch - w * sizeof(uintSrc);
byte palette[768];
src.grabPalette(palette, 0, 256);
for (int y = 0; y < h; ++y) {
for (int x = 0; x < w; ++x) {
uintDst *dest = reinterpret_cast<uintDst *>(dstPixels);
if (!aMask || (*dest & aMask)) {
uint8 sa, sr, sg, sb;
const uint32 color = *(reinterpret_cast<const uintSrc *>(srcPixels));
if (src.format.isCLUT8()) {
sa = 0xff;
sr = palette[color * 3 + 0];
sg = palette[color * 3 + 1];
sb = palette[color * 3 + 2];
} else {
src.format.colorToARGB(color, sa, sr, sg, sb);
}
if (sa == 0xFF || (sa && !alpha_blend)) {
*dest = format.RGBToColor((sr * ia + r) >> 8,
(sg * ia + g) >> 8,
(sb * ia + b) >> 8);
} else if (sa) {
uint8 r2, g2, b2;
format.colorToRGB(*dest, r2, g2, b2);
uint32 dr = r2 * (256 - sa);
uint32 dg = g2 * (256 - sa);
uint32 db = b2 * (256 - sa);
dr += sr * ia + ((r * sa) >> 8);
dg += sg * ia + ((g * sa) >> 8);
db += sb * ia + ((b * sa) >> 8);
*dest = format.RGBToColor(dr >> 8, dg >> 8, db >> 8);
}
}
srcPixels += srcStep;
dstPixels += dstStep;
}
srcPixels += srcDelta;
dstPixels += dstDelta;
}
}
} // End of anonymous namespace
//
// void RenderSurface::MaskedBlit(Graphics::ManagedSurface &src, const Common::Rect &srcRect, int32 dx, int32 dy, uint32 col32, bool alpha_blend=false)
//
// Desc Blit a region from a Texture with a Colour blend masked based on DestAlpha (AlphaTex == 0 || AlphaDest == 0 -> skipped. AlphaCol32 -> Blend Factors)
//
//
void RenderSurface::MaskedBlit(const Graphics::ManagedSurface &src, const Common::Rect &srcRect, int32 dx, int32 dy, uint32 col32, bool alpha_blend) {
if (_surface->format.bytesPerPixel == 4) {
if (src.format.bytesPerPixel == 4) {
maskedBlitLogic<uint32, uint32>(_pixels, _pitch, _clipWindow, _surface->format, src, srcRect, dx, dy, col32, alpha_blend);
} else if (src.format.bytesPerPixel == 2) {
maskedBlitLogic<uint32, uint16>(_pixels, _pitch, _clipWindow, _surface->format, src, srcRect, dx, dy, col32, alpha_blend);
} else if (src.format.isCLUT8()) {
maskedBlitLogic<uint32, uint8>(_pixels, _pitch, _clipWindow, _surface->format, src, srcRect, dx, dy, col32, alpha_blend);
} else {
error("MaskedBlit not supported from %s to %s", src.format.toString().c_str(), _surface->format.toString().c_str());
}
} else if (_surface->format.bytesPerPixel == 2) {
if (src.format.bytesPerPixel == 4) {
maskedBlitLogic<uint16, uint32>(_pixels, _pitch, _clipWindow, _surface->format, src, srcRect, dx, dy, col32, alpha_blend);
} else if (src.format.bytesPerPixel == 2) {
maskedBlitLogic<uint16, uint16>(_pixels, _pitch, _clipWindow, _surface->format, src, srcRect, dx, dy, col32, alpha_blend);
} else if (src.format.isCLUT8()) {
maskedBlitLogic<uint16, uint8>(_pixels, _pitch, _clipWindow, _surface->format, src, srcRect, dx, dy, col32, alpha_blend);
} else {
error("MaskedBlit not supported from %s to %s", src.format.toString().c_str(), _surface->format.toString().c_str());
}
} else {
error("MaskedBlit not supported from %s to %s", src.format.toString().c_str(), _surface->format.toString().c_str());
}
}
namespace {
template<typename uintX>
void inline paintLogic(uint8 *pixels, int32 pitch,
const Common::Rect &clipWindow,
const Graphics::PixelFormat &format,
const ShapeFrame *frame, int32 x, int32 y, bool mirrored,
const uint32 *map) {
const Graphics::Surface &src = frame->getSurface();
Common::Rect srcRect(0, 0, src.w, src.h);
Common::Rect dstRect(x, y, x, y);
if (mirrored) {
dstRect.right += frame->_xoff + 1;
dstRect.left = dstRect.right - srcRect.width();
if (dstRect.left < clipWindow.left) {
srcRect.right += dstRect.left - clipWindow.left;
dstRect.left = clipWindow.left;
}
if (dstRect.right > clipWindow.right) {
srcRect.left += dstRect.right - clipWindow.right;
dstRect.right = clipWindow.right;
}
} else {
dstRect.left -= frame->_xoff;
dstRect.right = dstRect.left + srcRect.width();
if (dstRect.left < clipWindow.left) {
srcRect.left -= dstRect.left - clipWindow.left;
dstRect.left = clipWindow.left;
}
if (dstRect.right > clipWindow.right) {
srcRect.right -= dstRect.right - clipWindow.right;
dstRect.right = clipWindow.right;
}
}
dstRect.top -= frame->_yoff;
dstRect.bottom = dstRect.top + srcRect.height();
if (dstRect.top < clipWindow.top) {
srcRect.top -= dstRect.top - clipWindow.top;
dstRect.top = clipWindow.top;
}
if (dstRect.bottom > clipWindow.bottom) {
srcRect.bottom -= dstRect.bottom - clipWindow.bottom;
dstRect.bottom = clipWindow.bottom;
}
const int srcStep = sizeof(uint8);
int dstStep = sizeof(uintX);
if (mirrored) {
x = dstRect.right - 1;
y = dstRect.top;
dstStep = -dstStep;
} else {
x = dstRect.left;
y = dstRect.top;
}
const int w = srcRect.width();
const int h = srcRect.height();
const int srcDelta = src.pitch - (w * srcStep);
const int dstDelta = pitch - (w * dstStep);
const uint8 keycolor = frame->_keycolor;
const uint8 *srcPixels = reinterpret_cast<const uint8 *>(src.getBasePtr(srcRect.left, srcRect.top));
uint8 *dstPixels = reinterpret_cast<uint8 *>(pixels + x * sizeof(uintX) + pitch * y);
for (int i = 0; i < h; i++) {
for (int j = 0; j < w; j++) {
const uint8 color = *srcPixels;
if (color != keycolor) {
uintX *dstpix = reinterpret_cast<uintX *>(dstPixels);
*dstpix = static_cast<uintX>(map[color]);
}
srcPixels += srcStep;
dstPixels += dstStep;
}
srcPixels += srcDelta;
dstPixels += dstDelta;
}
}
template<typename uintX>
void inline paintBlendedLogic(uint8 *pixels, int32 pitch,
const Common::Rect &clipWindow,
const Graphics::PixelFormat &format,
const ShapeFrame *frame, int32 x, int32 y,
bool mirrored, bool invisible, uint32 highlight,
const uint32 *map, const uint32 *xform_map) {
const Graphics::Surface &src = frame->getSurface();
Common::Rect srcRect(0, 0, src.w, src.h);
Common::Rect dstRect(x, y, x, y);
if (mirrored) {
dstRect.right += frame->_xoff + 1;
dstRect.left = dstRect.right - srcRect.width();
if (dstRect.left < clipWindow.left) {
srcRect.right += dstRect.left - clipWindow.left;
dstRect.left = clipWindow.left;
}
if (dstRect.right > clipWindow.right) {
srcRect.left += dstRect.right - clipWindow.right;
dstRect.right = clipWindow.right;
}
} else {
dstRect.left -= frame->_xoff;
dstRect.right = dstRect.left + srcRect.width();
if (dstRect.left < clipWindow.left) {
srcRect.left -= dstRect.left - clipWindow.left;
dstRect.left = clipWindow.left;
}
if (dstRect.right > clipWindow.right) {
srcRect.right -= dstRect.right - clipWindow.right;
dstRect.right = clipWindow.right;
}
}
dstRect.top -= frame->_yoff;
dstRect.bottom = dstRect.top + srcRect.height();
if (dstRect.top < clipWindow.top) {
srcRect.top -= dstRect.top - clipWindow.top;
dstRect.top = clipWindow.top;
}
if (dstRect.bottom > clipWindow.bottom) {
srcRect.bottom -= dstRect.bottom - clipWindow.bottom;
dstRect.bottom = clipWindow.bottom;
}
const int srcStep = sizeof(uint8);
int dstStep = sizeof(uintX);
if (mirrored) {
x = dstRect.right - 1;
y = dstRect.top;
dstStep = -dstStep;
} else {
x = dstRect.left;
y = dstRect.top;
}
const int w = srcRect.width();
const int h = srcRect.height();
const int srcDelta = src.pitch - (w * srcStep);
const int dstDelta = pitch - (w * dstStep);
const uint8 keycolor = frame->_keycolor;
const uint8 *srcPixels = reinterpret_cast<const uint8 *>(src.getBasePtr(srcRect.left, srcRect.top));
uint8 *dstPixels = reinterpret_cast<uint8 *>(pixels + x * sizeof(uintX) + pitch * y);
uint8 dr, dg, db;
uint8 sr, sg, sb;
if (highlight) {
uint32 ca = TEX32_A(highlight);
uint32 cr = TEX32_R(highlight);
uint32 cg = TEX32_G(highlight);
uint32 cb = TEX32_B(highlight);
uint32 ica = 255 - ca;
for (int i = 0; i < h; i++) {
for (int j = 0; j < w; j++) {
const uint8 color = *srcPixels;
if (color != keycolor) {
uintX *dstpix = reinterpret_cast<uintX *>(dstPixels);
format.colorToRGB(*dstpix, dr, dg, db);
if (xform_map && xform_map[color]) {
uint32 val = xform_map[color];
uint32 ia = 256 - TEX32_A(val);
uint32 r = (dr * ia + 256 * TEX32_R(val)) >> 8;
uint32 g = (dg * ia + 256 * TEX32_G(val)) >> 8;
uint32 b = (db * ia + 256 * TEX32_B(val)) >> 8;
sr = r > 0xFF ? 0xFF : r;
sg = g > 0xFF ? 0xFF : g;
sb = b > 0xFF ? 0xFF : b;
} else {
format.colorToRGB(map[color], sr, sg, sb);
}
if (invisible) {
dr = (((sr * ica + cr * ca) >> 1) + (dr << 7)) >> 8;
dg = (((sg * ica + cg * ca) >> 1) + (dg << 7)) >> 8;
db = (((sb * ica + cb * ca) >> 1) + (db << 7)) >> 8;
} else {
dr = (sr * ica + cr * ca) >> 8;
dg = (sg * ica + cg * ca) >> 8;
db = (sb * ica + cb * ca) >> 8;
}
*dstpix = static_cast<uintX>(format.RGBToColor(dr, dg, db));
}
srcPixels += srcStep;
dstPixels += dstStep;
}
srcPixels += srcDelta;
dstPixels += dstDelta;
}
} else if (invisible) {
for (int i = 0; i < h; i++) {
for (int j = 0; j < w; j++) {
const uint8 color = *srcPixels;
if (color != keycolor) {
uintX *dstpix = reinterpret_cast<uintX *>(dstPixels);
format.colorToRGB(*dstpix, dr, dg, db);
if (xform_map && xform_map[color]) {
uint32 val = xform_map[color];
uint32 ia = 256 - TEX32_A(val);
uint32 r = (dr * ia + 256 * TEX32_R(val)) >> 8;
uint32 g = (dg * ia + 256 * TEX32_G(val)) >> 8;
uint32 b = (db * ia + 256 * TEX32_B(val)) >> 8;
sr = r > 0xFF ? 0xFF : r;
sg = g > 0xFF ? 0xFF : g;
sb = b > 0xFF ? 0xFF : b;
} else {
format.colorToRGB(map[color], sr, sg, sb);
}
dr = (sr * 128 + dr * 128) >> 8;
dg = (sg * 128 + dg * 128) >> 8,
db = (sb * 128 + db * 128) >> 8;
*dstpix = static_cast<uintX>(format.RGBToColor(dr, dg, db));
}
srcPixels += srcStep;
dstPixels += dstStep;
}
srcPixels += srcDelta;
dstPixels += dstDelta;
}
} else {
for (int i = 0; i < h; i++) {
for (int j = 0; j < w; j++) {
const uint8 color = *srcPixels;
if (color != keycolor) {
uintX *dstpix = reinterpret_cast<uintX *>(dstPixels);
if (xform_map && xform_map[color]) {
format.colorToRGB(*dstpix, dr, dg, db);
uint32 val = xform_map[color];
uint32 ia = 256 - TEX32_A(val);
uint32 r = (dr * ia + 256 * TEX32_R(val)) >> 8;
uint32 g = (dg * ia + 256 * TEX32_G(val)) >> 8;
uint32 b = (db * ia + 256 * TEX32_B(val)) >> 8;
dr = r > 0xFF ? 0xFF : r;
dg = g > 0xFF ? 0xFF : g;
db = b > 0xFF ? 0xFF : b;
*dstpix = static_cast<uintX>(format.RGBToColor(dr, dg, db));
} else {
*dstpix = static_cast<uintX>(map[color]);
}
}
srcPixels += srcStep;
dstPixels += dstStep;
}
srcPixels += srcDelta;
dstPixels += dstDelta;
}
}
}
} // End of anonymous namespace
//
// void RenderSurface::Paint(Shape*s, uint32 framenum, int32 x, int32 y, bool mirrored)
//
// Desc: Standard shape drawing functions. Clips but doesn't do anything else
//
void RenderSurface::Paint(const Shape *s, uint32 framenum, int32 x, int32 y, bool mirrored) {
const ShapeFrame *frame = s->getFrame(framenum);
if (!frame || !s->getPalette())
return;
const uint32 *map = s->getPalette()->_native;
if (_surface->format.bytesPerPixel == 4)
paintLogic<uint32>(_pixels, _pitch, _clipWindow, _surface->format, frame, x, y, mirrored, map);
else if (_surface->format.bytesPerPixel == 2)
paintLogic<uint16>(_pixels, _pitch, _clipWindow, _surface->format, frame, x, y, mirrored, map);
else if (_surface->format.isCLUT8())
paintLogic<uint8>(_pixels, _pitch, _clipWindow, _surface->format, frame, x, y, mirrored, map);
else
error("Paint not supported for surface format: %s", _surface->format.toString().c_str());
}
//
// void RenderSurface::PaintTranslucent(Shape*s, uint32 framenum, int32 x, int32 y, bool mirrored)
//
// Desc: Standard shape drawing functions. Clips and XForms
//
void RenderSurface::PaintTranslucent(const Shape *s, uint32 framenum, int32 x, int32 y, bool mirrored) {
const ShapeFrame *frame = s->getFrame(framenum);
if (!frame || !s->getPalette())
return;
const uint32 *map = s->getPalette()->_native;
const uint32 *xform_map = s->getPalette()->_xform;
if (_surface->format.bytesPerPixel == 4)
paintBlendedLogic<uint32>(_pixels, _pitch, _clipWindow, _surface->format, frame, x, y, mirrored, false, 0, map, xform_map);
else if (_surface->format.bytesPerPixel == 2)
paintBlendedLogic<uint16>(_pixels, _pitch, _clipWindow, _surface->format, frame, x, y, mirrored, false, 0, map, xform_map);
else
error("PaintTranslucent not supported for surface format: %s", _surface->format.toString().c_str());
}
//
// void RenderSurface::PaintInvisible(Shape* s, uint32 frame, int32 x, int32 y, bool mirrored)
//
// Desc: Standard shape drawing functions. Invisible, Clips, and conditionally Flips and Xforms
//
void RenderSurface::PaintInvisible(const Shape *s, uint32 framenum, int32 x, int32 y, bool trans, bool mirrored) {
const ShapeFrame *frame = s->getFrame(framenum);
if (!frame || !s->getPalette())
return;
const uint32 *map = s->getPalette()->_native;
const uint32 *xform_map = trans ? s->getPalette()->_xform : nullptr;
if (_surface->format.bytesPerPixel == 4)
paintBlendedLogic<uint32>(_pixels, _pitch, _clipWindow, _surface->format, frame, x, y, mirrored, true, 0, map, xform_map);
else if (_surface->format.bytesPerPixel == 2)
paintBlendedLogic<uint16>(_pixels, _pitch, _clipWindow, _surface->format, frame, x, y, mirrored, true, 0, map, xform_map);
else
error("PaintInvisible not supported for surface format: %s", _surface->format.toString().c_str());
}
//
// void RenderSurface::PaintHighlight(Shape* s, uint32 frame, int32 x, int32 y, bool mirrored)
//
// Desc: Standard shape drawing functions. Highlights, Clips, and conditionally Flips and Xforms
//
void RenderSurface::PaintHighlight(const Shape *s, uint32 framenum, int32 x, int32 y, bool trans, bool mirrored, uint32 col32) {
const ShapeFrame *frame = s->getFrame(framenum);
if (!frame || !s->getPalette())
return;
const uint32 *map = s->getPalette()->_native;
const uint32 *xform_map = trans ? s->getPalette()->_xform : nullptr;
if (_surface->format.bytesPerPixel == 4)
paintBlendedLogic<uint32>(_pixels, _pitch, _clipWindow, _surface->format, frame, x, y, mirrored, false, col32, map, xform_map);
else if (_surface->format.bytesPerPixel == 2)
paintBlendedLogic<uint16>(_pixels, _pitch, _clipWindow, _surface->format, frame, x, y, mirrored, false, col32, map, xform_map);
else
error("PaintHighlight not supported for surface format: %s", _surface->format.toString().c_str());
}
//
// void RenderSurface::PaintHighlightInvis(Shape* s, uint32 frame, int32 x, int32 y, bool mirrored)
//
// Desc: Standard shape drawing functions. Highlights, Clips, and conditionally Flips and Xforms. 50% translucent
//
void RenderSurface::PaintHighlightInvis(const Shape *s, uint32 framenum, int32 x, int32 y, bool trans, bool mirrored, uint32 col32) {
const ShapeFrame *frame = s->getFrame(framenum);
if (!frame || !s->getPalette())
return;
const uint32 *map = s->getPalette()->_native;
const uint32 *xform_map = trans ? s->getPalette()->_xform : nullptr;
if (_surface->format.bytesPerPixel == 4)
paintBlendedLogic<uint32>(_pixels, _pitch, _clipWindow, _surface->format, frame, x, y, mirrored, true, col32, map, xform_map);
else if (_surface->format.bytesPerPixel == 2)
paintBlendedLogic<uint16>(_pixels, _pitch, _clipWindow, _surface->format, frame, x, y, mirrored, true, col32, map, xform_map);
else
error("PaintHighlightInvis not supported for surface format: %s", _surface->format.toString().c_str());
}
} // End of namespace Ultima8
} // End of namespace Ultima

View File

@@ -0,0 +1,185 @@
/* ScummVM - Graphic Adventure Engine
*
* ScummVM is the legal property of its developers, whose names
* are too numerous to list here. Please refer to the COPYRIGHT
* file distributed with this source distribution.
*
* This program is free software: you can redistribute it and/or modify
* it under the terms of the GNU General Public License as published by
* the Free Software Foundation, either version 3 of the License, or
* (at your option) any later version.
*
* This program is distributed in the hope that it will be useful,
* but WITHOUT ANY WARRANTY; without even the implied warranty of
* MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
* GNU General Public License for more details.
*
* You should have received a copy of the GNU General Public License
* along with this program. If not, see <http://www.gnu.org/licenses/>.
*
*/
#ifndef ULTIMA8_GFX_RENDERSURFACE_H
#define ULTIMA8_GFX_RENDERSURFACE_H
#include "graphics/managed_surface.h"
namespace Ultima {
namespace Ultima8 {
class Shape;
//
// RenderSurface
//
// Desc: The base class for rendering in Pentagram
//
class RenderSurface {
private:
// Frame buffer
uint8 *_pixels; // Pointer to logical pixel 0,0
// Dimensions
int32 _ox, _oy; // Physical Pixel for Logical Origin
int32 _pitch; // Frame buffer pitch (bytes) (could be negated)
bool _flipped;
// Clipping Rectangle
Common::Rect _clipWindow;
// Locking count
uint32 _lockCount; // Number of locks on surface
Graphics::ManagedSurface *_surface;
DisposeAfterUse::Flag _disposeAfterUse;
// Update the Pixels Pointer
void SetPixelsPointer();
public:
// Create a render surface
RenderSurface(int width, int height, const Graphics::PixelFormat &format);
// Create from a managed surface
RenderSurface(Graphics::ManagedSurface *s, DisposeAfterUse::Flag disposeAfterUse = DisposeAfterUse::YES);
~RenderSurface();
//
// Being/End Painting
//
//! Begin painting to the buffer. MUST BE CALLED BEFORE DOING ANYTHING TO THE SURFACE!
// \note Can be called multiple times
// \return true on success, false on failure
bool BeginPainting();
//! Finish paining to the buffer.
// \note MUST BE CALLED FOR EACH CALL TO BeginPainting()
// \return true on success, false on failure
bool EndPainting();
//
// Surface Properties
//
//! Set the Origin of the Surface
void SetOrigin(int32 x, int32 y);
//! Set the Origin of the Surface
void GetOrigin(int32 &x, int32 &y) const;
//! Get the Surface Dimensions
Common::Rect32 getSurfaceDims() const;
//! Get Clipping Rectangle
Common::Rect32 getClippingRect() const;
//! Set Clipping Rectangle
void setClippingRect(const Common::Rect32 &);
//! Flip the surface
void SetFlipped(bool flipped);
//! Has the render surface been flipped?
bool IsFlipped() const;
//! Get a reference to the underlying surface that's being encapsulated
Graphics::ManagedSurface *getRawSurface() const {
return _surface;
};
//! Fill the region with a color in the pixel format
void fillRect(const Common::Rect32 &r, uint32 color);
//! Fill the region with a color in the pixel format
void frameRect(const Common::Rect32 &r, uint32 color);
// Draw a line with a color in the pixel format
void drawLine(int32 sx, int32 sy, int32 ex, int32 ey, uint32 color);
//! Fill the region with a color in the TEX32_PACK_RGB format
void fill32(uint32 rgb, int32 sx, int32 sy, int32 w, int32 h) {
fill32(rgb, Common::Rect32(sx, sy, sx + w, sy + h));
}
//! Fill the region with a color in the TEX32_PACK_RGB format
void fill32(uint32 rgb, const Common::Rect32 &r);
//! Fill the region doing alpha blending with a color in the TEX32_PACK_RGBA format
void fillBlended(uint32 rgba, const Common::Rect32 &r);
//! Fill the region with a color in the TEX32_PACK_RGB format
void frameRect32(uint32 rgb, const Common::Rect32 &r);
// Draw a line with a color in the TEX32_PACK_RGB format
void drawLine32(uint32 rgb, int32 sx, int32 sy, int32 ex, int32 ey);
//
// The rule for painting methods:
//
// First arg are the source object to 'draw' with
// Next args are any other required data to define the 'source'
// Next args are the destination position
//
//
// Basic Shape Painting
//
//! Paint a Shape
void Paint(const Shape *s, uint32 frame, int32 x, int32 y, bool mirrored = false);
//! Paint a Translucent Shape.
void PaintTranslucent(const Shape *s, uint32 frame, int32 x, int32 y, bool mirrored = false);
//! Paint an Invisible Shape
void PaintInvisible(const Shape *s, uint32 frame, int32 x, int32 y, bool trans, bool mirrored);
//! Paint a Highlighted Shape of using the 32 Bit Colour col32 (0xAARRGGBB Alpha is blend level)
void PaintHighlight(const Shape *s, uint32 frame, int32 x, int32 y, bool trans, bool mirrored, uint32 col32);
//! Paint a Invisible Highlighted Shape of using the 32 Bit Colour col32 (0xAARRGGBB Alpha is blend level)
void PaintHighlightInvis(const Shape *s, uint32 frame, int32 x, int32 y, bool trans, bool mirrored, uint32 col32);
//
// Basic Texture Blitting
//
//! Blit a region from a Texture (Alpha == 0 -> skipped)
void Blit(const Graphics::ManagedSurface &src, const Common::Rect &srcRect, int32 dx, int32 dy, bool alpha_blend = false);
void CrossKeyBlitMap(const Graphics::Surface &src, const Common::Rect &srcRect, int32 dx, int32 dy, const uint32 *map, const uint32 key);
//! Blit a region from a Texture with a Colour blend (AlphaTex == 0 -> skipped. AlphaCol32 -> Blend Factors)
void FadedBlit(const Graphics::ManagedSurface &src, const Common::Rect &srcRect, int32 dx, int32 dy, uint32 col32, bool alpha_blend = false);
//! Blit a region from a Texture with a Colour blend masked based on DestAlpha (AlphaTex == 0 || AlphaDest == 0 -> skipped. AlphaCol32 -> Blend Factors)
void MaskedBlit(const Graphics::ManagedSurface &src, const Common::Rect &srcRect, int32 dx, int32 dy, uint32 col32, bool alpha_blend = false);
};
} // End of namespace Ultima8
} // End of namespace Ultima
#endif

View File

@@ -0,0 +1,286 @@
/* ScummVM - Graphic Adventure Engine
*
* ScummVM is the legal property of its developers, whose names
* are too numerous to list here. Please refer to the COPYRIGHT
* file distributed with this source distribution.
*
* This program is free software: you can redistribute it and/or modify
* it under the terms of the GNU General Public License as published by
* the Free Software Foundation, either version 3 of the License, or
* (at your option) any later version.
*
* This program is distributed in the hope that it will be useful,
* but WITHOUT ANY WARRANTY; without even the implied warranty of
* MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
* GNU General Public License for more details.
*
* You should have received a copy of the GNU General Public License
* along with this program. If not, see <http://www.gnu.org/licenses/>.
*
*/
#include "ultima/ultima8/misc/debugger.h"
#include "ultima/ultima8/gfx/shape.h"
#include "ultima/ultima8/gfx/shape_frame.h"
#include "ultima/ultima8/gfx/raw_shape_frame.h"
#include "ultima/ultima8/convert/u8/convert_shape_u8.h"
#include "ultima/ultima8/convert/crusader/convert_shape_crusader.h"
#include "ultima/ultima8/misc/stream_util.h"
#include "common/memstream.h"
namespace Ultima {
namespace Ultima8 {
Shape::Shape(const uint8 *data, uint32 size, const ConvertShapeFormat *format,
const uint16 id, const uint32 shape)
: _flexId(id), _shapeNum(shape), _palette(nullptr) {
// NB: U8 style!
loadFrames(data, size, format);
delete[] const_cast<uint8 *>(data);
}
Shape::Shape(Common::SeekableReadStream *src, const ConvertShapeFormat *format)
: _flexId(0), _shapeNum(0), _palette(nullptr) {
// NB: U8 style!
uint32 size = src->size();
uint8 *data = new uint8[size];
src->read(data, size);
loadFrames(data, size, format);
delete[] data;
}
Shape::~Shape() {
for (uint i = 0; i < _frames.size(); ++i)
delete _frames[i];
}
void Shape::loadFrames(const uint8 *data, uint32 size, const ConvertShapeFormat *format) {
if (!format)
format = DetectShapeFormat(data, size);
if (!format) {
// Should be fatal?
warning("Unable to detect shape format");
return;
}
Common::Array<RawShapeFrame *> rawframes;
// Load it as u8
if (format == &U8ShapeFormat || format == &U82DShapeFormat)
rawframes = loadU8Format(data, size, format);
else if (format == &PentagramShapeFormat)
rawframes = loadPentagramFormat(data, size, format);
else
rawframes = loadGenericFormat(data, size, format);
for (uint i = 0; i < rawframes.size(); i++) {
_frames.push_back(new ShapeFrame(rawframes[i]));
delete rawframes[i];
}
}
void Shape::getShapeId(uint16 &id, uint32 &shape) const {
id = _flexId;
shape = _shapeNum;
}
// This will load a u8 style shape 'optimized'.
Common::Array<RawShapeFrame *> Shape::loadU8Format(const uint8 *data, uint32 size, const ConvertShapeFormat *format) {
Common::MemoryReadStream stream(data, size);
stream.skip(4); // skip header
unsigned int framecount = stream.readUint16LE();
Common::Array<RawShapeFrame *> frames;
if (framecount == 0) {
return loadGenericFormat(data, size, format);
}
frames.reserve(framecount);
for (uint i = 0; i < framecount; ++i) {
uint32 frameoffset = stream.readUint32LE() & 0xFFFFFF;
uint32 framesize = stream.readUint16LE();
frames.push_back(new RawShapeFrame(data + frameoffset, framesize, format));
}
return frames;
}
// This will load a pentagram style shape 'optimized'.
Common::Array<RawShapeFrame *> Shape::loadPentagramFormat(const uint8 *data, uint32 size, const ConvertShapeFormat *format) {
Common::MemoryReadStream stream(data, size);
stream.skip(4); // skip header
unsigned int framecount = stream.readUint16LE();
Common::Array<RawShapeFrame *> frames;
if (framecount == 0) {
return loadGenericFormat(data, size, format);
}
frames.reserve(framecount);
for (uint i = 0; i < framecount; ++i) {
uint32 frameoffset = stream.readUint32LE();
uint32 framesize = stream.readUint32LE();
frames.push_back(new RawShapeFrame(data + frameoffset, framesize, format));
}
return frames;
}
// This will load any sort of shape via a ConvertShapeFormat struct
Common::Array<RawShapeFrame *> Shape::loadGenericFormat(const uint8 *data, uint32 size, const ConvertShapeFormat *format) {
uint32 framecount;
uint32 frameoffset;
uint32 framesize;
Common::MemoryReadStream ds(data, size);
Common::Array<RawShapeFrame *> frames;
if (format->_bytes_ident) {
uint8 *ident = new uint8[format->_bytes_ident];
ds.read(ident, format->_bytes_ident);
bool match = memcmp(ident, format->_ident, format->_bytes_ident) == 0;
delete[] ident;
if (!match) {
frames.clear();
return frames;
}
}
// Read special buffer
uint8 special[256];
if (format->_bytes_special) {
memset(special, 0, 256);
for (uint32 i = 0; i < format->_bytes_special; i++) special[ds.readByte() & 0xFF] = i + 2;
}
// Skip unknown
if (format->_bytes_header_unk && format != &Crusader2DShapeFormat) {
//uint32 val =
readX(ds, format->_bytes_header_unk);
//uint16 lowval = val & 0xff;
//uint16 highval = (val >> 16) & 0xff;
//uint32 dummy = 0 + lowval + highval + val;
} else {
// Appears to be shape Width x Height for Crusader 2D shapes,
// not needed - we get them by frame.
ds.skip(format->_bytes_header_unk);
}
// Read framecount, default 1 if no
if (format->_bytes_num_frames) framecount = readX(ds, format->_bytes_num_frames);
else framecount = 1;
if (framecount == 0) framecount = ConvertShape::CalcNumFrames(ds, format, size, 0);
frames.reserve(framecount);
for (uint i = 0; i < framecount; ++i) {
// Read the offset
if (format->_bytes_frame_offset) frameoffset = readX(ds, format->_bytes_frame_offset) + format->_bytes_special;
else frameoffset = format->_len_header + (format->_len_frameheader * i);
// Skip the unknown
if (format->_bytes_frameheader_unk) {
readX(ds, format->_bytes_frameheader_unk);
}
// Read frame_length
if (format->_bytes_frame_length) framesize = readX(ds, format->_bytes_frame_length) + format->_bytes_frame_length_kludge;
else framesize = size - frameoffset;
if (framesize > size) {
warning("shape frame %d goes off the end of the buffer, stopping early", i);
break;
}
ConvertShapeFrame *prev = nullptr, p;
if (format->_bytes_special && i > 0) {
prev = &p;
frames[i - 1]->getConvertShapeFrame(p);
}
frames.push_back(new RawShapeFrame(data + frameoffset, framesize, format, special, prev));
}
return frames;
}
// This will detect the format of a shape
const ConvertShapeFormat *Shape::DetectShapeFormat(const uint8 *data, uint32 size) {
Common::MemoryReadStream ds(data, size);
return Shape::DetectShapeFormat(ds, size);
}
const ConvertShapeFormat *Shape::DetectShapeFormat(Common::SeekableReadStream &ds, uint32 size) {
const ConvertShapeFormat *ret = nullptr;
if (ConvertShape::CheckUnsafe(ds, &PentagramShapeFormat, size))
ret = &PentagramShapeFormat;
else if (ConvertShape::CheckUnsafe(ds, &U8SKFShapeFormat, size))
ret = &U8SKFShapeFormat;
else if (ConvertShape::CheckUnsafe(ds, &U8ShapeFormat, size))
ret = &U8ShapeFormat;
else if (ConvertShape::CheckUnsafe(ds, &U82DShapeFormat, size))
ret = &U82DShapeFormat;
else if (ConvertShape::CheckUnsafe(ds, &CrusaderShapeFormat, size))
ret = &CrusaderShapeFormat;
else if (ConvertShape::CheckUnsafe(ds, &Crusader2DShapeFormat, size))
ret = &Crusader2DShapeFormat;
else if (ConvertShape::CheckUnsafe(ds, &U8CMPShapeFormat, size))
ret = &U8CMPShapeFormat;
return ret;
}
void Shape::getTotalDimensions(int32 &w, int32 &h, int32 &x, int32 &y) const {
if (_frames.empty()) {
w = 0;
h = 0;
x = 0;
y = 0;
return;
}
int32 minx = 1000000, maxx = -1000000;
int32 miny = 1000000, maxy = -1000000;
for (uint i = 0; i < _frames.size(); ++i) {
ShapeFrame *frame = _frames[i];
if (-frame->_xoff < minx)
minx = -frame->_xoff;
if (-frame->_yoff < miny)
miny = -frame->_yoff;
if (frame->_width - frame->_xoff - 1 > maxx)
maxx = frame->_width - frame->_xoff - 1;
if (frame->_height - frame->_yoff - 1 > maxy)
maxy = frame->_height - frame->_yoff - 1;
}
w = maxx - minx + 1;
h = maxy - miny + 1;
x = -minx;
y = -miny;
}
const ShapeFrame *Shape::getFrame(unsigned int frame) const {
if (frame < _frames.size())
return _frames[frame];
else
return nullptr;
}
} // End of namespace Ultima8
} // End of namespace Ultima

View File

@@ -0,0 +1,94 @@
/* ScummVM - Graphic Adventure Engine
*
* ScummVM is the legal property of its developers, whose names
* are too numerous to list here. Please refer to the COPYRIGHT
* file distributed with this source distribution.
*
* This program is free software: you can redistribute it and/or modify
* it under the terms of the GNU General Public License as published by
* the Free Software Foundation, either version 3 of the License, or
* (at your option) any later version.
*
* This program is distributed in the hope that it will be useful,
* but WITHOUT ANY WARRANTY; without even the implied warranty of
* MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
* GNU General Public License for more details.
*
* You should have received a copy of the GNU General Public License
* along with this program. If not, see <http://www.gnu.org/licenses/>.
*
*/
#ifndef ULTIMA8_GFX_SHAPE_H
#define ULTIMA8_GFX_SHAPE_H
#include "ultima/shared/std/containers.h"
#include "common/stream.h"
namespace Ultima {
namespace Ultima8 {
class ShapeFrame;
class RawShapeFrame;
class Palette;
struct ConvertShapeFormat;
class Shape {
public:
// Parse data, create frames.
// NB: Shape uses data without copying it. It is deleted on destruction
// If format is not specified it will be autodetected
Shape(const uint8 *data, uint32 size, const ConvertShapeFormat *format,
const uint16 flexId, const uint32 shapenum);
Shape(Common::SeekableReadStream *src, const ConvertShapeFormat *format);
virtual ~Shape();
void setPalette(const Palette *pal) {
_palette = pal;
}
const Palette *getPalette() const {
return _palette;
}
uint32 frameCount() const {
return static_cast<uint32>(_frames.size());
}
//! Returns the dimensions of all frames combined
//! (w,h) = size of smallest rectangle covering all frames
//! (x,y) = coordinates of origin relative to top-left point of rectangle
void getTotalDimensions(int32 &w, int32 &h, int32 &x, int32 &y) const;
const ShapeFrame *getFrame(unsigned int frame) const;
void getShapeId(uint16 &flexId, uint32 &shapenum) const;
// This will detect the format of a shape
static const ConvertShapeFormat *DetectShapeFormat(const uint8 *data, uint32 size);
static const ConvertShapeFormat *DetectShapeFormat(Common::SeekableReadStream &ds, uint32 size);
private:
void loadFrames(const uint8 *data, uint32 size, const ConvertShapeFormat *format);
// This will load a u8 style shape 'optimized'.
static Common::Array<RawShapeFrame *> loadU8Format(const uint8 *data, uint32 size, const ConvertShapeFormat *format);
// This will load a pentagram style shape 'optimized'.
static Common::Array<RawShapeFrame *> loadPentagramFormat(const uint8 *data, uint32 size, const ConvertShapeFormat *format);
// This will load any sort of shape via a ConvertShapeFormat struct
// Crusader shapes must be loaded this way
static Common::Array<RawShapeFrame *> loadGenericFormat(const uint8 *data, uint32 size, const ConvertShapeFormat *format);
Common::Array<ShapeFrame *> _frames;
const Palette *_palette;
const uint16 _flexId;
const uint32 _shapeNum;
};
} // End of namespace Ultima8
} // End of namespace Ultima
#endif

View File

@@ -0,0 +1,87 @@
/* ScummVM - Graphic Adventure Engine
*
* ScummVM is the legal property of its developers, whose names
* are too numerous to list here. Please refer to the COPYRIGHT
* file distributed with this source distribution.
*
* This program is free software: you can redistribute it and/or modify
* it under the terms of the GNU General Public License as published by
* the Free Software Foundation, either version 3 of the License, or
* (at your option) any later version.
*
* This program is distributed in the hope that it will be useful,
* but WITHOUT ANY WARRANTY; without even the implied warranty of
* MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
* GNU General Public License for more details.
*
* You should have received a copy of the GNU General Public License
* along with this program. If not, see <http://www.gnu.org/licenses/>.
*
*/
#include "ultima/ultima8/misc/debugger.h"
#include "ultima/ultima8/gfx/shape_archive.h"
#include "ultima/ultima8/gfx/shape.h"
#include "ultima/ultima8/convert/convert_shape.h"
namespace Ultima {
namespace Ultima8 {
ShapeArchive::~ShapeArchive() {
Archive::uncache();
}
Shape *ShapeArchive::getShape(uint32 shapenum) {
if (shapenum >= _count)
return nullptr;
cache(shapenum);
return _shapes[shapenum];
}
void ShapeArchive::cache(uint32 shapenum) {
if (shapenum >= _count) return;
if (_shapes.empty()) _shapes.resize(_count);
if (_shapes[shapenum]) return;
uint32 shpsize;
uint8 *data = getRawObject(shapenum, &shpsize);
if (!data || shpsize == 0) return;
// Auto detect format
if (!_format) {
_format = Shape::DetectShapeFormat(data, shpsize);
}
if (!_format) {
delete [] data;
warning("Unable to detect shape format for flex.");
return;
}
Shape *shape = new Shape(data, shpsize, _format, _id, shapenum);
if (_palette) shape->setPalette(_palette);
_shapes[shapenum] = shape;
}
void ShapeArchive::uncache(uint32 shapenum) {
if (shapenum >= _count) return;
if (_shapes.empty()) return;
delete _shapes[shapenum];
_shapes[shapenum] = nullptr;
}
bool ShapeArchive::isCached(uint32 shapenum) const {
if (shapenum >= _count) return false;
if (_shapes.empty()) return false;
return (_shapes[shapenum] != nullptr);
}
} // End of namespace Ultima8
} // End of namespace Ultima

View File

@@ -0,0 +1,61 @@
/* ScummVM - Graphic Adventure Engine
*
* ScummVM is the legal property of its developers, whose names
* are too numerous to list here. Please refer to the COPYRIGHT
* file distributed with this source distribution.
*
* This program is free software: you can redistribute it and/or modify
* it under the terms of the GNU General Public License as published by
* the Free Software Foundation, either version 3 of the License, or
* (at your option) any later version.
*
* This program is distributed in the hope that it will be useful,
* but WITHOUT ANY WARRANTY; without even the implied warranty of
* MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
* GNU General Public License for more details.
*
* You should have received a copy of the GNU General Public License
* along with this program. If not, see <http://www.gnu.org/licenses/>.
*
*/
#ifndef ULTIMA8_GFX_SHAPEARCHIVE_H
#define ULTIMA8_GFX_SHAPEARCHIVE_H
#include "ultima/ultima8/filesys/archive.h"
namespace Ultima {
namespace Ultima8 {
class Shape;
struct ConvertShapeFormat;
class Palette;
class ShapeArchive : public Archive {
public:
ShapeArchive(uint16 id, const Palette *pal = 0,
const ConvertShapeFormat *format = 0)
: Archive(), _id(id), _format(format), _palette(pal) { }
ShapeArchive(Common::SeekableReadStream *rs, uint16 id, const Palette *pal = 0,
const ConvertShapeFormat *format = 0)
: Archive(rs), _id(id), _format(format), _palette(pal) { }
~ShapeArchive() override;
Shape *getShape(uint32 shapenum);
void cache(uint32 shapenum) override;
void uncache(uint32 shapenum) override;
bool isCached(uint32 shapenum) const override;
protected:
uint16 _id;
const ConvertShapeFormat *_format;
const Palette *_palette;
Std::vector<Shape *> _shapes;
};
} // End of namespace Ultima8
} // End of namespace Ultima
#endif

View File

@@ -0,0 +1,119 @@
/* ScummVM - Graphic Adventure Engine
*
* ScummVM is the legal property of its developers, whose names
* are too numerous to list here. Please refer to the COPYRIGHT
* file distributed with this source distribution.
*
* This program is free software: you can redistribute it and/or modify
* it under the terms of the GNU General Public License as published by
* the Free Software Foundation, either version 3 of the License, or
* (at your option) any later version.
*
* This program is distributed in the hope that it will be useful,
* but WITHOUT ANY WARRANTY; without even the implied warranty of
* MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
* GNU General Public License for more details.
*
* You should have received a copy of the GNU General Public License
* along with this program. If not, see <http://www.gnu.org/licenses/>.
*
*/
#include "ultima/ultima8/misc/common_types.h"
#include "ultima/ultima8/gfx/shape_frame.h"
#include "ultima/ultima8/gfx/raw_shape_frame.h"
namespace Ultima {
namespace Ultima8 {
ShapeFrame::ShapeFrame(const RawShapeFrame *rawframe) :
_width(_surface.w), _height(_surface.h),
_xoff(rawframe->_xoff), _yoff(rawframe->_yoff),
_keycolor(0xFF) {
_surface.create(rawframe->_width, rawframe->_height, Graphics::PixelFormat::createFormatCLUT8());
// load adjusting keycolor until success
if (!load(rawframe, _keycolor)) {
_keycolor = 0;
while (!load(rawframe, _keycolor) && _keycolor < 0xFF) {
_keycolor++;
}
}
}
ShapeFrame::~ShapeFrame() {
_surface.free();
}
bool ShapeFrame::load(const RawShapeFrame *rawframe, uint8 keycolor) {
bool result = true;
uint8 *pixels = reinterpret_cast<uint8 *>(_surface.getPixels());
memset(pixels, keycolor, _surface.w * _surface.h);
for (int y = 0; y < _surface.h; y++) {
int32 xpos = 0;
const uint8 *linedata = rawframe->_rle_data + rawframe->_line_offsets[y];
do {
xpos += *linedata++;
if (xpos >= _surface.w)
break;
int32 dlen = *linedata++;
int type = 0;
if (rawframe->_compressed) {
type = dlen & 1;
dlen >>= 1;
}
for (int doff = 0; doff < dlen; doff++) {
if (*linedata == keycolor)
result = false;
pixels[y * _surface.w + xpos + doff] = *linedata;
if (!type) {
linedata++;
}
}
xpos += dlen;
if (type) {
linedata++;
}
} while (xpos < _surface.w);
}
return result;
}
// Checks to see if the frame has a pixel at the point
bool ShapeFrame::hasPoint(int x, int y) const {
// Add the offset
x += _xoff;
y += _yoff;
// First gross culling based on dims
if (x < 0 || y < 0 || x >= _surface.w || y >= _surface.h)
return false;
return _surface.getPixel(x, y) != _keycolor;
}
// Get the pixel at the point
uint8 ShapeFrame::getPixel(int x, int y) const {
// Add the offset
x += _xoff;
y += _yoff;
// First gross culling based on dims
if (x < 0 || y < 0 || x >= _surface.w || y >= _surface.h)
return _keycolor;
return _surface.getPixel(x, y);
}
} // End of namespace Ultima8
} // End of namespace Ultima

View File

@@ -0,0 +1,66 @@
/* ScummVM - Graphic Adventure Engine
*
* ScummVM is the legal property of its developers, whose names
* are too numerous to list here. Please refer to the COPYRIGHT
* file distributed with this source distribution.
*
* This program is free software: you can redistribute it and/or modify
* it under the terms of the GNU General Public License as published by
* the Free Software Foundation, either version 3 of the License, or
* (at your option) any later version.
*
* This program is distributed in the hope that it will be useful,
* but WITHOUT ANY WARRANTY; without even the implied warranty of
* MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
* GNU General Public License for more details.
*
* You should have received a copy of the GNU General Public License
* along with this program. If not, see <http://www.gnu.org/licenses/>.
*
*/
#ifndef ULTIMA8_GFX_SHAPEFRAME_H
#define ULTIMA8_GFX_SHAPEFRAME_H
#include "graphics/surface.h"
namespace Ultima {
namespace Ultima8 {
class RawShapeFrame;
/** A decompressed version of the RawShapeFrame for easier rendering */
class ShapeFrame {
public:
ShapeFrame(const RawShapeFrame *rawframe);
~ShapeFrame();
int16 &_width;
int16 &_height;
int16 _xoff, _yoff;
uint8 _keycolor;
bool hasPoint(int x, int y) const; // Check to see if a point is in the frame
uint8 getPixel(int x, int y) const; // Get the pixel at the point
const Graphics::Surface &getSurface() const { return _surface; }
private:
Graphics::Surface _surface;
/**
* Load the pixel data from the raw shape rle data using key color for transparency
* @param rawframe the raw shape to load rle data
* @param keycolor the color representing transparency
* @return false if the keycolor is found in the raw shape frame data
*/
bool load(const RawShapeFrame *rawframe, uint8 keycolor);
};
} // End of namespace Ultima8
} // End of namespace Ultima
#endif

View File

@@ -0,0 +1,101 @@
/* ScummVM - Graphic Adventure Engine
*
* ScummVM is the legal property of its developers, whose names
* are too numerous to list here. Please refer to the COPYRIGHT
* file distributed with this source distribution.
*
* This program is free software: you can redistribute it and/or modify
* it under the terms of the GNU General Public License as published by
* the Free Software Foundation, either version 3 of the License, or
* (at your option) any later version.
*
* This program is distributed in the hope that it will be useful,
* but WITHOUT ANY WARRANTY; without even the implied warranty of
* MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
* GNU General Public License for more details.
*
* You should have received a copy of the GNU General Public License
* along with this program. If not, see <http://www.gnu.org/licenses/>.
*
*/
#include "ultima/ultima8/misc/common_types.h"
#include "ultima/ultima8/gfx/shape_info.h"
#include "ultima/ultima8/ultima8.h"
namespace Ultima {
namespace Ultima8 {
bool ShapeInfo::getTypeFlag(int typeFlag) const {
if (GAME_IS_U8)
return getTypeFlagU8(typeFlag);
else if (GAME_IS_CRUSADER)
return getTypeFlagCrusader(typeFlag);
warning("Invalid game type for shape info");
return false;
}
bool ShapeInfo::getTypeFlagU8(int typeFlag) const {
// This is not nice. The Typeflags in U8 were stored in an 8 byte array
// and they could access them with a number from 0 to 63
// Problem: We don't store them in an 8 byte array so we can't access
// with a number from 0 to 63
// So what we do is split the flag up into the bits
if (typeFlag <= 11) { // flags Byte 0, 1:0-3 Bits 0-11
return (_flags >> typeFlag) & 1;
} else if (typeFlag <= 15) { // family Byte 1:4-7 Bits 11-15
return (_family >> (typeFlag - 12)) & 1;
} else if (typeFlag <= 19) { // equipType Byte 2:0-3 Bits 16-19
return (_equipType >> (typeFlag - 16)) & 1;
} else if (typeFlag <= 23) { // x Byte 2:4-7 Bits 20-23
return (_x >> (typeFlag - 20)) & 1;
} else if (typeFlag <= 27) { // y Byte 3:0-3 Bits 24-27
return (_y >> (typeFlag - 24)) & 1;
} else if (typeFlag <= 31) { // z Byte 3:4-7 Bits 28-31
return (_z >> (typeFlag - 28)) & 1;
} else if (typeFlag <= 35) { // animtype Byte 4:0-3 Bits 32-35
return (_animType >> (typeFlag - 32)) & 1;
} else if (typeFlag <= 39) { // animdata Byte 4:4-7 Bits 36-49
return (_animData >> (typeFlag - 36)) & 1;
} else if (typeFlag <= 43) { // unknown Byte 5:0-3 Bits 40-43
return (_animSpeed >> (typeFlag - 40)) & 1;
} else if (typeFlag <= 47) { // _flags Byte 5:4-7 Bits 44-47
return (_flags >> (12 + typeFlag - 44)) & 1;
} else if (typeFlag <= 55) { // weight Byte 6 Bits 48-55
return (_weight >> (typeFlag - 48)) & 1;
} else if (typeFlag <= 63) { // volume Byte 7 Bits 56-63
return (_volume >> (typeFlag - 56)) & 1;
}
return false;
}
bool ShapeInfo::getTypeFlagCrusader(int typeFlag) const {
if (typeFlag <= 11) { // _flags Byte 0, 1:0-3 Bits 0-11
return (_flags >> typeFlag) & 1;
} else if (typeFlag <= 16) { // _family Byte 1:4-7,2:0 Bits 12-16
return (_family >> (typeFlag - 12)) & 1;
} else if (typeFlag <= 20) { // unknown Byte 2:0-3 Bits 17-20
warning("unknown typeFlag %d requested.", typeFlag);
} else if (typeFlag <= 26) { // x Byte 2:4-7,3:0-1 Bits 21-26
return (_x >> (typeFlag - 21)) & 1;
} else if (typeFlag <= 31) { // y Byte 3:2-6 Bits 27-31
return (_y >> (typeFlag - 27)) & 1;
} else if (typeFlag <= 36) { // z Byte 3:7,4:0-3 Bits 32-36
return (_z >> (typeFlag - 32)) & 1;
} else if (typeFlag <= 47) {
warning("unknown typeFlag %d requested.", typeFlag);
} else if (typeFlag <= 55) { // _flags Byte 6: 0-7 Bits 48-55
return (_flags >> (12 + typeFlag - 55)) & 1;
} else if (typeFlag <= 71) {
warning("unknown typeFlag %d requested.", typeFlag);
}
return false;
}
} // End of namespace Ultima8
} // End of namespace Ultima

View File

@@ -0,0 +1,199 @@
/* ScummVM - Graphic Adventure Engine
*
* ScummVM is the legal property of its developers, whose names
* are too numerous to list here. Please refer to the COPYRIGHT
* file distributed with this source distribution.
*
* This program is free software: you can redistribute it and/or modify
* it under the terms of the GNU General Public License as published by
* the Free Software Foundation, either version 3 of the License, or
* (at your option) any later version.
*
* This program is distributed in the hope that it will be useful,
* but WITHOUT ANY WARRANTY; without even the implied warranty of
* MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
* GNU General Public License for more details.
*
* You should have received a copy of the GNU General Public License
* along with this program. If not, see <http://www.gnu.org/licenses/>.
*
*/
#ifndef ULTIMA8_GFX_SHAPEINFO_H
#define ULTIMA8_GFX_SHAPEINFO_H
#include "ultima/ultima8/world/weapon_info.h"
#include "ultima/ultima8/world/armour_info.h"
#include "ultima/ultima8/world/damage_info.h"
#include "ultima/ultima8/world/actors/monster_info.h"
namespace Ultima {
namespace Ultima8 {
class ShapeInfo {
public:
enum SFlags {
SI_FIXED = 0x0001,
SI_SOLID = 0x0002,
SI_SEA = 0x0004,
SI_LAND = 0x0008,
SI_OCCL = 0x0010,
SI_BAG = 0x0020,
SI_DAMAGING = 0x0040,
SI_NOISY = 0x0080,
SI_DRAW = 0x0100,
SI_IGNORE = 0x0200,
SI_ROOF = 0x0400, // reflective in Crusader?
SI_TRANSL = 0x0800,
SI_EDITOR = 0x1000,
// Note: overlapping names for the rest of the bits depending on U8 or Cru.
SI_U8_EXPLODE = 0x2000,
SI_CRU_SELECTABLE = 0x2000,
SI_UNKNOWN46 = 0x4000,
SI_CRU_PRELOAD = 0x4000, // we don't need this flag, we preload everything.
SI_UNKNOWN47 = 0x8000,
SI_CRU_SOUND = 0x8000, // TODO: how is this used?
SI_CRU_TARGETABLE = 0x10000,
SI_CRU_NPC = 0x20000,
SI_CRU_UNK66 = 0x40000,
SI_CRU_UNK67 = 0x80000
};
enum SFamily {
SF_GENERIC = 0,
SF_QUALITY = 1,
SF_QUANTITY = 2,
SF_GLOBEGG = 3,
// "Unk" eggs are not "unknown", they are triggers for usecode
// (unk is the source language for usecode)
SF_UNKEGG = 4,
SF_BREAKABLE = 5,
SF_CONTAINER = 6,
SF_MONSTEREGG = 7,
SF_TELEPORTEGG = 8,
SF_REAGENT = 9,
SF_CRUWEAPON = 10, // Used in Crusader
SF_CRUAMMO = 11, // Used in Crusader
SF_CRUBOMB = 12, // Used in Crusader
SF_CRUINVITEM = 13, // Used in Crusader
SF_15 = 15
};
enum SEquipType {
SE_NONE = 0,
SE_SHIELD = 1,
SE_ARM = 2,
SE_HEAD = 3,
SE_BODY = 4,
SE_LEGS = 5,
SE_WEAPON = 6,
SE_BACKPACK = 7
};
uint32 _flags;
uint32 _x, _y, _z;
uint32 _family;
uint32 _equipType;
uint32 _animType, _animData, _animSpeed;
uint32 _weight, _volume;
WeaponInfo *_weaponInfo;
ArmourInfo *_armourInfo;
MonsterInfo *_monsterInfo;
DamageInfo *_damageInfo;
inline bool is_fixed() const {
return (_flags & SI_FIXED) != 0;
}
inline bool is_solid() const {
return (_flags & SI_SOLID) != 0;
}
inline bool is_sea() const {
return (_flags & SI_SEA) != 0;
}
inline bool is_land() const {
return (_flags & SI_LAND) != 0;
}
inline bool is_occl() const {
return (_flags & SI_OCCL) != 0;
}
inline bool is_bag() const {
return (_flags & SI_BAG) != 0;
}
inline bool is_damaging() const {
return (_flags & SI_DAMAGING) != 0;
}
inline bool is_noisy() const {
return (_flags & SI_NOISY) != 0;
}
inline bool is_draw() const {
return (_flags & SI_DRAW) != 0;
}
inline bool is_ignore() const {
return (_flags & SI_IGNORE) != 0;
}
inline bool is_roof() const {
return (_flags & SI_ROOF) != 0;
}
inline bool is_translucent() const {
return (_flags & SI_TRANSL) != 0;
}
inline bool is_editor() const {
return (_flags & SI_EDITOR) != 0;
}
inline bool is_u8_explode() const {
return (_flags & SI_U8_EXPLODE) != 0;
}
inline bool is_targetable() const {
return (_flags & (SI_OCCL | SI_CRU_TARGETABLE));
}
inline bool is_invitem() const {
return (_family == SF_CRUINVITEM);
}
bool hasQuantity() const {
return (_family == SF_QUANTITY || _family == SF_REAGENT);
}
bool takesDamage() const {
return (_damageInfo && _damageInfo->takesDamage());
}
bool getTypeFlag(int typeFlag) const;
bool getTypeFlagU8(int typeFlag) const;
bool getTypeFlagCrusader(int typeFlag) const;
inline void getFootpadWorld(int32 &x, int32 &y, int32 &z, uint16 flipped) const;
ShapeInfo() :
_flags(0), _x(0), _y(0), _z(0),
_family(0), _equipType(0), _animType(0), _animData(0),
_animSpeed(0), _weight(0), _volume(0),
_weaponInfo(nullptr), _armourInfo(nullptr),
_monsterInfo(nullptr), _damageInfo(nullptr) { }
~ShapeInfo() {
delete _weaponInfo;
delete[] _armourInfo;
delete _monsterInfo;
delete _damageInfo;
}
};
inline void ShapeInfo::getFootpadWorld(int32 &x, int32 &y, int32 &z, uint16 flipped) const {
z = _z * 8;
if (flipped) {
x = _y * 32;
y = _x * 32;
} else {
x = _x * 32;
y = _y * 32;
}
}
} // End of namespace Ultima8
} // End of namespace Ultima
#endif

View File

@@ -0,0 +1,325 @@
/* ScummVM - Graphic Adventure Engine
*
* ScummVM is the legal property of its developers, whose names
* are too numerous to list here. Please refer to the COPYRIGHT
* file distributed with this source distribution.
*
* This program is free software: you can redistribute it and/or modify
* it under the terms of the GNU General Public License as published by
* the Free Software Foundation, either version 3 of the License, or
* (at your option) any later version.
*
* This program is distributed in the hope that it will be useful,
* but WITHOUT ANY WARRANTY; without even the implied warranty of
* MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
* GNU General Public License for more details.
*
* You should have received a copy of the GNU General Public License
* along with this program. If not, see <http://www.gnu.org/licenses/>.
*
*/
#include "ultima/ultima.h"
#include "ultima/ultima8/ultima8.h"
#include "ultima/ultima8/misc/debugger.h"
#include "ultima/ultima8/gfx/skf_player.h"
#include "ultima/ultima8/convert/u8/convert_shape_u8.h"
#include "ultima/ultima8/filesys/raw_archive.h"
#include "ultima/ultima8/gfx/shape.h"
#include "ultima/ultima8/gfx/texture.h"
#include "ultima/ultima8/audio/music_process.h"
#include "ultima/ultima8/audio/audio_process.h"
#include "ultima/ultima8/audio/raw_audio_sample.h"
#include "ultima/ultima8/gfx/fonts/font.h"
#include "ultima/ultima8/gfx/fonts/font_manager.h"
#include "ultima/ultima8/gfx/fonts/rendered_text.h"
#include "common/config-manager.h"
#include "common/system.h"
namespace Ultima {
namespace Ultima8 {
enum SKFAction {
SKF_PlayMusic = 3,
SKF_SlowStopMusic = 4,
SKF_PlaySFX = 5,
SKF_StopSFX = 6,
SKF_SetSpeed = 7,
SKF_FadeOut = 8,
SKF_FadeIn = 9,
SKF_Wait = 12,
SKF_PlaySound = 14,
SKF_FadeWhite = 15,
SKF_ClearSubs = 18
};
struct SKFEvent {
unsigned int _frame;
SKFAction _action;
unsigned int _data;
};
// number of steps in a fade
static const int FADESTEPS = 16; // HACK: half speed
SKFPlayer::SKFPlayer(Common::SeekableReadStream *rs, int width, int height, bool introMusicHack)
: _width(width), _height(height), _curFrame(0), _curObject(0), _curAction(0),
_curEvent(0), _playing(false), _timer(0), _frameRate(15), _fadeColour(0),
_fadeLevel(0), _buffer(nullptr), _subs(nullptr), _introMusicHack(introMusicHack),
_lastUpdate(0), _subtitleY(0) {
_skf = new RawArchive(rs);
Common::ReadStream *eventlist = _skf->get_datasource(0);
if (!eventlist) {
warning("No eventlist found in SKF");
return;
}
parseEventList(eventlist);
delete eventlist;
Graphics::Screen *screen = Ultima8Engine::get_instance()->getScreen();
_buffer = new RenderSurface(_width, _height, screen->format);
}
SKFPlayer::~SKFPlayer() {
for (unsigned int i = 0; i < _events.size(); ++i)
delete _events[i];
delete _skf;
delete _buffer;
delete _subs;
}
void SKFPlayer::parseEventList(Common::ReadStream *eventlist) {
uint16 frame = eventlist->readUint16LE();
while (frame != 0xFFFF) {
SKFEvent *ev = new SKFEvent;
ev->_frame = frame;
ev->_action = static_cast<SKFAction>(eventlist->readUint16LE());
ev->_data = eventlist->readUint16LE();
_events.push_back(ev);
frame = eventlist->readUint16LE();
}
}
void SKFPlayer::start() {
uint32 color = TEX32_PACK_RGB(0, 0, 0);
_buffer->fill32(color, 0, 0, _width, _height);
MusicProcess *musicproc = MusicProcess::get_instance();
if (musicproc) musicproc->playMusic(0);
_playing = true;
_lastUpdate = g_system->getMillis();
}
void SKFPlayer::stop() {
MusicProcess *musicproc = MusicProcess::get_instance();
if (musicproc && !_introMusicHack) musicproc->playMusic(0);
_playing = false;
}
void SKFPlayer::paint(RenderSurface *surf, int /*lerp*/) {
if (!_buffer) return;
if (!_fadeLevel) {
Common::Rect srcRect(_width, _height);
surf->Blit(*_buffer->getRawSurface(), srcRect, 0, 0);
if (_subs)
_subs->draw(surf, 60, _subtitleY);
} else {
uint32 fade = TEX32_PACK_RGBA(_fadeColour, _fadeColour, _fadeColour,
(_fadeLevel * 255) / FADESTEPS);
Common::Rect srcRect(_width, _height);
surf->FadedBlit(*_buffer->getRawSurface(), srcRect, 0, 0, fade);
if (_subs)
_subs->drawBlended(surf, 60, _subtitleY, fade);
}
}
void SKFPlayer::run() {
if (!_playing || !_buffer) return;
MusicProcess *musicproc = MusicProcess::get_instance();
// if doing something, continue
if (_curAction) {
if (_curAction == SKF_FadeOut || _curAction == SKF_FadeWhite) {
_fadeLevel++;
if (_fadeLevel == FADESTEPS) _curAction = 0; // done
} else if (_curAction == SKF_FadeIn) {
_fadeLevel--;
if (_fadeLevel == 0) _curAction = 0; // done
} else if (_curAction == SKF_SlowStopMusic) {
if (!musicproc || !musicproc->isFading()) {
if (musicproc)
musicproc->playMusic(0); // stop playback
_curAction = 0; // done
} else {
// continue to wait for fade to finish
return;
}
} else {
debugC(kDebugVideo, "Unknown fade action: %u", _curAction);
}
}
// CHECKME: this timing may not be accurate enough...
uint32 now = g_system->getMillis();
if (_lastUpdate + (1000 / _frameRate) > now) return;
_lastUpdate += (1000 / _frameRate);
// if waiting, continue to wait
if (_timer) {
_timer--;
return;
}
Font *redfont;
redfont = FontManager::get_instance()->getGameFont(6, true);
AudioProcess *audioproc = AudioProcess::get_instance();
bool subtitles = ConfMan.getBool("subtitles");
bool speechMute = ConfMan.getBool("speech_mute");
// handle _events for the current frame
while (_curEvent < _events.size() && _events[_curEvent]->_frame <= _curFrame) {
debugCN(kDebugVideo, "Event %u: ", _curEvent);
switch (_events[_curEvent]->_action) {
case SKF_FadeOut:
_curAction = SKF_FadeOut;
_fadeColour = 0;
_fadeLevel = 0;
debugC(kDebugVideo, "FadeOut");
break;
case SKF_FadeIn:
_curAction = SKF_FadeIn;
_fadeLevel = FADESTEPS;
debugC(kDebugVideo, "FadeIn");
break;
case SKF_FadeWhite:
_curAction = SKF_FadeWhite;
_fadeColour = 0xFF;
_fadeLevel = 0;
debugC(kDebugVideo, "FadeWhite");
break;
case SKF_Wait:
debugC(kDebugVideo, "Wait %u", _events[_curEvent]->_data);
_timer = _events[_curEvent]->_data;
_curEvent++;
return;
case SKF_PlayMusic:
debugC(kDebugVideo, "PlayMusic %u", _events[_curEvent]->_data);
if (musicproc) musicproc->playMusic(_events[_curEvent]->_data);
break;
case SKF_SlowStopMusic:
debugC(kDebugVideo, "SlowStopMusic");
if (musicproc)
musicproc->fadeMusic(1500);
_curAction = SKF_SlowStopMusic;
break;
case SKF_PlaySFX:
debugC(kDebugVideo, "PlaySFX %u", _events[_curEvent]->_data);
if (audioproc) audioproc->playSFX(_events[_curEvent]->_data, 0x60, 0, 0);
break;
case SKF_StopSFX:
debugC(kDebugVideo, "StopSFX %u", _events[_curEvent]->_data);
if (audioproc) audioproc->stopSFX(_events[_curEvent]->_data, 0);
break;
case SKF_SetSpeed:
debugC(kDebugVideo, "SetSpeed %u", _events[_curEvent]->_data);
// _frameRate = _events[_curEvent]->_data;
break;
case SKF_PlaySound: {
debugC(kDebugVideo, "PlaySound %u", _events[_curEvent]->_data);
if (!speechMute && audioproc) {
uint8 *buf = _skf->get_object(_events[_curEvent]->_data);
uint32 bufsize = _skf->get_size(_events[_curEvent]->_data);
AudioSample *s;
uint32 rate = buf[6] + (buf[7] << 8);
bool stereo = (buf[8] == 2);
s = new RawAudioSample(buf + 34, bufsize - 34,
rate, true, stereo);
audioproc->playSample(s, 0x60, 0, true);
// FIXME: memory leak! (sample is never deleted)
}
// subtitles
char *textbuf = reinterpret_cast<char *>(
_skf->get_object(_events[_curEvent]->_data - 1));
uint32 textsize = _skf->get_size(_events[_curEvent]->_data - 1);
if (subtitles && textsize > 7) {
Std::string subtitle = (textbuf + 6);
delete _subs;
_subtitleY = textbuf[4] + (textbuf[5] << 8);
unsigned int remaining;
_subs = redfont->renderText(subtitle, remaining, 200, 0,
Font::TEXT_CENTER);
}
delete textbuf;
break;
}
case SKF_ClearSubs:
debugC(kDebugVideo, "ClearSubs");
delete _subs;
_subs = nullptr;
break;
default:
debugC(kDebugVideo, "Unknown action %d", _events[_curEvent]->_action);
break;
}
_curEvent++;
}
_curFrame++;
uint16 objecttype = 0;
do {
_curObject++;
if (_curObject >= _skf->getCount()) {
stop(); // done
return;
}
// read object
Common::SeekableReadStream * object = _skf->get_datasource(_curObject);
if (!object)
continue;
objecttype = object->size() > 2 ? object->readUint16LE() : 0;
debugC(kDebugVideo, "Object %u/%u, type = %u", _curObject, _skf->getCount(), objecttype);
switch (objecttype) {
case 1:
_palette.load(*object);
_palette.updateNativeMap(_buffer->getRawSurface()->format);
break;
case 2: {
object->seek(0);
Shape *shape = new Shape(object, &U8SKFShapeFormat);
shape->setPalette(&_palette);
_buffer->BeginPainting();
_buffer->Paint(shape, 0, 0, 0);
_buffer->EndPainting();
delete shape;
break;
}
default:
break;
}
delete object;
} while (objecttype != 2);
_timer = 1; // HACK! timing is rather broken currently...
}
} // End of namespace Ultima8
} // End of namespace Ultima

View File

@@ -0,0 +1,77 @@
/* ScummVM - Graphic Adventure Engine
*
* ScummVM is the legal property of its developers, whose names
* are too numerous to list here. Please refer to the COPYRIGHT
* file distributed with this source distribution.
*
* This program is free software: you can redistribute it and/or modify
* it under the terms of the GNU General Public License as published by
* the Free Software Foundation, either version 3 of the License, or
* (at your option) any later version.
*
* This program is distributed in the hope that it will be useful,
* but WITHOUT ANY WARRANTY; without even the implied warranty of
* MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
* GNU General Public License for more details.
*
* You should have received a copy of the GNU General Public License
* along with this program. If not, see <http://www.gnu.org/licenses/>.
*
*/
#ifndef ULTIMA8_GFX_SKFPLAYER_H
#define ULTIMA8_GFX_SKFPLAYER_H
#include "ultima/shared/std/containers.h"
#include "ultima/ultima8/gfx/movie_player.h"
#include "ultima/ultima8/gfx/render_surface.h"
#include "ultima/ultima8/gfx/palette.h"
namespace Ultima {
namespace Ultima8 {
struct SKFEvent;
class RawArchive;
class RenderedText;
class Palette;
class SKFPlayer : public MoviePlayer {
public:
SKFPlayer(Common::SeekableReadStream *rs, int width, int height, bool introMusicHack = false);
~SKFPlayer();
void run();
void paint(RenderSurface *surf, int lerp);
void start();
void stop();
bool isPlaying() const {
return _playing;
}
private:
void parseEventList(Common::ReadStream *rs);
int _width, _height;
RawArchive *_skf;
Std::vector<SKFEvent *> _events;
unsigned int _curFrame, _curObject;
unsigned int _curAction;
unsigned int _curEvent;
bool _playing;
unsigned int _lastUpdate;
unsigned int _timer;
unsigned int _frameRate;
uint8 _fadeColour, _fadeLevel;
RenderSurface *_buffer;
Palette _palette;
RenderedText *_subs;
int _subtitleY;
bool _introMusicHack;
};
} // End of namespace Ultima8
} // End of namespace Ultima
#endif

View File

@@ -0,0 +1,58 @@
/* ScummVM - Graphic Adventure Engine
*
* ScummVM is the legal property of its developers, whose names
* are too numerous to list here. Please refer to the COPYRIGHT
* file distributed with this source distribution.
*
* This program is free software: you can redistribute it and/or modify
* it under the terms of the GNU General Public License as published by
* the Free Software Foundation, either version 3 of the License, or
* (at your option) any later version.
*
* This program is distributed in the hope that it will be useful,
* but WITHOUT ANY WARRANTY; without even the implied warranty of
* MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
* GNU General Public License for more details.
*
* You should have received a copy of the GNU General Public License
* along with this program. If not, see <http://www.gnu.org/licenses/>.
*
*/
#ifndef ULTIMA8_GFX_TEXTURE_H
#define ULTIMA8_GFX_TEXTURE_H
namespace Ultima {
namespace Ultima8 {
//
// Texturing Helper Macros
//
// 32 Bit Texture bit operations
#define TEX32_A_SHIFT 24
#define TEX32_A_MASK 0xFF000000
#define TEX32_A(col32) (((col32)&TEX32_A_MASK)>>TEX32_A_SHIFT)
#define TEX32_G_SHIFT 8
#define TEX32_G_MASK 0x0000FF00
#define TEX32_G(col32) (((col32)&TEX32_G_MASK)>>TEX32_G_SHIFT)
#define TEX32_B_SHIFT 16
#define TEX32_B_MASK 0x00FF0000
#define TEX32_B(col32) (((col32)&TEX32_B_MASK)>>TEX32_B_SHIFT)
#define TEX32_R_SHIFT 0
#define TEX32_R_MASK 0x000000FF
#define TEX32_R(col32) (((col32)&TEX32_R_MASK)>>TEX32_R_SHIFT)
#define TEX32_PACK_RGB(r, g, b) (uint32)(((0xFF) << TEX32_A_SHIFT) | ((r) << TEX32_R_SHIFT) | \
((g) << TEX32_G_SHIFT) | ((b) << TEX32_B_SHIFT))
#define TEX32_PACK_RGBA(r, g, b, a) (uint32)(((a) << TEX32_A_SHIFT) | ((r) << TEX32_R_SHIFT) | \
((g) << TEX32_G_SHIFT) | ((b) << TEX32_B_SHIFT))
} // End of namespace Ultima8
} // End of namespace Ultima
#endif

View File

@@ -0,0 +1,472 @@
/* ScummVM - Graphic Adventure Engine
*
* ScummVM is the legal property of its developers, whose names
* are too numerous to list here. Please refer to the COPYRIGHT
* file distributed with this source distribution.
*
* This program is free software: you can redistribute it and/or modify
* it under the terms of the GNU General Public License as published by
* the Free Software Foundation, either version 3 of the License, or
* (at your option) any later version.
*
* This program is distributed in the hope that it will be useful,
* but WITHOUT ANY WARRANTY; without even the implied warranty of
* MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
* GNU General Public License for more details.
*
* You should have received a copy of the GNU General Public License
* along with this program. If not, see <http://www.gnu.org/licenses/>.
*
*/
#include "ultima/ultima8/misc/debugger.h"
#include "ultima/ultima8/gfx/type_flags.h"
#include "ultima/ultima8/conf/config_file_manager.h"
#include "ultima/ultima8/ultima8.h"
#include "ultima/ultima8/games/game_data.h"
#include "ultima/ultima8/gfx/main_shape_archive.h"
#include "ultima/ultima8/gfx/shape.h"
#include "ultima/ultima8/games/treasure_loader.h"
namespace Ultima {
namespace Ultima8 {
TypeFlags::TypeFlags() {
}
TypeFlags::~TypeFlags() {
}
ShapeInfo *TypeFlags::getShapeInfo(uint32 shapenum) {
if (shapenum < _shapeInfo.size())
return &(_shapeInfo[shapenum]);
else
return nullptr;
}
void TypeFlags::load(Common::SeekableReadStream *rs) {
unsigned int blocksize = 8;
if (GAME_IS_CRUSADER) {
blocksize = 9;
}
uint32 size = rs->size();
uint32 count = size / blocksize;
_shapeInfo.clear();
_shapeInfo.resize(count);
for (uint32 i = 0; i < count; ++i) {
uint8 data[9];
rs->read(data, blocksize);
ShapeInfo si;
si._flags = 0;
if (GAME_IS_U8) {
if (data[0] & 0x01) si._flags |= ShapeInfo::SI_FIXED;
if (data[0] & 0x02) si._flags |= ShapeInfo::SI_SOLID;
if (data[0] & 0x04) si._flags |= ShapeInfo::SI_SEA;
if (data[0] & 0x08) si._flags |= ShapeInfo::SI_LAND;
if (data[0] & 0x10) si._flags |= ShapeInfo::SI_OCCL;
if (data[0] & 0x20) si._flags |= ShapeInfo::SI_BAG;
if (data[0] & 0x40) si._flags |= ShapeInfo::SI_DAMAGING;
if (data[0] & 0x80) si._flags |= ShapeInfo::SI_NOISY;
if (data[1] & 0x01) si._flags |= ShapeInfo::SI_DRAW;
if (data[1] & 0x02) si._flags |= ShapeInfo::SI_IGNORE;
if (data[1] & 0x04) si._flags |= ShapeInfo::SI_ROOF;
if (data[1] & 0x08) si._flags |= ShapeInfo::SI_TRANSL;
si._family = data[1] >> 4;
si._equipType = data[2] & 0x0F;
si._x = data[2] >> 4;
si._y = data[3] & 0x0F;
si._z = data[3] >> 4;
si._animType = data[4] & 0x0F;
si._animData = data[4] >> 4;
si._animSpeed = data[5] & 0x0F;
if (data[5] & 0x10) si._flags |= ShapeInfo::SI_EDITOR;
if (data[5] & 0x20) si._flags |= ShapeInfo::SI_U8_EXPLODE;
if (data[5] & 0x40) si._flags |= ShapeInfo::SI_UNKNOWN46;
if (data[5] & 0x80) si._flags |= ShapeInfo::SI_UNKNOWN47;
si._weight = data[6];
si._volume = data[7];
} else if (GAME_IS_CRUSADER) {
// Changes from U8 to Crusader:
// * SI_OCCL seems to be used more like "target" in Cru
// * SI_BAG bit seems to have a different meaning in Cru
// (multi-panel?), but we don't use it anyway
// * Family/x/y/z are all now 5 bits
// * There are more and different flags in the last byte
if (data[0] & 0x01) si._flags |= ShapeInfo::SI_FIXED;
if (data[0] & 0x02) si._flags |= ShapeInfo::SI_SOLID;
if (data[0] & 0x04) si._flags |= ShapeInfo::SI_SEA;
if (data[0] & 0x08) si._flags |= ShapeInfo::SI_LAND;
if (data[0] & 0x10) si._flags |= ShapeInfo::SI_OCCL;
if (data[0] & 0x20) si._flags |= ShapeInfo::SI_BAG;
if (data[0] & 0x40) si._flags |= ShapeInfo::SI_DAMAGING;
if (data[0] & 0x80) si._flags |= ShapeInfo::SI_NOISY;
if (data[1] & 0x01) si._flags |= ShapeInfo::SI_DRAW;
if (data[1] & 0x02) si._flags |= ShapeInfo::SI_IGNORE;
if (data[1] & 0x04) si._flags |= ShapeInfo::SI_ROOF;
if (data[1] & 0x08) si._flags |= ShapeInfo::SI_TRANSL;
si._family = data[1] >> 4;
si._family += (data[2] & 1) << 4;
si._equipType = (data[2] >> 1) & 0xF;
si._x = ((data[3] << 3) | (data[2] >> 5)) & 0x1F;
si._y = (data[3] >> 2) & 0x1F;
si._z = ((data[4] << 1) | (data[3] >> 7)) & 0x1F;
si._animType = data[4] >> 4;
si._animData = data[5] & 0x0F;
si._animSpeed = data[5] >> 4;
if (si._animType != 0 && si._animSpeed == 0) {
// avoid invalid speeds.
warning("fixing anim speed 0 for shape %d", i);
si._animSpeed = 1;
}
if (data[6] & 0x01) si._flags |= ShapeInfo::SI_EDITOR;
if (data[6] & 0x02) si._flags |= ShapeInfo::SI_CRU_SELECTABLE;
if (data[6] & 0x04) si._flags |= ShapeInfo::SI_CRU_PRELOAD;
if (data[6] & 0x08) si._flags |= ShapeInfo::SI_CRU_SOUND;
if (data[6] & 0x10) si._flags |= ShapeInfo::SI_CRU_TARGETABLE;
if (data[6] & 0x20) si._flags |= ShapeInfo::SI_CRU_NPC;
if (data[6] & 0x40) si._flags |= ShapeInfo::SI_CRU_UNK66;
if (data[6] & 0x80) si._flags |= ShapeInfo::SI_CRU_UNK67;
si._weight = data[7];
si._volume = data[8];
} else {
error("unknown game type in type flags");
}
si._weaponInfo = nullptr;
si._armourInfo = nullptr;
_shapeInfo[i] = si;
}
if (GAME_IS_U8) {
// Workaround for incorrectly set solid flags on some "moss
// curtains" in the catacombs. See also docs/u8bugs.txt
for (uint32 i = 459; i <= 464; ++i) {
_shapeInfo[i]._flags &= ~ShapeInfo::SI_SOLID;
}
}
loadWeaponInfo();
loadArmourInfo();
loadMonsterInfo();
}
// load weapon info from the 'weapons' config root
void TypeFlags::loadWeaponInfo() {
ConfigFileManager *config = ConfigFileManager::get_instance();
// load weapons
Std::vector<Std::string> weaponkeys;
Std::string category = "weapons";
weaponkeys = config->listSections(category);
for (const auto &section : weaponkeys) {
WeaponInfo *wi = new WeaponInfo;
int val = 0;
wi->_name = section;
config->get(category, section, "shape", val);
wi->_shape = static_cast<uint32>(val);
config->get(category, section, "overlay", val);
wi->_overlayType = static_cast<uint8>(val);
config->get(category, section, "overlay_shape", val);
wi->_overlayShape = static_cast<uint32>(val);
config->get(category, section, "damage_mod", val);
wi->_damageModifier = static_cast<uint8>(val);
config->get(category, section, "base_damage", val);
wi->_baseDamage = static_cast<uint8>(val);
if (config->get(category, section, "attack_dex", val))
wi->_dexAttackBonus = static_cast<uint8>(val);
else
wi->_dexAttackBonus = 0;
if (config->get(category, section, "defend_dex", val))
wi->_dexDefendBonus = static_cast<uint8>(val);
else
wi->_dexDefendBonus = 0;
if (config->get(category, section, "armour", val))
wi->_armourBonus = static_cast<uint8>(val);
else
wi->_armourBonus = 0;
config->get(category, section, "damage_type", val);
wi->_damageType = static_cast<uint16>(val);
if (config->get(category, section, "treasure_chance", val))
wi->_treasureChance = static_cast<uint16>(val);
else
wi->_treasureChance = 0;
// Crusader-specific fields:
if (config->get(category, section, "ammo_type", val))
wi->_ammoType = static_cast<uint16>(val);
else
wi->_ammoType = 0;
if (config->get(category, section, "ammo_shape", val))
wi->_ammoShape = static_cast<uint16>(val);
else
wi->_ammoShape = 0;
if (config->get(category, section, "sound", val))
wi->_sound = static_cast<uint16>(val);
else
wi->_sound = 0;
if (config->get(category, section, "reload_sound", val))
wi->_reloadSound = static_cast<uint16>(val);
else
wi->_reloadSound = 0;
if (config->get(category, section, "display_frame", val))
wi->_displayGumpFrame = static_cast<uint16>(val);
else
wi->_displayGumpFrame = 0;
if (config->get(category, section, "display_shape", val))
wi->_displayGumpShape = static_cast<uint16>(val);
else
wi->_displayGumpShape = 3;
if (config->get(category, section, "small", val))
wi->_small = static_cast<uint8>(val);
else
wi->_small = 0;
if (config->get(category, section, "clip_size", val))
wi->_clipSize = static_cast<uint16>(val);
else
wi->_clipSize = 10;
if (config->get(category, section, "energy", val))
wi->_energyUse = static_cast<uint16>(val);
else
wi->_energyUse = 0;
if (config->get(category, section, "shot_delay", val))
wi->_shotDelay = static_cast<uint16>(val);
else
wi->_shotDelay = 0;
// TODO: this should be 1, 2, or 3 depending on weapon.
// It's used in the AttackProcess
wi->_field8 = 1;
if (wi->_shape > _shapeInfo.size()) {
warning("ignoring weapon info for shape %d beyond size %d.",
wi->_shape, _shapeInfo.size());
delete wi;
continue;
}
_shapeInfo[wi->_shape]._weaponInfo = wi;
}
}
void TypeFlags::loadArmourInfo() {
ConfigFileManager *config = ConfigFileManager::get_instance();
MainShapeArchive *msf = GameData::get_instance()->getMainShapes();
// load armour
Std::vector<Std::string> armourkeys;
Std::string category = "armour";
armourkeys = config->listSections(category);
for (const auto &section : armourkeys) {
ArmourInfo ai;
int val;
config->get(category, section, "shape", val);
ai._shape = static_cast<uint32>(val);
assert(ai._shape < _shapeInfo.size());
assert(msf->getShape(ai._shape));
unsigned int framecount = msf->getShape(ai._shape)->frameCount();
ArmourInfo *aia = _shapeInfo[ai._shape]._armourInfo;
if (!aia) {
aia = new ArmourInfo[framecount];
_shapeInfo[ai._shape]._armourInfo = aia;
for (unsigned int i = 0; i < framecount; ++i) {
aia[i]._shape = 0;
aia[i]._frame = 0;
aia[i]._armourClass = 0;
aia[i]._defenseType = 0;
aia[i]._kickAttackBonus = 0;
}
}
config->get(category, section, "frame", val);
ai._frame = static_cast<uint32>(val);
assert(ai._frame < framecount);
config->get(category, section, "armour", val);
ai._armourClass = static_cast<uint16>(val);
if (config->get(category, section, "type", val))
ai._defenseType = static_cast<uint16>(val);
else
ai._defenseType = 0;
if (config->get(category, section, "kick_bonus", val))
ai._kickAttackBonus = static_cast<uint16>(val);
else
ai._kickAttackBonus = 0;
aia[ai._frame] = ai;
}
}
void TypeFlags::loadMonsterInfo() {
ConfigFileManager *config = ConfigFileManager::get_instance();
TreasureLoader treasureLoader;
treasureLoader.loadDefaults();
// load monsters
Std::vector<Std::string> monsterkeys;
Std::string category = "monsters";
monsterkeys = config->listSections(category);
for (const auto &section : monsterkeys) {
MonsterInfo *mi = new MonsterInfo;
int val;
config->get(category, section, "shape", val);
mi->_shape = static_cast<uint32>(val);
config->get(category, section, "hp_min", val);
mi->_minHp = static_cast<uint16>(val);
config->get(category, section, "hp_max", val);
mi->_maxHp = static_cast<uint16>(val);
config->get(category, section, "dex_min", val);
mi->_minDex = static_cast<uint16>(val);
config->get(category, section, "dex_max", val);
mi->_maxDex = static_cast<uint16>(val);
config->get(category, section, "damage_min", val);
mi->_minDmg = static_cast<uint16>(val);
config->get(category, section, "damage_max", val);
mi->_maxDmg = static_cast<uint16>(val);
config->get(category, section, "armour", val);
mi->_armourClass = static_cast<uint16>(val);
config->get(category, section, "alignment", val);
mi->_alignment = static_cast<uint8>(val);
config->get(category, section, "unk", val);
mi->_unk = (val != 0);
config->get(category, section, "damage_type", val);
mi->_damageType = static_cast<uint16>(val);
config->get(category, section, "defense_type", val);
mi->_defenseType = static_cast<uint16>(val);
if (config->get(category, section, "resurrection", val))
mi->_resurrection = (val != 0);
else
mi->_resurrection = false;
if (config->get(category, section, "ranged", val))
mi->_ranged = (val != 0);
else
mi->_ranged = false;
if (config->get(category, section, "shifter", val))
mi->_shifter = (val != 0);
else
mi->_shifter = false;
if (config->get(category, section, "explode", val))
mi->_explode = val;
else
mi->_explode = 0;
Std::string treasure;
if (config->get(category, section, "treasure", treasure)) {
bool ok = treasureLoader.parse(treasure, mi->_treasure);
if (!ok) {
warning("failed to parse treasure info for monster '%s;", section.c_str());
mi->_treasure.clear();
}
} else {
mi->_treasure.clear();
}
assert(mi->_shape < _shapeInfo.size());
_shapeInfo[mi->_shape]._monsterInfo = mi;
}
}
void TypeFlags::loadDamageDat(Common::SeekableReadStream *rs) {
uint32 count = rs->size() / 6;
if (_shapeInfo.size() < count) {
warning("more damage info than shape info");
return;
}
for (uint32 i = 0; i < count; i++) {
byte damagedata[6];
rs->read(damagedata, 6);
if (damagedata[0] == 0)
continue;
if (GAME_IS_REGRET && damagedata[0] == 1 && !damagedata[1] &&
!damagedata[2] && !damagedata[3] &&
!damagedata[4] && !damagedata[5]) {
// WORKAROUND: No Regret has 3 shapes with this data pattern
// which doesn't seem to be correct - eg, the elevator buttons
// can be destroyed by gunshots which breaks the game.
// Just ignore this pattern.
// In No Remorse these maybe this pattern should be ignored too,
// but there on some shapes that don't break the game.
debug("Ignoring weird damage dat, shape %d (1 flag and rest 0s)", i);
continue;
}
DamageInfo *di = new DamageInfo(damagedata);
_shapeInfo[i]._damageInfo = di;
}
}
} // End of namespace Ultima8
} // End of namespace Ultima

View File

@@ -0,0 +1,51 @@
/* ScummVM - Graphic Adventure Engine
*
* ScummVM is the legal property of its developers, whose names
* are too numerous to list here. Please refer to the COPYRIGHT
* file distributed with this source distribution.
*
* This program is free software: you can redistribute it and/or modify
* it under the terms of the GNU General Public License as published by
* the Free Software Foundation, either version 3 of the License, or
* (at your option) any later version.
*
* This program is distributed in the hope that it will be useful,
* but WITHOUT ANY WARRANTY; without even the implied warranty of
* MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
* GNU General Public License for more details.
*
* You should have received a copy of the GNU General Public License
* along with this program. If not, see <http://www.gnu.org/licenses/>.
*
*/
#ifndef ULTIMA8_GFX_TYPEFLAGS_H
#define ULTIMA8_GFX_TYPEFLAGS_H
#include "ultima/shared/std/containers.h"
#include "ultima/ultima8/gfx/shape_info.h"
namespace Ultima {
namespace Ultima8 {
class TypeFlags {
public:
TypeFlags();
~TypeFlags();
void load(Common::SeekableReadStream *rs);
void loadDamageDat(Common::SeekableReadStream *rs);
ShapeInfo *getShapeInfo(uint32 shape);
private:
void loadWeaponInfo();
void loadArmourInfo();
void loadMonsterInfo();
Std::vector<ShapeInfo> _shapeInfo;
};
} // End of namespace Ultima8
} // End of namespace Ultima
#endif

View File

@@ -0,0 +1,104 @@
/* ScummVM - Graphic Adventure Engine
*
* ScummVM is the legal property of its developers, whose names
* are too numerous to list here. Please refer to the COPYRIGHT
* file distributed with this source distribution.
*
* This program is free software: you can redistribute it and/or modify
* it under the terms of the GNU General Public License as published by
* the Free Software Foundation, either version 3 of the License, or
* (at your option) any later version.
*
* This program is distributed in the hope that it will be useful,
* but WITHOUT ANY WARRANTY; without even the implied warranty of
* MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
* GNU General Public License for more details.
*
* You should have received a copy of the GNU General Public License
* along with this program. If not, see <http://www.gnu.org/licenses/>.
*
*/
#include "ultima/ultima8/misc/debugger.h"
#include "ultima/ultima8/gfx/wpn_ovlay_dat.h"
#include "ultima/ultima8/world/actors/weapon_overlay.h"
#include "ultima/ultima8/filesys/raw_archive.h"
#include "ultima/ultima8/games/game_data.h"
#include "ultima/ultima8/gfx/main_shape_archive.h"
#include "ultima/ultima8/world/actors/anim_action.h"
namespace Ultima {
namespace Ultima8 {
WpnOvlayDat::WpnOvlayDat() {
}
WpnOvlayDat::~WpnOvlayDat() {
for (unsigned int i = 0; i < _overlay.size(); i++)
delete _overlay[i];
}
const WeaponOverlayFrame *WpnOvlayDat::getOverlayFrame(uint32 action, int type,
Direction direction,
int frame) const {
if (action >= _overlay.size())
return nullptr;
if (!_overlay[action])
return nullptr;
return _overlay[action]->getFrame(type, direction, frame);
}
void WpnOvlayDat::load(RawArchive *overlaydat) {
MainShapeArchive *msf = GameData::get_instance()->getMainShapes();
assert(msf);
_overlay.resize(overlaydat->getCount());
for (unsigned int action = 0; action < _overlay.size(); action++) {
Common::SeekableReadStream *rs = overlaydat->get_datasource(action);
_overlay[action] = nullptr;
if (rs && rs->size()) {
// get Avatar's animation
const AnimAction *anim = msf->getAnim(1, action);
if (!anim) {
warning("Skipping wpnovlay action %u because avatar animation doesn't exist.", action);
continue;
}
AnimWeaponOverlay *awo = new AnimWeaponOverlay;
_overlay[action] = awo;
unsigned int animlength = anim->getSize();
unsigned int dircount = anim->getDirCount();
unsigned int typecount = rs->size() / (4 * dircount * animlength);
awo->_overlay.resize(typecount);
for (unsigned int type = 0; type < typecount; type++) {
awo->_overlay[type]._dirCount = dircount;
awo->_overlay[type]._frames =
new Std::vector<WeaponOverlayFrame>[dircount];
for (unsigned int dir = 0; dir < dircount; dir++) {
awo->_overlay[type]._frames[dir].resize(animlength);
for (unsigned int frame = 0; frame < animlength; frame++) {
WeaponOverlayFrame f;
f._xOff = rs->readSByte();
f._yOff = rs->readSByte();
f._frame = rs->readUint16LE();
awo->_overlay[type]._frames[dir][frame] = f;
}
}
}
}
delete rs;
}
}
} // End of namespace Ultima8
} // End of namespace Ultima

View File

@@ -0,0 +1,53 @@
/* ScummVM - Graphic Adventure Engine
*
* ScummVM is the legal property of its developers, whose names
* are too numerous to list here. Please refer to the COPYRIGHT
* file distributed with this source distribution.
*
* This program is free software: you can redistribute it and/or modify
* it under the terms of the GNU General Public License as published by
* the Free Software Foundation, either version 3 of the License, or
* (at your option) any later version.
*
* This program is distributed in the hope that it will be useful,
* but WITHOUT ANY WARRANTY; without even the implied warranty of
* MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
* GNU General Public License for more details.
*
* You should have received a copy of the GNU General Public License
* along with this program. If not, see <http://www.gnu.org/licenses/>.
*
*/
#ifndef ULTIMA8_GFX_WPNOVLAYDAT_H
#define ULTIMA8_GFX_WPNOVLAYDAT_H
#include "ultima/ultima8/misc/direction.h"
namespace Ultima {
namespace Ultima8 {
class RawArchive;
struct AnimWeaponOverlay;
struct WeaponOverlayFrame;
class WpnOvlayDat {
public:
WpnOvlayDat();
~WpnOvlayDat();
//! load weapon overlay data from wpnovlay.dat
//! NB: anim.dat must have already been read
void load(RawArchive *overlaydat);
const WeaponOverlayFrame *getOverlayFrame(uint32 action, int type,
Direction direction, int frame) const;
private:
Std::vector<AnimWeaponOverlay *> _overlay;
};
} // End of namespace Ultima8
} // End of namespace Ultima
#endif

View File

@@ -0,0 +1,67 @@
/* ScummVM - Graphic Adventure Engine
*
* ScummVM is the legal property of its developers, whose names
* are too numerous to list here. Please refer to the COPYRIGHT
* file distributed with this source distribution.
*
* This program is free software: you can redistribute it and/or modify
* it under the terms of the GNU General Public License as published by
* the Free Software Foundation, either version 3 of the License, or
* (at your option) any later version.
*
* This program is distributed in the hope that it will be useful,
* but WITHOUT ANY WARRANTY; without even the implied warranty of
* MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
* GNU General Public License for more details.
*
* You should have received a copy of the GNU General Public License
* along with this program. If not, see <http://www.gnu.org/licenses/>.
*
*/
#include "ultima/ultima8/gfx/xform_blend.h"
namespace Ultima {
namespace Ultima8 {
const uint8 U8XFormPal[1024] = {
0, 0, 0, 0,
0, 0, 0, 0,
0, 0, 0, 0,
0, 0, 0, 0,
0, 0, 0, 0,
0, 0, 0, 0,
0, 0, 0, 0,
0, 0, 0, 0,
48, 48, 48, 80, // (green->dark grey)
24, 24, 24, 128, // (*->vdark grey)
64, 64, 24, 64, // (yellow)
80, 80, 80, 80, // (white->grey)
180, 90, 0, 80, // (red->orange)
0, 0, 252, 40, // (blue)
0, 0, 104, 40, // (blue)
0, 0, 0, 0
};
// TODO: Only eyeballed these, should try and make it more exact to original.
const uint8 CruXFormPal[1024] = {
0, 0, 0, 0,
0, 0, 0, 0,
0, 0, 0, 0,
0, 0, 0, 0,
0, 0, 0, 0,
0, 0, 0, 0,
0, 0, 0, 0,
0, 0, 0, 0,
48, 48, 48, 80, // (green->dark grey)
24, 24, 24, 128, // (*->vdark grey)
64, 64, 24, 64, // (yellow)
80, 80, 80, 80,
48, 48, 48, 140, // (*->grey)
24, 24, 24, 140, // (*->dark grey) 13
10, 10, 10, 140, // (*->vdark grey) 14
0, 0, 0, 0
};
} // End of namespace Ultima8
} // End of namespace Ultima

View File

@@ -0,0 +1,36 @@
/* ScummVM - Graphic Adventure Engine
*
* ScummVM is the legal property of its developers, whose names
* are too numerous to list here. Please refer to the COPYRIGHT
* file distributed with this source distribution.
*
* This program is free software: you can redistribute it and/or modify
* it under the terms of the GNU General Public License as published by
* the Free Software Foundation, either version 3 of the License, or
* (at your option) any later version.
*
* This program is distributed in the hope that it will be useful,
* but WITHOUT ANY WARRANTY; without even the implied warranty of
* MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
* GNU General Public License for more details.
*
* You should have received a copy of the GNU General Public License
* along with this program. If not, see <http://www.gnu.org/licenses/>.
*
*/
#ifndef ULTIMA8_GFX_XFORMBLEND_H
#define ULTIMA8_GFX_XFORMBLEND_H
#include "common/scummsys.h"
namespace Ultima {
namespace Ultima8 {
extern const uint8 U8XFormPal[1024];
extern const uint8 CruXFormPal[1024];
} // End of namespace Ultima8
} // End of namespace Ultima
#endif