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,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/>.
*
*/
#include "ultima/ultima8/misc/common_types.h"
#include "ultima/ultima8/audio/audio_channel.h"
#include "ultima/ultima8/audio/audio_process.h"
#include "ultima/ultima8/audio/audio_sample.h"
#include "common/memstream.h"
#include "audio/audiostream.h"
#include "audio/decoders/raw.h"
namespace Ultima {
namespace Ultima8 {
AudioChannel::AudioChannel(Audio::Mixer *mixer, uint32 sampleRate, bool stereo) :
_mixer(mixer), _priority(0) {
}
AudioChannel::~AudioChannel(void) {
}
void AudioChannel::playSample(AudioSample *sample, int loop, int priority, bool isSpeech, uint32 pitchShift, byte volume, int8 balance) {
if (!sample)
return;
_priority = priority;
// Create the _sample
Audio::SeekableAudioStream *audioStream = sample->makeStream();
int loops = loop;
if (loop == -1) {
// loop forever
loops = 0;
}
Audio::AudioStream *stream = (loop <= 1 && loop != -1) ?
(Audio::AudioStream *)audioStream :
new Audio::LoopingAudioStream(audioStream, loops);
_mixer->stopHandle(_soundHandle);
_mixer->playStream(isSpeech ? Audio::Mixer::kSpeechSoundType : Audio::Mixer::kSFXSoundType, &_soundHandle, stream, -1, volume, balance);
if (pitchShift != AudioProcess::PITCH_SHIFT_NONE)
_mixer->setChannelRate(_soundHandle, stream->getRate() * pitchShift / AudioProcess::PITCH_SHIFT_NONE);
}
bool AudioChannel::isPlaying() {
return _mixer->isSoundHandleActive(_soundHandle);
}
void AudioChannel::stop() {
_mixer->stopHandle(_soundHandle);
}
void AudioChannel::setPaused(bool paused) {
_mixer->pauseHandle(_soundHandle, paused);
}
} // End of namespace Ultima8
} // End of namespace Ultima

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/>.
*
*/
#ifndef ULTIMA8_AUDIO_AUDIOCHANNEL_H
#define ULTIMA8_AUDIO_AUDIOCHANNEL_H
#include "audio/mixer.h"
namespace Ultima {
namespace Ultima8 {
class AudioSample;
class AudioChannel {
private:
Audio::SoundHandle _soundHandle;
Audio::Mixer *_mixer;
int _priority;
public:
AudioChannel(Audio::Mixer *mixer, uint32 sampleRate, bool stereo);
~AudioChannel(void);
void stop();
void playSample(AudioSample *sample, int loop, int priority,
bool isSpeech, uint32 pitchShift, byte volume, int8 balance);
bool isPlaying();
void setVolume(byte volume, int8 balance) {
_mixer->setChannelVolume(_soundHandle, volume);
_mixer->setChannelBalance(_soundHandle, balance);
}
void setPriority(int priority) {
_priority = priority;
}
int getPriority() const {
return _priority;
}
void setPaused(bool paused);
};
} // End of namespace Ultima8
} // End of namespace Ultima
#endif

View File

@@ -0,0 +1,148 @@
/* 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/audio_mixer.h"
#include "ultima/ultima8/audio/audio_process.h"
#include "ultima/ultima8/audio/u8_music_process.h"
#include "ultima/ultima8/audio/cru_music_process.h"
#include "ultima/ultima8/audio/audio_channel.h"
#include "ultima/ultima8/audio/midi_player.h"
#include "ultima/ultima8/kernel/kernel.h"
#include "ultima/ultima8/ultima8.h"
namespace Ultima {
namespace Ultima8 {
AudioMixer *AudioMixer::_audioMixer = nullptr;
static const uint32 SAMPLE_RATE = 22050;
static const int BASE_CHANNEL_COUNT = 16;
static const int AMBIENT_CHANNEL_COUNT = 4;
static const int TOTAL_CHANNEL_COUNT = BASE_CHANNEL_COUNT + AMBIENT_CHANNEL_COUNT;
AudioMixer::AudioMixer(Audio::Mixer *mixer) : _mixer(mixer), _midiPlayer(nullptr) {
_audioMixer = this;
_channels.resize(TOTAL_CHANNEL_COUNT);
for (int idx = 0; idx < TOTAL_CHANNEL_COUNT; ++idx)
_channels[idx] = new AudioChannel(_mixer, SAMPLE_RATE, true);
debug(1, "Creating AudioMixer...");
}
void AudioMixer::createProcesses() {
Kernel *kernel = Kernel::get_instance();
// Create the Audio Process
kernel->addProcess(new AudioProcess());
// Create the Music Process
if (GAME_IS_U8) {
kernel->addProcess(new U8MusicProcess(_midiPlayer));
} else if (GAME_IS_CRUSADER) {
kernel->addProcess(new CruMusicProcess());
}
}
AudioMixer::~AudioMixer(void) {
_audioMixer = nullptr;
debug(1, "Destroying AudioMixer...");
closeMidiOutput();
for (int idx = 0; idx < TOTAL_CHANNEL_COUNT; ++idx)
delete _channels[idx];
}
void AudioMixer::reset() {
_mixer->stopAll();
}
int AudioMixer::playSample(AudioSample *sample, int loop, int priority, bool isSpeech, uint32 pitch_shift, byte volume, int8 balance, bool ambient) {
int lowest = -1;
int lowprior = 65536;
int i;
const int minchan = (ambient ? BASE_CHANNEL_COUNT : 0);
const int maxchan = (ambient ? TOTAL_CHANNEL_COUNT : BASE_CHANNEL_COUNT);
for (i = minchan; i < maxchan; i++) {
if (!_channels[i]->isPlaying()) {
lowest = i;
break;
}
else if (_channels[i]->getPriority() < priority) {
lowprior = _channels[i]->getPriority();
lowest = i;
}
}
if (i != maxchan || lowprior < priority)
_channels[lowest]->playSample(sample, loop, priority, isSpeech, pitch_shift, volume, balance);
else
lowest = -1;
return lowest;
}
bool AudioMixer::isPlaying(int chan) {
if (chan >= TOTAL_CHANNEL_COUNT || chan < 0)
return false;
bool playing = _channels[chan]->isPlaying();
return playing;
}
void AudioMixer::stopSample(int chan) {
if (chan >= TOTAL_CHANNEL_COUNT || chan < 0)
return;
_channels[chan]->stop();
}
void AudioMixer::setPaused(int chan, bool paused) {
if (chan >= TOTAL_CHANNEL_COUNT || chan < 0)
return;
_channels[chan]->setPaused(paused);
}
void AudioMixer::setVolume(int chan, byte volume, int8 balance) {
if (chan >= TOTAL_CHANNEL_COUNT || chan < 0)
return;
_channels[chan]->setVolume(volume, balance);
}
void AudioMixer::openMidiOutput() {
_midiPlayer = new MidiPlayer();
}
void AudioMixer::closeMidiOutput() {
delete _midiPlayer;
_midiPlayer = nullptr;
}
} // End of namespace Ultima8
} // End of namespace Ultima

View File

@@ -0,0 +1,72 @@
/* 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_AUDIO_AUDIOMIXER_H
#define ULTIMA8_AUDIO_AUDIOMIXER_H
#include "audio/mixer.h"
#include "audio/mididrv.h"
#include "common/array.h"
namespace Ultima {
namespace Ultima8 {
class MidiPlayer;
class AudioChannel;
class AudioSample;
class AudioMixer {
private:
static AudioMixer *_audioMixer;
Audio::Mixer *_mixer;
MidiPlayer *_midiPlayer;
Common::Array<AudioChannel *> _channels;
public:
AudioMixer(Audio::Mixer *mixer);
~AudioMixer();
MidiPlayer *getMidiPlayer() const {
return _midiPlayer;
}
static AudioMixer *get_instance() {
return _audioMixer;
}
void reset();
void createProcesses();
int playSample(AudioSample *sample, int loop, int priority, bool isSpeech, uint32 pitch_shift, byte volume, int8 balance, bool ambient);
bool isPlaying(int chan);
void stopSample(int chan);
void setPaused(int chan, bool paused);
void setVolume(int chan, byte volume, int8 balance);
void openMidiOutput();
void closeMidiOutput();
};
} // End of namespace Ultima8
} // End of namespace Ultima
#endif

View File

@@ -0,0 +1,668 @@
/* 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/audio_process.h"
#include "ultima/ultima8/usecode/uc_machine.h"
#include "ultima/ultima8/games/game_data.h"
#include "ultima/ultima8/audio/speech_flex.h"
#include "ultima/ultima8/audio/audio_mixer.h"
#include "ultima/ultima8/world/get_object.h"
#include "ultima/ultima8/world/item.h"
#include "ultima/ultima8/world/camera_process.h"
namespace Ultima {
namespace Ultima8 {
// p_dynamic_class stuff
DEFINE_RUNTIME_CLASSTYPE_CODE(AudioProcess)
AudioProcess *AudioProcess::_theAudioProcess = nullptr;
const uint32 AudioProcess::PITCH_SHIFT_NONE = 0x10000;
AudioProcess::AudioProcess(void) : _paused(0) {
_theAudioProcess = this;
_type = 1; // persistent
}
AudioProcess::~AudioProcess(void) {
_theAudioProcess = nullptr;
}
bool AudioProcess::calculateSoundVolume(ObjId objId, int16 &volume, int8 &balance) const {
Item *item = getItem(objId);
if (!item) {
volume = 255;
balance = 0;
return false;
}
// Need to get items relative coords from avatar
Point3 a = CameraProcess::GetCameraLocation();
Point3 i = item->getLocationAbsolute();
i.x -= a.x;
i.y -= a.y;
i.z -= a.z;
//
// Convert to screenspace
//
// Note that this should also correct for Crusader too.
//
int x = (i.x - i.y) / 4;
int y = (i.x + i.y) / 8 - i.z;
// Fall off over 350 pixels
int limit = 350 * 350;
int dist = limit - (x * x + y * y);
dist = (dist * 256) / limit;
volume = CLIP(dist, 0, 255); // range is 0 ~ 255
int b = (x * 127) / 160;
balance = CLIP(b, -127, 127); // range is -127 ~ +127
return true;
}
void AudioProcess::run() {
AudioMixer *mixer = AudioMixer::get_instance();
// Update the channels
Std::list<SampleInfo>::iterator it;
for (it = _sampleInfo.begin(); it != _sampleInfo.end();) {
bool finished = false;
if (!mixer->isPlaying(it->_channel)) {
if (it->_sfxNum == -1)
finished = !continueSpeech(*it);
else
finished = true;
}
if (it->_loops == -1) {
// check if an ever-looping sfx for an item has left the
// fast area.. if so we are "finished".
Item *item = getItem(it->_objId);
if (item && !item->hasFlags(Item::FLG_FASTAREA) && mixer->isPlaying(it->_channel)) {
finished = true;
mixer->stopSample(it->_channel);
}
}
if (finished)
it = _sampleInfo.erase(it);
else {
if (it->_sfxNum != -1 && it->_objId) {
calculateSoundVolume(it->_objId, it->_calcVol, it->_balance);
}
mixer->setVolume(it->_channel, (it->_calcVol * it->_volume) / 256, it->_balance);
++it;
}
}
}
bool AudioProcess::continueSpeech(SampleInfo &si) {
assert(si._sfxNum == -1);
SpeechFlex *speechflex;
speechflex = GameData::get_instance()->getSpeechFlex(si._priority);
if (!speechflex) return false;
if (si._curSpeechEnd >= si._barked.size()) return false;
si._curSpeechStart = si._curSpeechEnd;
int index = speechflex->getIndexForPhrase(si._barked,
si._curSpeechStart,
si._curSpeechEnd);
if (!index) return false;
AudioSample *sample = speechflex->getSample(index);
if (!sample) return false;
// hack to prevent playSample from deleting 'si'
si._channel = -1;
int channel = playSample(sample, 200, 0, true);
if (channel == -1)
return false;
si._channel = channel;
return true;
}
void AudioProcess::saveData(Common::WriteStream *ws) {
Process::saveData(ws);
ws->writeByte(static_cast<uint8>(_sampleInfo.size()));
for (const auto &si : _sampleInfo) {
ws->writeUint16LE(si._sfxNum);
ws->writeUint16LE(si._priority);
ws->writeUint16LE(si._objId);
ws->writeUint16LE(si._loops);
ws->writeUint32LE(si._pitchShift);
ws->writeUint16LE(si._volume);
if (si._sfxNum == -1) { // Speech
ws->writeUint32LE(static_cast<uint32>(si._barked.size()));
ws->write(si._barked.c_str(), static_cast<uint32>(si._barked.size()));
}
}
}
bool AudioProcess::loadData(Common::ReadStream *rs, uint32 version) {
if (!Process::loadData(rs, version)) return false;
uint32 count = rs->readByte();
while (count--) {
int16 sfxNum = rs->readUint16LE();
int16 priority = rs->readUint16LE();
int16 objId = rs->readUint16LE();
int16 loops = rs->readUint16LE();
uint32 pitchShift = rs->readUint32LE();
uint16 volume = rs->readUint16LE();
if (sfxNum != -1) { // SFX
int16 calcVol = 0;
int8 balance = 0;
if (objId != 0) {
calcVol = 255;
}
// Note: Small inconsistency for backward compatibility - reload ambient sounds as non-ambient.
playSFX(sfxNum, priority, objId, loops, false, pitchShift, volume, calcVol, balance, false);
} else { // Speech
uint32 slen = rs->readUint32LE();
char *buf = new char[slen + 1];
rs->read(buf, slen);
buf[slen] = 0;
Std::string text = buf;
delete[] buf;
playSpeech(text, priority, objId, pitchShift, volume);
}
}
return true;
}
int AudioProcess::playSample(AudioSample *sample, int priority, int loops, bool isSpeech, uint32 pitchShift, int16 volume, int8 balance, bool ambient) {
AudioMixer *mixer = AudioMixer::get_instance();
int channel = mixer->playSample(sample, loops, priority, isSpeech, pitchShift, volume, balance, ambient);
if (channel == -1) return channel;
// Erase old sample using channel (if any)
Std::list<SampleInfo>::iterator it;
for (it = _sampleInfo.begin(); it != _sampleInfo.end();) {
if (it->_channel == channel) {
it = _sampleInfo.erase(it);
} else {
++it;
}
}
return channel;
}
void AudioProcess::playSFX(int sfxNum, int priority, ObjId objId, int loops,
bool no_duplicates, uint32 pitchShift, uint16 volume,
int16 calcVol, int8 balance, bool ambient) {
SoundFlex *soundflx = GameData::get_instance()->getSoundFlex();
AudioMixer *mixer = AudioMixer::get_instance();
if (no_duplicates) {
Std::list<SampleInfo>::iterator it;
for (it = _sampleInfo.begin(); it != _sampleInfo.end();) {
if (it->_sfxNum == sfxNum && it->_objId == objId &&
it->_loops == loops) {
// Exactly the same (and playing) so just return
//if (it->priority == priority)
if (mixer->isPlaying(it->_channel)) {
debug(1, "Sound %d already playing on obj %u", sfxNum, objId);
return;
} else {
it = _sampleInfo.erase(it);
continue;
}
}
++it;
}
}
AudioSample *sample = soundflx->getSample(sfxNum);
if (!sample) return;
if (calcVol == -1) {
calculateSoundVolume(objId, calcVol, balance);
}
int channel = playSample(sample, priority, loops, false, pitchShift, (calcVol * volume) / 256, balance, ambient);
if (channel == -1) return;
// Update list
_sampleInfo.push_back(SampleInfo(sfxNum, priority, objId, loops, channel, pitchShift, volume, calcVol, balance, ambient));
}
void AudioProcess::stopSFX(int sfxNum, ObjId objId) {
AudioMixer *mixer = AudioMixer::get_instance();
Std::list<SampleInfo>::iterator it;
for (it = _sampleInfo.begin(); it != _sampleInfo.end();) {
if ((sfxNum == -1 || it->_sfxNum == sfxNum)
&& it->_objId == objId) {
if (mixer->isPlaying(it->_channel)) mixer->stopSample(it->_channel);
it = _sampleInfo.erase(it);
} else {
++it;
}
}
}
bool AudioProcess::isSFXPlaying(int sfxNum) {
AudioMixer *mixer = AudioMixer::get_instance();
for (const auto &si : _sampleInfo) {
if (si._sfxNum == sfxNum && mixer->isPlaying(si._channel))
return true;
}
return false;
}
bool AudioProcess::isSFXPlayingForObject(int sfxNum, ObjId objId) {
AudioMixer *mixer = AudioMixer::get_instance();
for (const auto &si : _sampleInfo) {
if ((si._sfxNum == sfxNum || sfxNum == -1) && (objId == si._objId) && mixer->isPlaying(si._channel))
return true;
}
return false;
}
void AudioProcess::setVolumeSFX(int sfxNum, uint8 volume) {
AudioMixer *mixer = AudioMixer::get_instance();
for (auto &si : _sampleInfo) {
if (si._sfxNum == sfxNum && si._sfxNum != -1) {
si._volume = volume;
calculateSoundVolume(si._objId, si._calcVol, si._balance);
mixer->setVolume(si._channel, (si._calcVol * si._volume) / 256, si._balance);
}
}
}
void AudioProcess::setVolumeForObjectSFX(ObjId objId, int sfxNum, uint8 volume) {
AudioMixer *mixer = AudioMixer::get_instance();
for (auto &si : _sampleInfo) {
if (si._sfxNum == sfxNum && si._sfxNum != -1 && objId == si._objId) {
si._volume = volume;
calculateSoundVolume(si._objId, si._calcVol, si._balance);
mixer->setVolume(si._channel, (si._calcVol * si._volume) / 256, si._balance);
}
}
}
//
// Speech
//
bool AudioProcess::playSpeech(const Std::string &barked, int shapeNum, ObjId objId, uint32 pitchShift, uint16 volume) {
SpeechFlex *speechflex = GameData::get_instance()->getSpeechFlex(shapeNum);
if (!speechflex) return false;
AudioMixer *mixer = AudioMixer::get_instance();
Std::list<SampleInfo>::iterator it;
for (it = _sampleInfo.begin(); it != _sampleInfo.end();) {
if (it->_sfxNum == -1 && it->_barked == barked &&
it->_priority == shapeNum && it->_objId == objId) {
if (mixer->isPlaying(it->_channel)) {
debug(1, "Speech already playing");
return true;
} else {
it = _sampleInfo.erase(it);
continue;
}
}
++it;
}
uint32 speech_start = 0;
uint32 speech_end;
int index = speechflex->getIndexForPhrase(barked, speech_start, speech_end);
if (!index) return false;
AudioSample *sample = speechflex->getSample(index);
if (!sample) return false;
int channel = playSample(sample, 200, 0, true, pitchShift, volume, volume);
if (channel == -1) return false;
// Update list
_sampleInfo.push_back(SampleInfo(barked, shapeNum, objId, channel,
speech_start, speech_end, pitchShift, volume, 255, 0, false));
return true;
}
uint32 AudioProcess::getSpeechLength(const Std::string &barked, int shapenum) const {
SpeechFlex *speechflex = GameData::get_instance()->getSpeechFlex(shapenum);
if (!speechflex) return 0;
return speechflex->getSpeechLength(barked);
}
void AudioProcess::stopSpeech(const Std::string &barked, int shapenum, ObjId objId) {
AudioMixer *mixer = AudioMixer::get_instance();
Std::list<SampleInfo>::iterator it;
for (it = _sampleInfo.begin(); it != _sampleInfo.end();) {
if (it->_sfxNum == -1 && it->_priority == shapenum &&
it->_objId == objId && it->_barked == barked) {
if (mixer->isPlaying(it->_channel)) mixer->stopSample(it->_channel);
it = _sampleInfo.erase(it);
} else {
++it;
}
}
}
bool AudioProcess::isSpeechPlaying(const Std::string &barked, int shapeNum) {
Std::list<SampleInfo>::iterator it;
for (auto &si : _sampleInfo) {
if (si._sfxNum == -1 && si._priority == shapeNum &&
si._barked == barked) {
return true;
}
}
return false;
}
void AudioProcess::pauseAllSamples() {
_paused++;
if (_paused != 1) return;
AudioMixer *mixer = AudioMixer::get_instance();
Std::list<SampleInfo>::iterator it;
for (it = _sampleInfo.begin(); it != _sampleInfo.end();) {
if (mixer->isPlaying(it->_channel)) {
mixer->setPaused(it->_channel, true);
++it;
} else {
it = _sampleInfo.erase(it);
}
}
}
void AudioProcess::unpauseAllSamples() {
_paused--;
if (_paused != 0) return;
AudioMixer *mixer = AudioMixer::get_instance();
Std::list<SampleInfo>::iterator it;
for (it = _sampleInfo.begin(); it != _sampleInfo.end();) {
if (mixer->isPlaying(it->_channel)) {
mixer->setPaused(it->_channel, false);
++it;
} else {
it = _sampleInfo.erase(it);
}
}
}
void AudioProcess::stopAllExceptSpeech() {
AudioMixer *mixer = AudioMixer::get_instance();
Std::list<SampleInfo>::iterator it;
for (it = _sampleInfo.begin(); it != _sampleInfo.end();) {
if (it->_barked.empty()) {
if (mixer->isPlaying(it->_channel)) mixer->stopSample(it->_channel);
it = _sampleInfo.erase(it);
} else {
++it;
}
}
}
//
// Intrinsics
//
uint32 AudioProcess::I_playSFX(const uint8 *args, unsigned int argsize) {
ARG_SINT16(sfxNum);
int16 priority = 0x60;
if (argsize >= 4) {
ARG_SINT16(priority_);
priority = priority_;
}
ObjId objId = 0;
if (argsize == 6) {
ARG_OBJID(objectId);
objId = objectId;
}
AudioProcess *ap = AudioProcess::get_instance();
if (ap)
ap->playSFX(sfxNum, priority, objId, 0);
else
warning("No AudioProcess");
return 0;
}
uint32 AudioProcess::I_playAmbientSFX(const uint8 *args, unsigned int argsize) {
ARG_SINT16(sfxNum);
int16 priority = 0x60;
if (argsize >= 4) {
ARG_SINT16(priority_);
priority = priority_;
}
ObjId objId = 0;
if (argsize == 6) {
ARG_OBJID(objectId);
objId = objectId;
}
AudioProcess *ap = AudioProcess::get_instance();
if (ap)
ap->playSFX(sfxNum, priority, objId, -1, true, PITCH_SHIFT_NONE, 0xff, true);
else
warning("No AudioProcess");
return 0;
}
uint32 AudioProcess::I_playSFXCru(const uint8 *args, unsigned int argsize) {
ARG_ITEM_FROM_PTR(item)
ARG_SINT16(sfxNum);
if (!item) {
warning("I_playSFXCru: Couldn't get item %d", id_item);
} else {
AudioProcess *ap = AudioProcess::get_instance();
if (ap) {
// Crusader stops any existing item sounds before starting the next.
ap->stopSFX(-1, item->getObjId());
ap->playSFX(sfxNum, 0x10, item->getObjId(), 0, true, PITCH_SHIFT_NONE, 0x80, false);
} else {
warning("I_playSFXCru Error: No AudioProcess");
}
}
return 0;
}
uint32 AudioProcess::I_playAmbientSFXCru(const uint8 *args, unsigned int argsize) {
// Similar to I_playAmbientSFX, but the params are different.
ARG_ITEM_FROM_PTR(item)
ARG_SINT16(sfxNum);
if (!item) {
warning("I_playAmbientSFXCru: Couldn't get item %d", id_item);
} else {
AudioProcess *ap = AudioProcess::get_instance();
if (ap)
ap->playSFX(sfxNum, 0x10, item->getObjId(), -1, true, PITCH_SHIFT_NONE, 0xff, true);
else
warning("I_playAmbientSFXCru Error: No AudioProcess");
}
return 0;
}
uint32 AudioProcess::I_isSFXPlaying(const uint8 *args, unsigned int argsize) {
ARG_SINT16(sfxNum);
AudioProcess *ap = AudioProcess::get_instance();
if (ap)
return ap->isSFXPlaying(sfxNum);
else
warning("No AudioProcess");
return 0;
}
uint32 AudioProcess::I_isSFXPlayingForObject(const uint8 *args, unsigned int argsize) {
ARG_ITEM_FROM_PTR(item)
ARG_SINT16(sfxNum);
if (!item) {
warning("I_isSFXPlayingForObject: Couldn't get item");
} else {
AudioProcess *ap = AudioProcess::get_instance();
if (ap)
return ap->isSFXPlayingForObject(sfxNum, item->getObjId());
else
warning("I_isSFXPlayingForObject Error: No AudioProcess");
}
return 0;
}
uint32 AudioProcess::I_setVolumeSFX(const uint8 *args, unsigned int /*argsize*/) {
// Sets volume for last played instances of sfxNum (???)
ARG_SINT16(sfxNum);
ARG_UINT8(volume);
AudioProcess *ap = AudioProcess::get_instance();
if (ap)
ap->setVolumeSFX(sfxNum, volume);
else
warning("No AudioProcess");
return 0;
}
uint32 AudioProcess::I_setVolumeForObjectSFX(const uint8 *args, unsigned int /*argsize*/) {
// Sets volume for last played instances of sfxNum on object
ARG_ITEM_FROM_PTR(item);
ARG_SINT16(sfxNum);
ARG_UINT8(volume);
if (!item) {
warning("I_setVolumeForObjectSFX: Couldn't get item");
} else {
AudioProcess *ap = AudioProcess::get_instance();
if (ap)
ap->setVolumeForObjectSFX(item->getObjId(), sfxNum, volume);
else
warning("I_setVolumeForObjectSFX: No AudioProcess");
}
return 0;
}
uint32 AudioProcess::I_stopSFX(const uint8 *args, unsigned int argsize) {
ARG_SINT16(sfxNum);
ObjId objId = 0;
if (argsize == 4) {
ARG_OBJID(objectId);
objId = objectId;
}
AudioProcess *ap = AudioProcess::get_instance();
if (ap)
ap->stopSFX(sfxNum, objId);
else
warning("No AudioProcess");
return 0;
}
uint32 AudioProcess::I_stopSFXCru(const uint8 *args, unsigned int argsize) {
int16 sfxNum = -1;
ARG_ITEM_FROM_PTR(item);
if (!item) {
warning("Invalid item in I_stopSFXCru");
return 0;
}
if (argsize == 6) {
ARG_SINT16(sfxNumber);
sfxNum = sfxNumber;
}
AudioProcess *ap = AudioProcess::get_instance();
if (ap)
ap->stopSFX(sfxNum, item->getObjId());
else
warning("No AudioProcess");
return 0;
}
uint32 AudioProcess::I_stopAllSFX(const uint8 * /*args*/, unsigned int /*argsize*/) {
AudioProcess *ap = AudioProcess::get_instance();
// Not *exactly* the same, but close enough for this intrinsic.
if (ap)
ap->stopAllExceptSpeech();
else
warning("No AudioProcess");
return 0;
}
} // End of namespace Ultima8
} // End of namespace Ultima

View File

@@ -0,0 +1,152 @@
/* 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_AUDIO_AUDIOPROCESS_H
#define ULTIMA8_AUDIO_AUDIOPROCESS_H
#include "ultima/ultima8/kernel/process.h"
#include "ultima/ultima8/usecode/intrinsics.h"
#include "ultima/shared/std/string.h"
#include "ultima/ultima8/misc/classtype.h"
namespace Ultima {
namespace Ultima8 {
class AudioSample;
class AudioProcess : public Process {
public:
static const uint32 PITCH_SHIFT_NONE;
struct SampleInfo {
int32 _sfxNum;
int32 _priority;
ObjId _objId;
int32 _loops;
int32 _channel;
Std::string _barked;
uint32 _curSpeechStart, _curSpeechEnd;
uint32 _pitchShift; // PITCH_SHIFT_NONE is normal
uint16 _volume; // 0-255
int16 _calcVol;
int8 _balance;
bool _ambient;
SampleInfo() : _sfxNum(-1) { }
SampleInfo(int32 s, int32 p, ObjId o, int32 l, int32 c, uint32 ps, uint16 v, int16 cv, int8 bal, bool ambient) :
_sfxNum(s), _priority(p), _objId(o), _loops(l), _channel(c),
_pitchShift(ps), _volume(v), _calcVol(cv), _balance(bal),
_curSpeechStart(0), _curSpeechEnd(0), _ambient(ambient) { }
SampleInfo(const Std::string &b, int32 shpnum, ObjId o, int32 c,
uint32 s, uint32 e, uint32 ps, uint16 v, int16 cv, int8 bal, bool ambient) :
_sfxNum(-1), _priority(shpnum), _objId(o), _loops(0), _channel(c), _barked(b),
_curSpeechStart(s), _curSpeechEnd(e), _pitchShift(ps), _volume(v),
_calcVol(cv), _balance(bal), _ambient(ambient) { }
};
Std::list<SampleInfo> _sampleInfo;
public:
// p_dynamic_class stuff
ENABLE_RUNTIME_CLASSTYPE()
AudioProcess(void);
~AudioProcess(void) override;
//! Get the current instance of the Audio Processes
static AudioProcess *get_instance() {
return _theAudioProcess;
}
INTRINSIC(I_playSFX);
INTRINSIC(I_playAmbientSFX);
INTRINSIC(I_playSFXCru);
INTRINSIC(I_playAmbientSFXCru);
INTRINSIC(I_isSFXPlaying);
INTRINSIC(I_isSFXPlayingForObject);
INTRINSIC(I_setVolumeSFX);
INTRINSIC(I_setVolumeForObjectSFX);
INTRINSIC(I_stopSFX);
INTRINSIC(I_stopSFXCru);
INTRINSIC(I_stopAllSFX);
void run() override;
void playSFX(int sfxNum, int priority, ObjId objId, int loops,
bool no_duplicates, uint32 pitchShift,
uint16 volume, int16 calcVol, int8 balance,
bool ambient);
void playSFX(int sfxNum, int priority, ObjId objId, int loops,
bool no_duplicates = false, uint32 pitchShift = PITCH_SHIFT_NONE,
uint16 volume = 0x80, bool ambient = false) {
playSFX(sfxNum, priority, objId, loops, no_duplicates, pitchShift, volume, -1, 0, ambient);
}
//! stop sfx on object. set sfxNum = -1 to stop all for object.
void stopSFX(int sfxNum, ObjId objId);
bool isSFXPlaying(int sfxNum);
bool isSFXPlayingForObject(int sfxNum, ObjId objId);
void setVolumeSFX(int sfxNum, uint8 volume);
void setVolumeForObjectSFX(ObjId objId, int sfxNum, uint8 volume);
bool playSpeech(const Std::string &barked, int shapenum, ObjId objId,
uint32 pitchShift = PITCH_SHIFT_NONE, uint16 volume = 255);
void stopSpeech(const Std::string &barked, int shapenum, ObjId objId);
bool isSpeechPlaying(const Std::string &barked, int shapenum);
//! get length (in milliseconds) of speech
uint32 getSpeechLength(const Std::string &barked, int shapenum) const;
//! play a sample (without storing a SampleInfo)
//! returns channel sample is played on, or -1
int playSample(AudioSample *sample, int priority, int loops, bool isSpeech = false,
uint32 pitchShift = PITCH_SHIFT_NONE, int16 volume = 255,
int8 balance = 0, bool ambient = false);
//! pause all currently playing samples
void pauseAllSamples();
//! unpause all currently playing samples
void unpauseAllSamples();
//! stop all samples except speech
void stopAllExceptSpeech();
bool loadData(Common::ReadStream *rs, uint32 version);
void saveData(Common::WriteStream *ws) override;
private:
uint32 _paused;
//! play the next speech sample for the text in this SampleInfo
//! note: si is reused if successful
//! returns true if there was speech left to play, or false if finished
bool continueSpeech(SampleInfo &si);
bool calculateSoundVolume(ObjId objId, int16 &volume, int8 &balance) const;
static AudioProcess *_theAudioProcess;
};
} // End of namespace Ultima8
} // End of namespace Ultima
#endif

View File

@@ -0,0 +1,39 @@
/* 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/audio/audio_sample.h"
namespace Ultima {
namespace Ultima8 {
AudioSample::AudioSample(const uint8 *buffer, uint32 size, uint32 bits, bool stereo, bool deleteBuffer) :
_sampleRate(0), _bits(bits), _stereo(stereo), _length(0),
_bufferSize(size), _buffer(buffer), _deleteBuffer(deleteBuffer) {
}
AudioSample::~AudioSample(void) {
if (_deleteBuffer)
delete [] _buffer;
}
} // End of namespace Ultima8
} // End of namespace Ultima

View File

@@ -0,0 +1,69 @@
/* 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_AUDIO_AUDIOSAMPLE_H
#define ULTIMA8_AUDIO_AUDIOSAMPLE_H
namespace Audio {
class SeekableAudioStream;
}
namespace Ultima {
namespace Ultima8 {
class AudioSample {
protected:
uint32 _sampleRate;
uint32 _bits;
bool _stereo;
uint32 _length;
uint32 _bufferSize;
uint8 const *_buffer;
bool _deleteBuffer;
public:
AudioSample(const uint8 *buffer, uint32 size, uint32 bits, bool stereo, bool deleteBuffer);
virtual ~AudioSample(void);
inline uint32 getRate() const {
return _sampleRate;
}
inline uint32 getBits() const {
return _bits;
}
inline bool isStereo() const {
return _stereo;
}
//! get AudioSample _length (in samples)
inline uint32 getLength() const {
return _length;
}
virtual Audio::SeekableAudioStream *makeStream() const = 0;
};
} // End of namespace Ultima8
} // End of namespace Ultima
#endif

View File

@@ -0,0 +1,263 @@
/* 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/file.h"
#include "common/system.h"
#include "common/config-manager.h"
#include "ultima/ultima8/ultima8.h"
#include "ultima/ultima8/audio/cru_music_process.h"
#include "audio/mods/mod_xm_s3m.h"
#include "ultima/ultima8/world/world.h"
#include "ultima/ultima8/world/current_map.h"
namespace Ultima {
namespace Ultima8 {
static const int MAX_TRACK_REMORSE = 21;
static const int MAX_TRACK_REGRET = 22;
// NOTE: The order of these lists has to be the same as the original games
// as they come as numbers from the usecode.
static const char *const TRACK_FILE_NAMES_REMORSE[] = {
nullptr,
"M01",
"M02",
"M03",
"M04",
"M05",
"M06",
"M07",
"M08",
"M09",
"M10",
"M11",
"M12",
"M13",
"M14",
"M15",
"M16A",
"M16B",
"M16C",
"cred",
"menu",
"buyme" // for demo
};
static const char *const TRACK_FILE_NAMES_REGRET[] = {
nullptr,
"ninth",
"phil",
"straight",
"party",
"demo",
"stint",
"mk",
"space2",
"d3",
"space",
"rhythm",
"intent",
"m03",
"silver",
"m01",
"techno",
"cred",
"regret",
"m13",
"retro",
"metal",
"xmas" // for christmas easter egg
};
static const int REGRET_MAP_TRACKS[] = {
0, 1, 10, 2, 0, 3, 11, 4,
16, 5, 20, 6, 0, 7, 13, 8,
15, 9, 12, 10, 19, 14, 21, 0};
DEFINE_RUNTIME_CLASSTYPE_CODE(CruMusicProcess)
CruMusicProcess::CruMusicProcess() : MusicProcess(), _currentTrack(0), _savedTrack(0), _m16offset(0) {
_maxTrack = (GAME_IS_REMORSE ? MAX_TRACK_REMORSE : MAX_TRACK_REGRET);
_trackNames = (GAME_IS_REMORSE ? TRACK_FILE_NAMES_REMORSE
: TRACK_FILE_NAMES_REGRET);
}
CruMusicProcess::~CruMusicProcess() {
// We shouldn't need to do anything here - the mixer will
// clean up the stream for us.
}
void CruMusicProcess::playMusic(int track) {
if (GAME_IS_REGRET && track == 0x45) {
// Play the default track for the current map
uint32 curmap = World::get_instance()->getCurrentMap()->getNum();
if (curmap < ARRAYSIZE(REGRET_MAP_TRACKS)) {
track = REGRET_MAP_TRACKS[curmap];
} else {
track = 0;
}
// Regret has a Christmas music easter egg.
if (!GAME_IS_DEMO) {
TimeDate t;
g_system->getTimeAndDate(t);
if ((t.tm_mon == 11 && t.tm_mday >= 24) || ConfMan.getBool("always_christmas")) {
track = 22;
}
}
}
playMusic_internal(track);
}
void CruMusicProcess::playCombatMusic(int track) {
// Only U8 has combat music.. ignore it.
}
void CruMusicProcess::queueMusic(int track) {
playMusic_internal(track);
}
void CruMusicProcess::unqueueMusic() {
}
void CruMusicProcess::restoreMusic() {
}
void CruMusicProcess::saveTrackState() {
assert(!_savedTrack);
_savedTrack = _currentTrack;
}
void CruMusicProcess::restoreTrackState() {
int saved = _savedTrack;
_savedTrack = 0;
playMusic_internal(saved);
}
void CruMusicProcess::playMusic_internal(int track) {
if (track < 0 || track > _maxTrack) {
warning("Not playing track %d (max is %d)", track, _maxTrack);
playMusic_internal(0);
return;
}
if (GAME_IS_REMORSE && track == 16) {
// Loop through m16a / m16b / m16c
track += _m16offset;
_m16offset = (_m16offset + 1) % 4;
}
Audio::Mixer *mixer = Ultima8Engine::get_instance()->_mixer;
assert(mixer);
if (track == _currentTrack && (track == 0 || mixer->isSoundHandleActive(_soundHandle)))
// Already playing what we want.
return;
mixer->stopHandle(_soundHandle);
_soundHandle = Audio::SoundHandle();
_currentTrack = track;
if (track > 0) {
// TODO: It's a bit ugly having this here. Should be in GameData.
const Std::string fname = Std::string::format("sound/%s.amf", _trackNames[track]);
auto *rs = new Common::File();
if (!rs->open(Common::Path(fname))) {
// This happens in No Regret demo.
warning("Couldn't load AMF file: %s", fname.c_str());
delete rs;
return;
}
Audio::AudioStream *stream = Audio::makeModXmS3mStream(rs, DisposeAfterUse::YES);
if (!stream) {
error("Couldn't create stream from AMF file: %s", fname.c_str());
return;
}
mixer->playStream(Audio::Mixer::kMusicSoundType, &_soundHandle, stream, -1, Audio::Mixer::kMaxChannelVolume, 0, DisposeAfterUse::YES);
}
}
void CruMusicProcess::run() {
Audio::Mixer *mixer = Ultima8Engine::get_instance()->_mixer;
assert(mixer);
if (mixer->isSoundHandleActive(_soundHandle)) {
return;
}
// Hit end of stream, play it again. This normally won't happen because
// the mods should loop infinitely, but just in case.
playMusic_internal(_currentTrack);
}
void CruMusicProcess::saveData(Common::WriteStream *ws) {
Process::saveData(ws);
ws->writeUint32LE(static_cast<uint32>(_currentTrack));
ws->writeUint32LE(static_cast<uint32>(_savedTrack));
ws->writeByte(_m16offset);
}
bool CruMusicProcess::loadData(Common::ReadStream *rs, uint32 version) {
if (!Process::loadData(rs, version)) return false;
_currentTrack = static_cast<int32>(rs->readUint32LE());
_savedTrack = static_cast<int32>(rs->readUint32LE());
_m16offset = rs->readByte();
_theMusicProcess = this;
// Slight hack - resuming from savegame we want to restore the game
// track (not the menu track)
if (_savedTrack)
restoreTrackState();
return true;
}
bool CruMusicProcess::isPlaying() {
Audio::Mixer *mixer = Ultima8Engine::get_instance()->_mixer;
return _currentTrack != 0 && mixer && mixer->isSoundHandleActive(_soundHandle);
}
void CruMusicProcess::pauseMusic() {
Audio::Mixer *mixer = Ultima8Engine::get_instance()->_mixer;
assert(mixer);
if (mixer->isSoundHandleActive(_soundHandle))
mixer->pauseHandle(_soundHandle, true);
}
void CruMusicProcess::unpauseMusic() {
Audio::Mixer *mixer = Ultima8Engine::get_instance()->_mixer;
assert(mixer);
if (mixer->isSoundHandleActive(_soundHandle))
mixer->pauseHandle(_soundHandle, false);
}
} // End of namespace Ultima8
} // End of namespace Ultima

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/>.
*
*/
#ifndef ULTIMA8_AUDIO_CRUMUSICPROCESS_H
#define ULTIMA8_AUDIO_CRUMUSICPROCESS_H
#include "ultima/ultima8/audio/music_process.h"
#include "ultima/ultima8/misc/classtype.h"
#include "audio/audiostream.h"
#include "audio/mixer.h"
namespace Ultima {
namespace Ultima8 {
class Debugger;
class MidiPlayer;
class CruMusicProcess : public MusicProcess {
friend class Debugger;
protected:
//! Play a music track
//! \param track The track number to play. Pass 0 to stop music
void playMusic_internal(int track) override;
private:
int _currentTrack; //! Currently playing track (don't save)
int _savedTrack;
uint8 _m16offset;
Audio::SoundHandle _soundHandle;
// These are both initialized in constructor and do not need to be saved.
int _maxTrack;
const char *const *_trackNames;
public:
CruMusicProcess();
~CruMusicProcess() override;
ENABLE_RUNTIME_CLASSTYPE()
//! Play some background music. Does not change the current track if combat music is active. If another track is currently queued, just queues this track for play.
void playMusic(int track) override;
//! Play some combat music - the last played track will be remembered
void playCombatMusic(int track) override;
//! Queue a track to start once the current one finishes
void queueMusic(int track) override;
//! Clear any queued track (does not affect currently playing track)
void unqueueMusic() override;
//! Restore the last requested non-combat track (eg, at the end of combat)
void restoreMusic() override;
//! Fading is not used, so this does nothing
void fadeMusic(uint16 length) override { };
//! Fading is not used, so this returns false
bool isFading() override { return false; };
//! Save the current track state - used when the menu is opened
void saveTrackState() override;
//! Bring back the track state from before it was put on hold
void restoreTrackState() override;
//! Is a track currently playing?
bool isPlaying() override;
//! Pause the currently playing track
void pauseMusic() override;
//! Resume the current track after pausing
void unpauseMusic() override;
void run() override;
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,266 @@
/* 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/midi_player.h"
#include "ultima/ultima8/ultima8.h"
#include "ultima/ultima8/audio/music_flex.h"
#include "ultima/ultima8/games/game_data.h"
#include "audio/midiparser.h"
#include "audio/miles.h"
namespace Ultima {
namespace Ultima8 {
byte MidiPlayer::_callbackData[2];
MidiPlayer::MidiPlayer() : _parser(nullptr), _transitionParser(nullptr), _playingTransition(false) {
MidiDriver::DeviceHandle dev = MidiDriver::detectDevice(MDT_MIDI | MDT_ADLIB | MDT_PREFER_GM);
MusicType musicType = MidiDriver::getMusicType(dev);
switch (musicType) {
case MT_ADLIB:
MusicFlex *musicFlex;
musicFlex = GameData::get_instance()->getMusic();
_driver = Audio::MidiDriver_Miles_AdLib_create("", "", musicFlex->getAdlibTimbres(), nullptr);
break;
case MT_MT32:
case MT_GM:
_driver = Audio::MidiDriver_Miles_MIDI_create(MT_GM, "");
break;
default:
_driver = new MidiDriver_NULL_Multisource();
break;
}
_isFMSynth = (musicType == MT_ADLIB);
_callbackData[0] = 0;
_callbackData[1] = 0;
if (_driver) {
int retValue = _driver->open();
if (retValue == 0) {
_driver->property(MidiDriver::PROP_USER_VOLUME_SCALING, true);
_driver->setTimerCallback(this, &timerCallback);
syncSoundSettings();
} else {
delete _driver;
_driver = nullptr;
}
}
}
MidiPlayer::~MidiPlayer() {
if (_parser) {
_parser->unloadMusic();
delete _parser;
}
if (_transitionParser) {
_transitionParser->unloadMusic();
delete _transitionParser;
}
if (_driver) {
_driver->close();
delete _driver;
}
}
void MidiPlayer::load(byte *data, size_t size, int seqNo) {
if (!_driver)
return;
assert(seqNo == 0 || seqNo == 1);
if (_parser) {
_parser->unloadMusic();
delete _parser;
_parser = nullptr;
}
if (size < 4)
error("load() wrong music resource size");
if (READ_BE_UINT32(data) != MKTAG('F', 'O', 'R', 'M')) {
warning("load() Unexpected signature");
} else {
_parser = MidiParser::createParser_XMIDI(xmidiCallback, _callbackData + seqNo, 0);
_parser->setMidiDriver(_driver);
_parser->setTimerRate(_driver->getBaseTempo());
_parser->property(MidiParser::mpSendSustainOffOnNotesOff, 1);
_parser->property(MidiParser::mpDisableAutoStartPlayback, 1);
if (!_parser->loadMusic(data, size))
error("load() wrong music resource");
}
}
void MidiPlayer::loadTransitionData(byte* data, size_t size) {
if (!_driver)
return;
if (size < 4)
error("loadTransitionData() wrong music resource size");
if (READ_BE_UINT32(data) != MKTAG('F', 'O', 'R', 'M'))
error("loadTransitionData() Unexpected signature");
_transitionParser = MidiParser::createParser_XMIDI(nullptr, nullptr, 0);
_transitionParser->setMidiDriver(_driver);
_transitionParser->setTimerRate(_driver->getBaseTempo());
_transitionParser->property(MidiParser::mpDisableAutoStartPlayback, 1);
if (!_transitionParser->loadMusic(data, size))
error("loadTransitionData() wrong music resource");
}
void MidiPlayer::play(int trackNo, int branchIndex) {
if (!_parser || !_driver)
return;
if (!_parser->setTrack(trackNo)) {
warning("play() invalid track number %i", trackNo);
return;
}
if (branchIndex >= 0) {
if (!_parser->jumpToIndex(branchIndex, false)) {
warning("play() invalid branch index %i", branchIndex);
// Track will play from the beginning instead
}
}
// Abort any active fades and reset the source volume to neutral.
if (_driver->isFading(0))
_driver->abortFade(0);
_driver->resetSourceVolume(0);
if (_transitionParser) {
_transitionParser->stopPlaying();
_playingTransition = false;
}
if (!_parser->startPlaying()) {
warning("play() failed to start playing");
}
}
void MidiPlayer::playTransition(int trackNo, bool overlay) {
if (!overlay && _parser)
_parser->stopPlaying();
if (!_transitionParser) {
warning("playTransition() transition data not loaded");
if (_parser)
_parser->stopPlaying();
return;
}
_transitionParser->setTrack(trackNo);
if (overlay)
_transitionParser->setTempo(_driver->getBaseTempo() * 2);
_transitionParser->property(MidiParser::mpDisableAllNotesOffMidiEvents, overlay);
_transitionParser->startPlaying();
_playingTransition = true;
}
void MidiPlayer::stop() {
if (_parser)
_parser->stopPlaying();
if (_transitionParser) {
_transitionParser->stopPlaying();
_playingTransition = false;
}
}
void MidiPlayer::pause(bool pause) {
if (pause) {
if (_parser)
_parser->pausePlaying();
if (_transitionParser)
_transitionParser->pausePlaying();
} else {
if (_parser)
_parser->resumePlaying();
if (_transitionParser)
_transitionParser->resumePlaying();
}
}
bool MidiPlayer::isPlaying() {
return (_parser && _parser->isPlaying()) || _playingTransition;
}
void MidiPlayer::startFadeOut(uint16 length) {
if (_driver)
_driver->startFade(0, 1500, 0);
}
bool MidiPlayer::isFading() {
return _driver && _driver->isFading(0);
}
void MidiPlayer::syncSoundSettings() {
if (_driver)
_driver->syncSoundSettings();
}
bool MidiPlayer::hasBranchIndex(uint8 index) {
return _parser && _parser->hasJumpIndex(index);
}
void MidiPlayer::setLooping(bool loop) {
if (_parser)
_parser->property(MidiParser::mpAutoLoop, loop);
}
void MidiPlayer::xmidiCallback(byte eventData, void *data) {
if (data == nullptr)
return;
*static_cast<byte*>(data) = eventData;
}
void MidiPlayer::onTimer() {
if (_parser)
_parser->onTimer();
if (_transitionParser) {
_transitionParser->onTimer();
if (_playingTransition && !_transitionParser->isPlaying()) {
// Transition has finished.
if (_parser)
// Stop the main track (which is still playing if the
// transition was overlaid).
_parser->stopPlaying();
_playingTransition = false;
}
}
}
void MidiPlayer::timerCallback(void *data) {
((MidiPlayer *)data)->onTimer();
}
} // End of namespace Ultima8
} // End of namespace Ultima

View File

@@ -0,0 +1,132 @@
/* 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_AUDIO_MIDI_PLAYER_H
#define ULTIMA8_AUDIO_MIDI_PLAYER_H
#include "audio/mixer.h"
#include "audio/mididrv_ms.h"
#include "audio/midiparser.h"
namespace Ultima {
namespace Ultima8 {
class MidiPlayer {
public:
MidiPlayer();
~MidiPlayer();
/**
* Load the specified music data
*/
void load(byte *data, size_t size, int seqNo);
/**
* Load the XMIDI data containing the transition tracks.
* Call this function before calling playTransition.
*/
void loadTransitionData(byte *data, size_t size);
/**
* Play the specified music track, starting at the
* specified branch. Use branchNo -1 to start from the
* beginning.
*/
void play(int trackNo, int branchNo);
/**
* Plays the specified transition track. If overlay is specified, the
* transition is overlaid on the currently playing music track and this
* track is stopped when the transition ends. If overlay is not specified,
* the currently playing music track is stopped before the transition is
* started.
*/
void playTransition(int trackNo, bool overlay);
/**
* Stop the currently playing track.
*/
void stop();
/**
* Pause or resume playback of the current track.
*/
void pause(bool pause);
/**
* Returns true if a track is playing.
*/
bool isPlaying();
/**
* Starts a fade-out of the specified duration (in milliseconds).
*/
void startFadeOut(uint16 length);
/**
* Returns true if the music is currently fading.
*/
bool isFading();
/**
* Synchronizes the user volume settings with those of the game.
*/
void syncSoundSettings();
/**
* Sets whether the music should loop
*/
void setLooping(bool loop);
/**
* Returns true if the current music track has a branch
* defined for the specified index.
*/
bool hasBranchIndex(uint8 index);
bool isFMSynth() const {
return _isFMSynth;
};
static void xmidiCallback(byte eventData, void *data);
byte getSequenceCallbackData(int seq) const {
assert(seq == 0 || seq == 1);
return _callbackData[seq];
}
void onTimer();
static void timerCallback(void *data);
private:
MidiDriver_Multisource *_driver;
MidiParser *_parser;
MidiParser *_transitionParser;
bool _isFMSynth;
bool _playingTransition;
static byte _callbackData[2];
};
} // End of namespace Ultima8
} // End of namespace Ultima
#endif

View File

@@ -0,0 +1,244 @@
/* 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/shared/std/string.h"
#include "ultima/ultima8/misc/debugger.h"
#include "ultima/ultima8/audio/music_flex.h"
#include "common/memstream.h"
namespace Ultima {
namespace Ultima8 {
MusicFlex::MusicFlex(Common::SeekableReadStream *rs) : Archive(rs) {
memset(_info, 0, sizeof(SongInfo *) * 128);
_songs = new XMidiData *[_count];
memset(_songs, 0, sizeof(XMidiData *) * _count);
loadSongInfo();
}
MusicFlex::~MusicFlex() {
uint32 i;
for (i = 0; i < 128; i++) {
delete _info[i];
}
for (i = 0; i < _count; i++) {
delete _songs[i];
}
delete [] _songs;
}
MusicFlex::SongInfo::SongInfo() : _numMeasures(0), _loopJump(0) {
memset(_filename, 0, 17);
memset(_transitions, 0, 128 * sizeof(int *));
}
MusicFlex::SongInfo::~SongInfo() {
for (int i = 0; i < 128; i++) {
delete [] _transitions[i];
}
}
MusicFlex::XMidiData *MusicFlex::getXMidi(uint32 index) {
if (index >= _count)
return nullptr;
cache(index);
return _songs[index];
}
const MusicFlex::SongInfo *MusicFlex::getSongInfo(uint32 index) const {
if (index > 127)
return nullptr;
return _info[index];
}
void MusicFlex::cache(uint32 index) {
if (index >= _count) return;
uint32 size;
uint8 *data = getRawObject(index, &size);
if (!data) {
// Note: multiple sorcerer scenes (such as MALCHIR::03F2)
// request track 122, which is blank in the Gold Edition
// music flex.
warning("Unable to cache song %d from sound/music.flx", index);
return;
}
_songs[index] = new XMidiData(data, size);
}
void MusicFlex::uncache(uint32 index) {
if (index >= _count) return;
delete _songs[index];
_songs[index] = nullptr;
}
bool MusicFlex::isCached(uint32 index) const {
if (index >= _count) return false;
return (_songs[index] != nullptr);
}
Common::SeekableReadStream *MusicFlex::getAdlibTimbres() {
uint32 size;
const uint8 *data = getRawObject(259, &size);
return new Common::MemoryReadStream(data, size, DisposeAfterUse::YES);
}
void MusicFlex::loadSongInfo() {
uint32 size;
const uint8 *buf = getRawObject(0, &size);
if (!buf || !size) {
error("Unable to load song info from sound/music.flx");
}
Common::MemoryReadStream ds(buf, size);
Std::string line;
// Read first section till we hit a #
for (;;) {
line = ds.readLine();
// We have hit the end of the section
if (line.at(0) == '#') break;
Std::string::size_type begIdx, endIdx;
// Find the first not space, which will get us the name
begIdx = line.findFirstNotOf(' ');
endIdx = line.findFirstOf(' ', begIdx);
Std::string name = line.substr(begIdx, endIdx - begIdx);
// Now find the first not space after the name, which will get us the num
begIdx = line.findFirstNotOf(' ', endIdx);
endIdx = line.findFirstOf(' ', begIdx);
int num = line.at(begIdx);
// Now number of measures
begIdx = line.findFirstNotOf(' ', endIdx);
endIdx = line.findFirstOf(' ', begIdx);
int measures = atoi(line.substr(begIdx, endIdx - begIdx).c_str());
// Now finally _loopJump
begIdx = line.findFirstNotOf(' ', endIdx);
endIdx = line.findFirstOf(' ', begIdx);
int loopJump = atoi(line.substr(begIdx, endIdx - begIdx).c_str());
// Uh oh
if (num < 0 || num > 127)
error("Invalid Section 1 song _info data. num out of range");
if (_info[num])
error("Invalid Section 1 song _info data. num already defined");
_info[num] = new SongInfo();
strncpy(_info[num]->_filename, name.c_str(), 16);
_info[num]->_numMeasures = measures;
_info[num]->_loopJump = loopJump;
};
// Read 'Section2', or more like skip it, since it's only trans.xmi
// Read first section till we hit a #
for (;;) {
line = ds.readLine();
// We have hit the end of the section
if (line.at(0) == '#') break;
}
// Skip 'Section3'
for (;;) {
line = ds.readLine();
// We have hit the end of the section
if (line.at(0) == '#') break;
}
// Read 'Section4' (trans _info)
for (;;) {
line = ds.readLine();
// We have hit the end of the section
if (line.at(0) == '#') break;
Std::string::size_type begIdx, endIdx;
// Get 'from' name
begIdx = line.findFirstNotOf(' ');
endIdx = line.findFirstOf(' ', begIdx);
Std::string from = line.substr(begIdx, endIdx - begIdx);
// Get 'to' name
begIdx = line.findFirstNotOf(' ', endIdx);
endIdx = line.findFirstOf(' ', begIdx);
Std::string to = line.substr(begIdx, endIdx - begIdx);
// Find index of from name
int fi;
for (fi = 0; fi < 128; fi++) {
if (_info[fi] && from == _info[fi]->_filename) break;
}
if (fi == 128)
error("Invalid Section 4 song _info data. Unable to find 'from' index (%s)", from.c_str());
// Find index of to name
int ti;
for (ti = 0; ti < 128; ti++) {
if (_info[ti] && to == _info[ti]->_filename) break;
}
if (ti == 128)
error("Invalid Section 4 song _info data. Unable to find 'to' index (%s)", to.c_str());
// Allocate Transition _info
_info[fi]->_transitions[ti] = new int[_info[fi]->_numMeasures];
// Now attempt to read the trans _info for the
for (int m = 0; m < _info[fi]->_numMeasures; m++) {
// Get trans _info name
begIdx = line.findFirstNotOf(' ', endIdx);
endIdx = line.findFirstOf(' ', begIdx);
if (begIdx == Std::string::npos)
error("Invalid Section 4 song _info data. Unable to read _transitions for all measures");
Std::string trans = line.substr(begIdx, endIdx - begIdx);
const char *str = trans.c_str();
int num = 0;
// Overlayed
if (*str == '!')
num = 0 - atoi(str + 1);
else
num = atoi(str);
_info[fi]->_transitions[ti][m] = num;
}
}
// Skip all remaining sections
delete[] buf;
}
} // 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_AUDIO_MUSICFLEX_H
#define ULTIMA8_AUDIO_MUSICFLEX_H
#include "ultima/ultima8/filesys/archive.h"
namespace Ultima {
namespace Ultima8 {
class MusicFlex : public Archive {
public:
struct SongInfo {
SongInfo();
~SongInfo();
char _filename[17];
int _numMeasures;
int _loopJump;
int *_transitions[128];
};
struct XMidiData {
XMidiData(byte *data, uint32 size) : _data(data), _size(size)
{}
byte *_data;
uint32 _size;
};
MusicFlex(Common::SeekableReadStream *rs);
~MusicFlex() override;
//! Get an xmidi
XMidiData *getXMidi(uint32 index);
//! Get song info
const SongInfo *getSongInfo(uint32 index) const;
//! Get the Adlib Timbres (index 259)
Common::SeekableReadStream *getAdlibTimbres();
void cache(uint32 index) override;
void uncache(uint32 index) override;
bool isCached(uint32 index) const override;
private:
SongInfo *_info[128];
XMidiData **_songs;
//! Load the song info
void loadSongInfo();
};
} // End of namespace Ultima8
} // End of namespace Ultima
#endif

View File

@@ -0,0 +1,71 @@
/* 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"
namespace Ultima {
namespace Ultima8 {
DEFINE_RUNTIME_CLASSTYPE_CODE(MusicProcess)
MusicProcess *MusicProcess::_theMusicProcess = nullptr;
MusicProcess::MusicProcess() {
_theMusicProcess = this;
_type = 1; // persistent
setRunPaused();
}
MusicProcess::~MusicProcess() {
_theMusicProcess = nullptr;
}
uint32 MusicProcess::I_stopMusic(const uint8 * /*args*/,
unsigned int /*argsize*/) {
if (_theMusicProcess) _theMusicProcess->playMusic_internal(0);
return 0;
}
uint32 MusicProcess::I_playMusic(const uint8 *args,
unsigned int /*argsize*/) {
ARG_UINT8(song);
if (_theMusicProcess) _theMusicProcess->playMusic(song & 0x7F);
return 0;
}
uint32 MusicProcess::I_pauseMusic(const uint8 *args,
unsigned int /*argsize*/) {
// This is only used in Crusader: No Regret.
if (_theMusicProcess) _theMusicProcess->pauseMusic();
return 0;
}
uint32 MusicProcess::I_unpauseMusic(const uint8 *args,
unsigned int /*argsize*/) {
// This is only used in Crusader: No Regret.
if (_theMusicProcess) _theMusicProcess->unpauseMusic();
return 0;
}
} // End of namespace Ultima8
} // End of namespace Ultima

View File

@@ -0,0 +1,96 @@
/* 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_AUDIO_MUSICPROCESS_H
#define ULTIMA8_AUDIO_MUSICPROCESS_H
#include "ultima/ultima8/kernel/process.h"
#include "ultima/ultima8/usecode/intrinsics.h"
#include "ultima/ultima8/misc/classtype.h"
#include "audio/mididrv.h"
namespace Ultima {
namespace Ultima8 {
class Debugger;
class MidiPlayer;
class MusicProcess : public Process {
friend class Debugger;
protected:
//! Play a music track
//! \param track The track number to play. Pass 0 to stop music
virtual void playMusic_internal(int track) = 0;
static MusicProcess *_theMusicProcess;
public:
MusicProcess();
~MusicProcess() override;
ENABLE_RUNTIME_CLASSTYPE()
//! Get the current instance of the Music Processes
static MusicProcess *get_instance() {
return _theMusicProcess;
}
//! Play some background music. Does not change the current track if combat music is active. If another track is currently queued, just queues this track for play.
virtual void playMusic(int track) = 0;
//! Play some combat music - the last played track will be remembered
virtual void playCombatMusic(int track) = 0;
//! Queue a track to start once the current one finishes
virtual void queueMusic(int track) = 0;
//! Clear any queued track (does not affect currently playing track)
virtual void unqueueMusic() = 0;
//! Restore the last requested non-combat track (eg, at the end of combat)
virtual void restoreMusic() = 0;
//! Fades out the music over the specified time (in milliseconds)
virtual void fadeMusic(uint16 length) = 0;
//! Returns true if the music is currently fading
virtual bool isFading() = 0;
//! Save the current track state - used when the menu is opened
virtual void saveTrackState() = 0;
//! Bring back the track state from before it was put on hold
virtual void restoreTrackState() = 0;
//! Is a track currently playing?
virtual bool isPlaying() = 0;
//! Pause the currently playing track
virtual void pauseMusic() = 0;
//! Resume the current track after pausing
virtual void unpauseMusic() = 0;
INTRINSIC(I_playMusic);
INTRINSIC(I_stopMusic);
INTRINSIC(I_pauseMusic);
INTRINSIC(I_unpauseMusic);
};
} // End of namespace Ultima8
} // End of namespace Ultima
#endif

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/>.
*
*/
#include "ultima/ultima8/misc/common_types.h"
#include "ultima/ultima8/audio/raw_audio_sample.h"
#include "common/memstream.h"
#include "audio/decoders/raw.h"
namespace Ultima {
namespace Ultima8 {
RawAudioSample::RawAudioSample(const uint8 *buffer, uint32 size, uint32 rate,
bool signedData, bool stereo)
: AudioSample(buffer, size, 8, stereo, false), _signedData(signedData) {
_sampleRate = rate;
_length = size;
}
RawAudioSample::~RawAudioSample() {
}
Audio::SeekableAudioStream *RawAudioSample::makeStream() const {
Common::MemoryReadStream *stream = new Common::MemoryReadStream(_buffer, _bufferSize, DisposeAfterUse::NO);
byte flags = 0;
if (isStereo())
flags |= Audio::FLAG_STEREO;
if (!_signedData)
flags |= Audio::FLAG_UNSIGNED;
return Audio::makeRawStream(stream, getRate(), flags, DisposeAfterUse::YES);
}
} // End of namespace Ultima8
} // End of namespace Ultima

View File

@@ -0,0 +1,45 @@
/* 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_AUDIO_RAWAUDIOSAMPLE_H
#define ULTIMA8_AUDIO_RAWAUDIOSAMPLE_H
#include "ultima/ultima8/audio/audio_sample.h"
namespace Ultima {
namespace Ultima8 {
class RawAudioSample : public AudioSample {
public:
RawAudioSample(const uint8 *buffer, uint32 size,
uint32 rate, bool signeddata, bool stereo);
~RawAudioSample() override;
Audio::SeekableAudioStream *makeStream() const override;
protected:
bool _signedData;
};
} // End of namespace Ultima8
} // End of namespace Ultima
#endif

View File

@@ -0,0 +1,246 @@
/* 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/audio/sonarc_audio_sample.h"
#include "common/memstream.h"
#include "audio/audiostream.h"
#include "audio/decoders/raw.h"
namespace Ultima {
namespace Ultima8 {
bool SonarcAudioSample::_generatedOneTable = false;
int SonarcAudioSample::_oneTable[256];
SonarcAudioSample::SonarcAudioSample(uint8 const *buffer, uint32 size) :
AudioSample(buffer, size, 8, false, true), _srcOffset(0x20) {
if (!_generatedOneTable) GenerateOneTable();
_length = *_buffer;
_length |= *(_buffer + 1) << 8;
_length |= *(_buffer + 2) << 16;
_length |= *(_buffer + 3) << 24;
_sampleRate = *(_buffer + 4);
_sampleRate |= *(_buffer + 5) << 8;
// Get frame bytes... we need to compensate for 'large' files
uint32 frame_bytes = *(_buffer + _srcOffset);
frame_bytes |= (*(_buffer + _srcOffset + 1)) << 8;
if (frame_bytes == 0x20 && _length > 32767) {
_srcOffset += 0x100;
}
// Get Num Frame Samples
_frameSize = *(_buffer + _srcOffset + 2);
_frameSize |= (*(_buffer + _srcOffset + 3)) << 8;
}
SonarcAudioSample::~SonarcAudioSample(void) {
}
//
// Sonarc Audio Decompressor
//
void SonarcAudioSample::GenerateOneTable() {
// _oneTable[x] gives the number of consecutive 1's on the low side of x
for (int i = 0; i < 256; ++i)
_oneTable[i] = 0;
for (int power = 2; power < 32; power *= 2)
for (int col = power - 1; col < 16; col += power)
for (int row = 0; row < 16; ++row)
_oneTable[row * 16 + col]++;
for (int i = 0; i < 16; ++i)
_oneTable[i * 16 + 15] += _oneTable[i];
}
void SonarcAudioSample::decode_EC(int mode, int samplecount,
const uint8 *source, int sourcesize,
uint8 *dest) {
bool zerospecial = false;
uint32 data = 0;
int inputbits = 0; // current 'fill rate' of data window
if (mode >= 7) {
mode -= 7;
zerospecial = true;
}
while (samplecount) {
// fill data window
while (sourcesize && inputbits <= 24) {
data |= (*source++) << inputbits;
sourcesize--;
inputbits += 8;
}
if (zerospecial && !(data & 0x1)) {
*dest++ = 0x80; // output zero
data >>= 1;
inputbits--;
} else {
if (zerospecial) {
data >>= 1; // strip one
inputbits--;
}
uint8 lowByte = data & 0xFF;
int ones = _oneTable[lowByte];
if (ones == 0) {
data >>= 1; // strip zero
// low byte contains (mode+1) _bits of the sample
const uint8 usample = data & 0xFF;
int8 sample = usample << (7 - mode);
sample >>= (7 - mode); // sign extend
*dest++ = (uint8)(sample + 0x80);
data >>= mode + 1;
inputbits -= mode + 2;
} else if (ones < 7 - mode) {
data >>= ones + 1; // strip ones and zero
// low byte contains (mode+ones) _bits of the sample
const uint8 usample = data & 0xFF;
int8 sample = usample << (7 - mode - ones);
sample &= 0x7F;
if (!(sample & 0x40))
sample |= 0x80; // reconstruct sign bit
sample >>= (7 - mode - ones); // sign extend
*dest++ = (uint8)(sample + 0x80);
data >>= (mode + ones);
inputbits -= mode + 2 * ones + 1;
} else {
data >>= (7 - mode); // strip ones
// low byte contains 7 _bits of the sample
int8 sample = data & 0xFF;
sample &= 0x7F;
if (!(sample & 0x40))
sample |= 0x80; // reconstruct sign bit
*dest++ = (uint8)(sample + 0x80);
data >>= 7;
inputbits -= 2 * 7 - mode;
}
}
samplecount--;
}
}
void SonarcAudioSample::decode_LPC(int order, int nsamples,
uint8 *dest, const uint8 *factors) {
uint8 *startdest = dest;
dest -= order;
// basic linear predictive (de)coding
// the errors this produces are fixed by decode_EC
for (int i = 0; i < nsamples; ++i) {
uint8 *loopdest = dest++;
int accum = 0;
for (int j = order - 1; j >= 0; --j) {
int8 val1 = (loopdest < startdest) ? 0 : (*loopdest);
loopdest++;
val1 ^= 0x80;
int16 val2 = factors[j * 2] + (factors[j * 2 + 1] << 8);
accum += (int)val1 * val2;
}
accum += 0x00000800;
*loopdest -= (int8)((accum >> 12) & 0xFF);
}
}
int SonarcAudioSample::audio_decode(const uint8 *source, uint8 *dest) {
int size = source[0] + (source[1] << 8);
uint16 checksum = 0;
for (int i = 0; i < size / 2; ++i) {
uint16 val = source[2 * i] + (source[2 * i + 1] << 8);
checksum ^= val;
}
if (checksum != 0xACED) return -1;
int order = source[7];
int mode = source[6] - 8;
int samplecount = source[2] + (source[3] << 8);
decode_EC(mode, samplecount,
source + 8 + 2 * order, size - 8 - 2 * order,
dest);
decode_LPC(order, samplecount, dest, source + 8);
// Try to fix a number of clipped samples
for (int i = 1; i < samplecount; ++i)
if (dest[i] == 0 && dest[i - 1] > 192) dest[i] = 0xFF;
return 0;
}
uint32 SonarcAudioSample::decompressFrame(SonarcDecompData *decomp, uint8 *samples) const {
if (decomp->_pos == _bufferSize) return 0;
if (decomp->_samplePos == _length) return 0;
// Get Frame size
uint32 frame_bytes = *(_buffer + decomp->_pos);
frame_bytes |= (*(_buffer + decomp->_pos + 1)) << 8;
// Get Num Frame Samples
uint32 frame_samples = *(_buffer + decomp->_pos + 2);
frame_samples |= (*(_buffer + decomp->_pos + 3)) << 8;
audio_decode(_buffer + decomp->_pos, samples);
decomp->_pos += frame_bytes;
decomp->_samplePos += frame_samples;
return frame_samples;
}
Audio::SeekableAudioStream *SonarcAudioSample::makeStream() const {
// Init the _sample decompressor
SonarcDecompData decomp;
decomp._pos = _srcOffset;
decomp._samplePos = 0;
// Get the data for the _sample
Common::MemoryWriteStreamDynamic streamData(DisposeAfterUse::NO);
uint8 *framePtr = new uint8[_frameSize * 2];
uint32 frameSize;
while ((frameSize = decompressFrame(&decomp, framePtr)) != 0)
streamData.write(framePtr, frameSize);
delete[] framePtr;
// Create the _sample
return Audio::makeRawStream(
new Common::MemoryReadStream(streamData.getData(), streamData.size(), DisposeAfterUse::YES),
getRate(),
isStereo() ? Audio::FLAG_STEREO | Audio::FLAG_UNSIGNED : Audio::FLAG_UNSIGNED,
DisposeAfterUse::YES);
}
} // End of namespace Ultima8
} // End of namespace Ultima

View File

@@ -0,0 +1,64 @@
/* 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_AUDIO_SONARCAUDIOSAMPLE_H
#define ULTIMA8_AUDIO_SONARCAUDIOSAMPLE_H
#include "ultima/ultima8/audio/audio_sample.h"
namespace Ultima {
namespace Ultima8 {
class SonarcAudioSample : public AudioSample {
struct SonarcDecompData {
uint32 _pos;
uint32 _samplePos;
};
static bool _generatedOneTable;
static int _oneTable[256];
static void GenerateOneTable();
static void decode_EC(int mode, int samplecount,
const uint8 *source, int sourcesize,
uint8 *dest);
static void decode_LPC(int order, int nsamples,
uint8 *dest, const uint8 *factors);
static int audio_decode(const uint8 *source, uint8 *dest);
int _frameSize;
uint32 _srcOffset;
public:
SonarcAudioSample(const uint8 *buffer, uint32 size);
~SonarcAudioSample(void) override;
Audio::SeekableAudioStream *makeStream() const override;
private:
uint32 decompressFrame(SonarcDecompData *decompData, uint8 *samples) const;
};
} // 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/audio/sound_flex.h"
#include "ultima/ultima8/audio/sonarc_audio_sample.h"
#include "ultima/ultima8/audio/raw_audio_sample.h"
#include "common/memstream.h"
namespace Ultima {
namespace Ultima8 {
SoundFlex::SoundFlex(Common::SeekableReadStream *rs) : Archive(rs), _samples(nullptr) {
uint32 size = 0;
uint8 *buf = getRawObject(0, &size);
if (!size || !buf) {
warning("couldn't load sound flex");
return;
}
Common::MemoryReadStream st(buf, size);
_index.push_back(SoundFlexEntry(""));
if (buf[0] == 0xFF) {
// Crusader flex has an index in the first object with the format:
// [00 or FF] [ 3 bytes, often 'oB0' or 'pB0' ] [ null-terminated name ]
// read this data in and work out how to interpret it - probably tells
// some info about how to play back the raw sounds (eg, loop points?)
while (!st.eos() && _index.size() < _count) {
uint32 data = st.readUint32LE();
Std::string str;
char c = st.readByte();
while (c != 0 && !st.eos()) {
str.push_back(c);
c = st.readByte();
}
_index.push_back(SoundFlexEntry(str.c_str(), data));
}
} else {
// In U8 the first object just has 8-byte names.
char name[9] = {0};
int entries = MIN(size / 8, _count);
for (int i = 0; i < entries; i++) {
st.read(name, 8);
_index.push_back(SoundFlexEntry(name));
}
}
}
SoundFlex::~SoundFlex() {
Archive::uncache();
delete [] _samples;
}
AudioSample *SoundFlex::getSample(uint32 index) {
if (index >= _count)
return nullptr;
cache(index);
return _samples[index];
}
void SoundFlex::cache(uint32 index) {
if (index >= _count) return;
if (!_samples) {
_samples = new AudioSample * [_count];
memset(_samples, 0, sizeof(AudioSample *) * _count);
}
if (_samples[index]) return;
// This will cache the data
uint32 size;
uint8 *buf = getRawObject(index, &size);
if (!buf || !size) return;
if (strncmp(reinterpret_cast<const char *>(buf), "ASFX", 4) == 0) {
// After the 32 byte header, ASFX (crusader audio) is just raw 11025 data
if (index < _index.size()) {
const SoundFlexEntry &entry = _index[index];
debug(6, "SoundFlex: Playing sfx %d (%s) with data 0x%04X", index, entry._name.c_str(), entry._data);
}
_samples[index] = new RawAudioSample(buf + 32, size - 32, 11025, true, false);
} else {
_samples[index] = new SonarcAudioSample(buf, size);
}
}
void SoundFlex::uncache(uint32 index) {
if (index >= _count) return;
if (!_samples) return;
delete _samples[index];
_samples[index] = nullptr;
}
bool SoundFlex::isCached(uint32 index) const {
if (index >= _count) return false;
if (!_samples) return false;
return (_samples[index] != nullptr);
}
} // 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_AUDIO_SOUNDFLEX_H
#define ULTIMA8_AUDIO_SOUNDFLEX_H
#include "ultima/shared/std/string.h"
#include "ultima/ultima8/filesys/archive.h"
namespace Ultima {
namespace Ultima8 {
class AudioSample;
class SoundFlexEntry {
public:
SoundFlexEntry(const char *name, uint32 data) : _name(name), _data(data) {}
SoundFlexEntry(const char *name) : _name(name), _data(0) {}
Std::string _name;
uint32 _data;
};
class SoundFlex : protected Archive {
public:
SoundFlex(Common::SeekableReadStream *rs);
~SoundFlex() override;
//! Get an audiosample
AudioSample *getSample(uint32 index);
void cache(uint32 index) override;
void uncache(uint32 index) override;
bool isCached(uint32 index) const override;
private:
AudioSample **_samples;
Std::vector<SoundFlexEntry> _index;
};
} // End of namespace Ultima8
} // End of namespace Ultima
#endif

View File

@@ -0,0 +1,121 @@
/* 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/audio/speech_flex.h"
#include "ultima/ultima8/audio/audio_sample.h"
namespace Ultima {
namespace Ultima8 {
SpeechFlex::SpeechFlex(Common::SeekableReadStream *rs) : SoundFlex(rs) {
uint32 size = getRawSize(0);
const uint8 *buf = getRawObject(0);
const char *cbuf = reinterpret_cast<const char *>(buf);
// Note: stream holds multiple null-terminated strings.
unsigned int off = 0;
while (off < size) {
unsigned int slen = 0;
while (off + slen < size && cbuf[off + slen])
slen++;
Std::string text(cbuf + off, slen);
text.replace('\t', ' ');
off += slen + 1;
Std::string::size_type pos1 = text.findFirstNotOf(' ');
if (pos1 == Std::string::npos) {
text = "";
}
else {
Std::string::size_type pos2 = text.findLastNotOf(' ');
text = text.substr(pos1, pos2 - pos1 + 1);
}
debug(6, "Found string: \"%s\"", text.c_str());
_phrases.push_back(text);
}
delete [] buf;
}
SpeechFlex::~SpeechFlex(void) {
}
int SpeechFlex::getIndexForPhrase(const Std::string &phrase,
uint32 start, uint32 &end) const {
int i = 1;
Std::string text = phrase.substr(start);
text.replace('\t', ' ');
Std::string::size_type pos1 = text.findFirstNotOf(' ');
if (pos1 == Std::string::npos)
return 0;
Std::string::size_type pos2 = text.findLastNotOf(' ');
text = text.substr(pos1, pos2 - pos1 + 1);
debug(6, "Looking for string: \"%s\"", text.c_str());
for (const auto &p : _phrases) {
if (!p.empty() && text.hasPrefixIgnoreCase(p)) {
debug(6, "Found: %d", i);
end = p.size() + start + pos1;
if (end >= start + pos2)
end = phrase.size();
return i;
}
i++;
}
debug(6, "Not found");
return 0;
}
uint32 SpeechFlex::getSpeechLength(const Std::string &phrase) {
uint32 start = 0, end = 0;
uint32 length = 0;
while (end < phrase.size()) {
start = end;
int index = getIndexForPhrase(phrase, start, end);
if (!index) break;
const AudioSample *sample = getSample(index);
if (!sample) break;
uint32 samples = sample->getLength();
uint32 rate = sample->getRate();
bool stereo = sample->isStereo();
if (stereo) rate *= 2;
length += (samples * 1000) / rate;
}
return length;
}
} // End of namespace Ultima8
} // End of namespace Ultima

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_AUDIO_SPEECHFLEX_H
#define ULTIMA8_AUDIO_SPEECHFLEX_H
#include "ultima/ultima8/audio/sound_flex.h"
#include "ultima/shared/std/containers.h"
namespace Ultima {
namespace Ultima8 {
class SpeechFlex : public SoundFlex {
Std::vector<Std::string> _phrases;
public:
SpeechFlex(Common::SeekableReadStream *rs);
~SpeechFlex(void) override;
int getIndexForPhrase(const Std::string &phrase,
uint32 start, uint32 &end) const;
//! get total length (in milliseconds) of speech for a phrase
uint32 getSpeechLength(const Std::string &phrase);
};
} // End of namespace Ultima8
} // End of namespace Ultima
#endif

View File

@@ -0,0 +1,308 @@
/* 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/u8_music_process.h"
#include "ultima/ultima8/games/game_data.h"
#include "ultima/ultima8/audio/music_flex.h"
#include "ultima/ultima8/audio/midi_player.h"
#include "ultima/ultima8/audio/audio_mixer.h"
namespace Ultima {
namespace Ultima8 {
DEFINE_RUNTIME_CLASSTYPE_CODE(U8MusicProcess)
U8MusicProcess::U8MusicProcess() : _midiPlayer(nullptr), _state(PLAYBACK_NORMAL),
_currentTrack(0), _combatMusicActive(false),
_savedTrackState(nullptr) {
memset(_songBranches, (byte)-1, 128 * sizeof(int));
}
U8MusicProcess::U8MusicProcess(MidiPlayer *player) : _midiPlayer(player),
_state(PLAYBACK_NORMAL), _currentTrack(0), _combatMusicActive(false),
_savedTrackState(nullptr) {
memset(_songBranches, (byte)-1, 128 * sizeof(int));
_theMusicProcess = this;
_type = 1; // persistent
setRunPaused();
// Now get the transition midi
MusicFlex *musicflex = GameData::get_instance()->getMusic();
int xmidi_index = _midiPlayer->isFMSynth() ? 260 : 258;
MusicFlex::XMidiData *xmidi = musicflex->getXMidi(xmidi_index);
_midiPlayer->loadTransitionData(xmidi->_data, xmidi->_size);
}
U8MusicProcess::~U8MusicProcess() {
if (_savedTrackState)
delete _savedTrackState;
if (_midiPlayer)
_midiPlayer->stop();
_theMusicProcess = nullptr;
}
void U8MusicProcess::playMusic(int track) {
_trackState._lastRequest = track;
if (_combatMusicActive)
return;
if (_trackState._queued) {
_trackState._queued = track;
return;
}
playMusic_internal(track);
}
void U8MusicProcess::playCombatMusic(int track) {
_combatMusicActive = (track != 0);
playMusic_internal(track);
}
void U8MusicProcess::queueMusic(int track) {
if (_trackState._wanted != track) {
_trackState._queued = track;
}
}
void U8MusicProcess::unqueueMusic() {
_trackState._queued = 0;
}
void U8MusicProcess::restoreMusic() {
_combatMusicActive = false;
if (_trackState._queued) {
_trackState._queued = _trackState._lastRequest;
return;
}
playMusic_internal(_trackState._lastRequest);
}
void U8MusicProcess::fadeMusic(uint16 length) {
if (_midiPlayer && _midiPlayer->isPlaying())
_midiPlayer->startFadeOut(length);
}
bool U8MusicProcess::isFading() {
return _midiPlayer && _midiPlayer->isFading();
}
void U8MusicProcess::getTrackState(TrackState &trackState) const {
trackState = _trackState;
}
void U8MusicProcess::setTrackState(const TrackState &trackState) {
_trackState = trackState;
_state = PLAYBACK_PLAY_WANTED;
}
void U8MusicProcess::saveTrackState() {
assert(!_savedTrackState);
_savedTrackState = new TrackState(_trackState);
}
void U8MusicProcess::restoreTrackState() {
if (_savedTrackState == nullptr)
return;
_trackState = *_savedTrackState;
_state = PLAYBACK_PLAY_WANTED;
delete _savedTrackState;
_savedTrackState = nullptr;
}
void U8MusicProcess::playMusic_internal(int track) {
if (track < 0 || track >= 128) {
playMusic_internal(0);
return;
}
MusicFlex *musicflex = GameData::get_instance()->getMusic();
// No current track if not playing
if (_midiPlayer && !_midiPlayer->isPlaying())
_trackState._wanted = _currentTrack = 0;
// It's already playing and we are not transitioning
if (_currentTrack == track && _state == PLAYBACK_NORMAL) {
return;
} else if (_currentTrack == 0 || _state != PLAYBACK_NORMAL || !_midiPlayer) {
_trackState._wanted = track;
_state = PLAYBACK_PLAY_WANTED;
} else {
// We want to do a transition
const MusicFlex::SongInfo *info = musicflex->getSongInfo(_currentTrack);
uint32 measure = _midiPlayer->getSequenceCallbackData(0);
// No transition info, or invalid measure, so fast change
if (!info || (measure >= (uint32)info->_numMeasures) ||
!info->_transitions[track] || !info->_transitions[track][measure]) {
_currentTrack = 0;
if (track == 0) {
_trackState._wanted = 0;
_state = PLAYBACK_PLAY_WANTED;
} else {
playMusic_internal(track);
}
return;
}
// Get transition info
int trans = info->_transitions[track][measure];
bool overlay = false;
if (trans < 0) {
trans = (-trans) - 1;
overlay = true;
} else {
trans = trans - 1;
}
warning("Doing a MIDI transition! trans: %d overlay: %d", trans, overlay);
_midiPlayer->playTransition(trans, overlay);
_trackState._wanted = track;
_state = PLAYBACK_TRANSITION;
}
}
void U8MusicProcess::run() {
switch (_state) {
case PLAYBACK_NORMAL:
if (_midiPlayer && !_midiPlayer->isPlaying() && _trackState._queued) {
_trackState._wanted = _trackState._queued;
_state = PLAYBACK_PLAY_WANTED;
_trackState._queued = 0;
}
break;
case PLAYBACK_TRANSITION:
if (!_midiPlayer || !_midiPlayer->isPlaying()) {
// Transition has finished. Play the next track.
_state = PLAYBACK_PLAY_WANTED;
}
break;
case PLAYBACK_PLAY_WANTED: {
if (_midiPlayer)
_midiPlayer->stop();
MusicFlex::XMidiData *xmidi = nullptr;
if (_trackState._wanted) {
int xmidi_index = _trackState._wanted;
if (_midiPlayer && _midiPlayer->isFMSynth())
xmidi_index += 128;
xmidi = GameData::get_instance()->getMusic()->getXMidi(xmidi_index);
}
if (xmidi && xmidi->_data) {
if (_midiPlayer) {
// if there's a track queued, only play this one once
bool repeat = (_trackState._queued == 0);
_midiPlayer->load(xmidi->_data, xmidi->_size, 0);
_midiPlayer->setLooping(repeat);
if (_songBranches[_trackState._wanted] >= 0 && !_midiPlayer->hasBranchIndex(_songBranches[_trackState._wanted])) {
if (_songBranches[_trackState._wanted] == 0) {
// This track does not have any branches.
_songBranches[_trackState._wanted] = -1;
} else {
// Current branch is past the end of the list of branches. Reset to 0.
_songBranches[_trackState._wanted] = 0;
}
}
_midiPlayer->play(0, _songBranches[_trackState._wanted]);
}
_currentTrack = _trackState._wanted;
// Start this track at a different point (branch) next time
_songBranches[_trackState._wanted]++;
} else {
_currentTrack = _trackState._wanted = 0;
}
_state = PLAYBACK_NORMAL;
}
break;
}
}
void U8MusicProcess::saveData(Common::WriteStream *ws) {
MusicProcess::saveData(ws);
// When saving the game we want to remember the track state
// from before the menu was opened
const TrackState *stateToSave = _savedTrackState;
if (stateToSave == nullptr)
stateToSave = &_trackState;
ws->writeUint32LE(static_cast<uint32>(stateToSave->_wanted));
ws->writeUint32LE(static_cast<uint32>(stateToSave->_lastRequest));
ws->writeUint32LE(static_cast<uint32>(stateToSave->_queued));
}
bool U8MusicProcess::loadData(Common::ReadStream *rs, uint32 version) {
if (!MusicProcess::loadData(rs, version)) return false;
_trackState._wanted = static_cast<int32>(rs->readUint32LE());
if (version >= 4) {
_trackState._lastRequest = static_cast<int32>(rs->readUint32LE());
_trackState._queued = static_cast<int32>(rs->readUint32LE());
} else {
_trackState._lastRequest = _trackState._wanted;
_trackState._queued = 0;
}
_state = PLAYBACK_PLAY_WANTED;
_theMusicProcess = this;
_midiPlayer = AudioMixer::get_instance()->getMidiPlayer();
return true;
}
bool U8MusicProcess::isPlaying() {
return _currentTrack != 0;
}
void U8MusicProcess::pauseMusic() {
// probably no real use for this?
warning("TODO: U8MusicProcess::pauseMusic Implement me.");
}
void U8MusicProcess::unpauseMusic() {
// probably no real use for this?
warning("TODO: U8MusicProcess::unpauseMusic Implement me.");
}
} // End of namespace Ultima8
} // End of namespace Ultima

View File

@@ -0,0 +1,134 @@
/* 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_AUDIO_U8MUSICPROCESS_H
#define ULTIMA8_AUDIO_U8MUSICPROCESS_H
#include "ultima/ultima8/audio/music_process.h"
#include "ultima/ultima8/misc/classtype.h"
#include "audio/mididrv.h"
namespace Ultima {
namespace Ultima8 {
class Debugger;
class MidiPlayer;
class U8MusicProcess : public MusicProcess {
friend class Debugger;
enum PlaybackStates {
PLAYBACK_NORMAL = 1,
PLAYBACK_TRANSITION = 2,
PLAYBACK_PLAY_WANTED = 3
};
public:
//! The saveable part of track state
struct TrackState {
//! Track we want to play
int _wanted;
//! Last requested track that was not a temporary (ie, combat) track
int _lastRequest;
//! Track queued to start after current
int _queued;
TrackState() : _wanted(0), _lastRequest(0), _queued(0) { }
TrackState(int wanted, int lastRequest, int queued) :
_wanted(wanted), _lastRequest(lastRequest), _queued(queued) { }
};
private:
//! Play a music track
//! \param track The track number to play. Pass 0 to stop music
void playMusic_internal(int track) override;
MidiPlayer *_midiPlayer;
PlaybackStates _state;
//! The branch (starting point) to use for each music track
int _songBranches[128];
int _currentTrack; //! Currently playing track (don't save)
TrackState _trackState;
//! The track state temporarily saved when using the menu etc
TrackState *_savedTrackState;
//! Is the current music "combat" music
bool _combatMusicActive;
public:
U8MusicProcess();
U8MusicProcess(MidiPlayer *player); // Note that this does NOT delete the driver
~U8MusicProcess() override;
ENABLE_RUNTIME_CLASSTYPE()
//! Get the current instance of the Music Processes
static MusicProcess *get_instance() {
return _theMusicProcess;
}
//! Play some background music. Does not change the current track if combat music is active. If another track is currently queued, just queues this track for play.
void playMusic(int track) override;
//! Play some combat music - the last played track will be remembered
void playCombatMusic(int track) override;
//! Queue a track to start once the current one finishes
void queueMusic(int track) override;
//! Clear any queued track (does not affect currently playing track)
void unqueueMusic() override;
//! Restore the last requested non-combat track (eg, at the end of combat)
void restoreMusic() override;
//! Fades out the music over the specified time (in milliseconds)
void fadeMusic(uint16 length) override;
//! Returns true if the music is currently fading
bool isFading() override;
//! Save the current track state - used when the menu is opened
void saveTrackState() override;
//! Bring back the track state from before it was put on hold
void restoreTrackState() override;
//! Get the state of tracks (wanted, requested, queued)
void getTrackState(TrackState &trackState) const;
void setTrackState(const TrackState &state);
//! Is a track currently playing?
bool isPlaying() override;
//! Pause the currently playing track
void pauseMusic() override;
//! Resume the current track after pausing
void unpauseMusic() override;
void run() override;
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,148 @@
/* 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/file.h"
#include "ultima/ultima8/misc/debugger.h"
#include "ultima/ultima8/conf/config_file_manager.h"
namespace Ultima {
namespace Ultima8 {
using Std::string;
ConfigFileManager *ConfigFileManager::_configFileManager = nullptr;
ConfigFileManager::ConfigFileManager() {
debug(1, "Creating ConfigFileManager...");
_configFileManager = this;
}
ConfigFileManager::~ConfigFileManager() {
debug(1, "Destroying ConfigFileManager...");
clear();
_configFileManager = nullptr;
}
bool ConfigFileManager::readConfigFile(const Common::Path &fname, const Std::string &category) {
Common::File f;
if (!f.open(fname))
return false;
ConfigFile *configFile = new ConfigFile();
configFile->_category = category;
// We need various characters as the inis are used for translations.
configFile->_iniFile.allowNonEnglishCharacters();
if (!configFile->_iniFile.loadFromStream(f)) {
delete configFile;
return false;
}
_configFiles.push_back(configFile);
return true;
}
void ConfigFileManager::clear() {
for (auto *i : _configFiles) {
delete i;
}
_configFiles.clear();
}
void ConfigFileManager::clearRoot(const Std::string &category) {
Std::vector<ConfigFile *>::iterator i = _configFiles.begin();
while (i != _configFiles.end()) {
if (category.equalsIgnoreCase((*i)->_category)) {
delete(*i);
i = _configFiles.erase(i);
} else {
++i;
}
}
}
bool ConfigFileManager::get(const Std::string &category, const Std::string &section, const Std::string &key, string &ret) const {
for (int i = _configFiles.size() - 1; i >= 0; --i) {
const ConfigFile *file = _configFiles[i];
if (category.equalsIgnoreCase(file->_category)) {
if (file->_iniFile.getKey(key, section, ret)) {
return true;
}
}
}
return false;
}
bool ConfigFileManager::get(const Std::string &category, const Std::string &section, const Std::string &key, int &ret) const {
string stringval;
if (!get(category, section, key, stringval))
return false;
ret = strtol(stringval.c_str(), 0, 0);
return true;
}
bool ConfigFileManager::get(const Std::string &category, const Std::string &section, const Std::string &key, bool &ret) const {
string stringval;
if (!get(category, section, key, stringval))
return false;
ret = (stringval == "yes" || stringval == "true");
return true;
}
Std::vector<Std::string> ConfigFileManager::listSections(const Std::string &category) const {
Std::vector<Std::string> sections;
for (const auto *i : _configFiles) {
if (category.equalsIgnoreCase(i->_category)) {
Common::INIFile::SectionList sectionList = i->_iniFile.getSections();
for (const auto &j : sectionList) {
sections.push_back(j.name);
}
}
}
return sections;
}
KeyMap ConfigFileManager::listKeyValues(const Std::string &category, const Std::string &section) const {
KeyMap values;
for (const auto *c : _configFiles) {
if (category.equalsIgnoreCase(c->_category) && c->_iniFile.hasSection(section)) {
Common::INIFile::SectionKeyList keys = c->_iniFile.getKeys(section);
for (const auto &j : keys) {
values[j.key] = j.value;
}
}
}
return values;
}
} // End of namespace Ultima8
} // End of namespace Ultima

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/>.
*
*/
#ifndef ULTIMA8_CONF_CONFIGFILEMANAGER_H
#define ULTIMA8_CONF_CONFIGFILEMANAGER_H
#include "common/formats/ini-file.h"
#include "ultima/shared/std/string.h"
#include "ultima/shared/std/containers.h"
namespace Ultima {
namespace Ultima8 {
typedef Common::HashMap<Std::string, Std::string, Common::IgnoreCase_Hash> KeyMap;
class ConfigFileManager {
public:
ConfigFileManager();
~ConfigFileManager();
struct ConfigFile {
Std::string _category;
Common::INIFile _iniFile;
};
static ConfigFileManager *get_instance() {
return _configFileManager;
}
//! read a config file. Multiple files may be read. Order is important.
//! \param fname The file to read
//! \param root The name of the root node in the file
//! \param readonly If true, don't write to this file's tree (or the file)
//! \return true if successful
bool readConfigFile(const Common::Path &fname, const Std::string &category);
//! clear everything
void clear();
//! clear everything in a root
void clearRoot(const Std::string &category);
//! get value
bool get(const Std::string &category, const Std::string &section, const Std::string &key, Std::string &ret) const;
//! get value
bool get(const Std::string &category, const Std::string &section, const Std::string &key, int &ret) const;
//! get value
bool get(const Std::string &category, const Std::string &section, const Std::string &key, bool &ret) const;
//! list all sections
//! \param category The config category to list all sections in
//! \return the sections. They have no guaranteed order.
Std::vector<Std::string> listSections(const Std::string &category) const;
//! list all key-value pairs in the given section.
//! \param category The config category for the section to list
//! \param section The section to list
//! \return the key-value pairs. They have no guaranteed order.
KeyMap listKeyValues(const Std::string &category, const Std::string &section) const;
private:
Std::vector<ConfigFile *> _configFiles;
static ConfigFileManager *_configFileManager;
};
} // End of namespace Ultima8
} // End of namespace Ultima
#endif

View File

@@ -0,0 +1,752 @@
/* 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/memstream.h"
#include "ultima/ultima.h"
#include "ultima/ultima8/convert/convert_shape.h"
#include "ultima/ultima8/misc/stream_util.h"
#include "ultima/ultima8/misc/debugger.h"
namespace Ultima {
namespace Ultima8 {
void ConvertShapeFrame::Free() {
delete [] _line_offsets;
_line_offsets = nullptr;
delete [] _rle_data;
_rle_data = nullptr;
}
ConvertShape::ConvertShape() : _num_frames(0), _frames(nullptr) {
}
void ConvertShape::Free() {
if (_frames)
for(uint32 i = 0; i < _num_frames; ++i)
_frames[i].Free();
delete [] _frames;
_frames = nullptr;
_num_frames = 0;
}
void ConvertShape::Read(Common::SeekableReadStream &source, const ConvertShapeFormat *csf, uint32 real_len) {
// Just to be safe
uint32 start_pos = source.pos();
// Read the ident
if (csf->_bytes_ident) {
char ident[4];
source.read(ident, csf->_bytes_ident);
if (memcmp(ident, csf->_ident, csf->_bytes_ident)) {
warning("Corrupt shape");
return;
}
}
// Read special buffer
uint8 special[256];
if (csf->_bytes_special) {
memset(special, 0, 256);
for (uint32 i = 0; i < csf->_bytes_special; i++) special[source.readByte()&0xFF] = i + 2;
}
// Read the header unknown
if (csf->_bytes_header_unk) source.read(_header_unknown, csf->_bytes_header_unk);
// Now read _num_frames
_num_frames = 1;
if (csf->_bytes_num_frames) _num_frames = readX(source, csf->_bytes_num_frames);
if (_num_frames == 0) _num_frames = CalcNumFrames(source,csf,real_len,start_pos);
// if (_num_frames == 0xFFFF || _num_frames == 0xFFFFFF || _num_frames == -1)
// {
// warning("Corrupt shape?);
// _num_frames = 0;
// _frames = 0;
// return;
// }
// Create _frames array
_frames = new ConvertShapeFrame[_num_frames]();
// Now read the _frames
for(uint32 f = 0; f < _num_frames; ++f) {
ConvertShapeFrame *frame = _frames + f;
// Seek to initial pos
source.seek(start_pos + csf->_len_header + (csf->_len_frameheader * f));
// Read the offset
uint32 frame_offset = csf->_len_header + (csf->_len_frameheader * f);
if (csf->_bytes_frame_offset) frame_offset = readX(source, csf->_bytes_frame_offset);
// Read the unknown
if (csf->_bytes_frameheader_unk) source.read(frame->_header_unknown, csf->_bytes_frameheader_unk);
// Read frame_length
uint32 frame_length = real_len-frame_offset;
if (csf->_bytes_frame_length) frame_length = readX(source, csf->_bytes_frame_length) + csf->_bytes_frame_length_kludge;
// Seek to start of frame
source.seek(start_pos + frame_offset + csf->_bytes_special);
if (csf->_bytes_special)
frame->ReadCmpFrame(source, csf, special, f > 0 ? _frames + f - 1 : 0);
else
frame->Read(source, csf, frame_length);
}
}
void ConvertShapeFrame::Read(Common::SeekableReadStream &source, const ConvertShapeFormat *csf, uint32 frame_length) {
// Read unknown
if (csf->_bytes_frame_unknown) source.read(_unknown, csf->_bytes_frame_unknown);
// Frame details
_compression = readX(source, csf->_bytes_frame_compression);
_width = readXS(source, csf->_bytes_frame_width);
_height = readXS(source, csf->_bytes_frame_height);
_xoff = readXS(source, csf->_bytes_frame_xoff);
_yoff = readXS(source, csf->_bytes_frame_yoff);
if (_compression != 0 && _compression != 1) {
_compression = 0;
_width = 0;
_height = 0;
_xoff = 0;
_yoff = 0;
warning("Corrupt frame?");
}
if (_height) {
// Line offsets
_line_offsets = new uint32 [_height];
for (int32 i = 0; i < _height; ++i) {
_line_offsets[i] = readX(source, csf->_bytes_line_offset);
// Now fudge with the value and turn it into an offset into the rle data
// If required
if (!csf->_line_offset_absolute)
_line_offsets[i] -= (_height - i) * csf->_bytes_line_offset;
}
// Calculate the number of bytes of RLE data
_bytes_rle = frame_length - (csf->_len_frameheader2 + (_height * csf->_bytes_line_offset));
if (_bytes_rle < 0) {
_bytes_rle = 0;
warning("Corrupt frame?");
}
} else
_line_offsets = nullptr;
// Read the RLE Data
if (_bytes_rle) {
_rle_data = new uint8[_bytes_rle];
source.read(_rle_data, _bytes_rle);
} else
_rle_data = nullptr;
}
void ConvertShapeFrame::ReadCmpFrame(Common::SeekableReadStream &source, const ConvertShapeFormat *csf, const uint8 special[256], ConvertShapeFrame *prev) {
Common::MemoryWriteStreamDynamic rlebuf(DisposeAfterUse::YES);
uint8 outbuf[512];
// Read unknown
if (csf->_bytes_frame_unknown) source.read(_unknown, csf->_bytes_frame_unknown);
// Frame details
_compression = readX(source, csf->_bytes_frame_compression);
_width = readXS(source, csf->_bytes_frame_width);
_height = readXS(source, csf->_bytes_frame_height);
_xoff = readXS(source, csf->_bytes_frame_xoff);
_yoff = readXS(source, csf->_bytes_frame_yoff);
_line_offsets = new uint32 [_height];
for(int32 y = 0; y < _height; ++y) {
_line_offsets[y] = rlebuf.pos();
int32 xpos = 0;
do {
uint8 skip = source.readByte();
xpos += skip;
if (xpos > _width) {
source.seek(-1, SEEK_CUR);
skip = _width-(xpos-skip);
}
rlebuf.writeByte(skip);
if (xpos >= _width) break;
uint32 dlen = source.readByte();
uint8 *o = outbuf;
// Is this required???? It seems hacky and pointless
if (dlen == 0 || dlen == 1) {
source.seek(-1, SEEK_CUR);
rlebuf.seek(-1, SEEK_CUR);
rlebuf.writeByte(skip + (_width - xpos));
break;
}
int type = 0;
if (_compression) {
type = dlen & 1;
dlen >>= 1;
}
if (!type) {
uint32 extra = 0;
for (uint32 j = 0; j < dlen; j++) {
uint8 c = source.readByte();
if (special[c] && prev) {
int32 count = special[c];
prev->GetPixels(o, count, xpos - _xoff, y - _yoff);
o+=count;
extra += count - 1;
xpos += count;
} else if (c == 0xFF && prev) {
int32 count = source.readByte();
prev->GetPixels(o, count, xpos - _xoff, y - _yoff);
o+=count;
extra += count - 2;
xpos += count;
j++;
} else {
*o++ = c;
xpos++;
}
}
if (((dlen+extra) << _compression) > 255) {
warning("Corrupt Frame. RLE dlen too large");
}
rlebuf.writeByte((dlen+extra) << _compression);
rlebuf.write(outbuf,dlen+extra);
} else {
rlebuf.writeByte((dlen << 1) | 1);
rlebuf.writeByte(source.readByte());
xpos+=dlen;
}
} while (xpos < _width);
}
_bytes_rle = rlebuf.pos();
_rle_data = new uint8[_bytes_rle];
memcpy(_rle_data, rlebuf.getData(), _bytes_rle);
}
void ConvertShapeFrame::GetPixels(uint8 *buf, int32 count, int32 x, int32 y) {
x += _xoff;
y += _yoff;
if (y > _height) return;
int32 xpos = 0;
const uint8 * linedata = _rle_data + _line_offsets[y];
do {
xpos += *linedata++;
if (xpos == _width) break;
int32 dlen = *linedata++;
int type = 0;
if (_compression) {
type = dlen & 1;
dlen >>= 1;
}
if (x >= xpos && x < (xpos+dlen)) {
int diff = x-xpos;
dlen-=diff;
xpos = x;
int num = count;
if (dlen < count) num = dlen;
if (!type) {
const uint8 *l = (linedata += diff);
while (num--) {
*buf++ = *l++;
count--;
x++;
}
} else {
uint8 l = *linedata;
while (num--) {
*buf++ = l;
count--;
x++;
}
}
if (count == 0) return;
}
if (!type) linedata+=dlen;
else linedata++;
xpos += dlen;
} while (xpos < _width);
}
int ConvertShape::CalcNumFrames(Common::SeekableReadStream &source, const ConvertShapeFormat *csf, uint32 real_len, uint32 start_pos) {
int f = 0;
uint32 first_offset = 0xFFFFFFFF;
uint32 save_pos = source.pos();
for (f = 0;; f++) {
// Seek to initial pos
source.seek(start_pos + csf->_len_header + (csf->_len_frameheader * f));
if ((source.pos()-start_pos) >= first_offset) break;
// Read the offset
uint32 frame_offset = csf->_len_header + (csf->_len_frameheader * f);
if (csf->_bytes_frame_offset) frame_offset = readX(source, csf->_bytes_frame_offset) + csf->_bytes_special;
if (frame_offset < first_offset) first_offset = frame_offset;
// Read the unknown
if (csf->_bytes_frameheader_unk) source.skip(csf->_bytes_frameheader_unk);
// Read frame_length
uint32 frame_length = real_len-frame_offset;
if (csf->_bytes_frame_length)
frame_length = readX(source, csf->_bytes_frame_length) + csf->_bytes_frame_length_kludge;
debugC(kDebugGraphics, "Frame %d length = %xh", f, frame_length);
}
source.seek(save_pos);
return f;
}
bool ConvertShape::Check(Common::SeekableReadStream &source, const ConvertShapeFormat *csf, uint32 real_len) {
debugC(kDebugGraphics, "Testing shape format %s...", csf->_name);
bool result = true;
// Just to be safe
int start_pos = source.pos();
// Read the ident
if (csf->_bytes_ident) {
char ident[5];
ident[csf->_bytes_ident] = 0;
source.read(ident, csf->_bytes_ident);
if (memcmp(ident, csf->_ident, csf->_bytes_ident)) {
// Return to start position
source.seek(start_pos);
return false;
}
}
// Read the header special colour
if (csf->_bytes_special) source.skip(csf->_bytes_special);
// Read the header unknown
if (csf->_bytes_header_unk) source.skip(csf->_bytes_header_unk);
// Now read _num_frames
int numFrames = 1;
if (csf->_bytes_num_frames) numFrames = readX(source, csf->_bytes_num_frames);
if (numFrames == 0) numFrames = CalcNumFrames(source,csf,real_len,start_pos);
// Create _frames array
ConvertShapeFrame oneframe;
memset(&oneframe, 0, sizeof(ConvertShapeFrame));
// Now read the _frames
for (int f = 0; f < numFrames; f++) {
ConvertShapeFrame *frame = &oneframe;
// Seek to initial pos
source.seek(start_pos + csf->_len_header + (csf->_len_frameheader * f));
// Read the offset
uint32 frame_offset = csf->_len_header + (csf->_len_frameheader * f);
if (csf->_bytes_frame_offset) frame_offset = readX(source, csf->_bytes_frame_offset) + csf->_bytes_special;
// Read the unknown
if (csf->_bytes_frameheader_unk) source.read(frame->_header_unknown, csf->_bytes_frameheader_unk);
// Read frame_length
uint32 frame_length = real_len-frame_offset;
if (csf->_bytes_frame_length) frame_length = readX(source, csf->_bytes_frame_length) + csf->_bytes_frame_length_kludge;
// Invalid frame length
if ((frame_length + frame_offset) > real_len) {
result = false;
break;
}
// Seek to start of frame
source.seek(start_pos + frame_offset);
// Read unknown
if (csf->_bytes_frame_unknown) source.read(frame->_unknown, csf->_bytes_frame_unknown);
// Frame details
frame->_compression = readX(source, csf->_bytes_frame_compression);
frame->_width = readXS(source, csf->_bytes_frame_width);
frame->_height = readXS(source, csf->_bytes_frame_height);
frame->_xoff = readXS(source, csf->_bytes_frame_xoff);
frame->_yoff = readXS(source, csf->_bytes_frame_yoff);
if ((frame->_compression != 0 && frame->_compression != 1) || frame->_width < 0 || frame->_height < 0) {
frame->_compression = 0;
frame->_width = 0;
frame->_height = 0;
frame->_xoff = 0;
frame->_yoff = 0;
result = false;
break;
}
if (frame->_height) {
// Line offsets
int32 highest_offset_byte = 0;
// Calculate the number of bytes of RLE data
frame->_bytes_rle = frame_length - (csf->_len_frameheader2 + (frame->_height * csf->_bytes_line_offset));
// Totally invalid shape
if (frame->_bytes_rle < 0) {
result = false;
break;
}
// Only attempt to decompress the shape if we are not a compressed shapes
if (!csf->_bytes_special) {
// Seek to first in offset table
source.seek(start_pos + frame_offset + csf->_len_frameheader2);
// Loop through each of the _frames and find the last rle run
for (int i = 0; i < frame->_height; i++) {
int32 line_offset = readX(source, csf->_bytes_line_offset);
// Now fudge with the value and turn it into an offset into the rle data
// if required
if (!csf->_line_offset_absolute)
line_offset -= (frame->_height - i) * csf->_bytes_line_offset;
if (line_offset > frame->_bytes_rle) {
result = false;
break;
}
if (line_offset > highest_offset_byte) highest_offset_byte = line_offset;
};
// Failed for whatever reason
if (result == false) break;
// Jump to the line offset and calculate the length of the run
source.seek(highest_offset_byte + start_pos + frame_offset + csf->_len_frameheader2 + frame->_height * csf->_bytes_line_offset);
int xpos = 0;
uint32 dlen = 0;
// Compressed
if (frame->_compression) {
do {
xpos += source.readByte();
if (xpos == frame->_width) break;
dlen = source.readByte();
int type = dlen & 1;
dlen >>= 1;
if (!type) source.skip(dlen);
else source.skip(1);
xpos += dlen;
} while (xpos < frame->_width);
// Uncompressed
} else {
do {
xpos += source.readByte();
if (xpos == frame->_width) break;
dlen = source.readByte();
source.skip(dlen);
xpos += dlen;
} while (xpos < frame->_width);
}
// Calc 'real' bytes rle
int32 highest_rle_byte = source.pos();
highest_rle_byte -= start_pos + frame_offset + csf->_len_frameheader2 + frame->_height * csf->_bytes_line_offset;
// Too many bytes
if (highest_rle_byte > frame->_bytes_rle) {
result = false;
break;
}
}
}
}
// Free frames
oneframe.Free();
// Return to start position
source.seek(start_pos);
if (result)
debugC(kDebugGraphics, "Detected Shape Format: %s", csf->_name);
return result;
}
bool ConvertShape::CheckUnsafe(Common::SeekableReadStream &source, const ConvertShapeFormat *csf, uint32 real_len) {
debugC(kDebugGraphics, "Testing shape format %s...", csf->_name);
bool result = true;
// Just to be safe
const uint32 start_pos = source.pos();
// Read the ident
if (csf->_bytes_ident) {
char ident[5];
ident[csf->_bytes_ident] = 0;
source.read(ident, csf->_bytes_ident);
if (memcmp(ident, csf->_ident, csf->_bytes_ident)) {
// Return to start position
source.seek(start_pos);
return false;
}
}
// Read the header special colour
if (csf->_bytes_special) source.skip(csf->_bytes_special);
// Read the header unknown
if (csf->_bytes_header_unk) source.skip(csf->_bytes_header_unk);
// Now read _num_frames
int numFrames = 1;
if (csf->_bytes_num_frames) numFrames = readX(source, csf->_bytes_num_frames);
if (numFrames == 0) numFrames = CalcNumFrames(source,csf,real_len,start_pos);
// Create _frames array
ConvertShapeFrame oneframe;
memset(&oneframe, 0, sizeof(ConvertShapeFrame));
// Now read the _frames
for (int f = 0; f < numFrames; f++) {
ConvertShapeFrame *frame = &oneframe;
// Seek to initial pos
source.seek(start_pos + csf->_len_header + (csf->_len_frameheader * f));
// Read the offset
uint32 frame_offset = csf->_len_header + (csf->_len_frameheader * f);
if (csf->_bytes_frame_offset) frame_offset = readX(source, csf->_bytes_frame_offset) + csf->_bytes_special;
// Read the unknown
if (csf->_bytes_frameheader_unk) source.read(frame->_header_unknown, csf->_bytes_frameheader_unk);
// Read frame_length
uint32 frame_length = real_len-frame_offset;
if (csf->_bytes_frame_length) frame_length = readX(source, csf->_bytes_frame_length) + csf->_bytes_frame_length_kludge;
// Invalid frame length
if ((frame_length + frame_offset) > real_len) {
result = false;
break;
}
// Seek to start of frame
source.seek(start_pos + frame_offset);
// Read unknown
if (csf->_bytes_frame_unknown) source.read(frame->_unknown, csf->_bytes_frame_unknown);
// Frame details
frame->_compression = readX(source, csf->_bytes_frame_compression);
frame->_width = readXS(source, csf->_bytes_frame_width);
frame->_height = readXS(source, csf->_bytes_frame_height);
frame->_xoff = readXS(source, csf->_bytes_frame_xoff);
frame->_yoff = readXS(source, csf->_bytes_frame_yoff);
if ((frame->_compression != 0 && frame->_compression != 1) || frame->_width < 0 || frame->_height < 0) {
frame->_compression = 0;
frame->_width = 0;
frame->_height = 0;
frame->_xoff = 0;
frame->_yoff = 0;
result = false;
break;
}
if (frame->_height) {
// Calculate the number of bytes of RLE data (may not be accurate but we don't care)
frame->_bytes_rle = frame_length - (csf->_len_frameheader2 + (frame->_height * csf->_bytes_line_offset));
// Totally invalid shape
if (frame->_bytes_rle < 0) {
result = false;
break;
}
}
}
// Free _frames
oneframe.Free();
// Return to start position
source.seek(start_pos);
if (result)
debugC(kDebugGraphics, "Detected Shape Format: %s", csf->_name);
return result;
}
void ConvertShape::Write(Common::SeekableWriteStream &dest, const ConvertShapeFormat *csf, uint32 &write_len) {
// Just to be safe
const uint32 start_pos = dest.pos();
// Write the ident
if (csf->_bytes_ident) dest.write(csf->_ident, csf->_bytes_ident);
// Write the header unknown
if (csf->_bytes_header_unk) dest.write(_header_unknown, csf->_bytes_header_unk);
// Now write _num_frames
if (csf->_bytes_num_frames) writeX(dest, _num_frames, csf->_bytes_num_frames);
else if (!csf->_bytes_num_frames && _num_frames > 1) {
warning("Error: Unable to convert multiple frame shapes to %s", csf->_name);
return;
}
// Write filler space for the frame details
for (uint32 i = 0; i < _num_frames*csf->_len_frameheader; i++) dest.writeByte(0);
// Now write the _frames
for(uint32 f = 0; f < _num_frames; f++) {
ConvertShapeFrame *frame = _frames + f;
// Get the frame offset
uint32 frame_offset = dest.pos() - start_pos;
// Seek to the frame header pos
dest.seek(start_pos + csf->_len_header + (csf->_len_frameheader * f));
// Write the offset
if (csf->_bytes_frame_offset) writeX(dest, frame_offset, csf->_bytes_frame_offset);
// Write the unknown
if (csf->_bytes_frameheader_unk) dest.write(frame->_header_unknown, csf->_bytes_frameheader_unk);
// Calc and write frame_length
if (csf->_bytes_frame_length) {
uint32 frame_length = csf->_len_frameheader2 + (frame->_height * csf->_bytes_line_offset) + frame->_bytes_rle;
writeX(dest, frame_length - csf->_bytes_frame_length_kludge, csf->_bytes_frame_length);
}
// Seek to start of frame
dest.seek(start_pos + frame_offset);
// Write unknown
if (csf->_bytes_frame_unknown) dest.write(frame->_unknown, csf->_bytes_frame_unknown);
// Frame details
writeX(dest, frame->_compression, csf->_bytes_frame_compression);
writeX(dest, frame->_width, csf->_bytes_frame_width);
writeX(dest, frame->_height, csf->_bytes_frame_height);
writeX(dest, frame->_xoff, csf->_bytes_frame_xoff);
writeX(dest, frame->_yoff, csf->_bytes_frame_yoff);
// Line offsets
for (int32 i = 0; i < frame->_height; i++) {
int32 actual_offset = frame->_line_offsets[i];
// Unfudge the value and write it, if requiretd
if (!csf->_line_offset_absolute)
actual_offset += (frame->_height - i) * csf->_bytes_line_offset;
writeX(dest, actual_offset, csf->_bytes_line_offset);
}
// Write the RLE Data
dest.write(frame->_rle_data, frame->_bytes_rle);
}
// Just cheat
write_len = dest.pos() - start_pos;
}
// Shape format configuration for Pentagram
const ConvertShapeFormat PentagramShapeFormat = {
"Pentagram",
8, // header
"PSHP", // ident
4, // bytes_ident
0, // bytes_special
0, // header_unk
4, // _num_frames
8, // frameheader
4, // frame_offset
0, // frameheader_unk
4, // frame_length
0, // frame_length_kludge
20, // frameheader2
0, // frame_unknown
4, // frame_compression
4, // frame_width
4, // frame_height
4, // frame_xoff
4, // frame_yoff
4, // line_offset
1 // line_offset_absolute
};
} // End of namespace Ultima8
} // End of namespace Ultima

View File

@@ -0,0 +1,128 @@
/* 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_CONVERT_CONVERTSHAPE_H
#define ULTIMA8_CONVERT_CONVERTSHAPE_H
#include "common/scummsys.h"
#include "common/stream.h"
namespace Ultima {
namespace Ultima8 {
// Convert shape C
/********** Update AutoShapeFormat in shapeconv/ShapeConv.cpp when changed! **********/
struct ConvertShapeFormat {
const char * _name;
// U8 U8 Gump U8.SKF Cru Cru2D Pent Comp
uint32 _len_header; // 6 6 2 6 6 8 11
const char * _ident; // "" "" "\2\0" "" "" "PSHP" ""
uint32 _bytes_ident; // 0 0 2 0 0 4 0
uint32 _bytes_special; // 0 0 0 0 0 0 5
uint32 _bytes_header_unk; // 4 4 0 4 4 0 4
uint32 _bytes_num_frames; // 2 2 0 2 2 4 2
uint32 _len_frameheader; // 6 6 0 8 8 8 6
uint32 _bytes_frame_offset; // 3 3 0 3 3 4 4
uint32 _bytes_frameheader_unk; // 1 2 0 2 2 0 0
uint32 _bytes_frame_length; // 2 2 0 3 3 4 2
uint32 _bytes_frame_length_kludge; // 0 8 0 0 0 0 0
uint32 _len_frameheader2; // 18 18 10 28 20 20 10
uint32 _bytes_frame_unknown; // 8 8 0 8 0 0 0
uint32 _bytes_frame_compression; // 2 2 2 4 4 4 2
uint32 _bytes_frame_width; // 2 2 2 4 4 4 2
uint32 _bytes_frame_height; // 2 2 2 4 4 4 2
uint32 _bytes_frame_xoff; // 2 2 2 4 4 4 2
uint32 _bytes_frame_yoff; // 2 2 2 4 4 4 2
uint32 _bytes_line_offset; // 2 2 2 4 4 4 0
uint32 _line_offset_absolute; // 0 0 0 0 0 1 0
};
// ConvertShapeFrame structure
struct ConvertShapeFrame {
uint8 _header_unknown[2];
uint8 _unknown[8];
uint32 _compression;
int32 _width;
int32 _height;
int32 _xoff;
int32 _yoff;
uint32 *_line_offsets; // Note these are offsets into rle_data
int32 _bytes_rle; // Number of bytes of RLE Data
uint8 *_rle_data;
void Free();
void Read(Common::SeekableReadStream &source, const ConvertShapeFormat *csf, uint32 frame_length);
void ReadCmpFrame(Common::SeekableReadStream &source, const ConvertShapeFormat *csf, const uint8 special[256], ConvertShapeFrame *prev);
void GetPixels(uint8 *buf, int32 count, int32 x, int32 y);
};
// ConvertShape structure
class ConvertShape
{
uint8 _header_unknown[4];
uint32 _num_frames;
ConvertShapeFrame *_frames;
public:
ConvertShape();
~ConvertShape()
{
Free();
}
void Free();
void Read(Common::SeekableReadStream &source, const ConvertShapeFormat *csf, uint32 real_len);
void Write(Common::SeekableWriteStream &source, const ConvertShapeFormat *csf, uint32 &write_len);
// This will check to see if a Shape is of a certain type. Return true if ok, false if bad
static bool Check(Common::SeekableReadStream &source, const ConvertShapeFormat *csf, uint32 real_len);
// This will also check to see if a shape is of a certain type. However it won't check
// the rle data, it only checks headers. Return true if ok, false if bad
static bool CheckUnsafe(Common::SeekableReadStream &source, const ConvertShapeFormat *csf, uint32 real_len);
// Algorithmically calculate the number of frames
static int CalcNumFrames(Common::SeekableReadStream &source, const ConvertShapeFormat *csf, uint32 real_len, uint32 start_pos);
};
// Shape format configuration for Pentagram format
extern const ConvertShapeFormat PentagramShapeFormat;
} // End of namespace Ultima8
} // End of namespace Ultima
#endif

View File

@@ -0,0 +1,39 @@
/* 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_CONVERT_CONVERTUSECODE_H
#define ULTIMA8_CONVERT_CONVERTUSECODE_H
namespace Ultima {
namespace Ultima8 {
class ConvertUsecode {
public:
virtual ~ConvertUsecode() {}
virtual const char* const *intrinsics() = 0;
virtual const char* const *event_names() = 0;
};
} // 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/convert/crusader/convert_shape_crusader.h"
namespace Ultima {
namespace Ultima8 {
// Shape format configuration for Crusader
const ConvertShapeFormat CrusaderShapeFormat = {
"Crusader",
6, // header
"", // ident
0, // bytes_ident
0, // bytes_special
4, // header_unk
2, // num_frames
8, // frameheader
3, // frame_offset
1, // frameheader_unk
4, // frame_length
0, // frame_length_kludge
28, // frameheader2 20 for mouse/gumps
8, // frame_unknown 0 for mouse/gumps
4, // frame_compression
4, // frame_width
4, // frame_height
4, // frame_xoff
4, // frame_yoff
4, // line_offset
0 // line_offset_absolute
};
// Shape format configuration for 2D Crusader Shapes
const ConvertShapeFormat Crusader2DShapeFormat = {
"Crusader 2D",
6, // header
"", // ident
0, // bytes_ident
0, // bytes_special
4, // header_unk
2, // num_frames
8, // frameheader
3, // frame_offset
1, // frameheader_unk
4, // frame_length
0, // frame_length_kludge
20, // frameheader2 20 for mouse/gumps
0, // frame_unknown 0 for mouse/gumps
4, // frame_compression
4, // frame_width
4, // frame_height
4, // frame_xoff
4, // frame_yoff
4, // line_offset
0 // line_offset_absolute
};
} // End of namespace Ultima8
} // End of namespace Ultima

View File

@@ -0,0 +1,39 @@
/* 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_CONVERT_CRUSADER_CONVERTSHAPECRUSADER_H
#define ULTIMA8_CONVERT_CRUSADER_CONVERTSHAPECRUSADER_H
#include "ultima/ultima8/convert/convert_shape.h"
namespace Ultima {
namespace Ultima8 {
// Shape format configuration for Crusader
extern const ConvertShapeFormat CrusaderShapeFormat;
// Shape format configuration for Crusader 2D Interface Elements
extern const ConvertShapeFormat Crusader2DShapeFormat;
} // End of namespace Ultima8
} // End of namespace Ultima
#endif

View File

@@ -0,0 +1,416 @@
/* 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_CONVERT_U8_CONVERTUSECODECRUSADER_H
#define ULTIMA8_CONVERT_U8_CONVERTUSECODECRUSADER_H
#include "ultima/ultima8/convert/convert_usecode.h"
namespace Ultima {
namespace Ultima8 {
class ConvertUsecodeCrusader : public ConvertUsecode {
public:
const char* const *intrinsics() override { return _intrinsics; };
const char* const *event_names() override { return _event_names; };
static const char* const _event_names[];
private:
static const char* const _intrinsics[512];
};
// By convention, last pushed argument goes first on the list.
const char* const ConvertUsecodeCrusader::_intrinsics[] = {
// 0000
"byte World::I_getAlertActive(void)",
"int16 Item::I_getFrame(Item *)",
"void Item::I_setFrame(Item *, frame)",
"int16 Item::I_getMapArray(Item *)", // See TRIGGER::ordinal21 - stored in a variable 'mapNum'
"int16 Item::I_getStatus(Item *)",
"void Item::I_orStatus(Item *, uint16 flags)",
"int16 Item::I_equip(6 bytes)", // same coff as 0B5
"byte Item::I_isOnScreen(Item *)", // called for gattling guns and camera
"byte Actor::I_isNPC(Item *)", // proably - actually checks is itemno < 256?
"byte Item::I_getZ(Item *)",
"void Item::I_destroy(Item *)", // probably? often called after creating a replacement object and setting it to the same position (eg, LUGGAGE::gotHit)
"int16 Actor::I_GetNPCDataField0x63_00B(Actor *)", // Some unknown value set for NPCs based on Q of egg.
"void Ultima8Engine::I_setAvatarInStasis(int)",
"byte Item::I_getDirToItem(Item *, itemno)", // based on disasm
"int16 Actor::I_turnToward(Actor *, direction, dir_16)",
"void I_playFlic(void), int16 I_playFlic(Item *, char *name, int16 sizex, int16 sizey)",
// 0010
"int16 Item::I_getQLo(Item *)", // same as 02B based on same coff as 02B, 066, 084, 0A1, 0AE, 0D9, 0EA
"int16 Actor::I_getMap(4 bytes)", // based on disasm.
"void MusicProcess:I_playMusic(int trackno)",
"int16 Item::I_getX(Item *)",
"int16 Item::I_getY(Item *)",
"void AudioProcess::I_playSFXCru(Item *, uint16 sfxnum)",
"int16 Item::I_getShape(Item *)", // in STEAMBOX::func0A, is compared to 0x511 (the STEAM2 shape number) to determine direction
"void Item::I_explode(Item *, exptype, destroy_item)",
"int16 UCMachine::I_rndRange(uint16 x, uint16 y)", // // probably.. always called with 2 constants, then result often compared to some number between
"byte Item::I_legalCreateAtCoords(Item *, int16 shapeno, int16 frame, int16 x, int16 y, int16 z)", // probably, see usage in DOOR2::ordinal37
"void Item::I_andStatus(Item *, uint16 status)", // part of same coff set 01A, 031, 069, 06E, 099, 0B2, 0BF, 0C1, 0C3, 0E9, 0FC, 101, 104, 106, 108, 10A, 10C, 10E, 110, 114, 117, 11A, 128, 132. Always associated with a bitwise-not or bitmask
"int16 World::I_getControlledNPCNum()",
"byte Actor::I_getDir(4 bytes)", // based on disasm. same coff as 112, 121
"int16 Actor::I_getLastAnimSet(4 bytes)", // based on disasm. part of same coff set 01D, 05A, 0B9, 0D7, 0E4, 124
"int16 Item::I_fireWeapon(Item *, x, y, z, byte, int, byte)",
"byte Item::I_create(Item *, uint16 shapenum, uint16 framenum)", // probably - used in MISS1EGG referencing keycards and NPCDEATH in creating blood spills
// 0020
"void Item::I_popToCoords(Item *, uint16 x, uint16 y, uint16 z)", // set coords, used after creating blood spills in NPCDEATH
"void Actor::I_setDead(4 bytes)", // part of same coff set 021, 060, 073, 0A0, 0A8, 0D8, 0E7, 135
"void Item::I_push(Item *)", // same code as U8
"int16 Item::I_getEtherealTop(void)", // based on disasm
"void Item::I_setShape(Item *, int16 shapeno)", // probably. See PEPSIEW::gotHit.
"void Item::I_touch(Item *)", // same code as U8
"int16 Item::I_getQHi(Item *)", // guess, based on variable name in BOUNCBOX::gotHit
"int16 I_getClosestDirectionInRange(x1, y1, x2, y2, numdirs, mindir, maxdir)",
"int16 Item::I_hurl(Item *,8 bytes)", // part of same coff set 028, 08D, 0BD, 0C0, 0C2, 0C8, 0F7, 0F9, 118, 11D
"int16 World::I_gameDifficulty(void)",
"void AudioProcess::I_playAmbientSFXCru(Item *, sndno)",
"int16 Item::I_getQLo(Item *)", // guess, based on variable name in BOUNCBOX::gotHit
"byte Item::I_inFastArea(Item *)",
"void Item::I_setQHi(Item *, uint16 qhi)", // probably setQHi, see usage in FREE::ordinal2E where object position is copied
"byte Item::I_legalMoveToPoint(Item *, Point *, int16 force)", // based on disasm
"byte CurrentMap::I_canExistAtPoint(int, int, shapeno, Point *)",
// 0030
"void Item::I_pop(Item *)", // same code as U8
"void Item::I_andStatus(Item *, uint16 status)", // part of same coff set 01A, 031, 069, 06E, 099, 0B2, 0BF, 0C1, 0C3, 0E9, 0FC, 101, 104, 106, 108, 10A, 10C, 10E, 110, 114, 117, 11A, 128, 132
"void Item::I_receiveHit(Item *, other, dir, damage, damagetype)", // based on disasm
"byte Actor::I_isBusy(Actor *)", // same code as U8
"int16 Item::I_getDirFromTo16(x1, y1, x2, y2)",
"byte Actor::I_isKneeling(Actor *)",
"int16 Actor::I_doAnim(12 bytes)", // v. similar code to U8
"byte MainActor::I_addItemCru(4 bytes)", // same coff as 0B8
"void AudioProcess::I_stopSFXCru(Item *, int16 sndno)",
"byte Actor::I_isDead(Item *)", // same coff as 122, 12E
"byte AudioProcess::I_isSFXPlayingForObject(Item *, int16 unk)",
"void Item::I_setQLo(Item *, int16 qlo)", // probably setQLo, see usage in FREE::ordinal2E where object position is copied. Disassembly confirms.
"int16 Item::I_getItemFamily(Item *)", // based on disasm
"void Container::I_destroyContents(Item *)",
"void Item::I_fallProbably_03E(Item *)", // similar disasm to U8, but not totally the same.
"int16 Egg::I_getEggId(Item *)", // from disasm
// 0040
"void CameraProcess::I_moveTo(x, y, z)",
"void CameraProcess::I_setCenterOn(objid)",
"byte Item::I_getRangeIfVisible(Item *, otheritem)",
"void AudioProcess::I_playSFXCru(Item *, soundno)", // TODO: Work out how this is different from Int015 - to a first approximation they are quite similar.
"byte Item::I_IsOn(Item *, uint16 itemno)", // part of same coff set 044, 046, 048, 04A, 04C, 04E, 0A5, 0BC, 0C5, 0DC, 0F1, 0FA, 12C
"int16 Item::I_getQHi(Item *)", // same as 026 based on same coff set 026, 045, 047, 049, 04B, 04D, 04F, 0AF, 0BE, 0C9, 0F0, 0F3, 0FB, 133
"byte Item::I_IsOn(Item *, uint16 itemno))", // part of same coff set 044, 046, 048, 04A, 04C, 04E, 0A5, 0BC, 0C5, 0DC, 0F1, 0FA, 12C
"int16 Item::I_getQHi(Item *)", // same as 026 based on same coff set 026, 045, 047, 049, 04B, 04D, 04F, 0AF, 0BE, 0C9, 0F0, 0F3, 0FB, 133
"byte Item::I_IsOn(Item *, uint16 itemno)", // part of same coff set 044, 046, 048, 04A, 04C, 04E, 0A5, 0BC, 0C5, 0DC, 0F1, 0FA, 12C
"int16 Item::I_getQHi(Item *)", // same as 026 based on same coff set 026, 045, 047, 049, 04B, 04D, 04F, 0AF, 0BE, 0C9, 0F0, 0F3, 0FB, 133
"byte Item::I_IsOn(Item *, uint16 itemno)", // part of same coff set 044, 046, 048, 04A, 04C, 04E, 0A5, 0BC, 0C5, 0DC, 0F1, 0FA, 12C
"int16 Item::I_getQHi(Item *)", // same as 026 based on same coff set 026, 045, 047, 049, 04B, 04D, 04F, 0AF, 0BE, 0C9, 0F0, 0F3, 0FB, 133
"byte Item::I_IsOn(Item *, uint16 itemno)", // part of same coff set 044, 046, 048, 04A, 04C, 04E, 0A5, 0BC, 0C5, 0DC, 0F1, 0FA, 12C
"int16 Item::I_getQHi(Item *)", // same as 026 based on same coff set 026, 045, 047, 049, 04B, 04D, 04F, 0AF, 0BE, 0C9, 0F0, 0F3, 0FB, 133
"byte Item::I_IsOn(Item *, uint16 itemno)", // part of same coff set 044, 046, 048, 04A, 04C, 04E, 0A5, 0BC, 0C5, 0DC, 0F1, 0FA, 12C
"int16 Item::I_getQHi(Item *)", // same as 026 based on same coff set 026, 045, 047, 049, 04B, 04D, 04F, 0AF, 0BE, 0C9, 0F0, 0F3, 0FB, 133
// 0050
"int16 Actor::I_getCurrentActivityNo(Actor *)",
"void Actor::I_clrInCombat(Actor *)", // probably, based on disasm.
"void Actor::I_setDefaultActivity0(Actor *, int)",
"void Actor::I_setDefaultActivity1(Actor *, int)",
"void Actor::I_setDefaultActivity2(Actor *, int)",
"void Actor::I_setActivity(Actor *, int)", // part of same coff set 055, 07D, 0CD, 0DB, 0F2, 131
"void World::I_setControlledNPCNum(int itemno)", // when you take over the Thermatron etc.
"int16 Item::I_getSurfaceWeight(Item *)",
"byte Item::I_isCentreOn(Item *, uint16 other)",
"void Item::I_setFrame(Item *, frame)", // based on same coff as 002
"int16 Actor::I_getLastAnimSet(4 bytes)", // part of same coff set 01D, 05A, 0B9, 0D7, 0E4, 124
"byte Item::I_legalCreateAtPoint(Item *, int16 shape, int16 frame, Point *)", // see PEPSIEW::use
"void Item::I_getPoint(Item *, Point *)",
"void StatusGump::I_hideStatusGump(void)", // Probably hides gumps at the bottom, among other things.
"int16 MovieGump::I_playMovieOverlay(uint32, char *, int16 a, int16 b)", // Play video (as texture? parameters like (150, 250, "MVA11A") and other mvas)
"void StatusGump::I_showStatusGump(void)", // Probably shows gumps at the bottom, among other things.
// 0060
"void Actor::I_setDead(4 bytes)", // part of same coff set 021, 060, 073, 0A0, 0A8, 0D8, 0E7, 135
"void Actor::I_create(8 bytes)",
"void CameraProcess::I_somethingAboutCameraUpdate(void)",
"void Actor::I_teleport(12 bytes)", // based on disasm same as U8
"void Item::I_getFootpad(Item *, uint *, uint *, uint *)", // based on disasm. same coff as 12D
"byte Item::I_isInNPC(Item *)", // based on disasm - following parent containers, is one of them an NPC
"int16 Item::I_getQLo(Item *)", // same as 02B based on same coff set 010, 02B, 066, 084, 0A1, 0AE, 0D9, 0EA
"int16 Item::I_getNPCNum(Item *)", // part of same coff set 067, 06D, 089, 08E, 0AD, 0F8, 100, 102, 105, 107, 109, 10B, 10D, 10F, 111, 115, 11C, 123, 129
"void Item::I_setNpcNum(Item *, uint16 npcnum)", // maybe, see EVENT::func0A or VALUEBOX::ordinal20.. right next to getNPCNum in coff (confirmed by disassembly)
"void Item::I_andStatus(Item *, uint16 status)", // part of same coff set 01A, 031, 069, 06E, 099, 0B2, 0BF, 0C1, 0C3, 0E9, 0FC, 101, 104, 106, 108, 10A, 10C, 10E, 110, 114, 117, 11A, 128, 132
"void Item::I_move(Item *, int16 x, int16 y, uint16 z)",
"int16 Game::I_isViolenceEnabled(void)",
"void Kernel::I_resetRef(int16, int16)", // same disasm as U8
"int16 Item::I_getNPCNum(Item *)", // part of same coff set 067, 06D, 089, 08E, 0AD, 0F8, 100, 102, 105, 107, 109, 10B, 10D, 10F, 111, 115, 11C, 123, 129
"void Item::I_andStatus(Item *, uint16 status)", // part of same coff set 01A, 031, 069, 06E, 099, 0B2, 0BF, 0C1, 0C3, 0E9, 0FC, 101, 104, 106, 108, 10A, 10C, 10E, 110, 114, 117, 11A, 128, 132
"byte Item::I_isCompletelyOn(Item *, uint16 other)",
// 0070
"byte Ultima8Engine::I_getCrusaderTeleporting(void)",
"void Ultima8Engine::I_setCrusaderTeleporting(void)",
"void Ultima8Engine::I_setCruStasis(void)",
"void Actor::I_setDead(4 bytes)", // part of same coff set 021, 060, 073, 0A0, 0A8, 0D8, 0E7, 135
"void Ultima8Engine::I_clrCrusaderTeleporting(void)",
"void Ultima8Engine::I_clrCruStasis(void)",
"void AudioProcess::I_stopSFX(Item *)",
"int16 PaletteFaderProcess::I_fadeToBlack(void)", // fade to black, no args (40 frames)
"void MainActor::I_clrKeycards(void)",
"int16 MainActor::I_teleportToEgg(int, int, int)",
"int16 PaletteFaderProcess::I_fadeToGamePal(void)", // from black, no arg (40 frames)
"void Actor::I_clrImmortal(Actor *)", // same coff as 130
"int16 Actor::I_getHp(Actor *)",
"void Actor::I_setActivity(Actor *, int)", // part of same coff set 055, 07D, 0CD, 0DB, 0F2, 131
"int16 Item::I_getQuality(Item *)", // based on disassembly
"void Item::I_setQuality(Item *, int)", // based on disassembly. same coff as 0BA, 125
// 0080
"int16 Item::I_use(Item *)", // same coff as 0D0, 0D5
"int16 MainActor::I_getMaxEnergy(Actor *)",
"int16 Actor::I_getMana(Actor *)",
"void Actor::I_setMana(Actor *, int)",
"int16 Item::I_getQLo(Item *)", // same as 02B based on same coff set 010, 02B, 066, 084, 0A1, 0AE, 0D9, 0EA
"void Actor::I_setImmortal(Actor *)",
"int16 CameraProcess::I_getCameraX(void)",
"int16 CameraProcess::I_getCameraY(void)",
"void Item::I_setMapArray(Item *, uint16 maparray)", // based on decompile - sets same value as read by getmaparray .. see VALUEBOX:ordinal20
"int16 Item::I_getNpcNum(Item *)", // part of same coff set 067, 06D, 089, 08E, 0AD, 0F8, 100, 102, 105, 107, 109, 10B, 10D, 10F, 111, 115, 11C, 123, 129
"void Item::I_shoot(Item *, Point3 *, int speed, int gravity)",
"int16 Item::I_enterFastArea(Item *)", // based on disasm, v similar to U8
"void Item::I_setIsBroken(Item *)", // same coff as 119, 12A
"int16 Item::I_hurl(Item *,8 bytes)", // part of same coff set 028, 08D, 0BD, 0C0, 0C2, 0C8, 0F7, 0F9, 118, 11D
"int16 Item::I_getNPCNum(Item *)", // part of same coff set 067, 06D, 089, 08E, 0AD, 0F8, 100, 102, 105, 107, 109, 10B, 10D, 10F, 111, 115, 11C, 123, 129
"void PaletteFaderProcess::I_jumpToAllBlack(void)",
// 0090
"void MusicProcess::I_stopMusic(void)",
"void I_setSomeMovieGlobal(void)", // sets global (cleared by 93) to control palette cycling
"void I_playMovieCutsceneFullscreen(char *)", // same coff as 0A9
"void I_clearSomeMovieGlobal(void)", // clears global (set by 91) to control palette cycling
"void Game::I_playCredits(void)",
"byte Kernel::I_getCurrentKeyDown(void)", // get global - something about keyboard (by disasm)
"int16 MainActor::I_teleportToEgg(int, int)", // a bit different to the U8 one - uses main actor map by default.
"void PaletteFaderProcess:I_jumpToGreyScale(void)",
"void I_resetVargasShieldTo500(void)",
"void Item::I_andStatus(Item *, uint16 status)", // part of same coff set 01A, 031, 069, 06E, 099, 0B2, 0BF, 0C1, 0C3, 0E9, 0FC, 101, 104, 106, 108, 10A, 10C, 10E, 110, 114, 117, 11A, 128, 132
"void PaletteFaderProcess::I_jumpToNormalPalette(void)",
"int16 PaletteFaderProcess::I_fadeToGamePal(nsteps)",
"int16 PaletteFaderProcess::I_fadeToGamePalWithParam(nsteps, unk)",
"int16 PaletteFaderProcess::I_fadeToBlack(nsteps)",
"int16 PaletteFaderProcess::I_fadeToBlackWithParam(nsteps, unk)",
"int16 PaletteFaderProcess::I_fadeToColor(r, g, b, nsteps, unk)",
// 00A0
"void Actor::I_setDead(Actor *)", // part of same coff set 021, 060, 073, 0A0, 0A8, 0D8, 0E7, 135
"int16 Item::I_getQLo(Item *)", // same as 02B based on same coff set 010, 02B, 066, 084, 0A1, 0AE, 0D9, 0EA
"int16 Item::I_getUnkEggType(Item *)", // based on disassembly, same as U8
"void Egg::I_setEggXRange(Egg *, int)", // based on disasm
"byte Item::I_overlaps(Item *, uint16 unk)", // same disasm as U8
"byte Item::I_isOn(Item *, itemno)", // part of same coff set 044, 046, 048, 04A, 04C, 04E, 0A5, 0BC, 0C5, 0DC, 0F1, 0FA, 12C
"int16 I_getAnimationsEnabled(void)",
"int16 Egg::I_getEggXRange(Egg *)", // based on disasm
"void Actor::I_setDead(Actor *)", // part of same coff set 021, 060, 073, 0A0, 0A8, 0D8, 0E7, 135
"void I_playMovieCutsceneFullscreen(char *)", // same coff as 092
"void AudioProcess::I_playSFX(2 bytes)", // same coff as 0D4
"byte Actor::I_isFalling(Actor *)",
"int16 Item::I_getFamilyOfType(Item *)", // per pentagram notes, matches disasm.
"int16 Item::I_getNPCNum(Item *)", // part of same coff set 067, 06D, 089, 08E, 0AD, 0F8, 100, 102, 105, 107, 109, 10B, 10D, 10F, 111, 115, 11C, 123, 129
"int16 Item::I_getQLo(Item *)", // same as 02B based on same coff set 010, 02B, 066, 084, 0A1, 0AE, 0D9, 0EA
"int16 Item::I_getQHi(Item *)", // same as 026 based on same coff set 026, 045, 047, 049, 04B, 04D, 04F, 0AF, 0BE, 0C9, 0F0, 0F3, 0FB, 133
// 00B0
"int16 Item::I_unequip(6 bytes)",
"int16 Item::I_spawnUsecodeEvent0x13(Item *, 2 bytes)", // based on disasm - what is event 0x13? "avatar stole something" in U8..
"void Item::I_andStatus(Item *, uint16 status)", // part of same coff set 01A, 031, 069, 06E, 099, 0B2, 0BF, 0C1, 0C3, 0E9, 0FC, 101, 104, 106, 108, 10A, 10C, 10E, 110, 114, 117, 11A, 128, 132
"int32 I_getCurrentTimerTick(void)",
"void Ultima8Engine::I_setAlertActive(void)",
"int16 Item::I_equip(6 bytes)",
"void Ultima8Engine::I_clrAlertActive(void)",
"int16 Ultima8Engine::I_getAvatarInStasis(void)",
"byte MainActor::I_addItemCru(4 bytes)", // same coff as 037
"int16 Actor::I_getLastAnimSet(4 bytes)", // part of same coff set 01D, 05A, 0B9, 0D7, 0E4, 124
"void Item::I_setQuality(Item *, int)", // same coff as 07F, 125
"byte CurrentMap::I_canExistAt(int shapeno, word x, word y, byte z)", // NOTE: actually slightly different, uses shape info for an imaginary chequered wall shape? (0x31A)
"byte Item::I_isOn(Item *, itemno)", // part of same coff set 044, 046, 048, 04A, 04C, 04E, 0A5, 0BC, 0C5, 0DC, 0F1, 0FA, 12C
"int16 Item::I_hurl(Item *,8 bytes)", // part of same coff set 028, 08D, 0BD, 0C0, 0C2, 0C8, 0F7, 0F9, 118, 11D
"int16 Item::I_getQHi(Item *)", // same as 026 based on same coff set 026, 045, 047, 049, 04B, 04D, 04F, 0AF, 0BE, 0C9, 0F0, 0F3, 0FB, 133
"void Item::I_andStatus(Item *, uint16 status)", // part of same coff set 01A, 031, 069, 06E, 099, 0B2, 0BF, 0C1, 0C3, 0E9, 0FC, 101, 104, 106, 108, 10A, 10C, 10E, 110, 114, 117, 11A, 128, 132
// 00C0
"int16 Item::I_hurl(Item *,8 bytes)", // part of same coff set 028, 08D, 0BD, 0C0, 0C2, 0C8, 0F7, 0F9, 118, 11D
"void Item::I_andStatus(Item *, uint16 status)", // part of same coff set 01A, 031, 069, 06E, 099, 0B2, 0BF, 0C1, 0C3, 0E9, 0FC, 101, 104, 106, 108, 10A, 10C, 10E, 110, 114, 117, 11A, 128, 132
"int16 Item::I_hurl(Item *,8 bytes)", // part of same coff set 028, 08D, 0BD, 0C0, 0C2, 0C8, 0F7, 0F9, 118, 11D
"void Item::I_andStatus(Item *, uint16 status)", // part of same coff set 01A, 031, 069, 06E, 099, 0B2, 0BF, 0C1, 0C3, 0E9, 0FC, 101, 104, 106, 108, 10A, 10C, 10E, 110, 114, 117, 11A, 128, 132
"int16 KeypadGump::I_showKeypad(int targetCode)",
"byte Item::I_isOn(Item *, itemno)", // part of same coff set 044, 046, 048, 04A, 04C, 04E, 0A5, 0BC, 0C5, 0DC, 0F1, 0FA, 12C
"void SpriteProcess::I_createSprite(word, word, word, word, uword, uword, ubyte)",
"byte Item::I_getDirFromItem(Item *, itemno)", // same disasm as U8
"int16 Item::I_hurl(Item *,8 bytes)", // part of same coff set 028, 08D, 0BD, 0C0, 0C2, 0C8, 0F7, 0F9, 118, 11D
"int16 Item::I_getQHi(Item *)", // same as 026 based on same coff set 026, 045, 047, 049, 04B, 04D, 04F, 0AF, 0BE, 0C9, 0F0, 0F3, 0FB, 133
"byte Actor::I_addHp(Actor *, int)",
"void MainActor::I_switchMap(int16 mapnum)",
"byte Actor::I_getInCombat(Actor *)",
"void Actor::I_setActivity(Actor *, int)", // part of same coff set 055, 07D, 0CD, 0DB, 0F2, 131
"int16 Game::I_isReleaseBuild(void)", // whether the string "GAME COMPILE=1" has the 1. Might be interesting to see what this does..
"void Item::I_setQAndCombine(Item *, int16 q)", // based on disassembly
// 00D0
"int16 Item::I_use(Item *)", // same coff as 080, 0D5
"void AudioProcess:I_stopAllSFX(void)", // based on disasm.
"void I_playMovieCutscene(int *item,char *flicname,word sizex,word sizey)", // play flic
"void I_clearKeyboardState(void)", // clears some globals and calls a keyboard reset function.. TODO: work out what those globals do?
"void I_playSFX(2 bytes)", // same coff as 0AA. Based on disasm.
"int16 Item::I_use(Item *)", // same coff as 080, 0D0
"byte CameraProcess::I_getCameraZ(void)",
"int16 Actor::I_getLastAnimSet(4 bytes)", // part of same coff set 01D, 05A, 0B9, 0D7, 0E4, 124
"void Actor::I_setDead(4 bytes)", // part of same coff set 021, 060, 073, 0A0, 0A8, 0D8, 0E7, 135
"int16 Item::I_getQLo(Item *)", // same as 02B based on same coff set 010, 02B, 066, 084, 0A1, 0AE, 0D9, 0EA
"void PaletteFaderProcess::I_setPalToAllGrey(void)", // sets all colors to 0x3F3F3F
"void Actor::I_setActivity(Actor *, int)", // part of same coff set 055, 07D, 0CD, 0DB, 0F2, 131
"byte Item::I_isOn(Item *, itemno)", // part of same coff set 044, 046, 048, 04A, 04C, 04E, 0A5, 0BC, 0C5, 0DC, 0F1, 0FA, 12C
"int16 Actor::I_getLastActivityNo(Actor *)",
"void Actor::I_setCombatTactic(Actor *, int)",
"int16 Actor::I_getEquip(6 bytes)", // based on disasm
// 00E0
"void Actor::I_setEquip(8 bytes)",
"int16 Actor::I_getDefaultActivity0(Actor *)",
"int16 Actor::I_getDefaultActivity1(Actor *)",
"int16 Actor::I_getDefaultActivity2(Actor *)",
"int16 Actor::I_getLastAnimSet(4 bytes)", // part of same coff set 01D, 05A, 0B9, 0D7, 0E4, 124
"void Actor::I_setTarget(Actor *, uint16 target)",
"void Actor::I_SetNPCDataField0x63_0E6(Actor *, int)",
"void Actor::I_setDead(4 bytes)", // part of same coff set 021, 060, 073, 0A0, 0A8, 0D8, 0E7, 135
"int16 Item::I_cast(6 bytes)",
"void Item::I_andStatus(Item *, uint16 status)", // part of same coff set 01A, 031, 069, 06E, 099, 0B2, 0BF, 0C1, 0C3, 0E9, 0FC, 101, 104, 106, 108, 10A, 10C, 10E, 110, 114, 117, 11A, 128, 132
"int16 Item::I_getQLo(Item *)", // same as 02B based on same coff set 010, 02B, 066, 084, 0A1, 0AE, 0D9, 0EA
"int16 I_GetQOfAvatarInventoryItem0x4ed_0EB(void)",
"void Item::I_popToEnd(Item*, int)", // similar code to U8
"void Item::I_popToContainer(Item*, int)", // same code as U8
"void BatteryChargerProcess::I_create(void)",
"int16 Kernel::I_getNumProcesses(int, int)", // same code as U8
// 00F0
"int16 Item::I_getQHi(Item *)", // same as 026 based on same coff set 026, 045, 047, 049, 04B, 04D, 04F, 0AF, 0BE, 0C9, 0F0, 0F3, 0FB, 133
"byte Item::I_isOn(Item *, itemno)", // part of same coff set 044, 046, 048, 04A, 04C, 04E, 0A5, 0BC, 0C5, 0DC, 0F1, 0FA, 12C
"void Actor::I_setActivity(Actor *, int)", // part of same coff set 055, 07D, 0CD, 0DB, 0F2, 131
"int16 Item::I_getQHi(Item *)", // same as 026 based on same coff set 026, 045, 047, 049, 04B, 04D, 04F, 0AF, 0BE, 0C9, 0F0, 0F3, 0FB, 133
"int16 Item::I_getQ(Item *)", // based on disassembly
"void Item::I_setQ(Item *, uint16 q)", // based on disassembly
"void CruHealer::I_create_0F6(void)",
"int16 Item::I_hurl(Item *,8 bytes)", // part of same coff set 028, 08D, 0BD, 0C0, 0C2, 0C8, 0F7, 0F9, 118, 11D
"int16 Item::I_getNPCNum(Item *)", // part of same coff set 067, 06D, 089, 08E, 0AD, 0F8, 100, 102, 105, 107, 109, 10B, 10D, 10F, 111, 115, 11C, 123, 129
"int16 Item::I_hurl(Item *,8 bytes)", // part of same coff set 028, 08D, 0BD, 0C0, 0C2, 0C8, 0F7, 0F9, 118, 11D
"byte Item::I_isOn(Item *, itemno)", // part of same coff set 044, 046, 048, 04A, 04C, 04E, 0A5, 0BC, 0C5, 0DC, 0F1, 0FA, 12C
"int16 Item::I_getQHi(Item *)", // same as 026 based on same coff set 026, 045, 047, 049, 04B, 04D, 04F, 0AF, 0BE, 0C9, 0F0, 0F3, 0FB, 133
"void Item::I_andStatus(Item *, uint16 status)", // part of same coff set 01A, 031, 069, 06E, 099, 0B2, 0BF, 0C1, 0C3, 0E9, 0FC, 101, 104, 106, 108, 10A, 10C, 10E, 110, 114, 117, 11A, 128, 132
"byte MainActor::I_hasKeycard(int)",
"void ComputerGump::I_readComputer(char *)",
"int16 UCMachine::I_numToStr(int16 num)", // same as 113 based on same coff set 0FF, 113, 126
// 0100
"int16 Item::I_getNPCNum(Item *)", // part of same coff set 067, 06D, 089, 08E, 0AD, 0F8, 100, 102, 105, 107, 109, 10B, 10D, 10F, 111, 115, 11C, 123, 129
"void Item::I_andStatus(Item *, uint16 status)", // part of same coff set 01A, 031, 069, 06E, 099, 0B2, 0BF, 0C1, 0C3, 0E9, 0FC, 101, 104, 106, 108, 10A, 10C, 10E, 110, 114, 117, 11A, 128, 132
"int16 Item::I_getNPCNum(Item *)", // Based on variable name in TRIGGER::ordinal21. Part of same coff set 067, 06D, 089, 08E, 0AD, 0F8, 100, 102, 105, 107, 109, 10B, 10D, 10F, 111, 115, 11C, 123, 129
"byte Item::I_isCrusTypeNPC(uint16 shapenum)",
"void Item::I_andStatus(Item *, uint16 status)", // part of same coff set 01A, 031, 069, 06E, 099, 0B2, 0BF, 0C1, 0C3, 0E9, 0FC, 101, 104, 106, 108, 10A, 10C, 10E, 110, 114, 117, 11A, 128, 132
"int16 Item::I_getNPCNum(Item *)", // part of same coff set 067, 06D, 089, 08E, 0AD, 0F8, 100, 102, 105, 107, 109, 10B, 10D, 10F, 111, 115, 11C, 123, 129
"void Item::I_andStatus(Item *, uint16 status)", // part of same coff set 01A, 031, 069, 06E, 099, 0B2, 0BF, 0C1, 0C3, 0E9, 0FC, 101, 104, 106, 108, 10A, 10C, 10E, 110, 114, 117, 11A, 128, 132
"int16 Item::I_getNPCNum(Item *)", // part of same coff set 067, 06D, 089, 08E, 0AD, 0F8, 100, 102, 105, 107, 109, 10B, 10D, 10F, 111, 115, 11C, 123, 129
"void Item::I_andStatus(Item *, uint16 status)", // part of same coff set 01A, 031, 069, 06E, 099, 0B2, 0BF, 0C1, 0C3, 0E9, 0FC, 101, 104, 106, 108, 10A, 10C, 10E, 110, 114, 117, 11A, 128, 132
"int16 Item::I_getNPCNum(Item *)", // part of same coff set 067, 06D, 089, 08E, 0AD, 0F8, 100, 102, 105, 107, 109, 10B, 10D, 10F, 111, 115, 11C, 123, 129
"void Item::I_andStatus(Item *, uint16 status)", // part of same coff set 01A, 031, 069, 06E, 099, 0B2, 0BF, 0C1, 0C3, 0E9, 0FC, 101, 104, 106, 108, 10A, 10C, 10E, 110, 114, 117, 11A, 128, 132
"int16 Item::I_getNPCNum(Item *)", // part of same coff set 067, 06D, 089, 08E, 0AD, 0F8, 100, 102, 105, 107, 109, 10B, 10D, 10F, 111, 115, 11C, 123, 129
"void Item::I_andStatus(Item *, uint16 status)", // part of same coff set 01A, 031, 069, 06E, 099, 0B2, 0BF, 0C1, 0C3, 0E9, 0FC, 101, 104, 106, 108, 10A, 10C, 10E, 110, 114, 117, 11A, 128, 132
"int16 Item::I_getNPCNum(Item *)", // part of same coff set 067, 06D, 089, 08E, 0AD, 0F8, 100, 102, 105, 107, 109, 10B, 10D, 10F, 111, 115, 11C, 123, 129
"void Item::I_andStatus(Item *, uint16 status)", // part of same coff set 01A, 031, 069, 06E, 099, 0B2, 0BF, 0C1, 0C3, 0E9, 0FC, 101, 104, 106, 108, 10A, 10C, 10E, 110, 114, 117, 11A, 128, 132
"int16 Item::I_getNPCNum(Item *)", // part of same coff set 067, 06D, 089, 08E, 0AD, 0F8, 100, 102, 105, 107, 109, 10B, 10D, 10F, 111, 115, 11C, 123, 129
// 0110
"void Item::I_andStatus(Item *, uint16 status)", // part of same coff set 01A, 031, 069, 06E, 099, 0B2, 0BF, 0C1, 0C3, 0E9, 0FC, 101, 104, 106, 108, 10A, 10C, 10E, 110, 114, 117, 11A, 128, 132
"int16 Item::I_getNPCNum(Item *)", // part of same coff set 067, 06D, 089, 08E, 0AD, 0F8, 100, 102, 105, 107, 109, 10B, 10D, 10F, 111, 115, 11C, 123, 129
"byte Actor::I_getDir(Actor *)", // same coff as 01C, 121
"int16 UCMachine::I_numToStr(int16 num)", // based on VMAIL::func0A example usage
"void Item::I_andStatus(Item *, uint16 status)", // part of same coff set 01A, 031, 069, 06E, 099, 0B2, 0BF, 0C1, 0C3, 0E9, 0FC, 101, 104, 106, 108, 10A, 10C, 10E, 110, 114, 117, 11A, 128, 132
"int16 Item::I_getNPCNum(Item *)", // part of same coff set 067, 06D, 089, 08E, 0AD, 0F8, 100, 102, 105, 107, 109, 10B, 10D, 10F, 111, 115, 11C, 123, 129
"byte Item::I_fireDistance(14 bytes)",
"void Item::I_andStatus(Item *, uint16 status)", // part of same coff set 01A, 031, 069, 06E, 099, 0B2, 0BF, 0C1, 0C3, 0E9, 0FC, 101, 104, 106, 108, 10A, 10C, 10E, 110, 114, 117, 11A, 128, 132
"int16 Item::I_hurl(Item *,8 bytes)", // part of same coff set 028, 08D, 0BD, 0C0, 0C2, 0C8, 0F7, 0F9, 118, 11D
"void Item::I_setIsBroken(Item *)", // same coff as 08C, 12A
"void Item::I_andStatus(Item *, uint16 status)", // part of same coff set 01A, 031, 069, 06E, 099, 0B2, 0BF, 0C1, 0C3, 0E9, 0FC, 101, 104, 106, 108, 10A, 10C, 10E, 110, 114, 117, 11A, 128, 132
"byte Item::I_getTypeFlag(Item *, uint16 shift)",
"int16 Item::I_getNPCNum(Item *)", // part of same coff set 067, 06D, 089, 08E, 0AD, 0F8, 100, 102, 105, 107, 109, 10B, 10D, 10F, 111, 115, 11C, 123, 129
"int16 Item::I_hurl(Item *,8 bytes)", // part of same coff set 028, 08D, 0BD, 0C0, 0C2, 0C8, 0F7, 0F9, 118, 11D
"int16 Item::I_getCY(Item *)", // same coff as 12B
"byte Item::I_getCZ(Item *)", // based on disasm
// 0120
"int16 Item::I_getCX(Item *)",
"byte Actor::I_getDir(4 bytes)", // same coff as 01C, 112
"byte Actor::I_isDead(Item *)", // same coff as 12E, 039
"int16 Item::I_getNPCNum(Item *)", // part of same coff set 067, 06D, 089, 08E, 0AD, 0F8, 100, 102, 105, 107, 109, 10B, 10D, 10F, 111, 115, 11C, 123, 129
"int16 Actor::I_getLastAnimSet(4 bytes)", // part of same coff set 01D, 05A, 0B9, 0D7, 0E4, 124
"void Item::I_setQuality(Item *, int)", // same coff as 07F, 0BA
"int16 UCMachine::I_numToStr(int16 num)", // same as 113 based on same coff set 0FF, 113, 126
"byte Item::I_getDirToCoords(Item *, uin16 x, uint16 y)", // based on disassembly
"void Item::I_andStatus(Item *, uint16 status)", // part of same coff set 01A, 031, 069, 06E, 099, 0B2, 0BF, 0C1, 0C3, 0E9, 0FC, 101, 104, 106, 108, 10A, 10C, 10E, 110, 114, 117, 11A, 128, 132
"int16 Item::I_getNPCNum(Item *)", // part of same coff set 067, 06D, 089, 08E, 0AD, 0F8, 100, 102, 105, 107, 109, 10B, 10D, 10F, 111, 115, 11C, 123, 129
"void Item::I_setIsBroken(Item *)", // same coff as 08C, 119
"int16 IItem::I_getCY(Item *)", // same coff as 11E
"byte Item::I_isOn(Item *, itemno)", // part of same coff set 044, 046, 048, 04A, 04C, 04E, 0A5, 0BC, 0C5, 0DC, 0F1, 0FA, 12C
"void Item::I_getFootpadData(Item *, uint *, uint *, uint *)", // same coff as 064
"byte Actor::I_isDead(Item *)", // same coff as 122, 039
"int16 Actor::I_createActorCru(Item *, uint16 other_itemno)",
// 0130
"void Actor::I_clrImmortal(Actor *)", // same coff as 07B
"void Actor::I_setActivity(Actor *, int)", // part of same coff set 055, 07D, 0CD, 0DB, 0F2, 131
"void Item::I_andStatus(Item *, int16 status)", // part of same coff set 01A, 031, 069, 06E, 099, 0B2, 0BF, 0C1, 0C3, 0E9, 0FC, 101, 104, 106, 108, 10A, 10C, 10E, 110, 114, 117, 11A, 128, 132
"int16 Item::I_getQHi(Item *)", // same as 026 based on same coff set 026, 045, 047, 049, 04B, 04D, 04F, 0AF, 0BE, 0C9, 0F0, 0F3, 0FB, 133
"void WeaselGump::I_showGump(uint16 param)",
"void Actor::I_setDead(Actor *)", // part of same coff set 021, 060, 073, 0A0, 0A8, 0D8, 0E7, 135
"void UNUSEDInt136()",
"void UNUSEDInt137()"
};
const char * const ConvertUsecodeCrusader::_event_names[] = {
"look()", // 0x00
"use()", // 0x01
"anim()", // 0x02
"setActivity()", // 0x03
"cachein()", // 0x04
"hit(uword, word)", // 0x05
"gotHit(uword, word)", // 0x06
"hatch()", // 0x07
"schedule()", // 0x08
"release()", // 0x09
"equip()", // 0x0A
"unequip()", // 0x0B
"combine()", // 0x0C
"func0D", // 0x0D
"calledFromAnim()", // 0x0E
"enterFastArea()", // 0x0F
"leaveFastArea()", // 0x10
"cast(uword)", // 0x11
"justMoved()", // 0x12
"avatarStoleSomething(uword)", // 0x13
"animGetHit()", // 0x14
"unhatch(word)", // 0x15
"func16", // 0x16
"func17", // 0x17
"func18", // 0x18
"func19", // 0x19
"func1A", // 0x1A
"func1B", // 0x1B
"func1C", // 0x1C
"func1D", // 0x1D
"func1E", // 0x1E
"func1F", // 0x1F
0
};
} // End of namespace Ultima8
} // End of namespace Ultima
#endif

View File

@@ -0,0 +1,420 @@
/* 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_CONVERT_U8_CONVERTUSECODEREGRET_H
#define ULTIMA8_CONVERT_U8_CONVERTUSECODEREGRET_H
#include "ultima/ultima8/convert/convert_usecode.h"
#include "ultima/ultima8/convert/crusader/convert_usecode_crusader.h"
namespace Ultima {
namespace Ultima8 {
class ConvertUsecodeRegret : public ConvertUsecode {
public:
const char* const *intrinsics() override { return _intrinsics; };
const char* const *event_names() override { return ConvertUsecodeCrusader::_event_names; };
private:
static const char* const _intrinsics[512];
};
const char* const ConvertUsecodeRegret::_intrinsics[] = {
// 0000
"World::I_getAlertActive()",
"Item::getFrame(void)",
"Item::setFrame(uint16)",
"Item::getMapNum(void)",
"Item::getStatus(void)",
"Item::orStatus(sint16)",
"Item::equip(sint16)",
"Item::isEntirelyOnScreen()",
"Item::isNpc(void)",
"Item::getZ(void)",
"World::I_gameDifficulty()",
"Item::getQLo(void)",
"Item::destroy(void)",
"Actor::I_getUnkByte()", // same as field 0x63 in No Remorse
"Item::getX(void)",
"Item::getY(void)",
// 0010
"Item::playSfxCru()",
"Item::getShape(void)",
"Item::I_explode()",
"UCMachine::I_rndRange()",
"Item::legalCreateAtCoords(uint16,uint16,uint16,uint16,uint16)",
"Item::andStatus(void)",
"World::I_getControlledNPCNum()",
"Actor::I_getDir()",
"Actor::I_getLastAnimSet()",
"Item::I_fireWeapon()",
"Item::create(uint16,uint16)",
"Item::popToCoords(uint16,uint16,uint8)",
"Actor::I_setDead()",
"Item::push(void)",
"Item::I_getEtherealTop()",
"Item::getQLo(void)",
// 0020
"Item::setQLo(sint16)",
"Item::getQHi(void)",
"Item::setQHi(sint16)",
"Item::I_getClosestDirectionInRange()",
"Item::hurl(sint16,sint16,sint16,sint16)",
"Item::getCY(void)",
"Item::getCX(void)",
"SpriteProcess::I_createSprite()",
"Item::setNpcNum(sint16)",
"AudioProcess::I_playSFXCru()",
"Item::setShape()",
"Item::pop(void)",
"AudioProcess::I_stopSFXCru()",
"Item::isCompletelyOn(uint16)",
"Item::popToContainer(uint16)",
"Actor::I_getHp()",
// 0030
"Actor::I_getMana()",
"Item::getFamily(void)",
"Actor::destroyContents(void)",
"AudioProcess::I_setVolumeForItemSFX(uint16,uint16,byte)",
"Item::getDirToItem(uint16)",
"AudioProcess::I_isSfxPlayingForObject(Item *,uint sfxno)",
"Item::I_getRangeIfVisible()",
"AudioProcess::I_playSFXCru()",
"Item::andStatus(void)",
"Kernel::resetRef(uint16,ProcessType)",
"Item::touch(void)",
"Egg::getEggId(void)",
"MainActor::I_addItemCru()",
"Actor::I_getMap()",
"Item::callEvent11(sint16)",
"CameraProcess::I_somethingAboutCameraUpdate()",
// 0040
"AudioProcess::I_stopSFXCru()",
"Item::isOn(uint16)",
"Item::getQHi(void)",
"Item::isOn(uint16)",
"Item::getQHi(void)",
"Item::isOn(uint16)",
"Item::getQHi(void)",
"Item::isOn(uint16)",
"Item::getQHi(void)",
"Item::isOn(uint16)",
"Item::getQHi(void)",
"Item::isOn(uint16)",
"Item::getQHi(void)",
"Actor::I_getCurrentActivityNo()",
"Actor::isDead(void)",
"Actor::I_clrInCombat()",
// 0050
"Actor::I_setDefaultActivity0()",
"Actor::I_setDefaultActivity1()",
"Actor::I_setDefaultActivity2()",
"Actor::I_setActivity()",
"World::I_setControlledNPCNum()",
"Item::I_receiveHit()",
"Game::I_isReleaseBuild()",
"MainActor::I_setMana()",
"Item::use(void)",
"Item::setUnkEggType(sint16)",
"MusicProcess::I_playMusic()",
"Item::getSurfaceWeight(void)",
"Item::I_isCentreOn()",
"Item::setFrame(uint16)",
"Actor::I_getLastAnimSet()",
"Ultima8Engine::I_setAvatarInStasis()",
// 0060
"Actor::I_isBusy()",
"Actor::I_getField0x13Flag2()",
"Actor::I_doAnim()",
"Item::legalCreateAtPoint(uint16,uint16,WorldPoint&)",
"Item::getPoint(WorldPoint&)",
"Item::legalMoveToPoint(WorldPoint&,uint16,uint16)",
"Item::fall(void)",
"Item::hurl(sint16,sint16,sint16,sint16)",
"Kernel::getNumProcesses(uint16,ProcessType)",
"Item::getCY(void)",
"U8Engine::I_isCfgAnimationOff()",
"ResetKeyboardState()",
"MusicProcess::I_pauseMusic()",
"MovieGump::I_playMovieCutsceneRegret()",
"MusicProcess::I_unpauseMusic()",
"Item::isInNpc(void)",
// 0070
"Ultima8Engine::I_setCruStasis()",
"Ultima8Engine::I_clrCruStasis()",
"PaletteFaderProcess::I_jumpToColor()",
"PaletteFaderProcess::I_fadeToGamePal()",
"Actor::isDead(void)",
"Item::getNpcNum(void)",
"IntrinsicReturn0",
"Game::I_isViolenceEnabled()",
"Item::unequip(sint16)",
"Item::andStatus(void)",
"Item::move(uint16,uint16,uint8)",
"Ultima8Engine::I_getCrusaderTeleporting()",
"Ultima8Engine::I_setCrusaderTeleporting()",
"Ultima8Engine::I_clrCrusaderTeleporting()",
"Actor::I_turnToward()",
"PaletteFaderProcess::I_fadeToBlack()",
// 0080
"MainActor::I_clrKeycards()",
"MusicProcess::I_stopMusic()",
"PaletteFaderProcess::I_jumpToAllBlack()",
"I_setUnkFlagA4()",
"I_clearUnkFlagA4()",
"MainActor::I_switchMap(int16)",
"teleportToEgg(sint16,int,uint8)",
"PaletteFaderProcess::I_fadeToGamePal()",
"Actor::I_clrImmortal()",
"Actor::I_setActivity()",
"Item::getQuality(void)",
"Item::setQuality(sint16)",
"MainActor::I_getMaxEnergy()",
"CameraProcess::I_moveTo(x,y,z)",
"Actor::I_setImmortal()",
"Camera::getX(void)",
// 0090
"Camera::getY(void)",
"Item::setMapNum(sint16)",
"Item::getNpcNum(void)",
"Item::shoot(WorldPoint&,sint16,sint16)",
"CameraProcess::I_setCenterOn()",
"Item::enterFastArea(void)",
"Item::setBroken()",
"Item::hurl(sint16,sint16,sint16,sint16)",
"Item::getNpcNum(void)",
"Ultima8Engine::I_moveKeyDownRecently()",
"teleportToEgg(sint16,uint8)",
"Actor::I_createActor()",
"Actor::I_clrInCombat()",
"PaletteFaderProcess::I_jumpToGreyScale()",
"PaletteFaderProcess::I_jumpToNormalPalette()",
"CruStatusGump::I_showStatusGump()",
// 00A0
"Item::andStatus(void)",
"Item::getUnkEggType(void)",
"Egg::setEggXRange(uint16)",
"Item::setFrame(uint16)",
"Item::overlaps(uint16)",
"Item::isOn(uint16)",
"Item::getQHi(void)",
"Actor::I_getLastAnimSet()",
"Item::getCY(void)",
"CurrentMap::I_canExistAt()", // Equivalent to Intrinsic00BB() in Remorse
"Item::isOn(uint16)",
"Actor::isDead(void)",
"Item::hurl(sint16,sint16,sint16,sint16)",
"Item::I_inFastArea()",
"Item::getQHi(void)",
"Item::andStatus(void)",
// 00B0
"Item::hurl(sint16,sint16,sint16,sint16)",
"Item::andStatus(void)",
"Item::hurl(sint16,sint16,sint16,sint16)",
"Item::andStatus(void)",
"Item::getDirToCoords(uint16,uint16)",
"MainActor::I_removeItem(uint16)",
"I_updateInventoryUI()",
"Item::getNpcNum(void)",
"Item::getCY(void)",
"Item::isOn(uint16)",
"Item::getFootpad(sint16&,sint16&,sint16&)",
"Actor::isDead(void)",
"Actor::I_createActorCru()",
"Actor::I_setActivity()",
"KeypadGump::I_showKeypad()",
"Item::andStatus(void)",
// 00C0
"ComputerGump::I_readComputer()",
"UCMachine::I_numToStr()",
"IntrinsicReturn0",
"Actor::I_getDir()",
"Item::getQHi(void)",
"Item::setQuality(sint16)",
"Item::hurl(sint16,sint16,sint16,sint16)",
"Actor::I_addHp()",
"CruHealerProcess::I_create()",
"Item::callEvent0A(sint16)",
"Item::setBroken()",
"Item::isOn(uint16)",
"Actor::I_teleport()",
"Item::I_getDirFromTo16()",
"Item::getQHi(void)",
"Item::isOn(uint16)",
// 00D0
"Actor::I_isInCombat()",
"Actor::I_getNPCDataField0x4()",
"Actor::I_setCombatTactic()",
"Actor::I_setDead())",
"CameraProcess::I_getCameraY()",
"Actor::I_getEquip()",
"Actor::I_setEquip()",
"Actor::I_getDefaultActivity0()",
"Actor::I_getDefaultActivity1()",
"Actor::I_getDefaultActivity2()",
"Actor::I_getLastAnimSet()",
"Actor::I_isFalling()",
"Item::getQLo(void)",
"Item::getQHi(void)",
"Item::getNpcNum(void)",
"Item::I_setField0x81()",
// 00E0
"Item::hurl(sint16,sint16,sint16,sint16)",
"Actor::I_setDead()",
"Item::getQLo(void)",
"Item::getCY(void)",
"Item::getNpcNum(void)",
"Item::hurl(sint16,sint16,sint16,sint16)",
"Item::getNpcNum(void)",
"Item::hurl(sint16,sint16,sint16,sint16)",
"Item::getNpcNum(void)",
"Item::hurl(sint16,sint16,sint16,sint16)",
"Item::getNpcNum(void)",
"Item::hurl(sint16,sint16,sint16,sint16)",
"Item::getNpcNum(void)",
"Item::hurl(sint16,sint16,sint16,sint16)",
"Item::getNpcNum(void)",
"Item::hurl(sint16,sint16,sint16,sint16)",
// 00F0
"Item::getNpcNum(void)",
"Item::hurl(sint16,sint16,sint16,sint16)",
"Item::getNpcNum(void)",
"Item::hurl(sint16,sint16,sint16,sint16)",
"Item::getNpcNum(void)",
"Item::hurl(sint16,sint16,sint16,sint16)",
"Item::getNpcNum(void)",
"Item::andStatus(void)",
"Actor::I_setDead()",
"Item::getQLo(void)",
"Actor::I_setDead()",
"Dtable::I_getMaxHPForNPC()",
"Actor::I_setHP()",
"Item::getQLo(void)",
"BatteryChargerProcess::I_create()",
"Item::hurl(sint16,sint16,sint16,sint16)",
// 0100
"Item::andStatus(void)",
"Item::isOn(uint16)",
"Actor::isDead(void)",
"Actor::I_setActivity()",
"Item::getQHi(void)",
"Actor::I_getLastAnimSet()",
"Actor::I_setDead()",
"Item::getQLo(void)",
"Item::isOn(uint16)",
"Item::getQHi(void)",
"Item::isOn(uint16)",
"Item::getQHi(void)",
"Item::hurl(sint16,sint16,sint16,sint16)",
"Item::getNpcNum(void)",
"Item::getCY(void)",
"Item::hurl(sint16,sint16,sint16,sint16)",
// 0110
"Item::isOn(uint16)",
"MainActor::I_hasKeycard()",
"IntrinsicReturn0",
"Actor::isDead(void)",
"Actor::I_clrImmortal()",
"UCMachine::I_numToStr()",
"Item::getQHi(void)",
"Actor::I_setActivity()",
"Item::andStatus(void)",
"Item::getNpcNum(void)",
"Item::andStatus(void)",
"Item::getNpcNum(void)",
"Item::I_isCrusTypeNPC()",
"Item::andStatus(void)",
"Item::getNpcNum(void)",
"Item::AvatarStoleSomehting(uint16)",
// 0120
"Item::andStatus(void)",
"Item::getNpcNum(void)",
"Item::getQ(void)",
"Item::setQ(uint)",
"Item::andStatus(void)",
"Item::getNpcNum(void)",
"Item::andStatus(void)",
"Item::getNpcNum(void)",
"Item::andStatus(void)",
"Item::getNpcNum(void)",
"Item::andStatus(void)",
"Item::getNpcNum(void)",
"Item::andStatus(void)",
"Item::getNpcNum(void)",
"Actor::I_getDir()",
"Item::andStatus(void)",
// 0130
"Item::getNpcNum(void)",
"Intrinsic0131()",
"Item::andStatus(void)",
"Item::hurl(sint16,sint16,sint16,sint16)",
"Item::andStatus(void)",
"Camera::getY(void)",
"Camera::getZ(void)",
"CruStatusGump::I_hideStatusGump()",
"Actor::I_clrInCombat()",
"Item::getTypeFlagCrusader(sint16)",
"Item::getNpcNum(void)",
"Item::hurl(sint16,sint16,sint16,sint16)",
"Item::getCY(void)",
"Item::getCZ(void)",
"Item::setFrame(uint16)",
"AudioProcess::I_playAmbientSFX()",
// 0140
"AudioProcess::I_isSFXPlaying()",
"World::I_clrAlertActiveRegret()",
"PaletteFaderProcess::I_fadeToGivenColor()",
"Actor::isDead(void)",
"Actor::I_setDead()",
"Game::I_playCredits()",
"PaletteFaderProcess::I_jumpToAllGrey()",
"Item::I_getFamilyOfType()",
"Item::getNpcNum(void)",
"Item::getQLo(void)",
"Item::andStatus(void)",
"Ultima8Engine::getCurrentTimerTick()",
"World::I_setAlertActiveRegret()",
"Ultima8Engine::I_getAvatarInStasis()",
"MainActor::I_addItemCru()",
"Egg::getEggXRange(void)",
// 0150
"Actor::I_clrInCombat()",
"PaletteFaderProcess::I_jumpToColor()",
"Item::setFrame(uint16)",
"UCMachine::I_numToStr()",
"Actor::I_getDir()",
"UCMachine::I_numToStr()",
"Item::isOn(uint16)",
"Actor::I_getDir()",
"Actor::I_setDead()",
"Item::getQHi(void)",
"Item::getQLo(void)",
"UCMachine::I_numToStr()",
"Actor::I_getDir()",
"Intrinsic015D()",
0
};
} // End of namespace Ultima8
} // End of namespace Ultima
#endif

View File

@@ -0,0 +1,140 @@
/* 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/convert/u8/convert_shape_u8.h"
namespace Ultima {
namespace Ultima8 {
// Shape format configuration for Ultima8
const ConvertShapeFormat U8ShapeFormat = {
"Ultima8",
6, // header
"", // ident
0, // bytes_ident
0, // bytes_special
4, // header_unk
2, // num_frames
6, // frameheader
3, // frame_offset
1, // frameheader_unk
2, // frame_length
0, // frame_length_kludge
18, // frameheader2
8, // frame_unknown
2, // frame_compression
2, // frame_width
2, // frame_height
2, // frame_xoff
2, // frame_yoff
2, // line_offset
0 // line_offset_absolute
};
// Shape format configuration for Ultima8 2D interface components
const ConvertShapeFormat U82DShapeFormat = {
"Ultima8 2D",
6, // header
"", // ident
0, // bytes_ident
0, // bytes_special
4, // header_unk
2, // num_frames
6, // frameheader
3, // frame_offset
1, // frameheader_unk
2, // frame_length
8, // frame_length_kludge
18, // frameheader2
8, // frame_unknown
2, // frame_compression
2, // frame_width
2, // frame_height
2, // frame_xoff
2, // frame_yoff
2, // line_offset
0 // line_offset_absolute
};
// Shape format configuration for Ultima8 SKF
const ConvertShapeFormat U8SKFShapeFormat = {
"Ultima8 SKF",
2, // header
"\2", // ident
2, // bytes_ident
0, // bytes_special
0, // header_unk
0, // num_frames
0, // frameheader
0, // frame_offset
0, // frameheader_unk
0, // frame_length
0, // frame_length_kludge
10, // frameheader2
0, // frame_unknown
2, // frame_compression
2, // frame_width
2, // frame_height
2, // frame_xoff
2, // frame_yoff
2, // line_offset
0 // line_offset_absolute
};
// Shape format configuration for Compressed Ultima 8 shapes
const ConvertShapeFormat U8CMPShapeFormat = {
"Ultima8 CMP",
11, // header
"", // ident
0, // bytes_ident
5, // bytes_special
4, // header_unk
2, // num_frames
6, // frameheader
4, // frame_offset
2, // frameheader_unk
0,//2, // frame_length
0,//-16, // frame_length_kludge
10, // frameheader2
0, // frame_unknown
2, // frame_compression
2, // frame_width
2, // frame_height
2, // frame_xoff
2, // frame_yoff
0, // line_offset
0 // line_offset_absolute
};
} // End of namespace Ultima8
} // End of namespace Ultima

View File

@@ -0,0 +1,45 @@
/* 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_CONVERT_U8_CONVERTSHAPEU8_H
#define ULTIMA8_CONVERT_U8_CONVERTSHAPEU8_H
#include "ultima/ultima8/convert/convert_shape.h"
namespace Ultima {
namespace Ultima8 {
// Shape format configuration for Ultima 8
extern const ConvertShapeFormat U8ShapeFormat;
// Shape format configuration for Ultima 8 2D
extern const ConvertShapeFormat U82DShapeFormat;
// Shape format configuration for Ultima 8 SKF file shapes
extern const ConvertShapeFormat U8SKFShapeFormat;
// Shape format configuration for Compressed Ultima 8 shapes
extern const ConvertShapeFormat U8CMPShapeFormat;
} // 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/>.
*
*/
#ifndef ULTIMA8_CONVERT_U8_CONVERTUSECODEU8_H
#define ULTIMA8_CONVERT_U8_CONVERTUSECODEU8_H
#include "ultima/ultima8/convert/convert_usecode.h"
namespace Ultima {
namespace Ultima8 {
class ConvertUsecodeU8 : public ConvertUsecode {
public:
const char* const *intrinsics() override { return _intrinsics; };
const char* const *event_names() override { return _event_names; };
private:
static const char* const _intrinsics[];
static const char* const _event_names[];
};
const char* const ConvertUsecodeU8::_intrinsics[] = {
// 0000
"process target()",
"Item::getNext()", // Unused
"Item::touch()",
"word Item::getX()",
"word Item::getY()",
"word Item::getZ()",
"word Item::getCX()",
"word Item::getCY()",
"word Item::getCZ()",
"word Item::Ultima8::getGumpX()", // Unused
"word Item::Ultima8::getGumpY()", // Unused
"void Item::setGumpXY(word x, word y)", // Unused
"Item::getPoint(WorldPoint*)",
"uword Item::getType()",
"void Item::setType(uword type)",
"uword Item::getFrame()",
// 0010
"void Item::setFrame(uword frame)",
"uword Item::getQuality()", // Get Q (Not generic familiy)
"uword Item::getUnkEggType()", // Get Q (UnkEgg family)
"uword Item::getQuantity()", // Get Q (Quantity or Reagent family)
"Item::getContents()", // Unused
"Item::getContainer()",
"Item::getRootContainer()",
"uword Item::getGlobNum()", // Get Q (GlobEgg family) - Unused
"void Item::setGlobNum(uword)", // Set Q (GlobEgg family) - Unused
"uword Item::getQ()", // Get Q
"void Item::setQ(uword)", // Set Q
"void Item::setQuality(word value)", // Set Q (Not generic familiy)
"void Item::setUnkEggType(word value)", // Set Q (UnkEgg family) - Unused
"void Item::setQuantity(word value)", // Set Q (Quantity and Reagent families)
"word Item::getFamily()",
"bool Item::getTypeFlag(word bit)", // Get TypeFlag (0 to 63 - not a mask!)
// 0020
"word Item::getStatus()", // Get Status Flags
"void Item::orStatus(word mask)", // Status |= mask;
"void Item::andStatus(word mask)", // Status &= mask;
"Item::getFootpad(word*, word*, word*)",
"Item::touches(uword)", // Unused
"Item::overlaps(uword)", // boundingbox Overlap in 3d
"Item::overlapsXY(uword)", // Unused
"Item::isOn(uword)", // 3dxy overlaps && other->ztop == this->zbot ???
"Item::isCompletelyOn(uword)", // Unused
"Item::isAbove(uword)", // Unused
"Item::isUnder(uword)", // Unused
"Item::ascend(word)", // used once
"Item::getWeight()",
"Item::getWeightIncludingContents()",
"Item::getSurfaceWeight()",
"Item::getVolume()", // Unused
// 0030
"Item::getCapacity()", // Unuses
"Item::legal_create(uword, uword, uword, uword, uword)",
"Item::create(uword, uword)",
"Item::legal_create(uword, uword, WorldPoint*)",
"Item::legal_create(uword, uword, uword, word)",
"Item::push()",
"Item::popToCoords(uword, uword, ubyte)",
"Item::popToContainer(uword)",
"Item::pop()",
"Item::popToEnd(uword)",
"Item::destroy()",
"Item::removeContents()",
"Item::destroyContents()",
"Item::isExplosive()",
"Item::move(uword, uword, ubyte)",
"Item::move(WorldPoint*)", // Unused
// 0040
"Item::legal_move(WorldPoint*, uword, uword)",
"Item::legal_move(uword*, uword)",
"Item::isNpc()",
"Item::isInNpc()", // Unused
"process Item::hurl(word, word, word, word)",
"Item::shoot(WorldPoint*, word, word)",
"Item::fall()",
"Item::grab()",
"Item::findTarget(word, word)", // Unused
"process Item::bark(char* str)",
"strptr process Item::ask(uword slist)",
"word Item::getSliderInput(word min, word max, word step)",
"Item::openGump(word)",
"Item::closeGump()",
"Item::isGumpOpen()", // Unused
"Item::getNpcArray()", // Unused
// 0050
"Item::getMapArray()",
"Item::setNpcArray(word)", // Unused
"Item::setMapArray(word)",
"Item::receiveHit(uword, byte, word, uword)",
"Item::explode()",
"Item::canReach(uword, word)",
"Item::getRange(uword)",
"Item::getRange(uword, uword, uword)", // Unused
"Item::getDirToCoords(uword, uword)",
"Item::getDirFromCoords(uword, uword)",
"Item::getDirToItem(uword)",
"Item::getDirFromItem(uword)",
"process Item::look()", // Call event 0x0
"process Item::use()", // Call event 0x1
"process Item::anim()", // Unused
"process Item::cachein()", // Unused
// 0060
"Item::hit(uword, word)", // Unused
"process Item::gotHit(uword, word)", // Call event ???
"process Item::release()", // Unused
"process Item::equip()", // Unused
"process Item::unequip()", // Unused
"process Item::combine()", // Unused
"process Item::calledFromAnim()", // Unused
"process Item::enterFastArea()", // Call event 0xF
"process Item::leaveFastArea()", // Unused
"process Item::cast(uword)", // Unused
"process Item::justMoved()", // Unused
"process Item::AvatarStoleSomething(uword)",// Unused
"process Item::animGetHit(uword)", // Unused
"process Item::guardianBark(word)", // Call event 0x15
"process Book::read(char*)",
"process Scroll::read(char*)",
// 0070
"process Grave::read(word,char*)",
"process Plaque::read(word,char*)",
"Egg::getEggXRange()",
"Egg::getEggYRange()",
"Egg::setEggXRange(uword)",
"Egg::setEggYRange(uword)", // Unused
"Egg::getEggId()",
"Egg::setEggId(uword)",
"Egg::hatch()", // Unused
"MonsterEgg::hatch()",
"MonsterEgg::getMonId()",
"MonsterEgg::getActivity()", // Unused
"MonsterEgg::getShapeType()", // Unused
"MonsterEgg::setMonId(word)", // Unused
"MonsterEgg::setActivity(word)", // Unused
"MonsterEgg::setShapeType(word)", // Unused
// 0080
"Npc::isBusy()",
"Npc::areEnemiesNear()",
"Npc::isInCombat()",
"Npc::setInCombat()",
"Npc::clrInCombat()",
"Npc::setTarget(uword)",
"Npc::getTarget()",
"Npc::setAlignment(ubyte)",
"Npc::getAlignment()", // Unused
"Npc::setEnemyAlignment(ubyte)",
"Npc::getEnemyAlignment()", // Unused
"Npc::isEnemy(uword)",
"Npc::isDead()",
"Npc::setDead()",
"Npc::clrDead()",
"Npc::isImmortal()", // Unused
// 0090
"Npc::setImmortal()",
"Npc::clrImmortal()",
"Npc::isWithstandDeath()", // Unused
"Npc::setWithstandDeath()",
"Npc::clrWithstandDeath()",
"Npc::isFeignDeath()", // Unused
"Npc::setFeignDeath()",
"Npc::clrFeignDeath()", // Unused
"Npc::freeEquip(uword)", // Unused
"Npc::clearEquip()", // Unused
"Npc::getNpcSlot()", // Unused
"Npc::freeNpcSlot()", // Unused
"Npc::getDir()",
"Npc::getMap()",
"Npc::teleport(uword, uword, ubyte, ubyte)",
"process Npc::doAnim(AnimSet, word, word, ubyte)",
// 00A0
"Npc::getLastAnimSet()",
"process Npc::pathfind(uword, uword, uword, uword)",
"process Npc::pathfind(uword, uword)",
"byte Npc::getStr()",
"byte Npc::getInt()",
"byte Npc::getDex()",
"ubyte Npc::getHp()",
"word Npc::getMana()",
"void Npc::setStr(byte str)",
"void Npc::setInt(byte int)",
"void Npc::setDex(byte dex)",
"void Npc::setHp(ubyte hp)",
"void Npc::setMana(word mana)",
"Npc::create(uword, uword)",
"process Npc::cSetActivity(Activity)",
"Npc::setAirWalkEnabled(ubyte)",
// 00B0
"Npc::getAirWalkEnabled()",
"Npc::schedule(ulong)",
"Npc::getEquip(word)",
"Npc::setEquip(word, uword)",
"closeAllGumps()",
"process Camera::scrollTo(uword, uword, ubyte, word)", // probably x, y, z, 'time'
"urandom(word)",
"rndRange(word,word)",
"castGrantPeaceSpell()",
"numToStr(uword)",
"strToNum(char*)", // Unused
"playMusic(byte)",
"getName()", // Returns the Avatar's name
"igniteChaos(uword, uword, ubyte)",
"Camera::setCenterOn(uword)",
"Camera::move_to(uword, uword, ubyte, word)",
// 00C0
"Camera::move_rel(word, word, word)", // Unused
"Camera::set_roof(word)", // Unused
"Camera::roof()", // Unused
"Camera::getX()", // Unused
"Camera::getY()", // Unused
"Camera::getZ()", // Unused
"Camera::startQuake(word)",
"Camera::stopQuake()",
"Camera::invertScreen(ubyte)",
"U8MousePointer::getDir()", // Unused
"Kernel::getNumProcesses(uword, ProcessType)",
"Kernel::resetRef(uword, ProcessType)",
"process teleportToEgg(word, word, ubyte)",
"resetRef(uword, uword)",
"setRef(uword, uword, uword)",
"getAvatarInStasis()",
// 00D0
"setAvatarInStasis(word)",
"getEtherealTop()",
"getCurrentTimerTick()",
"canGetThere(uword, uword, uword)", // Unused
"canExistAt(uword, uword, uword, uword, ubyte, uword, word)",
"createSprite(word, word, word, word, word, word, uword, uword, ubyte)",
"createSprite(word, word, word, word, uword, uword, ubyte)",
"word getFamilyOfType(uword type)",
"TimeInGameHours()",
"TimeInMinutes()",
"TimeInSeconds()", // Unused
"SetTimeInGameHours(word)",
"SetTimeInMinutes(long)", // Unused
"SetTimeInSeconds(long)", // unused
"process FadeToBlack()",
"process FadeFromBlack()",
// 00E0
"process FadeToPalette(word, word)",
"process LightningBolt()",
"process FadeToWhite()",
"process FadeFromWhite()",
"playEndgame()",
"FeedAvatar(word)",
"AccumulateStrength(word)", // Unused
"AccumulateIntelligence(word)",
"AccumulateDexterity(word)",
"ClrAvatarInCombat()",
"SetAvatarInCombat()",
"IsAvatarInCombat()", // Unused
"playSFX(word)",
"playSFX(word, ubyte)",
"playSFX(word, word, uword)",
"playAmbientSFX(word)", // Unused
// 00F0
"playAmbientSFX(word, word)", // Unused
"playAmbientSFX(word, word, uword)",
"isSFXPlaying(word)",
"setVolumeSFX(word, word)",
"stopSFX(word)",
"stopSFX(word, uword)",
"soundInit(word, word, word)", // Unused
"soundDeInit()", // Unused
"musicStop()",
"musicSlowStop()", // Unused
"musicPlay(word)", // Unused
"TonysBalls(word, word, uword, uword, uword)",
"AvatarCanCheat()",
"MakeAvatarACheater()",
"isGameRunning()",
"unused",
// 0100
"unused",
0
};
const char * const ConvertUsecodeU8::_event_names[] = {
"look()", // 0x00
"use()", // 0x01
"anim()", // 0x02
"setActivity()", // 0x03
"cachein()", // 0x04
"hit(uword, word)", // 0x05
"gotHit(uword, word)", // 0x06
"hatch()", // 0x07
"schedule()", // 0x08
"release()", // 0x09
"equip()", // 0x0A
"unequip()", // 0x0B
"combine()", // 0x0C
"func0D", // 0x0D
"calledFromAnim()", // 0x0E
"enterFastArea()", // 0x0F
"leaveFastArea()", // 0x10
"cast(uword)", // 0x11
"justMoved()", // 0x12
"AvatarStoleSomething(uword)", // 0x13
"animGetHit()", // 0x14
"guardianBark(word)", // 0x15
"func16", // 0x16
"func17", // 0x17
"func18", // 0x18
"func19", // 0x19
"func1A", // 0x1A
"func1B", // 0x1B
"func1C", // 0x1C
"func1D", // 0x1D
"func1E", // 0x1E
"func1F", // 0x1F
0
};
} // End of namespace Ultima8
} // End of namespace Ultima
#endif

View File

@@ -0,0 +1,435 @@
/* 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/debugtools.h"
#include "backends/imgui/imgui.h"
#include "backends/imgui/imgui_utils.h"
#include "ultima/ultima.h"
#include "ultima/ultima8/ultima8.h"
#include "ultima/ultima8/games/game_data.h"
#include "ultima/ultima8/gfx/palette.h"
#include "ultima/ultima8/gfx/palette_manager.h"
#include "ultima/ultima8/gumps/game_map_gump.h"
#include "ultima/ultima8/gumps/item_relative_gump.h"
#include "ultima/ultima8/gumps/target_gump.h"
#include "ultima/ultima8/usecode/usecode.h"
#include "ultima/ultima8/world/actors/main_actor.h"
#include "ultima/ultima8/world/actors/quick_avatar_mover_process.h"
#include "ultima/ultima8/world/get_object.h"
#include "ultima/ultima8/world/item.h"
#include "ultima/ultima8/misc/debugger.h"
namespace Ultima {
namespace Ultima8 {
typedef struct ImGuiState {
bool _itemStatsWindow = false;
bool _paletteWindow = false;
uint32 _targetItemId = kMainActorId;
ObjId _targetGumpId = 0;
} ImGuiState;
ImGuiState *_state = nullptr;
void showItemStats() {
if (!_state->_itemStatsWindow)
return;
ImGui::SetNextWindowPos(ImVec2(20, 20), ImGuiCond_FirstUseEver);
ImGui::SetNextWindowSize(ImVec2(300, 550), ImGuiCond_FirstUseEver);
if (ImGui::Begin("Item Stats", &_state->_itemStatsWindow)) {
if (_state->_targetGumpId) {
// Check if gump still exists and has a result
Gump *gump = getGump(_state->_targetGumpId);
if (gump) {
if (gump->GetResult()) {
_state->_targetItemId = gump->GetResult();
_state->_targetGumpId = 0;
}
} else {
_state->_targetGumpId = 0;
}
}
ImGui::BeginChild("##scrolling", ImVec2(0, -30), false, ImGuiWindowFlags_NoMove | ImGuiWindowFlags_NoNav);
if (_state->_targetItemId) {
Item *item = getItem(_state->_targetItemId);
if (item) {
const ShapeInfo *si = item->getShapeInfo();
if (ImGui::CollapsingHeader("Properties", ImGuiTreeNodeFlags_DefaultOpen)) {
if (ImGui::BeginTable("Properties", 2, ImGuiTableFlags_Borders | ImGuiTableFlags_RowBg)) {
// ImGui::TableSetupColumn("Name", ImGuiTableColumnFlags_WidthFixed);
// ImGui::TableSetupColumn("Value", ImGuiTableColumnFlags_WidthFixed);
// ImGui::TableHeadersRow();
ImGui::TableNextColumn();
ImGui::Text("Id");
ImGui::TableNextColumn();
ImGui::Text("%u", item->getObjId());
ImGui::TableNextColumn();
ImGui::Text("Class");
ImGui::TableNextColumn();
ImGui::Text("%s", item->GetClassType()._className);
ImGui::TableNextColumn();
ImGui::Text("Shape");
ImGui::TableNextColumn();
ImGui::Text("%u", item->getShape());
ImGui::TableNextColumn();
ImGui::Text("Frame");
ImGui::TableNextColumn();
ImGui::Text("%u", item->getFrame());
ImGui::TableNextColumn();
ImGui::Text("Map");
ImGui::TableNextColumn();
ImGui::Text("%u", item->getMapNum());
ImGui::TableNextColumn();
ImGui::Text("Location");
ImGui::TableNextColumn();
if (item->getParent()) {
int32 gx, gy;
item->getGumpLocation(gx, gy);
ImGui::Text("(%d, %d)", gx, gy);
} else {
Point3 p = item->getLocation();
ImGui::Text("(%d, %d, %d)", p.x, p.y, p.z);
}
ImGui::TableNextColumn();
ImGui::Text("Footpad");
ImGui::TableNextColumn();
int32 xd, yd, zd;
item->getFootpadData(xd, yd, zd);
ImGui::Text("%d, %d, %d", xd, yd, zd);
ImGui::TableNextColumn();
// Original weights appear to be different
ImGui::Text("Weight");
ImGui::TableNextColumn();
ImGui::Text("%d", item->getWeight());
ImGui::TableNextColumn();
ImGui::Text("Volume");
ImGui::TableNextColumn();
ImGui::Text("%d", item->getVolume());
ImGui::TableNextColumn();
// Original menu had "Anim" here - None, Unk, Normal, Fast
// Original menu had "Fr" and "Sp" here
ImGui::Text("Family");
ImGui::TableNextColumn();
ImGui::Text("%d", item->getFamily());
ImGui::TableNextColumn();
ImGui::Text("Usecode");
ImGui::TableNextColumn();
const char *ucname = GameData::get_instance()->getMainUsecode()->get_class_name(item->getShape());
ImGui::Text("%s", ucname != nullptr ? ucname : "");
ImGui::TableNextColumn();
ImGui::Text("Quality");
ImGui::TableNextColumn();
ImGui::Text("%u", item->getQuality());
ImGui::TableNextColumn();
ImGui::Text("NPC Number");
ImGui::TableNextColumn();
ImGui::Text("%u", item->getNpcNum());
ImGui::TableNextColumn();
ImGui::Text("Equipment Type");
ImGui::TableNextColumn();
ImGui::Text("0x%x", si ? si->_equipType : 0);
ImGui::EndTable();
}
}
// ShapeInfo "Type Flags"
Common::String shapeFlagsLabel = Common::String::format("Shape Flags: 0x%04x###shapeflags", si->_flags);
if (ImGui::CollapsingHeader(shapeFlagsLabel.c_str(), ImGuiTreeNodeFlags_DefaultOpen)) {
if (si) {
if (ImGui::BeginTable("Shape Flags", 2, ImGuiTableFlags_Borders | ImGuiTableFlags_RowBg)) {
ImGui::TableNextColumn();
ImGui::Text("Fixed");
ImGui::TableNextColumn();
ImGui::Text("%s", si->is_fixed() ? "true" : "false");
ImGui::TableNextColumn();
ImGui::Text("Solid");
ImGui::TableNextColumn();
ImGui::Text("%s", si->is_solid() ? "true" : "false");
ImGui::TableNextColumn();
ImGui::Text("Sea");
ImGui::TableNextColumn();
ImGui::Text("%s", si->is_sea() ? "true" : "false");
ImGui::TableNextColumn();
ImGui::Text("Land");
ImGui::TableNextColumn();
ImGui::Text("%s", si->is_land() ? "true" : "false");
ImGui::TableNextColumn();
ImGui::Text("Occlude");
ImGui::TableNextColumn();
ImGui::Text("%s", si->is_occl() ? "true" : "false");
ImGui::TableNextColumn();
ImGui::Text("Bag");
ImGui::TableNextColumn();
ImGui::Text("%s", si->is_bag() ? "true" : "false");
ImGui::TableNextColumn();
ImGui::Text("Damaging");
ImGui::TableNextColumn();
ImGui::Text("%s", si->is_damaging() ? "true" : "false");
ImGui::TableNextColumn();
ImGui::Text("Noisy");
ImGui::TableNextColumn();
ImGui::Text("%s", si->is_noisy() ? "true" : "false");
ImGui::TableNextColumn();
ImGui::Text("Draw");
ImGui::TableNextColumn();
ImGui::Text("%s", si->is_draw() ? "true" : "false");
ImGui::TableNextColumn();
ImGui::Text("Ignore");
ImGui::TableNextColumn();
ImGui::Text("%s", si->is_ignore() ? "true" : "false");
ImGui::TableNextColumn();
ImGui::Text("Roof");
ImGui::TableNextColumn();
ImGui::Text("%s", si->is_roof() ? "true" : "false");
ImGui::TableNextColumn();
ImGui::Text("Translucent");
ImGui::TableNextColumn();
ImGui::Text("%s", si->is_translucent() ? "true" : "false");
ImGui::TableNextColumn();
ImGui::Text("Editor");
ImGui::TableNextColumn();
ImGui::Text("%s", si->is_editor() ? "true" : "false");
ImGui::TableNextColumn();
ImGui::Text("Explode");
ImGui::TableNextColumn();
ImGui::Text("%s", si->is_u8_explode() ? "true" : "false");
// Original menu had "InDlist" flag here
ImGui::EndTable();
}
}
}
Common::String itemFlagsLabel = Common::String::format("Item Flags: 0x%04x###itemflags", item->getFlags());
if (ImGui::CollapsingHeader(itemFlagsLabel.c_str(), ImGuiTreeNodeFlags_DefaultOpen)) {
if (ImGui::BeginTable("Item Flags", 2, ImGuiTableFlags_Borders | ImGuiTableFlags_RowBg)) {
ImGui::TableNextColumn();
// Original menu had "InDlist" flag here - 0x0001?
ImGui::Text("Disposible");
ImGui::TableNextColumn();
ImGui::Text("%s", item->hasFlags(Item::FLG_DISPOSABLE) ? "true" : "false");
ImGui::TableNextColumn();
ImGui::Text("Owned"); // "Virtl"? Appears on items created by usecode
ImGui::TableNextColumn();
ImGui::Text("%s", item->hasFlags(Item::FLG_OWNED) ? "true" : "false");
ImGui::TableNextColumn();
ImGui::Text("Contained");
ImGui::TableNextColumn();
ImGui::Text("%s", item->hasFlags(Item::FLG_CONTAINED) ? "true" : "false");
ImGui::TableNextColumn();
ImGui::Text("Invisible");
ImGui::TableNextColumn();
ImGui::Text("%s", item->hasFlags(Item::FLG_INVISIBLE) ? "true" : "false");
ImGui::TableNextColumn();
ImGui::Text("Flipped");
ImGui::TableNextColumn();
ImGui::Text("%s", item->hasFlags(Item::FLG_FLIPPED) ? "true" : "false");
ImGui::TableNextColumn();
ImGui::Text("NPC");
ImGui::TableNextColumn();
ImGui::Text("%s", item->hasFlags(Item::FLG_IN_NPC_LIST) ? "true" : "false");
ImGui::TableNextColumn();
ImGui::Text("Fast Only"); // "GlobCr"
ImGui::TableNextColumn();
ImGui::Text("%s", item->hasFlags(Item::FLG_FAST_ONLY) ? "true" : "false");
ImGui::TableNextColumn();
ImGui::Text("Gump Open"); // "GumpUp"
ImGui::TableNextColumn();
ImGui::Text("%s", item->hasFlags(Item::FLG_GUMP_OPEN) ? "true" : "false");
ImGui::TableNextColumn();
ImGui::Text("Equipped");
ImGui::TableNextColumn();
ImGui::Text("%s", item->hasFlags(Item::FLG_EQUIPPED) ? "true" : "false");
ImGui::TableNextColumn();
ImGui::Text("Bounce");
ImGui::TableNextColumn();
ImGui::Text("%s", item->hasFlags(Item::FLG_BOUNCING) ? "true" : "false");
ImGui::TableNextColumn();
ImGui::Text("Ethereal");
ImGui::TableNextColumn();
ImGui::Text("%s", item->hasFlags(Item::FLG_ETHEREAL) ? "true" : "false");
ImGui::TableNextColumn();
ImGui::Text("Hanging");
ImGui::TableNextColumn();
ImGui::Text("%s", item->hasFlags(Item::FLG_HANGING) ? "true" : "false");
ImGui::TableNextColumn();
ImGui::Text("In Fast Area");
ImGui::TableNextColumn();
ImGui::Text("%s", item->hasFlags(Item::FLG_FASTAREA) ? "true" : "false");
ImGui::TableNextColumn();
ImGui::Text("Low Friction");
ImGui::TableNextColumn();
ImGui::Text("%s", item->hasFlags(Item::FLG_LOW_FRICTION) ? "true" : "false");
ImGui::TableNextColumn();
ImGui::Text("Item Relative Gump"); // "IRGump" - 0x8000?
ImGui::TableNextColumn();
bool isRelative = false;
if (item->getGump()) {
Gump *g = getGump(item->getGump());
if (g && dynamic_cast<ItemRelativeGump *>(g)) {
isRelative = true;
}
}
ImGui::Text("%s", isRelative ? "true" : "false");
ImGui::EndTable();
}
}
} else {
ImGui::Text("Item not found: %d", _state->_targetItemId);
}
}
ImGui::EndChild();
if (ImGui::Button("Set Target")) {
// If not targetting start a new target gump and wait
if (!_state->_targetGumpId) {
TargetGump *targetGump = new TargetGump(0, 0);
targetGump->InitGump(0);
_state->_targetGumpId = targetGump->getObjId();
}
}
}
ImGui::End();
}
static void showPalette() {
if (!_state->_paletteWindow) {
return;
}
ImGui::SetNextWindowSize(ImVec2(320, 550), ImGuiCond_FirstUseEver);
if (ImGui::Begin("Palettes", &_state->_paletteWindow)) {
PaletteManager *pm = PaletteManager::get_instance();
Palette *p = pm->getPalette(PaletteManager::Pal_Game);
if (p) {
ImGui::SeparatorText("Game palette");
ImGui::PushID("palette_0");
ImGuiEx::Palette(*p);
ImGui::PopID();
ImGui::NewLine();
}
for (uint i = 1; i < pm->getNumPalettes(); i++) {
p = pm->getPalette(static_cast<PaletteManager::PalIndex>(i));
if (p) {
Common::String text = Common::String::format("Palette %d", i);
Common::String id = Common::String::format("palette_%d", i);
ImGui::SeparatorText(text.c_str());
ImGui::PushID(id.c_str());
ImGuiEx::Palette(*p);
ImGui::PopID();
ImGui::NewLine();
}
}
}
ImGui::End();
}
void onImGuiInit() {
_state = new ImGuiState();
}
void onImGuiRender() {
ImGuiIO& io = ImGui::GetIO();
if (!debugChannelSet(-1, kDebugImGui)) {
io.ConfigFlags |= ImGuiConfigFlags_NoMouseCursorChange | ImGuiConfigFlags_NoMouse;
return;
}
if (!_state)
return;
io.ConfigFlags &= ~(ImGuiConfigFlags_NoMouseCursorChange | ImGuiConfigFlags_NoMouse);
Ultima8Engine *engine = Ultima8Engine::get_instance();
if (ImGui::BeginMainMenuBar()) {
if (ImGui::BeginMenu("Toggles")) {
if (ImGui::MenuItem("Cheats", NULL, engine->areCheatsEnabled())) {
bool flag = engine->areCheatsEnabled();
engine->setCheatMode(!flag);
}
if (ImGui::MenuItem("Editor Items", NULL, engine->isShowEditorItems())) {
bool flag = engine->isShowEditorItems();
engine->setShowEditorItems(!flag);
}
if (ImGui::MenuItem("Footpads", NULL, GameMapGump::getShowFootpads())) {
bool flag = GameMapGump::getShowFootpads();
GameMapGump::setShowFootpads(!flag);
}
if (ImGui::BeginMenu("Gridlines")) {
int gridlines = GameMapGump::getGridlines();
if (ImGui::MenuItem("Auto", NULL, gridlines == -1)) {
GameMapGump::setGridlines(gridlines == -1 ? 0 : -1);
}
if (ImGui::MenuItem("128 x 128", NULL, gridlines == 128)) {
GameMapGump::setGridlines(gridlines == 128 ? 0 : 128);
}
if (ImGui::MenuItem("256 x 256", NULL, gridlines == 256)) {
GameMapGump::setGridlines(gridlines == 256 ? 0 : 256);
}
if (ImGui::MenuItem("512 x 512", NULL, gridlines == 512)) {
GameMapGump::setGridlines(gridlines == 512 ? 0 : 512);
}
if (ImGui::MenuItem("1024 x 1024", NULL, gridlines == 1024)) {
GameMapGump::setGridlines(gridlines == 1024 ? 0 : 1024);
}
ImGui::EndMenu();
}
if (ImGui::MenuItem("Hack Mover", NULL, engine->isHackMoverEnabled())) {
bool flag = engine->isHackMoverEnabled();
engine->setHackMoverEnabled(!flag);
}
if (ImGui::MenuItem("Quick Movement", NULL, QuickAvatarMoverProcess::isEnabled())) {
bool flag = QuickAvatarMoverProcess::isEnabled();
QuickAvatarMoverProcess::setEnabled(!flag);
}
ImGui::EndMenu();
}
if (ImGui::BeginMenu("View")) {
ImGui::MenuItem("Item Stats", NULL, &_state->_itemStatsWindow);
ImGui::MenuItem("Palette", NULL, &_state->_paletteWindow);
ImGui::EndMenu();
}
ImGui::EndMainMenuBar();
}
showItemStats();
showPalette();
}
void onImGuiCleanup() {
delete _state;
_state = nullptr;
}
} // End of namespace Ultima8
} // End of namespace Ultima

View File

@@ -0,0 +1,33 @@
/* 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 ULTIMA_ULTIMA8_DEBUGTOOLS_H
#define ULTIMA_ULTIMA8_DEBUGTOOLS_H
namespace Ultima {
namespace Ultima8 {
void onImGuiInit();
void onImGuiRender();
void onImGuiCleanup();
} // End of namespace Ultima8
} // End of namespace Ultima
#endif // ULTIMA_ULTIMA8_DEBUGTOOLS_H

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/>.
*
*/
#include "ultima/ultima8/misc/debugger.h"
#include "ultima/ultima8/filesys/archive.h"
#include "ultima/ultima8/filesys/flex_file.h"
#include "ultima/ultima8/filesys/u8_save_file.h"
namespace Ultima {
namespace Ultima8 {
Archive::Archive() {
_count = 0;
}
Archive::~Archive() {
for (unsigned int i = 0; i < _sources.size(); ++i)
delete _sources[i];
_sources.clear();
}
Archive::Archive(Common::SeekableReadStream *rs) : _count(0) {
addSource(rs);
}
bool Archive::addSource(FlexFile *af) {
_sources.push_back(af);
uint32 indexcount = af->getCount();
if (indexcount > _count)
_count = indexcount;
return true;
}
bool Archive::addSource(Common::SeekableReadStream *rs) {
if (!rs)
return false;
FlexFile *s = new FlexFile(rs);
if (!s->isValid()) {
delete s;
return false;
}
return addSource(s);
}
void Archive::cache() {
for (unsigned int i = 0; i < _count; ++i)
cache(i);
}
void Archive::uncache() {
for (unsigned int i = 0; i < _count; ++i)
uncache(i);
}
uint8 *Archive::getRawObject(uint32 index, uint32 *sizep) {
FlexFile *f = findArchiveFile(index);
if (!f)
return nullptr;
return f->getObject(index, sizep);
}
uint32 Archive::getRawSize(uint32 index) const {
FlexFile *f = findArchiveFile(index);
if (!f) return 0;
return f->getSize(index);
}
FlexFile *Archive::findArchiveFile(uint32 index) const {
unsigned int n = _sources.size();
for (unsigned int i = 1; i <= n; ++i) {
if (_sources[n - i]->exists(index))
return _sources[n - i];
}
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_FILESYS_ARCHIVE_H
#define ULTIMA8_FILESYS_ARCHIVE_H
#include "common/scummsys.h"
#include "ultima/shared/std/containers.h"
namespace Common {
class SeekableReadStream;
}
namespace Ultima {
namespace Ultima8 {
class FlexFile;
class Archive {
public:
//! create Archive without any input sources
Archive();
//! create Archive with a single input source, autodetecting the type
//! Will create FlexFile; ids will be deleted.
explicit Archive(Common::SeekableReadStream *rs);
virtual ~Archive();
//! add input source.
//! FlexFile will be deleted on destruction
//! Input sources are used in the reversed order they are added.
//! Effect of adding sources after having accessed objects is undef.
bool addSource(FlexFile *af);
//! add input source, autodetecting the type (as the constructor)
bool addSource(Common::SeekableReadStream *rs);
//! Cache all objects
void cache();
//! Cache a single object
virtual void cache(uint32 index) = 0;
//! Uncache all objects
//! Potentially dangerous: all stored objects will be deleted; make sure
//! they are no longer in use.
void uncache();
//! Uncache a single object
//! Potentially dangerous. See uncache()
virtual void uncache(uint32 index) = 0;
//! Check if an object is cached
virtual bool isCached(uint32 index) const = 0;
uint32 getCount() const {
return _count;
}
protected:
uint32 _count;
uint8 *getRawObject(uint32 index, uint32 *sizep = 0);
uint32 getRawSize(uint32 index) const;
private:
Std::vector<FlexFile *> _sources;
FlexFile *findArchiveFile(uint32 index) const;
};
} // End of namespace Ultima8
} // End of namespace Ultima
#endif

View File

@@ -0,0 +1,132 @@
/* 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/memstream.h"
#include "ultima/ultima8/misc/debugger.h"
#include "ultima/ultima8/filesys/flex_file.h"
namespace Ultima {
namespace Ultima8 {
static const int FLEX_TABLE_OFFSET = 0x80;
static const int FLEX_HDR_SIZE = 0x52;
static const char FLEX_HDR_PAD = 0x1A;
FlexFile::FlexFile(Common::SeekableReadStream *rs) : _rs(rs) {
_valid = isFlexFile(_rs);
if (_valid)
_valid = readMetadata();
}
FlexFile::~FlexFile() {
delete _rs;
}
//static
bool FlexFile::isFlexFile(Common::SeekableReadStream *rs) {
rs->seek(0);
int i;
char buf[FLEX_HDR_SIZE];
rs->read(buf, FLEX_HDR_SIZE);
for (i = 0; i < FLEX_HDR_SIZE; ++i) {
if (buf[i] == FLEX_HDR_PAD) break;
}
if (i < FLEX_HDR_SIZE) {
for (++i; i < FLEX_HDR_SIZE; ++i) {
if (buf[i] != FLEX_HDR_PAD) return false;
}
return true;
}
return false;
}
bool FlexFile::readMetadata() {
_rs->seek(FLEX_HDR_SIZE + 2);
uint32 count = _rs->readUint32LE();
if (count > 4095) {
// In practice the largest flex in either Crusader or U8 games has
// 3074 entries, so this seems invalid.
warning("Flex invalid: improbable number of entries %d", count);
return false;
}
if (_rs->size() < FLEX_TABLE_OFFSET + 8 * count) {
warning("Flex invalid: stream not long enough for offset table");
return false;
}
_entries.reserve(count);
_rs->seek(FLEX_TABLE_OFFSET);
for (unsigned int i = 0; i < count; ++i) {
FileEntry fe;
fe._offset = _rs->readUint32LE();
fe._size = _rs->readUint32LE();
_entries.push_back(fe);
}
return true;
}
Common::SeekableReadStream *FlexFile::getDataSource(uint32 index, bool is_text) {
uint32 size;
uint8 *buf = getObject(index, &size);
if (!buf)
return nullptr;
return new Common::MemoryReadStream(buf, size, DisposeAfterUse::YES);
}
uint8 *FlexFile::getObject(uint32 index, uint32 *sizep) {
if (index >= _entries.size())
return nullptr;
uint32 size = _entries[index]._size;
if (size == 0)
return nullptr;
uint8 *object = new uint8[size];
uint32 offset = _entries[index]._offset;
_rs->seek(offset);
_rs->read(object, size);
if (sizep)
*sizep = size;
return object;
}
uint32 FlexFile::getSize(uint32 index) const {
if (index >= _entries.size())
return 0;
return _entries[index]._size;
}
} // End of namespace Ultima8
} // End of namespace Ultima

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/>.
*
*/
#ifndef ULTIMA8_FILESYS_FLEXFILE_H
#define ULTIMA8_FILESYS_FLEXFILE_H
namespace Ultima {
namespace Ultima8 {
class FlexFile {
public:
//! create FlexFile from datasource; FlexFile takes ownership of ds
//! and deletes it when destructed
explicit FlexFile(Common::SeekableReadStream *rs);
~FlexFile();
//! Check if constructed object is indeed a valid archive
bool isValid() const {
return _valid;
}
//! Check if numbered object exists
//! \param index index of object to check for
bool exists(uint32 index) {
return getSize(index) > 0;
}
//! Get object as a Common::SeekableReadStream
//! Delete the SeekableReadStream afterwards; that will delete the data as well
Common::SeekableReadStream *getDataSource(uint32 index, bool is_text = false);
//! Get object from file; returns NULL if index is invalid.
//! Must delete the returned buffer afterwards.
//! See also exists(uint32 index)
//! \param index index of object to fetch
//! \param size if non-NULL, size of object is stored in *size
uint8 *getObject(uint32 index, uint32 *size = nullptr);
//! Get size of object; returns zero if index is invalid.
//! See also exists(uint32 index)
//! \param index index of object to get size of
uint32 getSize(uint32 index) const;
//! Get upper bound for number of objects.
//! In an indexed file this is (probably) the highest index plus one,
//! while in a named file it's (probably) the actual count
uint32 getCount() const {
return _entries.size();
}
static bool isFlexFile(Common::SeekableReadStream *rs);
protected:
Common::SeekableReadStream *_rs;
bool _valid;
struct FileEntry {
uint32 _offset;
uint32 _size;
FileEntry() : _offset(0), _size(0) {}
};
Common::Array<FileEntry> _entries;
private:
bool readMetadata();
};
} // 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 "common/memstream.h"
#include "ultima/ultima8/misc/debugger.h"
#include "ultima/ultima8/filesys/raw_archive.h"
namespace Ultima {
namespace Ultima8 {
RawArchive::~RawArchive() {
Archive::uncache();
}
void RawArchive::cache(uint32 index) {
if (index >= _count) return;
if (_objects.empty()) _objects.resize(_count);
if (_objects[index]) return;
_objects[index] = getRawObject(index);
}
void RawArchive::uncache(uint32 index) {
if (index >= _count) return;
if (_objects.empty()) return;
if (_objects[index]) {
delete[] _objects[index];
_objects[index] = nullptr;
}
}
bool RawArchive::isCached(uint32 index) const {
if (index >= _count) return false;
if (_objects.empty()) return false;
return (_objects[index] != nullptr);
}
const uint8 *RawArchive::get_object_nodel(uint32 index) {
if (index >= _count)
return nullptr;
cache(index);
return _objects[index];
}
uint8 *RawArchive::get_object(uint32 index) {
if (index >= _count)
return nullptr;
if (index < _objects.size() && _objects[index]) {
// already cached
uint32 size = getRawSize(index);
if (size == 0)
return nullptr;
uint8 *object = new uint8[size];
memcpy(object, _objects[index], size);
return object;
}
return getRawObject(index);
}
uint32 RawArchive::get_size(uint32 index) const {
if (index >= _count)
return 0;
return getRawSize(index);
}
Common::SeekableReadStream *RawArchive::get_datasource(uint32 index) {
if (index >= _count)
return nullptr;
cache(index);
if (!_objects[index])
return nullptr;
return new Common::MemoryReadStream(_objects[index], getRawSize(index));
}
} // End of namespace Ultima8
} // End of namespace Ultima

View File

@@ -0,0 +1,64 @@
/* 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_FILESYS_RAWARCHIVE_H
#define ULTIMA8_FILESYS_RAWARCHIVE_H
#include "ultima/ultima8/filesys/archive.h"
namespace Ultima {
namespace Ultima8 {
class ArchiveFile;
class IDataSource;
class RawArchive : public Archive {
public:
RawArchive() : Archive() { }
explicit RawArchive(Common::SeekableReadStream *rs) : Archive(rs) { }
~RawArchive() override;
void cache(uint32 index) override;
void uncache(uint32 index) override;
bool isCached(uint32 index) const override;
//! return object. DON'T delete or modify!
virtual const uint8 *get_object_nodel(uint32 index);
//! return object. delete afterwards. This will not cache the object
virtual uint8 *get_object(uint32 index);
//! get size of object
virtual uint32 get_size(uint32 index) const;
//! return object as SeekableReadStream. Delete the SeekableReadStream afterwards,
//! but DON'T delete/modify the buffer it points to.
virtual Common::SeekableReadStream *get_datasource(uint32 index);
protected:
Std::vector<uint8 *> _objects;
};
} // End of namespace Ultima8
} // End of namespace Ultima
#endif

View File

@@ -0,0 +1,227 @@
/* 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/filesys/savegame.h"
#include "common/bufferedstream.h"
#include "common/compression/unzip.h"
namespace Ultima {
namespace Ultima8 {
#define SAVEGAME_IDENT MKTAG('V', 'M', 'U', '8')
#define PKZIP_IDENT MKTAG('P', 'K', 3, 4)
#define SAVEGAME_VERSION 6
#define SAVEGAME_MIN_VERSION 2
class FileEntryArchive : public Common::Archive {
struct FileEntry {
uint _offset;
uint _size;
FileEntry() : _offset(0), _size(0) {}
};
private:
typedef Common::HashMap<Common::Path, FileEntry, Common::Path::IgnoreCase_Hash, Common::Path::IgnoreCase_EqualTo> IndexMap;
IndexMap _index;
Common::SeekableReadStream *_file;
public:
FileEntryArchive(Common::SeekableReadStream *rs);
~FileEntryArchive() override;
// Common::Archive API implementation
bool hasFile(const Common::Path &path) const override;
int listMembers(Common::ArchiveMemberList &list) const override;
const Common::ArchiveMemberPtr getMember(const Common::Path &path) const override;
Common::SeekableReadStream *createReadStreamForMember(const Common::Path &path) const override;
};
FileEntryArchive::FileEntryArchive(Common::SeekableReadStream *rs) : _file(rs) {
// Load the index
uint count = _file->readUint16LE();
for (uint idx = 0; idx < count; ++idx) {
char name[12];
_file->read(name, 12);
name[11] = '\0';
FileEntry fe;
fe._size = _file->readUint32LE();
fe._offset = _file->pos();
_index[Common::Path(name, Common::Path::kNoSeparator)] = fe;
_file->skip(fe._size);
}
}
FileEntryArchive::~FileEntryArchive() {
}
bool FileEntryArchive::hasFile(const Common::Path &path) const {
return _index.contains(path);
}
int FileEntryArchive::listMembers(Common::ArchiveMemberList &list) const {
list.clear();
for (const auto &member : _index)
list.push_back(Common::ArchiveMemberPtr(new Common::GenericArchiveMember(member._key, *this)));
return list.size();
}
const Common::ArchiveMemberPtr FileEntryArchive::getMember(const Common::Path &path) const {
if (!hasFile(path))
return nullptr;
return Common::ArchiveMemberPtr(new Common::GenericArchiveMember(path, *this));
}
Common::SeekableReadStream *FileEntryArchive::createReadStreamForMember(const Common::Path &path) const {
assert(_index.contains(path));
const FileEntry &fe = _index[path];
uint8 *data = (uint8 *)malloc(fe._size);
_file->seek(fe._offset);
_file->read(data, fe._size);
return new Common::MemoryReadStream(data, fe._size, DisposeAfterUse::YES);
}
SavegameReader::SavegameReader(Common::SeekableReadStream *rs, bool metadataOnly) : _archive(nullptr), _version(0) {
// Validate the identifier for a valid savegame
uint32 ident = rs->readUint32LE();
if (ident == SAVEGAME_IDENT) {
_version = rs->readUint32LE();
if (!MetaEngine::readSavegameHeader(rs, &_header))
return;
if (metadataOnly)
return;
_archive = new FileEntryArchive(rs);
} else if (SWAP_BYTES_32(ident) == PKZIP_IDENT) {
// Note: Pentagram save description is the zip global comment
_header.description = "Pentagram Save";
// Hack to pull the comment if length < 255
char data[256];
uint16 size = sizeof(data);
rs->seek(-size, SEEK_END);
rs->read(data, size);
for (uint16 i = size; i >= 2; i--) {
uint16 length = size - i;
if (data[i - 2] == length && data[i - 1] == 0) {
if (length > 0)
_header.description = Common::String(data + i, length);
break;
}
}
Common::SeekableReadStream *stream = wrapBufferedSeekableReadStream(rs, 4096, DisposeAfterUse::NO);
_archive = Common::makeZipArchive(stream);
if (!_archive)
return;
Common::ArchiveMemberPtr member = _archive->getMember("VERSION");
if (member) {
_version = member->createReadStream()->readUint32LE();
_header.version = _version;
}
if (metadataOnly) {
delete _archive;
_archive = nullptr;
return;
}
}
}
SavegameReader::~SavegameReader() {
if (_archive)
delete _archive;
}
SavegameReader::State SavegameReader::isValid() const {
if (_version == 0)
return SAVE_CORRUPT;
else if (_version < SAVEGAME_MIN_VERSION)
return SAVE_OUT_OF_DATE;
else if (_version > SAVEGAME_VERSION)
return SAVE_TOO_RECENT;
return SAVE_VALID;
}
Common::SeekableReadStream *SavegameReader::getDataSource(const Common::Path &name) {
assert(_archive);
return _archive->createReadStreamForMember(name);
}
SavegameWriter::SavegameWriter(Common::WriteStream *ws) : _file(ws) {
assert(_file);
}
SavegameWriter::~SavegameWriter() {
}
bool SavegameWriter::finish() {
// Write ident and savegame version
_file->writeUint32LE(SAVEGAME_IDENT);
_file->writeUint32LE(SAVEGAME_VERSION);
// Iterate through writing out the files
_file->writeUint16LE(_index.size());
for (uint idx = 0; idx < _index.size(); ++idx) {
// Set up a 12 byte space containing the resource name
FileEntry &fe = _index[idx];
char name[12];
Common::fill(&name[0], &name[12], '\0');
strncpy(name, fe._name.c_str(), 11);
// Write out name, size, and data
_file->write(name, 12);
_file->writeUint32LE(fe.size());
_file->write(&fe[0], fe.size());
}
return true;
}
bool SavegameWriter::writeFile(const Std::string &name, const uint8 *data, uint32 size) {
assert(name.size() <= 11);
_index.push_back(FileEntry());
FileEntry &fe = _index.back();
fe._name = name;
fe.resize(size);
Common::copy(data, data + size, &fe[0]);
return true;
}
bool SavegameWriter::writeFile(const Std::string &name, Common::MemoryWriteStreamDynamic *buf) {
return writeFile(name, buf->getData(), buf->pos());
}
} // End of namespace Ultima8
} // End of namespace Ultima

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/>.
*
*/
#ifndef ULTIMA8_FILESYS_SAVEGAME_H
#define ULTIMA8_FILESYS_SAVEGAME_H
#include "ultima/shared/std/string.h"
#include "common/hashmap.h"
#include "common/stream.h"
#include "common/memstream.h"
#include "engines/metaengine.h"
namespace Ultima {
namespace Ultima8 {
class ZipFile;
class IDataSource;
class SavegameReader {
private:
ExtendedSavegameHeader _header;
Common::Archive *_archive;
uint32 _version;
public:
explicit SavegameReader(Common::SeekableReadStream *rs, bool metadataOnly = false);
~SavegameReader();
enum State { SAVE_CORRUPT, SAVE_VALID, SAVE_OUT_OF_DATE, SAVE_TOO_RECENT };
State isValid() const;
uint32 getVersion() const { return _version; }
Std::string getDescription() const { return _header.description; }
/**
* Get an entry/section within the save
*/
Common::SeekableReadStream *getDataSource(const Common::Path &name);
};
class SavegameWriter {
class FileEntry : public Common::Array<byte> {
public:
Std::string _name;
FileEntry() : Common::Array<byte>() {}
};
private:
Common::WriteStream *_file;
Common::Array<FileEntry> _index;
public:
explicit SavegameWriter(Common::WriteStream *ws);
virtual ~SavegameWriter();
//! write a file to the savegame
//! \param name name of the file
//! \param data the data
//! \param size (in bytes) of data
bool writeFile(const Std::string &name, const uint8 *data, uint32 size);
//! write a file to the savegame from a memory stream
//! \param name name of the file
//! \param buf the MemoryWriteStreamDynamic to save
bool writeFile(const Std::string &name, Common::MemoryWriteStreamDynamic *buf);
//! finish savegame
bool finish();
};
} // End of namespace Ultima8
} // End of namespace Ultima
#endif

View File

@@ -0,0 +1,106 @@
/* 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/memstream.h"
#include "ultima/ultima8/misc/debugger.h"
#include "ultima/ultima8/filesys/u8_save_file.h"
namespace Ultima {
namespace Ultima8 {
U8SaveFile::U8SaveFile(Common::SeekableReadStream *rs) : _rs(rs) {
_valid = isU8SaveFile(_rs);
if (_valid)
_valid = readMetadata();
}
U8SaveFile::~U8SaveFile() {
delete _rs;
}
//static
bool U8SaveFile::isU8SaveFile(Common::SeekableReadStream *rs) {
rs->seek(0);
char buf[24];
rs->read(buf, 23);
buf[23] = '\0';
return (strncmp(buf, "Ultima 8 SaveGame File.", 23) == 0);
}
bool U8SaveFile::readMetadata() {
_rs->seek(0x18);
uint16 count = _rs->readUint16LE();
for (unsigned int i = 0; i < count; ++i) {
uint32 namelen = _rs->readUint32LE();
char *name = new char[namelen];
_rs->read(name, namelen);
FileEntry fe;
fe._size = _rs->readUint32LE();
fe._offset = _rs->pos();
_map[Common::String(name)] = fe;
delete[] name;
_rs->skip(fe._size); // skip data
}
return true;
}
bool U8SaveFile::hasFile(const Common::Path &path) const {
return _map.contains(path.toString());
}
int U8SaveFile::listMembers(Common::ArchiveMemberList& list) const {
list.clear();
for (const auto &member : _map) {
list.push_back(Common::ArchiveMemberPtr(new Common::GenericArchiveMember(member._key, *this)));
}
return list.size();
}
const Common::ArchiveMemberPtr U8SaveFile::getMember(const Common::Path& path) const {
if (!hasFile(path))
return nullptr;
Common::String name = path.toString();
return Common::ArchiveMemberPtr(new Common::GenericArchiveMember(name, *this));
}
Common::SeekableReadStream* U8SaveFile::createReadStreamForMember(const Common::Path& path) const {
if (!hasFile(path))
return nullptr;
const FileEntry &fe = _map[path.toString()];
uint8 *data = (uint8 *)malloc(fe._size);
_rs->seek(fe._offset);
_rs->read(data, fe._size);
return new Common::MemoryReadStream(data, fe._size, DisposeAfterUse::YES);
}
} // End of namespace Ultima8
} // End of namespace Ultima

View File

@@ -0,0 +1,73 @@
/* 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_FILESYS_U8SAVEFILE_H
#define ULTIMA8_FILESYS_U8SAVEFILE_H
#include "common/archive.h"
#include "ultima/shared/std/containers.h"
#include "ultima/shared/std/string.h"
namespace Ultima {
namespace Ultima8 {
class U8SaveFile : public Common::Archive {
public:
//! create U8SaveFile from datasource; U8SaveFile takes ownership of ds
//! and deletes it when destructed
explicit U8SaveFile(Common::SeekableReadStream *rs);
~U8SaveFile() override;
//! Check if constructed object is indeed a valid archive
bool isValid() const {
return _valid;
}
// Common::Archive API implementation
bool hasFile(const Common::Path &path) const override;
int listMembers(Common::ArchiveMemberList &list) const override;
const Common::ArchiveMemberPtr getMember(const Common::Path &path) const override;
Common::SeekableReadStream *createReadStreamForMember(const Common::Path &path) const override;
static bool isU8SaveFile(Common::SeekableReadStream *rs);
protected:
Common::SeekableReadStream *_rs;
bool _valid;
struct FileEntry {
uint32 _offset;
uint32 _size;
FileEntry() : _offset(0), _size(0) {}
};
typedef Common::HashMap<Common::String, FileEntry, Common::IgnoreCase_Hash, Common::IgnoreCase_EqualTo> U8SaveFileMap;
U8SaveFileMap _map;
private:
bool readMetadata();
};
} // End of namespace Ultima8
} // End of namespace Ultima
#endif

View File

@@ -0,0 +1,245 @@
/* 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 "common/translation.h"
#include "ultima/ultima8/misc/common_types.h"
#include "ultima/ultima8/games/cru_game.h"
#include "ultima/ultima8/games/start_crusader_process.h"
#include "ultima/ultima8/gfx/palette_manager.h"
#include "ultima/ultima8/gumps/movie_gump.h"
#include "ultima/ultima8/gumps/gump_notify_process.h"
#include "ultima/ultima8/gumps/main_menu_process.h"
#include "ultima/ultima8/gumps/cru_credits_gump.h"
#include "ultima/ultima8/gumps/cru_demo_gump.h"
#include "ultima/ultima8/kernel/object_manager.h"
#include "ultima/ultima8/kernel/kernel.h"
#include "ultima/ultima8/world/world.h"
#include "ultima/ultima8/gfx/xform_blend.h"
#include "ultima/ultima8/games/game_data.h"
#include "ultima/ultima8/ultima8.h"
#include "ultima/ultima8/world/item_factory.h"
#include "ultima/ultima8/world/actors/main_actor.h"
#include "ultima/ultima8/world/actors/npc_dat.h"
#include "common/memstream.h"
#include "gui/message.h"
namespace Ultima {
namespace Ultima8 {
CruGame::CruGame() : Game(), _skipIntroMovie(false) {
}
CruGame::~CruGame() {
}
static bool loadPalette(const char *path, PaletteManager::PalIndex index) {
Common::File pf;
if (!pf.open(path)) {
warning("Unable to load %s", path);
return false;
}
Common::MemoryReadStream xfds(CruXFormPal, 1024);
PaletteManager::get_instance()->load(index, pf, xfds);
return true;
}
bool CruGame::loadFiles() {
// Load palette
debug(1, "Load Palettes");
if (!loadPalette("static/gamepal.pal", PaletteManager::Pal_Game))
return false;
// This one is not always present and only needed for the credits,
// let it fail if needed.
loadPalette("static/cred.pal", PaletteManager::Pal_Cred);
if (!loadPalette("static/diff.pal", PaletteManager::Pal_Diff))
return false;
if (!loadPalette("static/misc.pal", PaletteManager::Pal_Misc))
return false;
if (!loadPalette("static/misc2.pal", PaletteManager::Pal_Misc2))
return false;
// We don't use his one at the moment, ok to fail.
loadPalette("static/star.pal", PaletteManager::Pal_Star);
debug(1, "Load GameData");
GameData::get_instance()->loadRemorseData();
return true;
}
bool CruGame::startGame() {
// NOTE: assumes the entire engine has been reset!
debug(1, "Starting new Crusader: No Remorse game.");
ObjectManager *objman = ObjectManager::get_instance();
// reserve a number of objids just in case we'll need them sometime
for (uint16 i = 384; i < 512; ++i)
objman->reserveObjId(i);
Actor *actor = ItemFactory::createActor(1, 0, 0, Item::FLG_IN_NPC_LIST,
1, 1, Item::EXT_PERMANENT_NPC, false);
if (!actor)
error("Couldn't create MainActor");
const NPCDat *npcData = GameData::get_instance()->getNPCDataForShape(1);
actor->setStr(75);
actor->setHP(npcData->getMaxHp());
actor->setInt(5000); // max mana (energy) is 2x intelligence, or 10000.
actor->setMana(2500);
ObjectManager::get_instance()->assignActorObjId(actor, 1);
actor->setLocation(0, 0, 0); // Map 1 (mission 1)
// Some useful points to warp into for testing No Remorse
//actor->setLocation(60716, 59400, 16); // Map 1 (mission 1)
//actor->setLocation(42493, 26621, 16); // Map 2 (mission 1 / level 4)
//actor->setLocation(34302, 32254, 16); // Map 3 (mission 2)
//actor->setLocation(34813, 33789, 16); // Map 4
//actor->setLocation(37373, 30205, 16); // Map 5
//actor->setLocation(37373, 30205, 16); // Map 6
//actor->setLocation(35070, 26142, 96); // Map 7
//actor->setLocation(29693, 32253, 0); // Map 8 - unfinished area?
//actor->setLocation(2046, 2046, 0); // Map 9
//actor->setLocation(14845, 6141, 0); // Map 22 - debugging map
//actor->setLocation(34302, 32254, 16); // Map 40 (Rebel base)
World::get_instance()->switchMap(0);
return true;
}
bool CruGame::startInitialUsecode(int saveSlot) {
if (saveSlot >= 0 && ConfMan.getBool("skip_intro"))
_skipIntroMovie = true;
Process* proc = new StartCrusaderProcess(saveSlot);
Kernel::get_instance()->addProcess(proc);
return true;
}
static ProcId playMovie(const char *movieID, bool fade, bool noScale) {
MovieGump *gump = MovieGump::CruMovieViewer(movieID, 640, 480, nullptr, nullptr, 0);
if (!gump) {
debug(1, "RemorseGame::playIntro: movie %s not found.", movieID);
return 0;
}
gump->CreateNotifier();
return gump->GetNotifyProcess()->getPid();
}
ProcId CruGame::playIntroMovie(bool fade) {
if (_skipIntroMovie)
return 0;
const char *name = (GAME_IS_REMORSE ? "T01" : "origin");
ProcId pid = playMovie(name, fade, true);
if (!pid) {
GUI::MessageDialogWithURL dialog(_("Crusader intro movie file missing - check that the FLICS and SOUND directories have been copied from the CD. More instructions are on the wiki: https://wiki.scummvm.org/index.php?title=Crusader:_No_Remorse."), "https://wiki.scummvm.org/index.php?title=Crusader:_No_Remorse");
dialog.runModal();
}
return pid;
}
ProcId CruGame::playIntroMovie2(bool fade) {
if (_skipIntroMovie)
return 0;
const char *name = (GAME_IS_REMORSE ? "T02" : "ANIM01");
return playMovie(name, fade, false);
}
ProcId CruGame::playEndgameMovie(bool fade) {
return playMovie("O01", fade, false);
}
void CruGame::playDemoScreen() {
Process *menuproc = new MainMenuProcess();
Kernel::get_instance()->addProcess(menuproc);
const char *bmp_filename = "static/buyme.dat";
auto *bmprs = new Common::File();
if (!bmprs->open(bmp_filename)) {
warning("RemorseGame::playDemoScreen: error opening demo background: %s", bmp_filename);
delete bmprs;
return;
}
Gump *gump = new CruDemoGump(bmprs);
gump->InitGump(0);
gump->CreateNotifier();
Process *notifyproc = gump->GetNotifyProcess();
if (notifyproc) {
menuproc->waitFor(notifyproc);
}
}
ProcId CruGame::playCreditsNoMenu() {
const char *txt_filename = "static/credits.dat";
const char *bmp_filename = "static/cred.dat";
auto *txtrs = new Common::File();
auto *bmprs = new Common::File();
if (!txtrs->open(txt_filename)) {
warning("RemorseGame::playCredits: error opening credits text: %s", txt_filename);
delete txtrs;
delete bmprs;
return 0;
}
if (!bmprs->open(bmp_filename)) {
warning("RemorseGame::playCredits: error opening credits background: %s", bmp_filename);
delete txtrs;
delete bmprs;
return 0;
}
Gump *creditsgump = new CruCreditsGump(txtrs, bmprs);
creditsgump->InitGump(nullptr);
creditsgump->CreateNotifier();
Process *notifyproc = creditsgump->GetNotifyProcess();
return notifyproc->getPid();
}
void CruGame::playCredits() {
Process *menuproc = new MainMenuProcess();
Kernel::get_instance()->addProcess(menuproc);
ProcId creditsnotify = playCreditsNoMenu();
if (creditsnotify) {
menuproc->waitFor(creditsnotify);
}
}
void CruGame::writeSaveInfo(Common::WriteStream *ws) {
}
} // End of namespace Ultima8
} // End of namespace Ultima

View File

@@ -0,0 +1,69 @@
/* 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_GAMES_CRUGAME_H
#define ULTIMA8_GAMES_CRUGAME_H
#include "ultima/ultima8/games/game.h"
namespace Ultima {
namespace Ultima8 {
class CruGame : public Game {
public:
CruGame();
~CruGame() override;
//! load/init game's data files
bool loadFiles() override;
//! initialize new game
bool startGame() override;
//! start initial usecode
bool startInitialUsecode(int saveSlot = -1) override;
//! write game-specific savegame info (avatar stats, equipment, ...)
void writeSaveInfo(Common::WriteStream *ws) override;
ProcId playIntroMovie(bool fade) override;
ProcId playIntroMovie2(bool fade);
ProcId playEndgameMovie(bool fade) override;
void playCredits() override;
void playQuotes() override { }; // no quotes for Crusader
void playDemoScreen() override;
/** Play credits but without showing a menu at the end - just finish. */
ProcId playCreditsNoMenu();
void setSkipIntroMovie() {
_skipIntroMovie = true;
}
private:
/** Whether this game should skip the intro movie on startup */
bool _skipIntroMovie;
};
} // End of namespace Ultima8
} // End of namespace Ultima
#endif

View File

@@ -0,0 +1,92 @@
/* 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 "ultima/ultima8/misc/debugger.h"
#include "ultima/ultima8/games/u8_game.h"
#include "ultima/ultima8/games/cru_game.h"
#include "ultima/ultima8/gfx/palette_manager.h"
#include "ultima/ultima8/kernel/kernel.h"
#include "ultima/ultima8/gumps/main_menu_process.h"
namespace Ultima {
namespace Ultima8 {
Game *Game::_game = nullptr;
Game::Game() {
_game = this;
}
Game::~Game() {
assert(_game == this);
_game = nullptr;
}
// static
Game *Game::createGame(const GameInfo *info) {
switch (info->_type) {
case GameInfo::GAME_U8:
return new U8Game();
case GameInfo::GAME_REMORSE:
case GameInfo::GAME_REGRET:
return new CruGame();
default:
error("createGame: invalid game tyoe");
}
return nullptr;
}
uint32 Game::I_playEndgame(const uint8 *args, unsigned int /*argsize*/) {
ConfMan.setBool("endgame", true);
ConfMan.setBool("quotes", true);
ConfMan.flushToDisk();
PaletteManager *palman = PaletteManager::get_instance();
palman->untransformPalette(PaletteManager::Pal_Game);
Process *menuproc = new MainMenuProcess();
Kernel::get_instance()->addProcess(menuproc);
ProcId moviepid = Game::get_instance()->playEndgameMovie(false);
Process *movieproc = Kernel::get_instance()->getProcess(moviepid);
if (movieproc) {
menuproc->waitFor(movieproc);
}
return 0;
}
uint32 Game::I_playCredits(const uint8 */*args*/, unsigned int /*argsize*/) {
Game::get_instance()->playCredits();
return 0;
}
uint32 Game::I_playDemoScreen(const uint8 */*args*/, unsigned int /*argsize*/) {
Game::get_instance()->playDemoScreen();
return 0;
}
} // End of namespace Ultima8
} // End of namespace Ultima

View File

@@ -0,0 +1,71 @@
/* 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_GAMES_GAME_H
#define ULTIMA8_GAMES_GAME_H
#include "ultima/ultima8/games/game_info.h"
#include "ultima/ultima8/usecode/intrinsics.h"
namespace Ultima {
namespace Ultima8 {
class Game {
public:
Game();
virtual ~Game();
static Game *get_instance() {
return _game;
}
//! load/init game's data files
virtual bool loadFiles() = 0;
//! initialize new game
virtual bool startGame() = 0;
//! start initial usecode
virtual bool startInitialUsecode(int saveSlot = -1) = 0;
//! write game-specific savegame info (avatar stats, equipment, ...)
virtual void writeSaveInfo(Common::WriteStream *ws) = 0;
virtual ProcId playIntroMovie(bool fade) = 0;
virtual ProcId playEndgameMovie(bool fade) = 0;
virtual void playCredits() = 0;
virtual void playQuotes() = 0;
virtual void playDemoScreen() = 0;
static Game *createGame(const GameInfo *info);
INTRINSIC(I_playEndgame);
INTRINSIC(I_playCredits);
INTRINSIC(I_playDemoScreen);
protected:
static Game *_game;
};
} // End of namespace Ultima8
} // End of namespace Ultima
#endif

View File

@@ -0,0 +1,680 @@
/* 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/ultima8/misc/common_types.h"
#include "ultima/ultima8/misc/util.h"
#include "ultima/ultima8/games/game_data.h"
#include "ultima/ultima8/usecode/usecode_flex.h"
#include "ultima/ultima8/gfx/main_shape_archive.h"
#include "ultima/ultima8/gfx/fonts/font_shape_archive.h"
#include "ultima/ultima8/gfx/gump_shape_archive.h"
#include "ultima/ultima8/world/map_glob.h"
#include "ultima/ultima8/world/fire_type_table.h"
#include "ultima/ultima8/world/actors/npc_dat.h"
#include "ultima/ultima8/world/actors/combat_dat.h"
#include "ultima/ultima8/gfx/palette_manager.h"
#include "ultima/ultima8/gfx/shape.h"
#include "ultima/ultima8/gfx/wpn_ovlay_dat.h"
#include "ultima/ultima8/gfx/fonts/font_manager.h"
#include "ultima/ultima8/games/game_info.h"
#include "ultima/ultima8/gumps/weasel_dat.h"
#include "ultima/ultima8/conf/config_file_manager.h"
#include "ultima/ultima8/convert/crusader/convert_shape_crusader.h"
#include "ultima/ultima8/audio/music_flex.h"
#include "ultima/ultima8/audio/speech_flex.h"
namespace Ultima {
namespace Ultima8 {
GameData *GameData::_gameData = nullptr;
GameData::GameData(GameInfo *gameInfo)
: _fixed(nullptr), _mainShapes(nullptr), _mainUsecode(nullptr), _globs(),
_fonts(nullptr), _gumps(nullptr), _mouse(nullptr), _music(nullptr),
_weaponOverlay(nullptr), _soundFlex(nullptr), _gameInfo(gameInfo) {
debug(1, "Creating GameData...");
_gameData = this;
_speech.resize(1024);
}
GameData::~GameData() {
debug(1, "Destroying GameData...");
delete _fixed;
_fixed = nullptr;
delete _mainShapes;
_mainShapes = nullptr;
delete _mainUsecode;
_mainUsecode = nullptr;
for (unsigned int i = 0; i < _globs.size(); ++i)
delete _globs[i];
_globs.clear();
delete _fonts;
_fonts = nullptr;
delete _gumps;
_gumps = nullptr;
delete _mouse;
_mouse = nullptr;
delete _music;
_music = nullptr;
delete _weaponOverlay;
_weaponOverlay = nullptr;
delete _soundFlex;
_soundFlex = nullptr;
for (unsigned int i = 0; i < _npcTable.size(); ++i)
delete _npcTable[i];
_npcTable.clear();
_gameData = nullptr;
for (unsigned int i = 0; i < _speech.size(); ++i) {
SpeechFlex **s = _speech[i];
if (s) delete *s;
delete s;
}
_speech.clear();
}
MapGlob *GameData::getGlob(uint32 glob) const {
if (glob < _globs.size())
return _globs[glob];
else
return nullptr;
}
ShapeArchive *GameData::getShapeFlex(uint16 flexId) const {
switch (flexId) {
case MAINSHAPES:
return _mainShapes;
case GUMPS:
return _gumps;
default:
break;
};
return nullptr;
}
Shape *GameData::getShape(FrameID f) const {
ShapeArchive *sf = getShapeFlex(f._flexId);
if (!sf)
return nullptr;
Shape *shape = sf->getShape(f._shapeNum);
return shape;
}
const ShapeFrame *GameData::getFrame(FrameID f) const {
const Shape *shape = getShape(f);
if (!shape)
return nullptr;
const ShapeFrame *frame = shape->getFrame(f._frameNum);
return frame;
}
void GameData::loadTranslation() {
ConfigFileManager *config = ConfigFileManager::get_instance();
Common::Path translationfile;
if (_gameInfo->_type == GameInfo::GAME_U8) {
switch (_gameInfo->_language) {
case GameInfo::GAMELANG_ENGLISH:
// using "translation" to enable bug fixes
translationfile = "u8english.ini";
break;
case GameInfo::GAMELANG_FRENCH:
translationfile = "u8french.ini";
break;
case GameInfo::GAMELANG_GERMAN:
translationfile = "u8german.ini";
break;
case GameInfo::GAMELANG_SPANISH:
translationfile = "u8spanish.ini";
break;
case GameInfo::GAMELANG_JAPANESE:
translationfile = "u8japanese.ini";
break;
default:
warning("Unknown language.");
break;
}
}
if (!translationfile.empty()) {
debug(1, "Loading translation: %s", translationfile.toString().c_str());
config->readConfigFile(translationfile, "language");
}
}
Std::string GameData::translate(const Std::string &text) {
// TODO: maybe cache these lookups? config calls may be expensive
ConfigFileManager *config = ConfigFileManager::get_instance();
Std::string trans;
if (config->get("language", "text", text, trans)) {
return trans;
}
return text;
}
FrameID GameData::translate(FrameID f) {
// TODO: maybe cache these lookups? config calls may be expensive
// TODO: add any non-gump shapes when applicable
// TODO: allow translations to be in another shapeflex
ConfigFileManager *config = ConfigFileManager::get_instance();
Std::string category = "language";
Std::string section;
switch (f._flexId) {
case GUMPS:
section = "gumps";
break;
default:
return f;
}
char buf[100];
Common::sprintf_s(buf, "%d,%d", f._shapeNum, f._frameNum);
Std::string key = buf;
Std::string trans;
if (!config->get(category, section, key, trans)) {
return f;
}
FrameID t;
t._flexId = f._flexId;
int n = sscanf(trans.c_str(), "%u,%u", &t._shapeNum, &t._frameNum);
if (n != 2) {
warning("Invalid shape translation: %s", trans.c_str());
return f;
}
return t;
}
void GameData::loadU8Data() {
auto *fd = new Common::File();
if (!fd->open("static/fixed.dat"))
error("Unable to load static/fixed.dat");
_fixed = new RawArchive(fd);
char langletter = _gameInfo->getLanguageUsecodeLetter();
if (!langletter)
error("Unknown language. Unable to open usecode");
Std::string filename = "usecode/";
filename += langletter;
filename += "usecode.flx";
auto *uds = new Common::File();
if (!uds->open(Common::Path(filename)))
error("Unable to load %s", filename.c_str());
_mainUsecode = new UsecodeFlex(uds);
// Load main shapes
debug(1, "Load Shapes");
auto *sf = new Common::File();
if (!(sf->open("static/u8shapes.flx") || sf->open("static/u8shapes.cmp")))
error("Unable to load static/u8shapes.flx or static/u8shapes.cmp");
_mainShapes = new MainShapeArchive(sf, MAINSHAPES,
PaletteManager::get_instance()->getPalette(PaletteManager::Pal_Game));
// Load weapon, armour info
ConfigFileManager *config = ConfigFileManager::get_instance();
config->readConfigFile("u8weapons.ini", "weapons");
config->readConfigFile("u8armour.ini", "armour");
config->readConfigFile("u8monsters.ini", "monsters");
config->readConfigFile("u8game.ini", "game");
// Load typeflags
auto *tfs = new Common::File();
if (!tfs->open("static/typeflag.dat"))
error("Unable to load static/typeflag.dat");
_mainShapes->loadTypeFlags(tfs);
delete tfs;
// Load animdat
auto *af = new Common::File();
if (!af->open("static/anim.dat"))
error("Unable to load static/anim.dat");
_mainShapes->loadAnimDat(af);
delete af;
// Load weapon overlay data
auto *wod = new Common::File();
if (!wod->open("static/wpnovlay.dat"))
error("Unable to load static/wpnovlay.dat");
RawArchive *overlayflex = new RawArchive(wod);
_weaponOverlay = new WpnOvlayDat();
_weaponOverlay->load(overlayflex);
delete overlayflex;
// Load _globs
auto *gds = new Common::File();
if (!gds->open("static/glob.flx"))
error("Unable to load static/glob.flx");
RawArchive *globflex = new RawArchive(gds);
_globs.clear();
_globs.resize(globflex->getCount());
for (unsigned int i = 0; i < globflex->getCount(); ++i) {
MapGlob *glob = 0;
Common::SeekableReadStream *globrs = globflex->get_datasource(i);
if (globrs && globrs->size()) {
glob = new MapGlob();
glob->read(globrs);
}
delete globrs;
_globs[i] = glob;
}
delete globflex;
// Load fonts
auto *fds = new Common::File();
if (!fds->open("static/u8fonts.flx"))
error("Unable to load static/u8fonts.flx");
_fonts = new FontShapeArchive(fds, OTHER,
PaletteManager::get_instance()->getPalette(PaletteManager::Pal_Game));
_fonts->setHVLeads();
// Load \mouse
auto *msds = new Common::File();
if (!msds->open("static/u8mouse.shp"))
error("Unable to load static/u8mouse.shp");
_mouse = new Shape(msds, 0);
_mouse->setPalette(PaletteManager::get_instance()->getPalette(PaletteManager::Pal_Game));
delete msds;
auto *gumpds = new Common::File();
if (!gumpds->open("static/u8gumps.flx"))
error("Unable to load static/u8gumps.flx");
_gumps = new GumpShapeArchive(gumpds, GUMPS,
PaletteManager::get_instance()->getPalette(PaletteManager::Pal_Game));
auto *gumpageds = new Common::File();
if (!gumpageds->open("static/gumpage.dat"))
error("Unable to load static/gumpage.dat");
_gumps->loadGumpage(gumpageds);
delete gumpageds;
auto *mf = new Common::File();
if (!mf->open("sound/music.flx"))
error("Unable to load sound/music.flx");
_music = new MusicFlex(mf);
auto *sndflx = new Common::File();
if (!sndflx->open("sound/sound.flx"))
error("Unable to load sound/sound.flx");
_soundFlex = new SoundFlex(sndflx);
loadTranslation();
}
void GameData::setupFontOverrides() {
setupTTFOverrides("game", false);
if (_gameInfo->_language == GameInfo::GAMELANG_JAPANESE)
setupJPOverrides();
}
void GameData::setupJPOverrides() {
ConfigFileManager *config = ConfigFileManager::get_instance();
FontManager *fontmanager = FontManager::get_instance();
KeyMap jpkeyvals;
jpkeyvals = config->listKeyValues("language", "jpfonts");
for (const auto &i : jpkeyvals) {
int fontnum = atoi(i._key.c_str());
const Std::string &fontdesc = i._value;
Std::vector<Std::string> vals;
SplitString(fontdesc, ',', vals);
if (vals.size() != 2) {
warning("Invalid jpfont override: %s", fontdesc.c_str());
continue;
}
unsigned int jpfontnum = atoi(vals[0].c_str());
uint32 col32 = strtol(vals[1].c_str(), 0, 0);
if (!fontmanager->addJPOverride(fontnum, jpfontnum, col32)) {
warning("failed to setup jpfont override for font %d", fontnum);
}
}
setupTTFOverrides("language", true);
}
void GameData::setupTTFOverrides(const char *category, bool SJIS) {
ConfigFileManager *config = ConfigFileManager::get_instance();
FontManager *fontmanager = FontManager::get_instance();
KeyMap ttfkeyvals;
bool overridefonts = ConfMan.getBool("font_override");
if (!overridefonts) return;
ttfkeyvals = config->listKeyValues(category, "fontoverride");
for (const auto &i : ttfkeyvals) {
int fontnum = atoi(i._key.c_str());
const Std::string &fontdesc = i._value;
Std::vector<Std::string> vals;
SplitString(fontdesc, ',', vals);
if (vals.size() != 4) {
warning("Invalid ttf override: %s", fontdesc.c_str());
continue;
}
const Common::Path filename(vals[0]);
int pointsize = atoi(vals[1].c_str());
uint32 col32 = strtol(vals[2].c_str(), 0, 0);
int border = atoi(vals[3].c_str());
if (!fontmanager->addTTFOverride(fontnum, filename, pointsize,
col32, border, SJIS)) {
warning("failed to setup ttf override for font %d", fontnum);
}
}
}
SpeechFlex *GameData::getSpeechFlex(uint32 shapeNum) {
if (shapeNum >= _speech.size())
return nullptr;
SpeechFlex **s = _speech[shapeNum];
if (s)
return *s;
char langletter = _gameInfo->getLanguageFileLetter();
if (!langletter) {
warning("GameData::getSpeechFlex: Unknown language.");
return nullptr;
}
Common::Path path(Common::String::format("sound/%c%i.flx", langletter, shapeNum));
auto *sflx = new Common::File();
if (!sflx->open(path)) {
delete sflx;
return nullptr;
}
s = new SpeechFlex *;
*s = new SpeechFlex(sflx);
_speech[shapeNum] = s;
return *s;
}
const NPCDat *GameData::getNPCData(uint16 entry) const {
if (entry < _npcTable.size()) {
return _npcTable[entry];
}
return nullptr;
}
const NPCDat *GameData::getNPCDataForShape(uint16 shapeno) const {
for (const auto *npcdat : _npcTable) {
if (npcdat->getShapeNo() == shapeno)
return npcdat;
}
return nullptr;
}
const CombatDat *GameData::getCombatDat(uint16 entry) const {
if (entry < _combatData.size()) {
return _combatData[entry];
}
return nullptr;
}
const WeaselDat *GameData::getWeaselDat(uint16 entry) const {
if (entry < _weaselData.size()) {
return _weaselData[entry];
}
return nullptr;
}
const FireType *GameData::getFireType(uint16 type) const {
return FireTypeTable::get(type);
}
void GameData::loadRemorseData() {
auto *fd = new Common::File();
if (!fd->open("static/fixed.dat"))
error("Unable to load static/fixed.dat");
_fixed = new RawArchive(fd);
char langletter = _gameInfo->getLanguageUsecodeLetter();
if (!langletter)
error("Unknown language. Unable to open usecode");
Std::string filename = "usecode/";
filename += langletter;
filename += "usecode.flx";
auto *uds = new Common::File();
if (!uds->open(filename.c_str()))
error("Unable to load %s", filename.c_str());
_mainUsecode = new UsecodeFlex(uds);
// Load main shapes
debug(1, "Load Shapes");
auto *sf = new Common::File();
if (!sf->open("static/shapes.flx"))
error("Unable to load static/shapes.flx");
_mainShapes = new MainShapeArchive(sf, MAINSHAPES,
PaletteManager::get_instance()->getPalette(PaletteManager::Pal_Game),
&CrusaderShapeFormat);
ConfigFileManager *config = ConfigFileManager::get_instance();
// Load weapon, armour info
if (_gameInfo->_type == GameInfo::GAME_REMORSE)
config->readConfigFile("remorseweapons.ini", "weapons");
else
config->readConfigFile("regretweapons.ini", "weapons");
config->readConfigFile("remorsegame.ini", "game");
// Load typeflags
auto *tfs = new Common::File();
if (!tfs->open("static/typeflag.dat"))
error("Unable to load static/typeflag.dat");
_mainShapes->loadTypeFlags(tfs);
delete tfs;
// Load animdat
auto *af = new Common::File();
if (!af->open("static/anim.dat"))
error("Unable to load static/anim.dat");
_mainShapes->loadAnimDat(af);
delete af;
// Load weapon overlay data
auto *wod = new Common::File();
if (!wod->open("static/wpnovlay.dat"))
error("Unable to load static/wpnovlay.dat");
RawArchive *overlayflex = new RawArchive(wod);
_weaponOverlay = new WpnOvlayDat();
_weaponOverlay->load(overlayflex);
delete overlayflex;
// Load globs
auto *gds = new Common::File();
if (!gds->open("static/glob.flx"))
error("Unable to load static/glob.flx");
RawArchive *globflex = new RawArchive(gds);
_globs.clear();
_globs.resize(globflex->getCount());
for (unsigned int i = 0; i < globflex->getCount(); ++i) {
MapGlob *glob = 0;
Common::SeekableReadStream *globrs = globflex->get_datasource(i);
if (globrs && globrs->size()) {
glob = new MapGlob();
glob->read(globrs);
}
delete globrs;
_globs[i] = glob;
}
delete globflex;
// Load fonts
auto *fds = new Common::File();
if (!fds->open("static/fonts.flx"))
error("Unable to load static/fonts.flx");
_fonts = new FontShapeArchive(fds, OTHER,
PaletteManager::get_instance()->getPalette(PaletteManager::Pal_Game));
_fonts->setHVLeads();
// Load mouse
auto *msds = new Common::File();
if (!msds->open("static/mouse.shp"))
error("Unable to load static/mouse.shp");
_mouse = new Shape(msds, 0);
_mouse->setPalette(PaletteManager::get_instance()->getPalette(PaletteManager::Pal_Game));
delete msds;
auto *gumpds = new Common::File();
if (!gumpds->open("static/gumps.flx"))
error("Unable to load static/gumps.flx");
_gumps = new GumpShapeArchive(gumpds, GUMPS,
PaletteManager::get_instance()->getPalette(PaletteManager::Pal_Game));
auto *dtableds = new Common::File();
if (!dtableds->open("static/dtable.flx"))
error("Unable to load static/dtable.flx");
RawArchive *dtableflex = new RawArchive(dtableds);
_npcTable = NPCDat::load(dtableflex);
delete dtableflex;
auto *damageds = new Common::File();
if (!damageds->open("static/damage.flx"))
error("Unable to load static/damage.flx");
RawArchive *damageflex = new RawArchive(damageds);
if (damageflex->getCount() != 1)
error("static/damage.flx appears corrupted");
_mainShapes->loadDamageDat(damageflex->get_datasource(0));
delete damageflex;
auto *combatds = new Common::File();
if (!combatds->open("static/combat.dat"))
error("Unable to load static/combat.dat");
RawArchive *combatflex = new RawArchive(combatds);
_combatData.clear();
_combatData.resize(combatflex->getCount());
for (uint32 i = 0; i < combatflex->getCount(); i++) {
Common::SeekableReadStream *combatflexrs = combatflex->get_datasource(i);
if (combatflexrs && combatflexrs->size() > 20) {
_combatData[i] = new CombatDat(*combatflexrs);
}
delete combatflexrs;
}
delete combatflex;
auto *stuffds = new Common::File();
if (!stuffds->open("static/stuff.dat"))
error("Unable to load static/stuff.dat");
// Weasel shop data.
// 14 blocks of 323 bytes, references like W01 and I07
// (weapon and inventory)
while (!stuffds->eos()) {
WeaselDat *data = new WeaselDat(stuffds);
_weaselData.push_back(data);
}
delete stuffds;
auto *xformpalds = new Common::File();
if (!xformpalds->open("static/xformpal.dat"))
error("Unable to load static/xformpal.dat");
RawArchive *xformpalflex = new RawArchive(xformpalds);
// TODO: What's in this flex?
// Object 1: 32 bytes
// Object 2: 2304 bytes - presumably data for 3 palettes == 768 * 3
// almost no low numbers (so not raw palette data, would be missing black..)
delete xformpalflex;
// Note: No MusicFlex for Remorse, as the music is all in different AMF files.
// The remorse_music_process will load them.
auto *sndflx = new Common::File();
if (!sndflx->open("sound/sound.flx"))
error("Unable to load sound/sound.flx");
_soundFlex = new SoundFlex(sndflx);
loadTranslation();
}
} // End of namespace Ultima8
} // End of namespace Ultima

View File

@@ -0,0 +1,146 @@
/* 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_GAMES_GAMEDATA_H
#define ULTIMA8_GAMES_GAMEDATA_H
#include "ultima/shared/std/containers.h"
#include "ultima/shared/std/string.h"
#include "ultima/ultima8/gfx/frame_id.h"
namespace Ultima {
namespace Ultima8 {
class RawArchive;
class MainShapeArchive;
class FontShapeArchive;
class GumpShapeArchive;
class ShapeArchive;
class Usecode;
class MapGlob;
class Shape;
class MusicFlex;
class WpnOvlayDat;
class NPCDat;
class CombatDat;
class FireType;
class ShapeFrame;
class WeaselDat;
class SoundFlex;
class SpeechFlex;
struct GameInfo;
class GameData {
public:
GameData(GameInfo *gameinfo);
~GameData();
static GameData *get_instance() {
return _gameData;
}
void loadU8Data();
void loadRemorseData();
void setupFontOverrides();
Usecode *getMainUsecode() const {
return _mainUsecode;
}
MainShapeArchive *getMainShapes() const {
return _mainShapes;
}
RawArchive *getFixed() const {
return _fixed;
}
MapGlob *getGlob(uint32 glob) const;
FontShapeArchive *getFonts() const {
return _fonts;
}
GumpShapeArchive *getGumps() const {
return _gumps;
}
Shape *getMouse() const {
return _mouse;
}
MusicFlex *getMusic() const {
return _music;
}
WpnOvlayDat *getWeaponOverlay() const {
return _weaponOverlay;
}
SoundFlex *getSoundFlex() const {
return _soundFlex;
}
SpeechFlex *getSpeechFlex(uint32 shapenum);
ShapeArchive *getShapeFlex(uint16 flexId) const;
Shape *getShape(FrameID frameid) const;
const ShapeFrame *getFrame(FrameID frameid) const;
const NPCDat *getNPCData(uint16 entry) const;
const NPCDat *getNPCDataForShape(uint16 shapeno) const;
const CombatDat *getCombatDat(uint16 entry) const;
const FireType *getFireType(uint16 type) const;
const WeaselDat *getWeaselDat(uint16 level) const;
Std::string translate(const Std::string &text);
FrameID translate(FrameID frame);
enum ShapeFlexId {
OTHER = 0,
MAINSHAPES = 1,
GUMPS = 2
};
private:
void loadTranslation();
void setupTTFOverrides(const char *category, bool SJIS);
void setupJPOverrides();
RawArchive *_fixed;
MainShapeArchive *_mainShapes;
Usecode *_mainUsecode;
Std::vector<MapGlob *> _globs;
FontShapeArchive *_fonts;
GumpShapeArchive *_gumps;
Shape *_mouse;
MusicFlex *_music;
WpnOvlayDat *_weaponOverlay;
Std::vector<NPCDat *> _npcTable;
Std::vector<CombatDat *> _combatData;
Std::vector<WeaselDat *> _weaselData;
SoundFlex *_soundFlex;
Std::vector<SpeechFlex **> _speech;
GameInfo *_gameInfo;
static GameData *_gameData;
};
#define _TL_(x) (GameData::get_instance()->translate(x))
#define _TL_SHP_(x) (GameData::get_instance()->translate(x))
} // End of namespace Ultima8
} // End of namespace Ultima
#endif

View File

@@ -0,0 +1,225 @@
/* 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/games/game_info.h"
#include "ultima/ultima8/misc/util.h"
namespace Ultima {
namespace Ultima8 {
struct GameTypeDesc {
const char *shortname;
const char *longname;
};
struct GameLangDesc {
char letter;
char usecodeletter;
const char *name;
};
// Keep order the same as the GameType enum!
static const GameTypeDesc gametypes[] = {
{ "", "" },
{ "ultima8", "Ultima VIII: Pagan" },
{ "remorse", "Crusader: No Remorse" },
{ "regret", "Crusader: No Regret" },
{ "pentmenu", "Pentagram Menu" },
{ 0, 0 }
};
// Keep order the same as the GameLanguage enum!
static const GameLangDesc gamelangs[] = {
{ 0, 0, "unknown" },
{ 'e', 'e', "English" },
{ 'f', 'f', "French" },
{ 'g', 'g', "German" },
{ 'e', 'e', "Spanish" },
{ 'e', 'j', "Japanese" },
{ '\0', '\0', 0 }
};
GameInfo::GameInfo() : _type(GAME_UNKNOWN), version(0), _language(GAMELANG_UNKNOWN),
_ucOffVariant(GAME_UC_DEFAULT) {
for (int i = 0; i < 16; ++i)
_md5[i] = 0;
}
char GameInfo::getLanguageFileLetter() const {
switch (_type) {
case GAME_U8: {
unsigned int l = static_cast<unsigned int>(_language);
assert(l < (sizeof(gamelangs) / sizeof(gamelangs[0])) - 1);
return gamelangs[l].letter;
}
case GAME_REMORSE:
case GAME_REGRET:
return 'e';
default:
return 0;
}
}
char GameInfo::getLanguageUsecodeLetter() const {
switch (_type) {
case GAME_U8: {
unsigned int l = static_cast<unsigned int>(_language);
assert(l < (sizeof(gamelangs) / sizeof(gamelangs[0])) - 1);
return gamelangs[l].usecodeletter;
}
case GAME_REMORSE:
case GAME_REGRET:
return 'e';
default:
return 0;
}
}
Std::string GameInfo::getLanguage() const {
unsigned int l = static_cast<unsigned int>(_language);
assert(l < (sizeof(gamelangs) / sizeof(gamelangs[0])) - 1);
return gamelangs[l].name;
}
Std::string GameInfo::getGameTitle() const {
unsigned int t = static_cast<unsigned int>(_type);
assert(t < (sizeof(gametypes) / sizeof(gametypes[0])) - 1);
return gametypes[t].longname;
}
Std::string GameInfo::getPrintableVersion() const {
char buf[32];
Common::sprintf_s(buf, "%d.%02d", version / 100, version % 100);
return buf;
}
Std::string GameInfo::getPrintDetails() const {
Std::string ret;
Std::string title = getGameTitle();
if (title == "") title = "Unknown";
ret = title + ", ";
Std::string lang = getLanguage();
if (lang == "") lang = "Unknown";
ret += lang;
ret += ", version ";
ret += getPrintableVersion();
ret += ", md5 ";
ret += getPrintableMD5();
return ret;
}
Std::string GameInfo::getPrintableMD5() const {
Std::string ret;
char buf[33];
for (int i = 0; i < 16; ++i) {
Common::sprintf_s(buf + 2 * i, 3, "%02x", _md5[i]);
}
ret = buf;
return ret;
}
bool GameInfo::match(GameInfo &other, bool ignoreMD5) const {
if (_type != other._type) return false;
if (_language != other._language) return false;
if (ignoreMD5) return true;
// NOTE: Version and MD5 hash are not currently set
if (version != other.version) return false;
return (memcmp(_md5, other._md5, 16) == 0);
}
void GameInfo::save(Common::WriteStream *ws) {
unsigned int l = static_cast<unsigned int>(_language);
assert(l < (sizeof(gamelangs) / sizeof(gamelangs[0])) - 1);
unsigned int t = static_cast<unsigned int>(_type);
assert(t < (sizeof(gametypes) / sizeof(gametypes[0])) - 1);
Std::string game = gametypes[t].shortname;
Std::string lang = gamelangs[l].name;
char buf[16];
Common::sprintf_s(buf, "%d", version);
Std::string ver = buf;
Std::string md5Str = getPrintableMD5();
Std::string d = game + "," + lang + "," + ver + "," + md5Str + "\n";
ws->write(d.c_str(), d.size());
}
bool GameInfo::load(Common::SeekableReadStream *rs, uint32 ver) {
Std::string s;
Std::vector<Std::string> parts;
s = rs->readLine();
SplitString(s, ',', parts);
if (parts.size() != 4) return false;
int i = 0;
while (gametypes[i].shortname) {
if (parts[0] == gametypes[i].shortname) {
_type = static_cast<GameType>(i);
break;
}
i++;
}
if (!gametypes[i].shortname) return false;
i = 0;
while (gamelangs[i].name) {
if (parts[1] == gamelangs[i].name) {
_language = static_cast<GameLanguage>(i);
break;
}
i++;
}
if (!gamelangs[i].name) return false;
this->version = strtol(parts[2].c_str(), 0, 0);
for (i = 0; i < 16; ++i) {
char buf[3];
buf[0] = parts[3][2 * i];
buf[1] = parts[3][2 * i + 1];
buf[2] = 0;
long x = strtol(buf, 0, 16);
_md5[i] = static_cast<uint8>(x);
}
return true;
}
} // End of namespace Ultima8
} // End of namespace Ultima

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/>.
*
*/
#ifndef ULTIMA8_GAMES_GAMEINFO_H
#define ULTIMA8_GAMES_GAMEINFO_H
#include "ultima/shared/std/string.h"
namespace Ultima {
namespace Ultima8 {
class IDataSource;
//! GameInfo contains detailed information about the game
struct GameInfo {
GameInfo();
Std::string _name;
enum GameType {
GAME_UNKNOWN = 0,
GAME_U8,
GAME_REMORSE,
GAME_REGRET
} _type;
// Usecode coff variant
enum GameUsecodeOffsetVariant {
GAME_UC_DEFAULT, // Most versions of most games
GAME_UC_ORIG, // Original (pre-patch) CD versions of Crusader games
GAME_UC_DEMO, // Crusader: No Remorse or No Regret Demos
GAME_UC_REM_ES, // Crusader: No Remorse Spanish
GAME_UC_REM_FR, // Crusader: No Remorse French
GAME_UC_REM_JA, // Crusader: No Remorse Japanese
GAME_UC_REG_DE // Crusader: No Regret German
} _ucOffVariant;
//! version number, encoded as 100*major + minor
//! so, 2.12 becomes 212
//! 0 = unknown
int version;
enum GameLanguage {
GAMELANG_UNKNOWN = 0,
GAMELANG_ENGLISH,
GAMELANG_FRENCH,
GAMELANG_GERMAN,
GAMELANG_SPANISH,
GAMELANG_JAPANESE
} _language;
uint8 _md5[16];
char getLanguageFileLetter() const;
char getLanguageUsecodeLetter() const;
Std::string getLanguage() const;
Std::string getGameTitle() const;
Std::string getPrintableVersion() const;
Std::string getPrintDetails() const;
Std::string getPrintableMD5() const;
bool match(GameInfo &other, bool ignoreMD5 = false) const;
void save(Common::WriteStream *ws);
bool load(Common::SeekableReadStream *rs, uint32 /* version */);
};
} // End of namespace Ultima8
} // End of namespace Ultima
#endif

View File

@@ -0,0 +1,162 @@
/* 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/games/start_crusader_process.h"
#include "ultima/ultima8/games/cru_game.h"
#include "ultima/ultima8/world/actors/main_actor.h"
#include "ultima/ultima8/ultima8.h"
#include "ultima/ultima8/kernel/kernel.h"
#include "ultima/ultima8/gumps/difficulty_gump.h"
#include "ultima/ultima8/gumps/cru_status_gump.h"
#include "ultima/ultima8/gumps/cru_pickup_area_gump.h"
#include "ultima/ultima8/world/get_object.h"
#include "ultima/ultima8/world/item_factory.h"
#include "ultima/ultima8/world/actors/teleport_to_egg_process.h"
#include "ultima/ultima8/gfx/palette_fader_process.h"
#include "ultima/ultima8/gfx/texture.h"
namespace Ultima {
namespace Ultima8 {
DEFINE_RUNTIME_CLASSTYPE_CODE(StartCrusaderProcess)
StartCrusaderProcess::StartCrusaderProcess(int saveSlot) : Process(),
_initStage(PlayFirstMovie), _saveSlot(saveSlot) {
_flags |= PROC_PREVENT_SAVE;
}
void StartCrusaderProcess::run() {
if (_initStage == PlayFirstMovie) {
_initStage = PlaySecondMovie;
ProcId moviepid = Game::get_instance()->playIntroMovie(false);
Process *movieproc = Kernel::get_instance()->getProcess(moviepid);
if (movieproc) {
waitFor(movieproc);
}
return;
} else if (_initStage == PlaySecondMovie) {
_initStage = ShowDifficultyMenu;
CruGame *game = dynamic_cast<CruGame *>(Game::get_instance());
assert(game);
ProcId moviepid = game->playIntroMovie2(false);
Process *movieproc = Kernel::get_instance()->getProcess(moviepid);
if (movieproc) {
waitFor(movieproc);
}
return;
}
// Try to load the save game, if succeeded this process will terminate
if (_saveSlot >= 0) {
Common::Error loadError = Ultima8Engine::get_instance()->loadGameState(_saveSlot);
if (loadError.getCode() != Common::kNoError) {
Ultima8Engine::get_instance()->setError(loadError);
fail();
return;
}
terminate();
return;
}
if (_initStage == ShowDifficultyMenu) {
DifficultyGump *gump = new DifficultyGump();
_initStage = StartGame;
gump->InitGump(nullptr, true);
return;
}
Gump *statusGump = new CruStatusGump(true);
statusGump->InitGump(nullptr, false);
Gump *cruPickupAreaGump = new CruPickupAreaGump(true);
cruPickupAreaGump->InitGump(nullptr, false);
MainActor *avatar = getMainActor();
int mapnum = avatar->getMapNum();
// These items are the same in Regret and Remorse
Item *datalink = ItemFactory::createItem(0x4d4, 0, 0, 0, 0, mapnum, 0, true);
avatar->addItemCru(datalink, false);
Item *smiley = ItemFactory::createItem(0x598, 0, 0, 0, 0, mapnum, 0, true);
smiley->moveToContainer(avatar);
avatar->setShieldType(1);
#if 0
// Give the avatar *all the weapons and ammo*.. (handy for testing)
Ultima8Engine::get_instance()->setCheatMode(true);
static const uint32 wpnshapes[] = {
// Weapons
0x032E, 0x032F, 0x0330, 0x038C, 0x0332, 0x0333, 0x0334,
0x038E, 0x0388, 0x038A, 0x038D, 0x038B, 0x0386,
// Ammo
0x033D, 0x033E, 0x033F, 0x0340, 0x0341,
// No Regret Weapons
0x5F6, 0x5F5, 0x198,
// No Regret Ammo
0x615, 0x614
};
for (int i = 0; i < ARRAYSIZE(wpnshapes); i++) {
for (int j = 0; j < 5; j++) {
Item *wpn = ItemFactory::createItem(wpnshapes[i], 0, 0, 0, 0, mapnum, 0, true);
avatar->addItemCru(wpn, false);
}
}
#endif
avatar->teleport(1, 0x1e);
// The first level 0x1e teleporter in No Remorse goes straight to another
// teleport, so undo the flag that normally stops that.
avatar->setJustTeleported(false);
if (GAME_IS_REGRET) {
avatar->setInCombat(0);
avatar->setDir(dir_south);
avatar->setActorFlag(Actor::ACT_WEAPONREADY);
}
Process *fader = new PaletteFaderProcess(TEX32_PACK_RGBA(0xFF, 0xFF, 0xFF, 0x00), true, 0x7FFF, 60, false);
Kernel::get_instance()->addProcess(fader);
Ultima8Engine::get_instance()->setAvatarInStasis(false);
terminate();
}
void StartCrusaderProcess::saveData(Common::WriteStream *ws) {
warning("Attempted save of process with prevent save flag");
Process::saveData(ws);
}
bool StartCrusaderProcess::loadData(Common::ReadStream *rs, uint32 version) {
if (!Process::loadData(rs, version)) return false;
return true;
}
} // End of namespace Ultima8
} // End of namespace Ultima

View File

@@ -0,0 +1,60 @@
/* 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_GAMES_STARTCRUSADERPROCESS_H
#define ULTIMA8_GAMES_STARTCRUSADERPROCESS_H
#include "ultima/ultima8/kernel/process.h"
#include "ultima/ultima8/misc/classtype.h"
namespace Ultima {
namespace Ultima8 {
class Item;
class StartCrusaderProcess : public Process {
public:
enum CruInitStage {
PlayFirstMovie,
PlaySecondMovie,
ShowDifficultyMenu,
StartGame
};
protected:
CruInitStage _initStage;
int _saveSlot;
public:
StartCrusaderProcess(int saveSlot);
ENABLE_RUNTIME_CLASSTYPE()
void run() override;
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,130 @@
/* 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/games/start_u8_process.h"
#include "ultima/ultima8/games/game.h"
#include "ultima/ultima8/world/loop_script.h"
#include "ultima/ultima8/usecode/uc_list.h"
#include "ultima/ultima8/world/current_map.h"
#include "ultima/ultima8/world/egg.h"
#include "ultima/ultima8/world/camera_process.h"
#include "ultima/ultima8/world/world.h"
#include "ultima/ultima8/ultima8.h"
#include "ultima/ultima8/kernel/kernel.h"
#include "ultima/ultima8/gumps/menu_gump.h"
#include "ultima/ultima8/world/get_object.h"
#include "ultima/ultima8/gfx/palette_fader_process.h"
namespace Ultima {
namespace Ultima8 {
DEFINE_RUNTIME_CLASSTYPE_CODE(StartU8Process)
StartU8Process::StartU8Process(int saveSlot) : Process(),
_init(false), _saveSlot(saveSlot), _skipStart(saveSlot >= 0) {
_flags |= PROC_PREVENT_SAVE;
}
void StartU8Process::run() {
if (!_skipStart && !_init) {
_init = true;
ProcId moviepid = Game::get_instance()->playIntroMovie(false);
Process *movieproc = Kernel::get_instance()->getProcess(moviepid);
if (movieproc) {
waitFor(movieproc);
return;
}
}
// Try to load the save game, if succeeded this process will terminate
if (_saveSlot >= 0) {
Common::Error loadError = Ultima8Engine::get_instance()->loadGameState(_saveSlot);
if (loadError.getCode() != Common::kNoError) {
Ultima8Engine::get_instance()->setError(loadError);
fail();
return;
}
PaletteFaderProcess::I_fadeFromBlack(0, 0);
terminate();
return;
}
CurrentMap *currentmap = World::get_instance()->getCurrentMap();
UCList uclist(2);
if (!_skipStart) {
LOOPSCRIPT(script, LS_AND(LS_SHAPE_EQUAL1(73), LS_Q_EQUAL(36)));
currentmap->areaSearch(&uclist, script, sizeof(script),
0, 256, false, 16188, 7500);
if (uclist.getSize() < 1) {
warning("Unable to find FIRST egg");
return;
}
uint16 objid = uclist.getuint16(0);
Egg *egg = dynamic_cast<Egg *>(getObject(objid));
Point3 pt = egg->getLocation();
// Center on egg
CameraProcess::SetCameraProcess(new CameraProcess(pt));
egg->hatch();
}
// Music Egg
// Item 2145 (class Item, shape 562, 0, (11551,2079,48) q:52, m:0, n:0, f:2000, ef:2)
uclist.free();
LOOPSCRIPT(musicscript, LS_SHAPE_EQUAL1(562));
currentmap->areaSearch(&uclist, musicscript, sizeof(musicscript),
0, 256, false, 11551, 2079);
if (uclist.getSize() < 1) {
warning("Unable to find MUSIC egg");
} else {
ObjId objid = uclist.getuint16(0);
Item *musicEgg = getItem(objid);
musicEgg->callUsecodeEvent_cachein();
}
if (!_skipStart)
MenuGump::inputName();
else
Ultima8Engine::get_instance()->setAvatarInStasis(false);
terminate();
}
void StartU8Process::saveData(Common::WriteStream *ws) {
warning("Attempted save of process with prevent save flag");
Process::saveData(ws);
}
bool StartU8Process::loadData(Common::ReadStream *rs, uint32 version) {
if (!Process::loadData(rs, version)) return false;
return true;
}
} // 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_GAMES_STARTU8PROCESS_H
#define ULTIMA8_GAMES_STARTU8PROCESS_H
#include "ultima/ultima8/kernel/process.h"
#include "ultima/ultima8/misc/classtype.h"
namespace Ultima {
namespace Ultima8 {
class Item;
class StartU8Process : public Process {
protected:
bool _init;
bool _skipStart;
int _saveSlot;
public:
StartU8Process(int saveSlot = -1);
ENABLE_RUNTIME_CLASSTYPE()
void run() override;
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,233 @@
/* 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/games/treasure_loader.h"
#include "ultima/ultima8/conf/config_file_manager.h"
#include "ultima/ultima8/misc/util.h"
namespace Ultima {
namespace Ultima8 {
TreasureLoader::TreasureLoader() {
}
TreasureLoader::~TreasureLoader() {
}
void TreasureLoader::loadDefaults() {
ConfigFileManager *config = ConfigFileManager::get_instance();
KeyMap lootkeyvals;
// load default treasure types
lootkeyvals = config->listKeyValues("game", "treasure");
for (const auto &i : lootkeyvals) {
TreasureInfo ti;
bool ok = internalParse(i._value, ti, true);
if (ok) {
_defaultTreasure[i._key] = ti;
} else {
warning("Failed to parse treasure type '%s': %s", i._key.c_str(), i._value.c_str());
}
}
}
bool TreasureLoader::parse(const Std::string &desc,
Std::vector<TreasureInfo> &treasure) const {
treasure.clear();
Std::vector<Std::string> tr;
SplitString(desc, ';', tr);
TreasureInfo ti;
for (unsigned int i = 0; i < tr.size(); ++i) {
if (internalParse(tr[i], ti, false)) {
treasure.push_back(ti);
} else {
return false;
}
}
return true;
}
bool TreasureLoader::internalParse(const Std::string &desc, TreasureInfo &ti,
bool loadingDefault) const {
ti.clear();
bool loadedDefault = false;
Std::vector<Common::Pair<Std::string, Std::string> > kv;
SplitStringKV(desc, ' ', kv);
for (unsigned int i = 0; i < kv.size(); ++i) {
const Std::string &key = kv[i].first;
Std::string val = kv[i].second;
if (key == "shape") {
if (!parseUInt32Vector(val, ti._shapes)) {
warning("Failed to parse treasure shape list '%s'", val.c_str());
return false;
}
// validate the shapes are > 0 and < max shape
for (unsigned int j = 0; j < ti._shapes.size(); j++) {
if (ti._shapes[j] <= 0 || ti._shapes[j] > 65535) {
warning("Invalid treasure shape in list '%s'", val.c_str());
return false;
}
}
} else if (key == "frame") {
if (!parseUInt32Vector(val, ti._frames)) {
warning("Failed to parse treasure frame list '%s'", val.c_str());
return false;
}
// validate the frames are < max frame (0 frame is valid)
for (unsigned int j = 0; j < ti._frames.size(); j++) {
if (ti._frames[j] > 65535) {
warning("Invalid treasure frame in list '%s'", val.c_str());
return false;
}
}
} else if (key == "count") {
if (!parseUIntRange(val, ti._minCount, ti._maxCount)) {
int x;
if (!parseInt(val, x) || x <= 0) {
warning("Invalid treasure count '%s'", val.c_str());
return false;
}
ti._minCount = ti._maxCount = x;
}
} else if (key == "chance") {
if (!parseDouble(val, ti._chance) || ti._chance <= 0) {
warning("Invalid treasure chance '%s'", val.c_str());
return false;
}
} else if (key == "map") {
if (val.size() > 1 && val[0] == '!')
val[0] = '-'; // HACK: invert map for 'not this map'
if (!parseInt(val, ti._map))
return false;
} else if (key == "special" && loadingDefault) {
ti._special = val;
} else if (key == "type" && !loadingDefault) {
if (loadedDefault)
return false;
TreasureMap::const_iterator iter;
iter = _defaultTreasure.find(val);
if (iter != _defaultTreasure.end())
ti = iter->_value;
else
return false;
loadedDefault = true;
} else if (key == "mult" && !loadingDefault) {
if (!loadedDefault) {
warning("Need defaults before applying multiplier in treasure data '%s'", val.c_str());
return false;
}
unsigned int minmult, maxmult;
if (!parseUIntRange(val, minmult, maxmult)) {
int x;
if (!parseInt(val, x) || x <= 0) {
warning("Invalid treasure multiplier '%s'", val.c_str());
return false;
}
minmult = maxmult = x;
}
ti._minCount *= minmult;
ti._maxCount *= maxmult;
} else {
warning("Unknown key parsing treasure '%s'", key.c_str());
return false;
}
}
return true;
}
bool TreasureLoader::parseUInt32Vector(const Std::string &val_,
Std::vector<uint32> &vec) const {
Std::string val = val_;
vec.clear();
if (val.empty())
return false;
while (!val.empty()) {
Std::string::size_type pos = val.find(',');
const Std::string item = val.substr(0, pos);
Std::string::size_type itempos = val.find('-');
if (itempos != Std::string::npos) {
unsigned int min, max;
if (!parseUIntRange(item, min, max))
return false;
for (unsigned int i = min; i <= max; ++i)
vec.push_back(i);
} else {
int x;
if (!parseInt(item, x) || x < 0)
return false;
vec.push_back(x);
}
if (pos != Std::string::npos) pos++;
val.erase(0, pos);
}
return true;
}
bool TreasureLoader::parseUIntRange(const Std::string &val,
unsigned int &min, unsigned int &max) const {
Std::string::size_type pos = val.find('-');
if (pos == 0 || pos == Std::string::npos || pos + 1 >= val.size())
return false;
int t1 = 0;
int t2 = 0;
bool ok = true;
ok = ok && parseInt(val.substr(0, pos), t1);
ok = ok && parseInt(val.substr(pos + 1), t2);
ok = ok && (t1 <= t2 && t1 >= 0 && t2 >= 0);
if (ok) {
min = t1;
max = t2;
}
return ok;
}
bool TreasureLoader::parseDouble(const Std::string &val, double &d) const {
if (val.empty())
return false;
// TODO: error checking
d = atof(val.c_str());
return true;
}
bool TreasureLoader::parseInt(const Std::string &val, int &i) const {
if (val.empty())
return false;
// TODO: error checking
i = strtol(val.c_str(), 0, 0);
return true;
}
} // End of namespace Ultima8
} // End of namespace Ultima

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_GAMES_TREASURELOADER_H
#define ULTIMA8_GAMES_TREASURELOADER_H
#include "ultima/ultima8/world/actors/treasure_info.h"
#include "ultima/shared/std/containers.h"
namespace Ultima {
namespace Ultima8 {
typedef Common::HashMap<Std::string, TreasureInfo, Common::IgnoreCase_Hash> TreasureMap;
class TreasureLoader {
public:
TreasureLoader();
~TreasureLoader();
//! load defaults from 'game' ini section
void loadDefaults();
//! parse treasure string into vector of TreasureInfo objects
bool parse(const Std::string &, Std::vector<TreasureInfo> &treasure) const;
private:
TreasureMap _defaultTreasure;
bool internalParse(const Std::string &desc, TreasureInfo &ti, bool loadingDefault) const;
bool parseUInt32Vector(const Std::string &val, Std::vector<uint32> &vec) const;
bool parseUIntRange(const Std::string &val, unsigned int &min, unsigned int &max) const;
bool parseDouble(const Std::string &val, double &d) const;
bool parseInt(const Std::string &val, int &i) const;
};
} // End of namespace Ultima8
} // End of namespace Ultima
#endif

View File

@@ -0,0 +1,316 @@
/* 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 "common/translation.h"
#include "gui/error.h"
#include "ultima/ultima8/misc/debugger.h"
#include "ultima/ultima8/games/u8_game.h"
#include "ultima/ultima8/gfx/palette_manager.h"
#include "ultima/ultima8/gfx/fade_to_modal_process.h"
#include "ultima/ultima8/games/game_data.h"
#include "ultima/ultima8/gfx/xform_blend.h"
#include "ultima/ultima8/filesys/u8_save_file.h"
#include "ultima/ultima8/world/world.h"
#include "ultima/ultima8/world/actors/main_actor.h"
#include "ultima/ultima8/world/item_factory.h"
#include "ultima/ultima8/kernel/object_manager.h"
#include "ultima/ultima8/ultima8.h"
#include "ultima/ultima8/gumps/movie_gump.h"
#include "ultima/ultima8/gumps/credits_gump.h"
#include "ultima/ultima8/kernel/kernel.h"
#include "ultima/ultima8/audio/music_process.h"
#include "ultima/ultima8/games/start_u8_process.h"
#include "ultima/ultima8/world/get_object.h"
#include "common/memstream.h"
namespace Ultima {
namespace Ultima8 {
U8Game::U8Game() : Game() {
// Set some defaults for gameplay-related settings
ConfMan.registerDefault("endgame", false);
ConfMan.registerDefault("quotes", false);
ConfMan.registerDefault("footsteps", true);
ConfMan.registerDefault("targetedjump", true);
ConfMan.registerDefault("subtitles", true);
ConfMan.registerDefault("speech_mute", false);
const GameInfo *info = Ultima8Engine::get_instance()->getGameInfo();
if (info->_language == GameInfo::GAMELANG_JAPANESE) {
ConfMan.registerDefault("talkspeed", 24);
} else {
ConfMan.registerDefault("talkspeed", 60);
}
}
U8Game::~U8Game() {
}
bool U8Game::loadFiles() {
// Load palette
debug(1, "Load Palette");
Common::File pf;
if (!pf.open("static/u8pal.pal")) {
warning("Unable to load static/u8pal.pal.");
return false;
}
pf.seek(4); // seek past header
Common::MemoryReadStream xfds(U8XFormPal, 1024);
PaletteManager::get_instance()->load(PaletteManager::Pal_Game, pf, xfds);
debug(1, "Load GameData");
GameData::get_instance()->loadU8Data();
return true;
}
bool U8Game::startGame() {
// NOTE: assumes the entire engine has been reset!
debug(1, "Starting new Ultima 8 game.");
ObjectManager *objman = ObjectManager::get_instance();
// reserve a number of objids just in case we'll need them sometime
for (uint16 i = 384; i < 512; ++i)
objman->reserveObjId(i);
// Reserved for the Guardian Bark hack
objman->reserveObjId(kGuardianId);
auto *savers = new Common::File();
if (!savers->open("savegame/u8save.000")) {
Common::U32String errmsg = _(
"Missing Required File\n\n"
"Starting a game requires SAVEGAME/U8SAVE.000\n"
"from an original installation.\n\n"
"Please check you have copied all the files correctly.");
::GUI::displayErrorDialog(errmsg);
delete savers;
error("Unable to load savegame/u8save.000");
return false;
}
U8SaveFile *u8save = new U8SaveFile(savers);
Common::SeekableReadStream *nfd = u8save->createReadStreamForMember("NONFIXED.DAT");
if (!nfd) {
warning("Unable to load savegame/u8save.000/NONFIXED.DAT.");
return false;
}
World::get_instance()->loadNonFixed(nfd); // deletes nfd
Common::SeekableReadStream *icd = u8save->createReadStreamForMember("ITEMCACH.DAT");
if (!icd) {
warning("Unable to load savegame/u8save.000/ITEMCACH.DAT.");
return false;
}
Common::SeekableReadStream *npcd = u8save->createReadStreamForMember("NPCDATA.DAT");
if (!npcd) {
warning("Unable to load savegame/u8save.000/NPCDATA.DAT.");
delete icd;
return false;
}
World::get_instance()->loadItemCachNPCData(icd, npcd); // deletes icd, npcd
delete u8save;
MainActor *av = getMainActor();
assert(av);
av->setName("Avatar"); // default name
// avatar needs a backpack ... CONSTANTs and all that
Item *backpack = ItemFactory::createItem(529, 0, 0, 0, 0, 0, 0, true);
backpack->moveToContainer(av);
World::get_instance()->switchMap(av->getMapNum());
Ultima8Engine::get_instance()->setAvatarInStasis(true);
return true;
}
bool U8Game::startInitialUsecode(int saveSlot) {
Process *proc = new StartU8Process(saveSlot);
Kernel::get_instance()->addProcess(proc);
return true;
}
ProcId U8Game::playIntroMovie(bool fade) {
const GameInfo *gameinfo = Ultima8Engine::get_instance()->getGameInfo();
char langletter = gameinfo->getLanguageFileLetter();
if (!langletter) {
warning("U8Game::playIntro: Unknown language.");
return 0;
}
Common::String filename = "static/";
filename += langletter;
filename += "intro.skf";
auto *skf = new Common::File();
if (!skf->open(filename.c_str())) {
debug(1, "U8Game::playIntro: movie not found.");
delete skf;
return 0;
}
return MovieGump::U8MovieViewer(skf, fade, true, true);
}
ProcId U8Game::playEndgameMovie(bool fade) {
static const Common::Path filename = "static/endgame.skf";
auto *skf = new Common::File();
if (!skf->open(filename)) {
debug(1, "U8Game::playEndgame: movie not found.");
delete skf;
return 0;
}
return MovieGump::U8MovieViewer(skf, fade, false, true);
}
void U8Game::playCredits() {
const GameInfo *gameinfo = Ultima8Engine::get_instance()->getGameInfo();
char langletter = gameinfo->getLanguageFileLetter();
if (!langletter) {
warning("U8Game::playCredits: Unknown language.");
return;
}
Common::String filename = "static/";
filename += langletter;
filename += "credits.dat";
auto *rs = new Common::File();
if (!rs->open(filename.c_str())) {
warning("U8Game::playCredits: error opening credits file: %s", filename.c_str());
delete rs;
return;
}
Std::string text = getCreditText(rs);
delete rs;
MusicProcess *musicproc = MusicProcess::get_instance();
if (musicproc) musicproc->playMusic(51); // CONSTANT!
CreditsGump *gump = new CreditsGump(text);
gump->SetFlagWhenFinished("quotes");
FadeToModalProcess *p = new FadeToModalProcess(gump);
Kernel::get_instance()->addProcess(p);
}
void U8Game::playQuotes() {
static const Common::Path filename = "static/quotes.dat";
auto *rs = new Common::File();
if (!rs->open(filename)) {
warning("U8Game::playQuotes: error opening quotes file: %s", filename.toString().c_str());
delete rs;
return;
}
const Std::string text = getCreditText(rs);
delete rs;
MusicProcess *musicproc = MusicProcess::get_instance();
if (musicproc) musicproc->playMusic(113); // CONSTANT!
CreditsGump *gump = new CreditsGump(text, 80);
FadeToModalProcess *p = new FadeToModalProcess(gump);
Kernel::get_instance()->addProcess(p);
}
void U8Game::writeSaveInfo(Common::WriteStream *ws) {
MainActor *av = getMainActor();
const Std::string &avname = av->getName();
const uint8 namelength = static_cast<uint8>(avname.size());
ws->writeByte(namelength);
for (unsigned int i = 0; i < namelength; ++i)
ws->writeByte(static_cast<uint8>(avname[i]));
Point3 pt = av->getLocation();
ws->writeUint16LE(av->getMapNum());
ws->writeUint32LE(static_cast<uint32>(pt.x));
ws->writeUint32LE(static_cast<uint32>(pt.y));
ws->writeUint32LE(static_cast<uint32>(pt.z));
ws->writeUint16LE(av->getStr());
ws->writeUint16LE(av->getInt());
ws->writeUint16LE(av->getDex());
ws->writeUint16LE(av->getHP());
ws->writeUint16LE(av->getMaxHP());
ws->writeUint16LE(av->getMana());
ws->writeUint16LE(av->getMaxMana());
ws->writeUint16LE(av->getArmourClass());
ws->writeUint16LE(av->getTotalWeight());
for (unsigned int i = 1; i <= 6; i++) {
uint16 objid = av->getEquip(i);
Item *item = getItem(objid);
if (item) {
ws->writeUint32LE(item->getShape());
ws->writeUint32LE(item->getFrame());
} else {
ws->writeUint32LE(0);
ws->writeUint32LE(0);
}
}
}
Std::string U8Game::getCreditText(Common::SeekableReadStream *rs) {
unsigned int size = rs->size();
Std::string text(size, ' ');
for (unsigned int i = 0; i < size; ++i) {
uint8 c = rs->readByte();
int x;
switch (i) {
case 0:
case 1:
x = 0;
break;
case 2:
x = 0xE1;
break;
default:
x = 0x20 * (i + 1) + (i >> 1);
x += (i % 0x40) * ((i & 0xC0) >> 6) * 0x40;
break;
}
char d = (c ^ x) & 0xFF;
if (d == 0) d = '\n';
text[i] = d;
}
return text;
}
} // End of namespace Ultima8
} // End of namespace Ultima

View File

@@ -0,0 +1,60 @@
/* 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_GAMES_U8GAME_H
#define ULTIMA8_GAMES_U8GAME_H
#include "ultima/ultima8/games/game.h"
namespace Ultima {
namespace Ultima8 {
class U8Game: public Game {
public:
U8Game();
~U8Game() override;
//! load/init game's data files
bool loadFiles() override;
//! initialize new game
bool startGame() override;
//! start initial usecode
bool startInitialUsecode(int saveSlot) override;
//! write game-specific savegame info (avatar stats, equipment, ...)
void writeSaveInfo(Common::WriteStream *ws) override;
ProcId playIntroMovie(bool fade) override;
ProcId playEndgameMovie(bool fade) override;
void playCredits() override;
void playQuotes() override;
void playDemoScreen() override { }; // no demo for U8
protected:
Std::string getCreditText(Common::SeekableReadStream *rs);
};
} // End of namespace Ultima8
} // End of namespace Ultima
#endif

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

Some files were not shown because too many files have changed in this diff Show More