Initial commit
This commit is contained in:
78
engines/ultima/ultima8/audio/audio_channel.cpp
Normal file
78
engines/ultima/ultima8/audio/audio_channel.cpp
Normal 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
|
||||
67
engines/ultima/ultima8/audio/audio_channel.h
Normal file
67
engines/ultima/ultima8/audio/audio_channel.h
Normal 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
|
||||
148
engines/ultima/ultima8/audio/audio_mixer.cpp
Normal file
148
engines/ultima/ultima8/audio/audio_mixer.cpp
Normal 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
|
||||
72
engines/ultima/ultima8/audio/audio_mixer.h
Normal file
72
engines/ultima/ultima8/audio/audio_mixer.h
Normal 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
|
||||
668
engines/ultima/ultima8/audio/audio_process.cpp
Normal file
668
engines/ultima/ultima8/audio/audio_process.cpp
Normal 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
|
||||
152
engines/ultima/ultima8/audio/audio_process.h
Normal file
152
engines/ultima/ultima8/audio/audio_process.h
Normal 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
|
||||
39
engines/ultima/ultima8/audio/audio_sample.cpp
Normal file
39
engines/ultima/ultima8/audio/audio_sample.cpp
Normal 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
|
||||
69
engines/ultima/ultima8/audio/audio_sample.h
Normal file
69
engines/ultima/ultima8/audio/audio_sample.h
Normal 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
|
||||
263
engines/ultima/ultima8/audio/cru_music_process.cpp
Normal file
263
engines/ultima/ultima8/audio/cru_music_process.cpp
Normal 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
|
||||
101
engines/ultima/ultima8/audio/cru_music_process.h
Normal file
101
engines/ultima/ultima8/audio/cru_music_process.h
Normal 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
|
||||
266
engines/ultima/ultima8/audio/midi_player.cpp
Normal file
266
engines/ultima/ultima8/audio/midi_player.cpp
Normal 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
|
||||
132
engines/ultima/ultima8/audio/midi_player.h
Normal file
132
engines/ultima/ultima8/audio/midi_player.h
Normal 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
|
||||
244
engines/ultima/ultima8/audio/music_flex.cpp
Normal file
244
engines/ultima/ultima8/audio/music_flex.cpp
Normal 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
|
||||
78
engines/ultima/ultima8/audio/music_flex.h
Normal file
78
engines/ultima/ultima8/audio/music_flex.h
Normal 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
|
||||
71
engines/ultima/ultima8/audio/music_process.cpp
Normal file
71
engines/ultima/ultima8/audio/music_process.cpp
Normal 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
|
||||
96
engines/ultima/ultima8/audio/music_process.h
Normal file
96
engines/ultima/ultima8/audio/music_process.h
Normal 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
|
||||
52
engines/ultima/ultima8/audio/raw_audio_sample.cpp
Normal file
52
engines/ultima/ultima8/audio/raw_audio_sample.cpp
Normal 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
|
||||
45
engines/ultima/ultima8/audio/raw_audio_sample.h
Normal file
45
engines/ultima/ultima8/audio/raw_audio_sample.h
Normal 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
|
||||
246
engines/ultima/ultima8/audio/sonarc_audio_sample.cpp
Normal file
246
engines/ultima/ultima8/audio/sonarc_audio_sample.cpp
Normal 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
|
||||
64
engines/ultima/ultima8/audio/sonarc_audio_sample.h
Normal file
64
engines/ultima/ultima8/audio/sonarc_audio_sample.h
Normal 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
|
||||
127
engines/ultima/ultima8/audio/sound_flex.cpp
Normal file
127
engines/ultima/ultima8/audio/sound_flex.cpp
Normal 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
|
||||
62
engines/ultima/ultima8/audio/sound_flex.h
Normal file
62
engines/ultima/ultima8/audio/sound_flex.h
Normal 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
|
||||
121
engines/ultima/ultima8/audio/speech_flex.cpp
Normal file
121
engines/ultima/ultima8/audio/speech_flex.cpp
Normal 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
|
||||
48
engines/ultima/ultima8/audio/speech_flex.h
Normal file
48
engines/ultima/ultima8/audio/speech_flex.h
Normal 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
|
||||
308
engines/ultima/ultima8/audio/u8_music_process.cpp
Normal file
308
engines/ultima/ultima8/audio/u8_music_process.cpp
Normal 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
|
||||
134
engines/ultima/ultima8/audio/u8_music_process.h
Normal file
134
engines/ultima/ultima8/audio/u8_music_process.h
Normal 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
|
||||
148
engines/ultima/ultima8/conf/config_file_manager.cpp
Normal file
148
engines/ultima/ultima8/conf/config_file_manager.cpp
Normal 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 §ion, 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 §ion, 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 §ion, 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 §ion) 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
|
||||
87
engines/ultima/ultima8/conf/config_file_manager.h
Normal file
87
engines/ultima/ultima8/conf/config_file_manager.h
Normal 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 §ion, const Std::string &key, Std::string &ret) const;
|
||||
//! get value
|
||||
bool get(const Std::string &category, const Std::string §ion, const Std::string &key, int &ret) const;
|
||||
//! get value
|
||||
bool get(const Std::string &category, const Std::string §ion, 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 §ion) const;
|
||||
|
||||
private:
|
||||
Std::vector<ConfigFile *> _configFiles;
|
||||
|
||||
static ConfigFileManager *_configFileManager;
|
||||
};
|
||||
|
||||
} // End of namespace Ultima8
|
||||
} // End of namespace Ultima
|
||||
|
||||
#endif
|
||||
752
engines/ultima/ultima8/convert/convert_shape.cpp
Normal file
752
engines/ultima/ultima8/convert/convert_shape.cpp
Normal 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
|
||||
128
engines/ultima/ultima8/convert/convert_shape.h
Normal file
128
engines/ultima/ultima8/convert/convert_shape.h
Normal 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
|
||||
39
engines/ultima/ultima8/convert/convert_usecode.h
Normal file
39
engines/ultima/ultima8/convert/convert_usecode.h
Normal 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
|
||||
@@ -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
|
||||
@@ -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
|
||||
@@ -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
|
||||
420
engines/ultima/ultima8/convert/crusader/convert_usecode_regret.h
Normal file
420
engines/ultima/ultima8/convert/crusader/convert_usecode_regret.h
Normal 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
|
||||
140
engines/ultima/ultima8/convert/u8/convert_shape_u8.cpp
Normal file
140
engines/ultima/ultima8/convert/u8/convert_shape_u8.cpp
Normal 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
|
||||
45
engines/ultima/ultima8/convert/u8/convert_shape_u8.h
Normal file
45
engines/ultima/ultima8/convert/u8/convert_shape_u8.h
Normal 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
|
||||
358
engines/ultima/ultima8/convert/u8/convert_usecode_u8.h
Normal file
358
engines/ultima/ultima8/convert/u8/convert_usecode_u8.h
Normal 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
|
||||
435
engines/ultima/ultima8/debugtools.cpp
Normal file
435
engines/ultima/ultima8/debugtools.cpp
Normal 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
|
||||
|
||||
33
engines/ultima/ultima8/debugtools.h
Normal file
33
engines/ultima/ultima8/debugtools.h
Normal 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
|
||||
103
engines/ultima/ultima8/filesys/archive.cpp
Normal file
103
engines/ultima/ultima8/filesys/archive.cpp
Normal 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
|
||||
94
engines/ultima/ultima8/filesys/archive.h
Normal file
94
engines/ultima/ultima8/filesys/archive.h
Normal 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
|
||||
132
engines/ultima/ultima8/filesys/flex_file.cpp
Normal file
132
engines/ultima/ultima8/filesys/flex_file.cpp
Normal 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
|
||||
90
engines/ultima/ultima8/filesys/flex_file.h
Normal file
90
engines/ultima/ultima8/filesys/flex_file.h
Normal 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
|
||||
101
engines/ultima/ultima8/filesys/raw_archive.cpp
Normal file
101
engines/ultima/ultima8/filesys/raw_archive.cpp
Normal 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
|
||||
64
engines/ultima/ultima8/filesys/raw_archive.h
Normal file
64
engines/ultima/ultima8/filesys/raw_archive.h
Normal 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
|
||||
227
engines/ultima/ultima8/filesys/savegame.cpp
Normal file
227
engines/ultima/ultima8/filesys/savegame.cpp
Normal 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
|
||||
90
engines/ultima/ultima8/filesys/savegame.h
Normal file
90
engines/ultima/ultima8/filesys/savegame.h
Normal 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
|
||||
106
engines/ultima/ultima8/filesys/u8_save_file.cpp
Normal file
106
engines/ultima/ultima8/filesys/u8_save_file.cpp
Normal 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
|
||||
73
engines/ultima/ultima8/filesys/u8_save_file.h
Normal file
73
engines/ultima/ultima8/filesys/u8_save_file.h
Normal 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
|
||||
245
engines/ultima/ultima8/games/cru_game.cpp
Normal file
245
engines/ultima/ultima8/games/cru_game.cpp
Normal 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
|
||||
69
engines/ultima/ultima8/games/cru_game.h
Normal file
69
engines/ultima/ultima8/games/cru_game.h
Normal 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
|
||||
92
engines/ultima/ultima8/games/game.cpp
Normal file
92
engines/ultima/ultima8/games/game.cpp
Normal 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
|
||||
71
engines/ultima/ultima8/games/game.h
Normal file
71
engines/ultima/ultima8/games/game.h
Normal 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
|
||||
680
engines/ultima/ultima8/games/game_data.cpp
Normal file
680
engines/ultima/ultima8/games/game_data.cpp
Normal 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
|
||||
146
engines/ultima/ultima8/games/game_data.h
Normal file
146
engines/ultima/ultima8/games/game_data.h
Normal 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
|
||||
225
engines/ultima/ultima8/games/game_info.cpp
Normal file
225
engines/ultima/ultima8/games/game_info.cpp
Normal 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
|
||||
90
engines/ultima/ultima8/games/game_info.h
Normal file
90
engines/ultima/ultima8/games/game_info.h
Normal 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
|
||||
162
engines/ultima/ultima8/games/start_crusader_process.cpp
Normal file
162
engines/ultima/ultima8/games/start_crusader_process.cpp
Normal 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
|
||||
60
engines/ultima/ultima8/games/start_crusader_process.h
Normal file
60
engines/ultima/ultima8/games/start_crusader_process.h
Normal 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
|
||||
130
engines/ultima/ultima8/games/start_u8_process.cpp
Normal file
130
engines/ultima/ultima8/games/start_u8_process.cpp
Normal 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
|
||||
53
engines/ultima/ultima8/games/start_u8_process.h
Normal file
53
engines/ultima/ultima8/games/start_u8_process.h
Normal 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
|
||||
233
engines/ultima/ultima8/games/treasure_loader.cpp
Normal file
233
engines/ultima/ultima8/games/treasure_loader.cpp
Normal 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
|
||||
58
engines/ultima/ultima8/games/treasure_loader.h
Normal file
58
engines/ultima/ultima8/games/treasure_loader.h
Normal 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
|
||||
316
engines/ultima/ultima8/games/u8_game.cpp
Normal file
316
engines/ultima/ultima8/games/u8_game.cpp
Normal 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
|
||||
60
engines/ultima/ultima8/games/u8_game.h
Normal file
60
engines/ultima/ultima8/games/u8_game.h
Normal 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
|
||||
282
engines/ultima/ultima8/gfx/anim_dat.cpp
Normal file
282
engines/ultima/ultima8/gfx/anim_dat.cpp
Normal 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
|
||||
54
engines/ultima/ultima8/gfx/anim_dat.h
Normal file
54
engines/ultima/ultima8/gfx/anim_dat.h
Normal 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
|
||||
142
engines/ultima/ultima8/gfx/avi_player.cpp
Normal file
142
engines/ultima/ultima8/gfx/avi_player.cpp
Normal 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
|
||||
80
engines/ultima/ultima8/gfx/avi_player.h
Normal file
80
engines/ultima/ultima8/gfx/avi_player.h
Normal 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
|
||||
144
engines/ultima/ultima8/gfx/cycle_process.cpp
Normal file
144
engines/ultima/ultima8/gfx/cycle_process.cpp
Normal 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
|
||||
63
engines/ultima/ultima8/gfx/cycle_process.h
Normal file
63
engines/ultima/ultima8/gfx/cycle_process.h
Normal 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
|
||||
98
engines/ultima/ultima8/gfx/fade_to_modal_process.cpp
Normal file
98
engines/ultima/ultima8/gfx/fade_to_modal_process.cpp
Normal 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
|
||||
62
engines/ultima/ultima8/gfx/fade_to_modal_process.h
Normal file
62
engines/ultima/ultima8/gfx/fade_to_modal_process.h
Normal 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
|
||||
383
engines/ultima/ultima8/gfx/fonts/font.cpp
Normal file
383
engines/ultima/ultima8/gfx/fonts/font.cpp
Normal 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
|
||||
180
engines/ultima/ultima8/gfx/fonts/font.h
Normal file
180
engines/ultima/ultima8/gfx/fonts/font.h
Normal 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
|
||||
212
engines/ultima/ultima8/gfx/fonts/font_manager.cpp
Normal file
212
engines/ultima/ultima8/gfx/fonts/font_manager.cpp
Normal 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
|
||||
126
engines/ultima/ultima8/gfx/fonts/font_manager.h
Normal file
126
engines/ultima/ultima8/gfx/fonts/font_manager.h
Normal 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
|
||||
90
engines/ultima/ultima8/gfx/fonts/font_shape_archive.cpp
Normal file
90
engines/ultima/ultima8/gfx/fonts/font_shape_archive.cpp
Normal 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
|
||||
54
engines/ultima/ultima8/gfx/fonts/font_shape_archive.h
Normal file
54
engines/ultima/ultima8/gfx/fonts/font_shape_archive.h
Normal 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
|
||||
105
engines/ultima/ultima8/gfx/fonts/jp_font.cpp
Normal file
105
engines/ultima/ultima8/gfx/fonts/jp_font.cpp
Normal 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
|
||||
65
engines/ultima/ultima8/gfx/fonts/jp_font.h
Normal file
65
engines/ultima/ultima8/gfx/fonts/jp_font.h
Normal 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
|
||||
127
engines/ultima/ultima8/gfx/fonts/jp_rendered_text.cpp
Normal file
127
engines/ultima/ultima8/gfx/fonts/jp_rendered_text.cpp
Normal 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
|
||||
52
engines/ultima/ultima8/gfx/fonts/jp_rendered_text.h
Normal file
52
engines/ultima/ultima8/gfx/fonts/jp_rendered_text.h
Normal 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
|
||||
37
engines/ultima/ultima8/gfx/fonts/rendered_text.cpp
Normal file
37
engines/ultima/ultima8/gfx/fonts/rendered_text.cpp
Normal 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
|
||||
65
engines/ultima/ultima8/gfx/fonts/rendered_text.h
Normal file
65
engines/ultima/ultima8/gfx/fonts/rendered_text.h
Normal 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
|
||||
137
engines/ultima/ultima8/gfx/fonts/shape_font.cpp
Normal file
137
engines/ultima/ultima8/gfx/fonts/shape_font.cpp
Normal 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
|
||||
77
engines/ultima/ultima8/gfx/fonts/shape_font.h
Normal file
77
engines/ultima/ultima8/gfx/fonts/shape_font.h
Normal 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
|
||||
102
engines/ultima/ultima8/gfx/fonts/shape_rendered_text.cpp
Normal file
102
engines/ultima/ultima8/gfx/fonts/shape_rendered_text.cpp
Normal 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
|
||||
50
engines/ultima/ultima8/gfx/fonts/shape_rendered_text.h
Normal file
50
engines/ultima/ultima8/gfx/fonts/shape_rendered_text.h
Normal 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
|
||||
299
engines/ultima/ultima8/gfx/fonts/tt_font.cpp
Normal file
299
engines/ultima/ultima8/gfx/fonts/tt_font.cpp
Normal 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
|
||||
76
engines/ultima/ultima8/gfx/fonts/tt_font.h
Normal file
76
engines/ultima/ultima8/gfx/fonts/tt_font.h
Normal 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
|
||||
61
engines/ultima/ultima8/gfx/fonts/ttf_rendered_text.cpp
Normal file
61
engines/ultima/ultima8/gfx/fonts/ttf_rendered_text.cpp
Normal 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
|
||||
55
engines/ultima/ultima8/gfx/fonts/ttf_rendered_text.h
Normal file
55
engines/ultima/ultima8/gfx/fonts/ttf_rendered_text.h
Normal 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
|
||||
41
engines/ultima/ultima8/gfx/frame_id.cpp
Normal file
41
engines/ultima/ultima8/gfx/frame_id.cpp
Normal 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
|
||||
47
engines/ultima/ultima8/gfx/frame_id.h
Normal file
47
engines/ultima/ultima8/gfx/frame_id.h
Normal 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
|
||||
53
engines/ultima/ultima8/gfx/gump_shape_archive.cpp
Normal file
53
engines/ultima/ultima8/gfx/gump_shape_archive.cpp
Normal 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
|
||||
52
engines/ultima/ultima8/gfx/gump_shape_archive.h
Normal file
52
engines/ultima/ultima8/gfx/gump_shape_archive.h
Normal 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
|
||||
113
engines/ultima/ultima8/gfx/inverter_process.cpp
Normal file
113
engines/ultima/ultima8/gfx/inverter_process.cpp
Normal 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
|
||||
63
engines/ultima/ultima8/gfx/inverter_process.h
Normal file
63
engines/ultima/ultima8/gfx/inverter_process.h
Normal 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
Reference in New Issue
Block a user