ALL: synced with scummvm

This commit is contained in:
Pawel Kolodziejski 2012-11-18 19:07:46 +01:00
parent 0116625b75
commit adcb0b8383
91 changed files with 1059 additions and 696 deletions

View File

@ -403,7 +403,7 @@ public:
}
int readBuffer(int16 *buffer, const int numSamples) {
// Cap us off so we don't read past _totalSamples
// Cap us off so we don't read past _totalSamples
int samplesRead = _parentStream->readBuffer(buffer, MIN<int>(numSamples, _totalSamples - _samplesRead));
_samplesRead += samplesRead;
return samplesRead;
@ -414,7 +414,7 @@ public:
int getRate() const { return _parentStream->getRate(); }
private:
int getChannels() const { return isStereo() ? 2 : 1; }
int getChannels() const { return isStereo() ? 2 : 1; }
AudioStream *_parentStream;
DisposeAfterUse::Flag _disposeAfterUse;

View File

@ -23,6 +23,7 @@
/**
* @file
* Sound decoder used in engines:
* - pegasus
* - saga
* - sci
* - sword1

View File

@ -240,7 +240,7 @@ MidiDriver::DeviceHandle MidiDriver::detectDevice(int flags) {
devStr = ConfMan.hasKey("gm_device") ? ConfMan.get("gm_device") : Common::String("null");
else
devStr = "auto";
// Default to Null device here, since we also register a default null setting for
// the MT32 or GM device in the config manager.
hdl = getDeviceHandle(devStr.empty() ? Common::String("null") : devStr);

View File

@ -18,7 +18,7 @@
#include "mt32emu.h"
#include "AReverbModel.h"
using namespace MT32Emu;
namespace MT32Emu {
// Default reverb settings for modes 0-2
@ -235,3 +235,5 @@ void AReverbModel::process(const float *inLeft, const float *inRight, float *out
outRight++;
}
}
}

View File

@ -20,15 +20,14 @@
#include "mt32emu.h"
#include "DelayReverb.h"
using namespace MT32Emu;
namespace MT32Emu {
// CONFIRMED: The values below are found via analysis of digital samples. Checked with all time and level combinations.
// Obviously:
// rightDelay = (leftDelay - 2) * 2 + 2
// echoDelay = rightDelay - 1
// Leaving these separate in case it's useful for work on other reverb modes...
const Bit32u REVERB_TIMINGS[8][3]= {
static const Bit32u REVERB_TIMINGS[8][3]= {
// {leftDelay, rightDelay, feedbackDelay}
{402, 802, 801},
{626, 1250, 1249},
@ -40,7 +39,7 @@ const Bit32u REVERB_TIMINGS[8][3]= {
{8002, 16002, 16001}
};
const float REVERB_FADE[8] = {0.0f, -0.049400051f, -0.08220577f, -0.131861118f, -0.197344907f, -0.262956344f, -0.345162114f, -0.509508615f};
static const float REVERB_FADE[8] = {0.0f, -0.049400051f, -0.08220577f, -0.131861118f, -0.197344907f, -0.262956344f, -0.345162114f, -0.509508615f};
const float REVERB_FEEDBACK67 = -0.629960524947437f; // = -EXP2F(-2 / 3)
const float REVERB_FEEDBACK = -0.682034520443118f; // = -EXP2F(-53 / 96)
const float LPF_VALUE = 0.594603558f; // = EXP2F(-0.75f)
@ -148,3 +147,5 @@ bool DelayReverb::isActive() const {
}
return false;
}
}

View File

@ -20,7 +20,7 @@
#include "freeverb.h"
using namespace MT32Emu;
namespace MT32Emu {
FreeverbModel::FreeverbModel(float useScaleTuning, float useFiltVal, float useWet, Bit8u useRoom, float useDamp) {
freeverb = NULL;
@ -76,3 +76,5 @@ bool FreeverbModel::isActive() const {
// FIXME: Not bothering to do this properly since we'll be replacing Freeverb soon...
return false;
}
}

View File

@ -79,11 +79,12 @@ LA32Ramp::LA32Ramp() :
void LA32Ramp::startRamp(Bit8u target, Bit8u increment) {
// CONFIRMED: From sample analysis, this appears to be very accurate.
// FIXME: We could use a table for this in future
if (increment == 0) {
largeIncrement = 0;
} else {
largeIncrement = (unsigned int)(EXP2F(((increment & 0x7F) + 24) / 8.0f) + 0.125f);
// Using integer argument here, no precision loss:
// (unsigned int)(EXP2F(((increment & 0x7F) + 24) / 8.0f) + 0.125f)
largeIncrement = (unsigned int)(EXP2I(((increment & 0x7F) + 24) << 9) + 0.125f);
}
descending = (increment & 0x80) != 0;
if (descending) {

View File

@ -411,7 +411,7 @@ void RhythmPart::noteOn(unsigned int midiKey, unsigned int velocity) {
// According to info from Mok, keyShift does not appear to affect anything on rhythm part on LAPC-I, but may do on MT-32 - needs investigation
synth->printDebug(" Patch: (timbreGroup %u), (timbreNum %u), (keyShift %u), fineTune %u, benderRange %u, assignMode %u, (reverbSwitch %u)", patchTemp->patch.timbreGroup, patchTemp->patch.timbreNum, patchTemp->patch.keyShift, patchTemp->patch.fineTune, patchTemp->patch.benderRange, patchTemp->patch.assignMode, patchTemp->patch.reverbSwitch);
synth->printDebug(" PatchTemp: outputLevel %u, (panpot %u)", patchTemp->outputLevel, patchTemp->panpot);
synth->printDebug(" RhythmTemp: timbre %u, outputLevel %u, panpot %u, reverbSwitch %u", rhythmTemp[drumNum].timbre, rhythmTemp[drumNum].outputLevel, rhythmTemp[drumNum].panpot, rhythmTemp[drumNum].reverbSwitch);
synth->printDebug(" RhythmTemp: timbre %u, outputLevel %u, panpot %u, reverbSwitch %u", rhythmTemp[drumNum].timbre, rhythmTemp[drumNum].outputLevel, rhythmTemp[drumNum].panpot, rhythmTemp[drumNum].reverbSwitch);
#endif
#endif
playPoly(drumCache[drumNum], &rhythmTemp[drumNum], midiKey, key, velocity);
@ -544,9 +544,12 @@ void Part::allNotesOff() {
// should treat the hold pedal as usual.
for (Common::List<Poly *>::iterator polyIt = activePolys.begin(); polyIt != activePolys.end(); polyIt++) {
Poly *poly = *polyIt;
// FIXME: This has special handling of key 0 in NoteOff that Mok has not yet confirmed
// applies to AllNotesOff.
poly->noteOff(holdpedal);
// FIXME: This has special handling of key 0 in NoteOff that Mok has not yet confirmed applies to AllNotesOff.
// if (poly->canSustain() || poly->getKey() == 0) {
// FIXME: The real devices are found to be ignoring non-sustaining polys while processing AllNotesOff. Need to be confirmed.
if (poly->canSustain()) {
poly->noteOff(holdpedal);
}
}
}

View File

@ -22,7 +22,7 @@
#include "mt32emu.h"
#include "mmath.h"
using namespace MT32Emu;
namespace MT32Emu {
#ifdef INACCURATE_SMOOTH_PAN
// Mok wanted an option for smoother panning, and we love Mok.
@ -133,6 +133,25 @@ void Partial::startPartial(const Part *part, Poly *usePoly, const PatchCache *us
stereoVolume.leftVol = panVal / 7.0f;
stereoVolume.rightVol = 1.0f - stereoVolume.leftVol;
// SEMI-CONFIRMED: From sample analysis:
// Found that timbres with 3 or 4 partials (i.e. one using two partial pairs) are mixed in two different ways.
// Either partial pairs are added or subtracted, it depends on how the partial pairs are allocated.
// It seems that partials are grouped into quarters and if the partial pairs are allocated in different quarters the subtraction happens.
// Though, this matters little for the majority of timbres, it becomes crucial for timbres which contain several partials that sound very close.
// In this case that timbre can sound totally different depending of the way it is mixed up.
// Most easily this effect can be displayed with the help of a special timbre consisting of several identical square wave partials (3 or 4).
// Say, it is 3-partial timbre. Just play any two notes simultaneously and the polys very probably are mixed differently.
// Moreover, the partial allocator retains the last partial assignment it did and all the subsequent notes will sound the same as the last released one.
// The situation is better with 4-partial timbres since then a whole quarter is assigned for each poly. However, if a 3-partial timbre broke the normal
// whole-quarter assignment or after some partials got aborted, even 4-partial timbres can be found sounding differently.
// This behaviour is also confirmed with two more special timbres: one with identical sawtooth partials, and one with PCM wave 02.
// For my personal taste, this behaviour rather enriches the sounding and should be emulated.
// Also, the current partial allocator model probably needs to be refined.
if (debugPartialNum & 8) {
stereoVolume.leftVol = -stereoVolume.leftVol;
stereoVolume.rightVol = -stereoVolume.rightVol;
}
if (patchCache->PCMPartial) {
pcmNum = patchCache->pcm;
if (synth->controlROMMap->pcmCount > 128) {
@ -149,7 +168,7 @@ void Partial::startPartial(const Part *part, Poly *usePoly, const PatchCache *us
}
// CONFIRMED: pulseWidthVal calculation is based on information from Mok
pulseWidthVal = (poly->getVelocity() - 64) * (patchCache->srcPartial.wg.pulseWidthVeloSensitivity - 7) + synth->tables.pulseWidth100To255[patchCache->srcPartial.wg.pulseWidth];
pulseWidthVal = (poly->getVelocity() - 64) * (patchCache->srcPartial.wg.pulseWidthVeloSensitivity - 7) + Tables::getInstance().pulseWidth100To255[patchCache->srcPartial.wg.pulseWidth];
if (pulseWidthVal < 0) {
pulseWidthVal = 0;
} else if (pulseWidthVal > 255) {
@ -175,6 +194,7 @@ float Partial::getPCMSample(unsigned int position) {
}
unsigned long Partial::generateSamples(float *partialBuf, unsigned long length) {
const Tables &tables = Tables::getInstance();
if (!isActive() || alreadyOutputed) {
return 0;
}
@ -197,6 +217,9 @@ unsigned long Partial::generateSamples(float *partialBuf, unsigned long length)
deactivate();
break;
}
Bit16u pitch = tvp->nextPitch();
// SEMI-CONFIRMED: From sample analysis:
// (1) Tested with a single partial playing PCM wave 77 with pitchCoarse 36 and no keyfollow, velocity follow, etc.
// This gives results within +/- 2 at the output (before any DAC bitshifting)
@ -206,10 +229,17 @@ unsigned long Partial::generateSamples(float *partialBuf, unsigned long length)
// positive amps, so negative still needs to be explored, as well as lower levels.
//
// Also still partially unconfirmed is the behaviour when ramping between levels, as well as the timing.
float amp = EXP2F((32772 - ampRampVal / 2048) / -2048.0f);
Bit16u pitch = tvp->nextPitch();
float freq = synth->tables.pitchToFreq[pitch];
#if MT32EMU_ACCURATE_WG == 1
float amp = EXP2F((32772 - ampRampVal / 2048) / -2048.0f);
float freq = EXP2F(pitch / 4096.0f - 16.0f) * 32000.0f;
#else
static const float ampFactor = EXP2F(32772 / -2048.0f);
float amp = EXP2I(ampRampVal >> 10) * ampFactor;
static const float freqFactor = EXP2F(-16.0f) * 32000.0f;
float freq = EXP2I(pitch) * freqFactor;
#endif
if (patchCache->PCMPartial) {
// Render PCM waveform
@ -225,8 +255,14 @@ unsigned long Partial::generateSamples(float *partialBuf, unsigned long length)
// Linear interpolation
float firstSample = synth->pcmROMData[pcmAddr + intPCMPosition];
float nextSample = getPCMSample(intPCMPosition + 1);
sample = firstSample + (nextSample - firstSample) * (pcmPosition - intPCMPosition);
// We observe that for partial structures with ring modulation the interpolation is not applied to the slave PCM partial.
// It's assumed that the multiplication circuitry intended to perform the interpolation on the slave PCM partial
// is borrowed by the ring modulation circuit (or the LA32 chip has a similar lack of resources assigned to each partial pair).
if (pair == NULL || mixType == 0 || structurePosition == 0) {
sample = firstSample + (getPCMSample(intPCMPosition + 1) - firstSample) * (pcmPosition - intPCMPosition);
} else {
sample = firstSample;
}
float newPCMPosition = pcmPosition + positionDelta;
if (pcmWave->loop) {
@ -247,8 +283,8 @@ unsigned long Partial::generateSamples(float *partialBuf, unsigned long length)
// res corresponds to a value set in an LA32 register
Bit8u res = patchCache->srcPartial.tvf.resonance + 1;
// EXP2F(1.0f - (32 - res) / 4.0f);
float resAmp = synth->tables.resAmpMax[res];
// Using tiny exact table for computation of EXP2F(1.0f - (32 - res) / 4.0f)
float resAmp = tables.resAmpMax[res];
// The cutoffModifier may not be supposed to be directly added to the cutoff -
// it may for example need to be multiplied in some way.
@ -268,7 +304,8 @@ unsigned long Partial::generateSamples(float *partialBuf, unsigned long length)
#if MT32EMU_ACCURATE_WG == 1
cosineLen *= EXP2F((cutoffVal - 128.0f) / -16.0f); // found from sample analysis
#else
cosineLen *= synth->tables.cutoffToCosineLen[Bit32u((cutoffVal - 128.0f) * 8.0f)];
static const float cosineLenFactor = EXP2F(128.0f / -16.0f);
cosineLen *= EXP2I(Bit32u((256.0f - cutoffVal) * 256.0f)) * cosineLenFactor;
#endif
}
@ -281,7 +318,7 @@ unsigned long Partial::generateSamples(float *partialBuf, unsigned long length)
float pulseLen = 0.5f;
if (pulseWidthVal > 128) {
pulseLen += synth->tables.pulseLenFactor[pulseWidthVal - 128];
pulseLen += tables.pulseLenFactor[pulseWidthVal - 128];
}
pulseLen *= waveLen;
@ -303,7 +340,7 @@ unsigned long Partial::generateSamples(float *partialBuf, unsigned long length)
#if MT32EMU_ACCURATE_WG == 1
resAmp *= sinf(FLOAT_PI * (cutoffVal - 128.0f) / 32.0f);
#else
resAmp *= synth->tables.sinf10[Bit32u(64 * (cutoffVal - 128.0f))];
resAmp *= tables.sinf10[Bit32u(64 * (cutoffVal - 128.0f))];
#endif
}
@ -314,7 +351,7 @@ unsigned long Partial::generateSamples(float *partialBuf, unsigned long length)
#if MT32EMU_ACCURATE_WG == 1
sample = -cosf(FLOAT_PI * relWavePos / cosineLen);
#else
sample = -synth->tables.sinf10[Bit32u(2048.0f * relWavePos / cosineLen) + 1024];
sample = -tables.sinf10[Bit32u(2048.0f * relWavePos / cosineLen) + 1024];
#endif
} else
@ -328,7 +365,7 @@ unsigned long Partial::generateSamples(float *partialBuf, unsigned long length)
#if MT32EMU_ACCURATE_WG == 1
sample = cosf(FLOAT_PI * (relWavePos - (cosineLen + hLen)) / cosineLen);
#else
sample = synth->tables.sinf10[Bit32u(2048.0f * (relWavePos - (cosineLen + hLen)) / cosineLen) + 1024];
sample = tables.sinf10[Bit32u(2048.0f * (relWavePos - (cosineLen + hLen)) / cosineLen) + 1024];
#endif
} else {
@ -343,7 +380,8 @@ unsigned long Partial::generateSamples(float *partialBuf, unsigned long length)
#if MT32EMU_ACCURATE_WG == 1
sample *= EXP2F(-0.125f * (128.0f - cutoffVal));
#else
sample *= synth->tables.cutoffToFilterAmp[Bit32u(cutoffVal * 8.0f)];
static const float cutoffAttenuationFactor = EXP2F(-0.125f * 128.0f);
sample *= EXP2I(Bit32u(512.0f * cutoffVal)) * cutoffAttenuationFactor;
#endif
} else {
@ -363,11 +401,17 @@ unsigned long Partial::generateSamples(float *partialBuf, unsigned long length)
#if MT32EMU_ACCURATE_WG == 1
resSample *= sinf(FLOAT_PI * relWavePos / cosineLen);
#else
resSample *= synth->tables.sinf10[Bit32u(2048.0f * relWavePos / cosineLen) & 4095];
resSample *= tables.sinf10[Bit32u(2048.0f * relWavePos / cosineLen) & 4095];
#endif
// Resonance sine amp
float resAmpFade = EXP2F(-synth->tables.resAmpFadeFactor[res >> 2] * (relWavePos / cosineLen)); // seems to be exact
float resAmpFadeLog2 = -tables.resAmpFadeFactor[res >> 2] * (relWavePos / cosineLen); // seems to be exact
#if MT32EMU_ACCURATE_WG == 1
float resAmpFade = EXP2F(resAmpFadeLog2);
#else
static const float resAmpFadeFactor = EXP2F(-30.0f);
float resAmpFade = (resAmpFadeLog2 < -30.0f) ? 0.0f : EXP2I(Bit32u((30.0f + resAmpFadeLog2) * 4096.0f)) * resAmpFadeFactor;
#endif
// Now relWavePos set negative to the left from center of any cosine
relWavePos = wavePos;
@ -388,7 +432,7 @@ unsigned long Partial::generateSamples(float *partialBuf, unsigned long length)
#if MT32EMU_ACCURATE_WG == 1
resAmpFade *= 0.5f * (1.0f - cosf(FLOAT_PI * relWavePos / (0.5f * cosineLen)));
#else
resAmpFade *= 0.5f * (1.0f + synth->tables.sinf10[Bit32s(2048.0f * relWavePos / (0.5f * cosineLen)) + 3072]);
resAmpFade *= 0.5f * (1.0f + tables.sinf10[Bit32s(2048.0f * relWavePos / (0.5f * cosineLen)) + 3072]);
#endif
}
@ -400,7 +444,7 @@ unsigned long Partial::generateSamples(float *partialBuf, unsigned long length)
#if MT32EMU_ACCURATE_WG == 1
sample *= cosf(FLOAT_2PI * wavePos / waveLen);
#else
sample *= synth->tables.sinf10[(Bit32u(4096.0f * wavePos / waveLen) & 4095) + 1024];
sample *= tables.sinf10[(Bit32u(4096.0f * wavePos / waveLen) & 4095) + 1024];
#endif
}
@ -516,10 +560,9 @@ bool Partial::produceOutput(float *leftBuf, float *rightBuf, unsigned long lengt
}
}
if (numGenerated > pairNumGenerated) {
if (mixType == 1) {
mixBuffersRingMix(partialBuf + pairNumGenerated, NULL, numGenerated - pairNumGenerated);
} else {
mixBuffersRing(partialBuf + pairNumGenerated, NULL, numGenerated - pairNumGenerated);
if (mixType == 2) {
numGenerated = pairNumGenerated;
deactivate();
}
}
}
@ -555,3 +598,5 @@ void Partial::startDecayAll() {
tvp->startDecay();
tvf->startDecay();
}
}

View File

@ -37,7 +37,7 @@ private:
const int debugPartialNum; // Only used for debugging
// Number of the sample currently being rendered by generateSamples(), or 0 if no run is in progress
// This is only kept available for debugging purposes.
unsigned long sampleNum;
unsigned long sampleNum;
int ownerPart; // -1 if unassigned
int mixType;

View File

@ -20,7 +20,7 @@
#include "mt32emu.h"
#include "PartialManager.h"
using namespace MT32Emu;
namespace MT32Emu {
PartialManager::PartialManager(Synth *useSynth, Part **useParts) {
synth = useSynth;
@ -148,7 +148,7 @@ bool PartialManager::abortFirstPolyPreferHeldWhereReserveExceeded(int minPart) {
bool PartialManager::freePartials(unsigned int needed, int partNum) {
// CONFIRMED: Barring bugs, this matches the real LAPC-I according to information from Mok.
// BUG: There's a bug in the LAPC-I implementation:
// BUG: There's a bug in the LAPC-I implementation:
// When allocating for rhythm part, or when allocating for a part that is using fewer partials than it has reserved,
// held and playing polys on the rhythm part can potentially be aborted before releasing polys on the rhythm part.
// This bug isn't present on MT-32.
@ -248,3 +248,5 @@ const Partial *PartialManager::getPartial(unsigned int partialNum) const {
}
return partialTable[partialNum];
}
}

View File

@ -58,6 +58,9 @@ bool Poly::noteOff(bool pedalHeld) {
return false;
}
if (pedalHeld) {
if (state == POLY_Held) {
return false;
}
state = POLY_Held;
} else {
startDecay();

View File

@ -36,7 +36,7 @@
namespace MT32Emu {
const ControlROMMap ControlROMMaps[7] = {
static const ControlROMMap ControlROMMaps[7] = {
// ID IDc IDbytes PCMmap PCMc tmbrA tmbrAO, tmbrAC tmbrB tmbrBO, tmbrBC tmbrR trC rhythm rhyC rsrv panpot prog rhyMax patMax sysMax timMax
{0x4014, 22, "\000 ver1.04 14 July 87 ", 0x3000, 128, 0x8000, 0x0000, false, 0xC000, 0x4000, false, 0x3200, 30, 0x73A6, 85, 0x57C7, 0x57E2, 0x57D0, 0x5252, 0x525E, 0x526E, 0x520A},
{0x4014, 22, "\000 ver1.05 06 Aug, 87 ", 0x3000, 128, 0x8000, 0x0000, false, 0xC000, 0x4000, false, 0x3200, 30, 0x7414, 85, 0x57C7, 0x57E2, 0x57D0, 0x5252, 0x525E, 0x526E, 0x520A},
@ -423,7 +423,6 @@ bool Synth::open(SynthProperties &useProp) {
#if MT32EMU_MONITOR_INIT
printDebug("Initialising Constant Tables");
#endif
tables.init();
#if !MT32EMU_REDUCE_REVERB_MEMORY
for (int i = 0; i < 4; i++) {
reverbModels[i]->open(useProp.sampleRate);
@ -627,6 +626,11 @@ void Synth::playMsg(Bit32u msg) {
return;
}
playMsgOnPart(part, code, note, velocity);
// This ensures minimum 1-sample delay between sequential MIDI events
// Without this, a sequence of NoteOn and immediately succeeding NoteOff messages is always silent
// Technically, it's also impossible to send events through the MIDI interface faster than about each millisecond
prerender();
}
void Synth::playMsgOnPart(unsigned char part, unsigned char code, unsigned char note, unsigned char velocity) {
@ -1156,7 +1160,7 @@ void Synth::writeMemoryRegion(const MemoryRegion *region, Bit32u addr, Bit32u le
DT(partial[x].tva.envLevel[0]); \
DT(partial[x].tva.envLevel[1]); \
DT(partial[x].tva.envLevel[2]); \
DT(partial[x].tva.envLevel[3]);
DT(partial[x].tva.envLevel[3]);
DTP(0);
DTP(1);

View File

@ -321,7 +321,6 @@ private:
Bit32u renderedSampleCount;
Tables tables;
MemParams mt32ram, mt32default;

View File

@ -154,7 +154,7 @@ void TVA::reset(const Part *newPart, const TimbreParam::PartialParam *newPartial
playing = true;
Tables *tables = &partial->getSynth()->tables;
const Tables *tables = &Tables::getInstance();
int key = partial->getPoly()->getKey();
int velocity = partial->getPoly()->getVelocity();
@ -215,7 +215,7 @@ void TVA::recalcSustain() {
return;
}
// We're sustaining. Recalculate all the values
Tables *tables = &partial->getSynth()->tables;
const Tables *tables = &Tables::getInstance();
int newTarget = calcBasicAmp(tables, partial, system_, partialParam, patchTemp, rhythmTemp, biasAmpSubtraction, veloAmpSubtraction, part->getExpression());
newTarget += partialParam->tva.envLevel[3];
// Since we're in TVA_PHASE_SUSTAIN at this point, we know that target has been reached and an interrupt fired, so we can rely on it being the current amp.
@ -241,7 +241,7 @@ int TVA::getPhase() const {
}
void TVA::nextPhase() {
Tables *tables = &partial->getSynth()->tables;
const Tables *tables = &Tables::getInstance();
if (phase >= TVA_PHASE_DEAD || !playing) {
partial->getSynth()->printDebug("TVA::nextPhase(): Shouldn't have got here with phase %d, playing=%s", phase, playing ? "true" : "false");
@ -274,7 +274,7 @@ void TVA::nextPhase() {
}
int newTarget;
int newIncrement;
int newIncrement = 0;
int envPointIndex = phase;
if (!allLevelsZeroFromNowOn) {

View File

@ -64,11 +64,11 @@ static int calcBaseCutoff(const TimbreParam::PartialParam *partialParam, Bit32u
int biasPoint = partialParam->tvf.biasPoint;
if ((biasPoint & 0x40) == 0) {
// biasPoint range here: 0 to 63
int bias = biasPoint + 33 - key; // bias range here: -75 to 84
int bias = biasPoint + 33 - key; // bias range here: -75 to 84
if (bias > 0) {
bias = -bias; // bias range here: -1 to -84
baseCutoff += bias * biasLevelToBiasMult[partialParam->tvf.biasLevel]; // Calculation range: -7140 to 7140
// baseCutoff range now: -10164 to 10164
// baseCutoff range now: -10164 to 10164
}
} else {
// biasPoint range here: 64 to 127
@ -117,7 +117,7 @@ void TVF::reset(const TimbreParam::PartialParam *newPartialParam, unsigned int b
unsigned int key = partial->getPoly()->getKey();
unsigned int velocity = partial->getPoly()->getVelocity();
Tables *tables = &partial->getSynth()->tables;
const Tables *tables = &Tables::getInstance();
baseCutoff = calcBaseCutoff(newPartialParam, basePitch, key);
#if MT32EMU_MONITOR_TVF >= 1
@ -179,7 +179,7 @@ void TVF::startDecay() {
}
void TVF::nextPhase() {
Tables *tables = &partial->getSynth()->tables;
const Tables *tables = &Tables::getInstance();
int newPhase = phase + 1;
switch (newPhase) {

View File

@ -171,9 +171,14 @@ void TVP::updatePitch() {
if (newPitch < 0) {
newPitch = 0;
}
// Note: Temporary #ifdef until we have proper "quirk" configuration
// This is about right emulation of MT-32 GEN0 quirk exploited in Colonel's Bequest timbre "Lightning"
#ifndef MT32EMU_QUIRK_PITCH_ENVELOPE_OVERFLOW_MT32
if (newPitch > 59392) {
newPitch = 59392;
}
#endif
pitch = (Bit16u)newPitch;
// FIXME: We're doing this here because that's what the CM-32L does - we should probably move this somewhere more appropriate in future.

View File

@ -22,18 +22,14 @@
#include "mt32emu.h"
#include "mmath.h"
using namespace MT32Emu;
namespace MT32Emu {
Tables::Tables() {
initialised = false;
const Tables &Tables::getInstance() {
static const Tables instance;
return instance;
}
void Tables::init() {
if (initialised) {
return;
}
initialised = true;
Tables::Tables() {
int lf;
for (lf = 0; lf <= 100; lf++) {
// CONFIRMED:KG: This matches a ROM table found by Mok
@ -83,19 +79,9 @@ void Tables::init() {
pulseLenFactor[i] = (1.241857812f - pt) * pt; // seems to be 2 ^ (5 / 16) = 1.241857812f
}
for (int i = 0; i < 65536; i++) {
// Aka (slightly slower): EXP2F(pitchVal / 4096.0f - 16.0f) * 32000.0f
pitchToFreq[i] = EXP2F(i / 4096.0f - 1.034215715f);
}
// found from sample analysis
for (int i = 0; i < 1024; i++) {
cutoffToCosineLen[i] = EXP2F(i / -128.0f);
}
// found from sample analysis
for (int i = 0; i < 1024; i++) {
cutoffToFilterAmp[i] = EXP2F(-0.125f * (128.0f - i / 8.0f));
// The LA32 chip presumably has such a table inside as the internal computaions seem to be performed using fixed point math with 12-bit fractions
for (int i = 0; i < 4096; i++) {
exp2[i] = EXP2F(i / 4096.0f);
}
// found from sample analysis
@ -117,3 +103,5 @@ void Tables::init() {
sinf10[i] = sin(FLOAT_PI * i / 2048.0f);
}
}
}

View File

@ -20,14 +20,21 @@
namespace MT32Emu {
// Sample rate to use in mixing
const unsigned int SAMPLE_RATE = 32000;
const int MIDDLEC = 60;
class Synth;
class Tables {
bool initialised;
private:
Tables();
Tables(Tables &);
public:
static const Tables &getInstance();
// Constant LUTs
// CONFIRMED: This is used to convert several parameters to amp-modifying values in the TVA envelope:
@ -47,16 +54,11 @@ public:
// CONFIRMED:
Bit8u pulseWidth100To255[101];
float exp2[4096];
float pulseLenFactor[128];
float pitchToFreq[65536];
float cutoffToCosineLen[1024];
float cutoffToFilterAmp[1024];
float resAmpMax[32];
float resAmpFadeFactor[8];
float sinf10[5120];
Tables();
void init();
};
}

View File

@ -202,7 +202,7 @@ void revmodel::process(const float *inputL, const float *inputR, float *outputL,
// Calculate output REPLACING anything already there
*outputL = outL*wet1 + outR*wet2;
*outputR = outR*wet1 + outL*wet2;
inputL++;
inputR++;
outputL++;

View File

@ -52,6 +52,10 @@ static inline float EXP2F(float x) {
#endif
}
static inline float EXP2I(unsigned int i) {
return float(1 << (i >> 12)) * Tables::getInstance().exp2[i & 0x0FFF];
}
static inline float EXP10F(float x) {
return exp(FLOAT_LN_10 * x);
}

View File

@ -59,9 +59,16 @@
#define MT32EMU_MONITOR_TVA 0
#define MT32EMU_MONITOR_TVF 0
// 0: Use LUTs to speedup WG
// 1: Use precise float math
// The WG algorithm involves dozens of transcendent maths, e.g. exponents and trigonometry.
// Unfortunately, the majority of systems perform such computations inefficiently,
// standard math libs and FPUs make no optimisations for single precision floats,
// and use no LUTs to speedup computing internal taylor series. Though, there're rare exceptions,
// and there's a hope it will become common soon.
// So, this is the crucial point of speed optimisations. We have now eliminated all the transcendent maths
// within the critical path and use LUTs instead.
// Besides, since the LA32 chip is assumed to use similar LUTs inside, the overall emulation accuracy should be better.
// 0: Use LUTs to speedup WG. Most common setting. You can expect about 50% performance boost.
// 1: Use precise float math. Use this setting to achieve more accurate wave generator. If your system performs better with this setting, it is really notable. :)
#define MT32EMU_ACCURATE_WG 0
#define MT32EMU_USE_EXTINT 0

View File

@ -204,6 +204,8 @@ void SdlEventSource::SDLModToOSystemKeyFlags(SDLMod mod, Common::Event &event) {
#endif
if (mod & KMOD_CTRL)
event.kbd.flags |= Common::KBD_CTRL;
if (mod & KMOD_META)
event.kbd.flags |= Common::KBD_META;
// Sticky flags
if (mod & KMOD_NUM)

View File

@ -116,7 +116,7 @@ protected:
//@}
/**
* Assigns the mouse coords to the mouse event. Furthermore notify the
* Assigns the mouse coords to the mouse event. Furthermore notify the
* graphics manager about the position change.
* ResidualVM addon:
* The parameters relx and rely for relative mouse movement are Residual specific

View File

@ -172,10 +172,15 @@ int MidiDriver_CORE::open() {
// Load custom soundfont, if specified
if (ConfMan.hasKey("soundfont")) {
FSRef fsref;
FSSpec fsSpec;
const char *soundfont = ConfMan.get("soundfont").c_str();
// TODO: We should really check whether the file contains an
// actual soundfont...
#if USE_DEPRECATED_COREAUDIO_API
// Before 10.5, we need to use kMusicDeviceProperty_SoundBankFSSpec
FSRef fsref;
FSSpec fsSpec;
err = FSPathMakeRef ((const byte *)soundfont, &fsref, NULL);
if (err == noErr) {
@ -183,8 +188,6 @@ int MidiDriver_CORE::open() {
}
if (err == noErr) {
// TODO: We should really check here whether the file contains an
// actual soundfont...
err = AudioUnitSetProperty (
_synth,
kMusicDeviceProperty_SoundBankFSSpec, kAudioUnitScope_Global,
@ -192,9 +195,27 @@ int MidiDriver_CORE::open() {
&fsSpec, sizeof(fsSpec)
);
}
#else
// kMusicDeviceProperty_SoundBankFSSpec is present on 10.6+, but broken
// kMusicDeviceProperty_SoundBankURL was added in 10.5 as a replacement
CFURLRef url = CFURLCreateFromFileSystemRepresentation(kCFAllocatorDefault, (const UInt8 *)soundfont, strlen(soundfont), false);
if (url) {
err = AudioUnitSetProperty (
_synth,
kMusicDeviceProperty_SoundBankURL, kAudioUnitScope_Global,
0,
&url, sizeof(url)
);
CFRelease(url);
} else {
warning("Failed to allocate CFURLRef from '%s'", soundfont);
}
#endif
if (err != noErr)
warning("Failed loading custom sound font '%s' (error %ld)\n", soundfont, (long)err);
error("Failed loading custom sound font '%s' (error %ld)", soundfont, (long)err);
}
#ifdef COREAUDIO_DISABLE_REVERB

View File

@ -81,7 +81,7 @@ void MidiDriver_Sndio::send(uint32 b) {
if (!hdl)
return;
buf[0] = b & 0xff;
buf[0] = b & 0xff;
buf[1] = (b >> 8) & 0xff;
buf[2] = (b >> 16) & 0xff;
buf[3] = (b >> 24) & 0xff;
@ -101,7 +101,7 @@ void MidiDriver_Sndio::send(uint32 b) {
void MidiDriver_Sndio::sysEx(const byte *msg, uint16 length) {
if (!hdl)
return;
unsigned char buf[266];
assert(length + 2 <= ARRAYSIZE(buf));

View File

@ -69,13 +69,13 @@ void Sdl13MixerManager::init() {
warning("Could not open audio device: %s", SDL_GetError());
_mixer = new Audio::MixerImpl(g_system, desired.freq);
assert(_mixer);
assert(_mixer);
_mixer->setReady(false);
} else {
debug(1, "Output sample rate: %d Hz", _obtained.freq);
_mixer = new Audio::MixerImpl(g_system, _obtained.freq);
assert(_mixer);
assert(_mixer);
_mixer->setReady(true);
startAudio();

View File

@ -99,9 +99,8 @@ endif
ifdef AMIGAOS
MODULE_OBJS += \
fs/amigaos4/amigaos4-fs.o \
fs/amigaos4/amigaos4-fs-factory.o
#ResidualVM: disabled below
# midi/camd.o
fs/amigaos4/amigaos4-fs-factory.o \
midi/camd.o
endif
ifdef PLAYSTATION3

View File

@ -35,9 +35,8 @@
- (void)setAppleMenu:(NSMenu *)menu;
@end
NSString *constructNSStringFromCString(const char* rawCString, NSStringEncoding stringEncoding) {
NSData *nsData = [NSData dataWithBytes:rawCString length:strlen(rawCString)];
return [[NSString alloc] initWithData:nsData encoding:stringEncoding];
NSString *constructNSStringFromCString(const char *rawCString, CFStringEncoding stringEncoding) {
return (NSString *)CFStringCreateWithCString(NULL, rawCString, stringEncoding);
}
void replaceApplicationMenuItems() {
@ -59,11 +58,11 @@ void replaceApplicationMenuItems() {
// Get current encoding
#ifdef USE_TRANSLATION
nsString = constructNSStringFromCString((TransMan.getCurrentCharset()).c_str(), NSASCIIStringEncoding);
NSStringEncoding stringEncoding = CFStringConvertEncodingToNSStringEncoding(CFStringConvertIANACharSetNameToEncoding((CFStringRef)nsString));
nsString = constructNSStringFromCString(TransMan.getCurrentCharset().c_str(), NSASCIIStringEncoding);
CFStringEncoding stringEncoding = CFStringConvertIANACharSetNameToEncoding((CFStringRef)nsString);
[nsString release];
#else
NSStringEncoding stringEncoding = NSASCIIStringEncoding;
CFStringEncoding stringEncoding = kCFStringEncodingASCII;
#endif
// Add "About ScummVM" menu item

View File

@ -39,7 +39,6 @@
#include "ApplicationServices/ApplicationServices.h" // for LSOpenFSRef
#include "CoreFoundation/CoreFoundation.h" // for CF* stuff
#include "CoreServices/CoreServices.h" // for FSPathMakeRef
OSystem_MacOSX::OSystem_MacOSX()
:
@ -107,13 +106,9 @@ bool OSystem_MacOSX::displayLogFile() {
if (_logFilePath.empty())
return false;
FSRef ref;
OSStatus err;
err = FSPathMakeRef((const UInt8 *)_logFilePath.c_str(), &ref, NULL);
if (err == noErr) {
err = LSOpenFSRef(&ref, NULL);
}
CFURLRef url = CFURLCreateFromFileSystemRepresentation(kCFAllocatorDefault, (const UInt8 *)_logFilePath.c_str(), _logFilePath.size(), false);
OSStatus err = LSOpenCFURLRef(url, NULL);
CFRelease(url);
return err != noErr;
}
@ -156,7 +151,7 @@ Common::String OSystem_MacOSX::getSystemLanguage() const {
}
CFRelease(preferredLocalizations);
}
}
// Falback to POSIX implementation
return OSystem_POSIX::getSystemLanguage();

View File

@ -32,7 +32,7 @@ public:
virtual bool hasFeature(Feature f);
virtual bool displayLogFile();
virtual Common::String getSystemLanguage() const;
virtual void initBackend();

View File

@ -1,66 +0,0 @@
/* ScummVM - Graphic Adventure Engine
*
* ScummVM is the legal property of its developers, whose names
* are too numerous to list here. Please refer to the COPYRIGHT
* file distributed with this source distribution.
*
* This program is free software; you can redistribute it and/or
* modify it under the terms of the GNU General Public License
* as published by the Free Software Foundation; either version 2
* of the License, or (at your option) any later version.
* This program is distributed in the hope that it will be useful,
* but WITHOUT ANY WARRANTY; without even the implied warranty of
* MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
* GNU General Public License for more details.
* You should have received a copy of the GNU General Public License
* along with this program; if not, write to the Free Software
* Foundation, Inc., 51 Franklin Street, Fifth Floor, Boston, MA 02110-1301, USA.
*
*/
#include "common/scummsys.h"
// Several SDL based ports use a custom main, and hence do not want to compile
// of this file. The following "#if" ensures that.
#if !defined(POSIX) && \
!defined(WIN32) && \
!defined(MAEMO) && \
!defined(__SYMBIAN32__) && \
!defined(_WIN32_WCE) && \
!defined(__amigaos4__) && \
!defined(DINGUX) && \
!defined(CAANOO) && \
!defined(LINUXMOTO) && \
!defined(SAMSUNGTV) && \
!defined(PLAYSTATION3) && \
!defined(OPENPANDORA)
#include "backends/platform/sdl/sdl.h"
#include "backends/plugins/sdl/sdl-provider.h"
#include "base/main.h"
int main(int argc, char *argv[]) {
// Create our OSystem instance
g_system = new OSystem_SDL();
assert(g_system);
// Pre initialize the backend
((OSystem_SDL *)g_system)->init();
#ifdef DYNAMIC_MODULES
PluginManager::instance().addPluginProvider(new SDLPluginProvider());
#endif
// Invoke the actual ScummVM main entry point:
int res = scummvm_main(argc, argv);
// Free OSystem
delete (OSystem_SDL *)g_system;
return res;
}
#endif

View File

@ -70,7 +70,7 @@ DECLARE_INTERFACE_(IPropertyStore, IUnknown) {
STDMETHOD (GetValue) (REFPROPERTYKEY key, PROPVARIANT *pv) PURE;
STDMETHOD (SetValue) (REFPROPERTYKEY key, REFPROPVARIANT propvar) PURE;
STDMETHOD (Commit) (void) PURE;
private:
~IPropertyStore();
};
@ -137,7 +137,7 @@ DECLARE_INTERFACE_(ITaskbarList3, IUnknown) {
STDMETHOD (SetOverlayIcon) (THIS_ HWND hwnd, HICON hIcon, LPCWSTR pszDescription) PURE;
STDMETHOD (SetThumbnailTooltip) (THIS_ HWND hwnd, LPCWSTR pszTip) PURE;
STDMETHOD (SetThumbnailClip) (THIS_ HWND hwnd, RECT *prcClip) PURE;
private:
~ITaskbarList3();
};

View File

@ -156,7 +156,7 @@ void DefaultTimerManager::removeTimerProc(TimerProc callback) {
}
// We need to remove all names referencing the timer proc here.
//
//
// Else we run into troubles, when the client code removes and readds timer
// callbacks.
//

View File

@ -59,10 +59,14 @@ MacOSXUpdateManager::MacOSXUpdateManager() {
[sparkleUpdater setFeedURL:[NSURL URLWithString:feedbackURL]];
// Get current encoding
NSStringEncoding stringEncoding = CFStringConvertEncodingToNSStringEncoding(CFStringConvertIANACharSetNameToEncoding((CFStringRef)[NSString stringWithCString:(TransMan.getCurrentCharset()).c_str() encoding:NSASCIIStringEncoding]));
CFStringRef encStr = CFStringCreateWithCString(NULL, TransMan.getCurrentCharset().c_str(), kCFStringEncodingASCII);
CFStringEncoding stringEncoding = CFStringConvertIANACharSetNameToEncoding(encStr);
CFRelease(encStr);
// Add "Check for Updates..." menu item
NSMenuItem *updateMenuItem = [applicationMenu insertItemWithTitle:[NSString stringWithCString:_("Check for Updates...") encoding:stringEncoding] action:@selector(checkForUpdates:) keyEquivalent:@"" atIndex:1];
CFStringRef title = CFStringCreateWithCString(NULL, _("Check for Updates..."), stringEncoding);
NSMenuItem *updateMenuItem = [applicationMenu insertItemWithTitle:(NSString *)title action:@selector(checkForUpdates:) keyEquivalent:@"" atIndex:1];
CFRelease(title);
// Set the target of the new menu item
[updateMenuItem setTarget:sparkleUpdater];

View File

@ -57,6 +57,7 @@
#include "graphics/cursorman.h"
#include "graphics/fontman.h"
#include "graphics/yuv_to_rgb.h"
#ifdef USE_FREETYPE2
#include "graphics/fonts/ttf.h"
#endif
@ -512,6 +513,8 @@ extern "C" int scummvm_main(int argc, const char * const argv[]) {
#ifdef USE_FREETYPE2
Graphics::shutdownTTF();
#endif
EngineManager::destroy();
Graphics::YUVToRGBManager::destroy();
return 0;
}

View File

@ -111,8 +111,7 @@ public:
LINK_PLUGIN(DMEDIA)
#endif
#if defined(__amigaos4__)
//ResidualVM: disabled below
// LINK_PLUGIN(CAMD)
LINK_PLUGIN(CAMD)
#endif
#if defined(MACOSX)
LINK_PLUGIN(COREAUDIO)
@ -135,7 +134,7 @@ public:
#ifndef DISABLE_SID
// LINK_PLUGIN(C64)
#endif
// LINK_PLUGIN(AMIGA)
LINK_PLUGIN(AMIGA)
// LINK_PLUGIN(APPLEIIGS)
// LINK_PLUGIN(TOWNS)
// LINK_PLUGIN(PC98)

View File

@ -332,7 +332,7 @@ protected:
// Copy a part of the new data to the position inside the
// initialized space.
copy(first, first + (_size - idx), pos);
// Copy a part of the new data to the position inside the
// uninitialized space.
uninitialized_copy(first + (_size - idx), last, _storage + _size);

View File

@ -36,7 +36,7 @@ CosineTable::CosineTable(int bitPrecision) {
double freq = 2 * M_PI / m;
_table = new float[m];
// Table contains cos(2*pi*x/n) for 0<=x<=n/4,
// Table contains cos(2*pi*x/n) for 0<=x<=n/4,
// followed by its reverse
for (int i = 0; i <= m / 4; i++)
_table[i] = cos(i * freq);

View File

@ -55,7 +55,7 @@
#define GUIO_RENDERPC9821 "\037"
#define GUIO_RENDERPC9801 "\040"
// Special GUIO flags for the AdvancedDetector's caching of game specific
// Special GUIO flags for the AdvancedDetector's caching of game specific
// options.
#define GUIO_GAMEOPTIONS1 "\041"
#define GUIO_GAMEOPTIONS2 "\042"

View File

@ -224,12 +224,13 @@ enum {
KBD_CTRL = 1 << 0,
KBD_ALT = 1 << 1,
KBD_SHIFT = 1 << 2,
KBD_NON_STICKY = (KBD_CTRL|KBD_ALT|KBD_SHIFT),
KBD_META = 1 << 3,
KBD_NON_STICKY = (KBD_CTRL|KBD_ALT|KBD_SHIFT|KBD_META),
// Sticky modifier flags
KBD_NUM = 1 << 3,
KBD_CAPS = 1 << 4,
KBD_SCRL = 1 << 5,
KBD_NUM = 1 << 4,
KBD_CAPS = 1 << 5,
KBD_SCRL = 1 << 6,
KBD_STICKY = (KBD_NUM|KBD_CAPS|KBD_SCRL)
};

View File

@ -124,7 +124,7 @@ bool MacResManager::open(String filename) {
File *file = new File();
// First, let's try to see if the Mac converted name exists
if (file->open("._" + filename) && loadFromAppleDouble(*file)) {
if (file->open(constructAppleDoubleName(filename)) && loadFromAppleDouble(*file)) {
_baseFileName = filename;
return true;
}
@ -185,7 +185,7 @@ bool MacResManager::open(FSNode path, String filename) {
#endif
// First, let's try to see if the Mac converted name exists
FSNode fsNode = path.getChild("._" + filename);
FSNode fsNode = path.getChild(constructAppleDoubleName(filename));
if (fsNode.exists() && !fsNode.isDirectory()) {
SeekableReadStream *stream = fsNode.createReadStream();
if (loadFromAppleDouble(*stream)) {
@ -253,7 +253,7 @@ bool MacResManager::exists(const String &filename) {
return true;
// Check if we have an AppleDouble file
if (tempFile.open("._" + filename) && tempFile.readUint32BE() == 0x00051607)
if (tempFile.open(constructAppleDoubleName(filename)) && tempFile.readUint32BE() == 0x00051607)
return true;
return false;
@ -574,4 +574,20 @@ void MacResManager::readMap() {
}
}
Common::String MacResManager::constructAppleDoubleName(Common::String name) {
// Insert "._" before the last portion of a path name
for (int i = name.size() - 1; i >= 0; i--) {
if (i == 0) {
name.insertChar('_', 0);
name.insertChar('.', 0);
} else if (name[i] == '/') {
name.insertChar('_', i + 1);
name.insertChar('.', i + 1);
break;
}
}
return name;
}
} // End of namespace Common

View File

@ -25,6 +25,7 @@
* Macintosh resource fork manager used in engines:
* - groovie
* - mohawk
* - pegasus
* - sci
* - scumm
*/
@ -175,6 +176,8 @@ private:
bool loadFromMacBinary(SeekableReadStream &stream);
bool loadFromAppleDouble(SeekableReadStream &stream);
static Common::String constructAppleDoubleName(Common::String name);
enum {
kResForkNone = 0,
kResForkRaw,

View File

@ -92,13 +92,17 @@ private:
byte *_ptr;
const uint32 _bufSize;
uint32 _pos;
bool _err;
public:
MemoryWriteStream(byte *buf, uint32 len) : _ptr(buf), _bufSize(len), _pos(0) {}
MemoryWriteStream(byte *buf, uint32 len) : _ptr(buf), _bufSize(len), _pos(0), _err(false) {}
uint32 write(const void *dataPtr, uint32 dataSize) {
// Write at most as many bytes as are still available...
if (dataSize > _bufSize - _pos)
if (dataSize > _bufSize - _pos) {
dataSize = _bufSize - _pos;
// We couldn't write all the data => set error indicator
_err = true;
}
memcpy(_ptr, dataPtr, dataSize);
_ptr += dataSize;
_pos += dataSize;
@ -107,6 +111,9 @@ public:
uint32 pos() const { return _pos; }
uint32 size() const { return _bufSize; }
virtual bool err() const { return _err; }
virtual void clearErr() { _err = false; }
};
/**

View File

@ -80,6 +80,9 @@ public:
double toDouble() const;
frac_t toFrac() const;
int getNumerator() const { return _num; }
int getDenominator() const { return _denom; }
void debugPrint(int debuglevel = 0, const char *caption = "Rational:") const;
private:

View File

@ -169,6 +169,20 @@ struct Rect {
return (left < r.right) && (r.left < right) && (top < r.bottom) && (r.top < bottom);
}
/**
* Find the intersecting rectangle between this rectangle and the given rectangle
*
* @param r the intersecting rectangle
*
* @return the intersection of the rectangles or an empty rectangle if not intersecting
*/
Rect findIntersectingRect(const Rect &r) const {
if (!intersects(r))
return Rect();
return Rect(MAX(r.left, left), MAX(r.top, top), MIN(r.right, right), MIN(r.bottom, bottom));
}
/**
* Extend this rectangle so that it contains r
*

View File

@ -109,12 +109,12 @@ public:
*
* Saved games are compressed by default, and engines are expected to
* always write compressed saves.
*
*
* A notable exception is if uncompressed files are needed for
* compatibility with games not supported by ScummVM, such as character
* exports from the Quest for Glory series. QfG5 is a 3D game and won't be
* supported by ScummVM.
*
*
* @param name the name of the savefile
* @param compress toggles whether to compress the resulting save file
* (default) or not.

View File

@ -36,7 +36,7 @@ SineTable::SineTable(int bitPrecision) {
double freq = 2 * M_PI / m;
_table = new float[m];
// Table contains sin(2*pi*x/n) for 0<=x<=n/4,
// Table contains sin(2*pi*x/n) for 0<=x<=n/4,
// followed by its reverse
for (int i = 0; i <= m / 4; i++)
_table[i] = sin(i * freq);

View File

@ -114,7 +114,7 @@ bool inflateZlibInstallShield(byte *dst, uint dstLen, const byte *src, uint srcL
* returned).
*
* @param toBeWrapped the stream to be wrapped (if it is in gzip-format)
* @param knownSize a supplied length of the compressed data (if not available directly)
* @param knownSize a supplied length of the compressed data (if not available directly)
*/
SeekableReadStream *wrapCompressedReadStream(SeekableReadStream *toBeWrapped, uint32 knownSize = 0);

209
configure vendored
View File

@ -62,7 +62,7 @@ get_var() {
eval echo \$${1}
}
# Add an engine: id name build subengines dependencies
# Add an engine: id name build subengines base-games dependencies
add_engine() {
_engines="${_engines} ${1}"
if test "${3}" = "no" ; then
@ -72,9 +72,11 @@ add_engine() {
set_var _engine_${1}_build "${3}"
set_var _engine_${1}_build_default "${3}"
set_var _engine_${1}_subengines "${4}"
set_var _engine_${1}_deps "${5}"
set_var _engine_${1}_base "${5}"
set_var _engine_${1}_deps "${6}"
for sub in ${4}; do
set_var _engine_${sub}_sub "yes"
set_var _engine_${sub}_parent "${1}"
done
}
@ -95,7 +97,7 @@ _srcdir=`dirname $0`
# Default settings
#
#ResidualVM defaults: mpeg2=auto, faad=no, opengles=no, vorbis=no, tremor=no
# mt32emu=no, translation=no, flac=no, seq_midi=no, timidity=no, png=no
# mt32emu=no, translation=no, flac=no, seq_midi=no, snd_io=no, timidity=no, png=no
# theoradec=no, fluidsynth=no
#
# Default lib behaviour yes/no/auto
@ -106,6 +108,7 @@ _flac=no
_mad=auto
_alsa=auto
_seq_midi=no
_sndio=no
_timidity=no
_zlib=auto
_sparkle=auto
@ -121,7 +124,7 @@ _freetype2=auto
_taskbar=yes
_updates=no
_libunity=auto
# Default option behaviour yes/no
# Default option behavior yes/no
_debug_build=auto
_release_build=auto
_optimizations=auto
@ -513,6 +516,11 @@ get_engine_dependencies() {
get_var _engine_$1_deps
}
# Get the base engine game support description
get_engine_base() {
get_var _engine_$1_base
}
# Ask if this is a subengine
get_engine_sub() {
sub=`get_var _engine_$1_sub`
@ -522,6 +530,11 @@ get_engine_sub() {
echo $sub
}
# Get a subengine's parent (undefined for non-subengines)
get_subengine_parent() {
get_var _engine_$1_parent
}
# Enable *all* engines
engine_enable_all() {
for engine in $_engines; do
@ -549,9 +562,15 @@ engine_enable() {
engine=`echo $eng | sed 's/-/_/g'`
# Filter the parameter for the subengines
if test "`get_engine_sub ${engine}`" != "no" -a "$opt" != "yes" ; then
subengine_option_error ${engine}
return
if test "`get_engine_sub ${engine}`" != "no" ; then
if test "$opt" != "yes" ; then
subengine_option_error ${engine}
return
fi
parent=`get_subengine_parent ${engine}`
if test `get_engine_build ${parent}` = "no" ; then
set_var _engine_${parent}_build "yes"
fi
fi
if test "$opt" = "static" -o "$opt" = "dynamic" -o "$opt" = "yes" ; then
@ -587,7 +606,7 @@ check_engine_deps() {
unmet_deps=""
# Check whether the engine is enabled
if test `get_engine_build $1` = yes ; then
if test `get_engine_build $1` != "no" ; then
# Collect unmet dependencies
for dep in `get_engine_dependencies $1`; do
if test `get_feature_state $dep` = "no"; then
@ -646,27 +665,37 @@ prepare_engine_build_strings() {
# Get the string about building an engine
get_engine_build_string() {
engine=$1
request_status=$2
engine_string=""
engine_build=`get_engine_build $1`
engine_build_default=`get_engine_build_default $1`
engine_build_default=`get_engine_build_default $engine`
show=no
# Convert static/dynamic to yes to ease the check of subengines
if test $engine_build = no; then
subengine_filter=no
else
subengine_filter=yes
fi
# Check if the current engine should be shown for the current status
if test $engine_build = $2 ; then
if test $engine_build = $request_status ; then
show=yes
else
# Test for disabled sub-engines
if test $2 = no ; then
for subeng in `get_engine_subengines $1` ; do
if test $request_status = no ; then
for subeng in `get_engine_subengines $engine` ; do
if test `get_engine_build $subeng` = no ; then
engine_build=no
# In this case we to display _disabled_ subengines
subengine_filter=no
show=yes
fi
done
fi
# Test for enabled wip sub-engines
if test $2 = wip ; then
for subeng in `get_engine_subengines $1` ; do
if test $request_status = wip ; then
for subeng in `get_engine_subengines $engine` ; do
if test `get_engine_build $subeng` != no -a `get_engine_build_default $subeng` = no ; then
show=yes
fi
@ -674,85 +703,82 @@ get_engine_build_string() {
fi
fi
# Convert static/dynamic to yes to ease the check of subengines
if test $engine_build != no ; then
engine_build=yes
fi
# Check if it is a wip engine
if test "$2" = "wip" -a "$engine_build" != "no" -a "$engine_build_default" = no; then
if test "$request_status" = "wip" -a "$engine_build" != "no" -a "$engine_build_default" = no; then
show=yes
fi
# The engine should be shown, build the string
if test $show = yes ; then
build_string_func=get_${1}_build_string
if ( type $build_string_func | grep function ) 2> /dev/null > /dev/null ; then
engine_string=`$build_string_func $1 $engine_build $2`
else
engine_string=`get_subengines_build_string $1 $engine_build "" $2`
fi
engine_string="`get_engine_name $1` $engine_string"
engine_string=`get_subengines_build_string $engine $subengine_filter $request_status`
engine_string="`get_engine_name $engine` $engine_string"
fi
echo $engine_string
echo "$engine_string"
}
# Get the string about building subengines
get_subengines_build_string() {
all=yes
parent_engine=$1
subengine_string=$3
parent_status=$4
subengine_filter=$2
request_status=$3
parent_engine_build_default=`get_engine_build_default $parent_engine`
subengine_string=""
# If the base engine isn't built at all, no need to list subengines
# in any of the possible categories.
if test `get_engine_build $parent_engine` = no; then
return
fi
all=yes
# If there are no subengines, never display "[all games]" (for brevity).
if test -z "`get_engine_subengines $parent_engine`"; then
all=no
fi
# If the base engine does not fit the category we're displaying here
# (WIP or Skipped), we should never show "[all games]"
if test "$request_status" = wip; then
if test $parent_engine_build_default = yes; then
all=no
fi
fi
if test "$request_status" = no; then
# If we're here, the parent engine is built, so no need to check that.
all=no
fi
# In the static/dynamic categories, also display the engine's base games.
if test -n "`get_engine_subengines $parent_engine`" -a $request_status != no -a $request_status != wip; then
subengine_string="[`get_engine_base $parent_engine`]"
fi
for subeng in `get_engine_subengines $parent_engine` ; do
subengine_build=`get_engine_build $subeng`
subengine_build_default=`get_engine_build_default $subeng`
if test \( $subengine_build = $2 -a "$parent_status" != wip \) -o \( "$parent_status" = wip -a $subengine_build != no -a "$subengine_build_default" = no \) ; then
subengine_string="$subengine_string [`get_engine_name $subeng`]"
# Display this subengine if it matches the filter, unless it is
# a stable subengine in the WIP request.
if test $subengine_build = $subengine_filter -a \! \( "$request_status" = wip -a "$subengine_build_default" = yes \) ; then
s="[`get_engine_name $subeng`]"
if test -n "$subengine_string"; then
subengine_string="$subengine_string $s"
else
subengine_string="$s"
fi
else
all=no
fi
# handle engines that are on by default and have a single subengine that is off by default
if test "$parent_status" = wip ; then
if test $parent_engine_build_default = yes -a subengine ; then
all=no
fi
fi
done
if test $2 != no ; then
if test -n "$subengine_string" ; then
if test $all = yes ; then
subengine_string="[all games]"
fi
fi
# Summarize the full list, where applicable
if test $all = yes ; then
subengine_string="[all games]"
fi
echo $subengine_string
}
# Engine specific build strings
get_scumm_build_string() {
if test `get_engine_build $1` != no ; then
if test $2 != no -a "$3" != wip ; then
base="[v0-v6 games]"
fi
get_subengines_build_string $1 $2 "$base" $3
fi
}
get_saga_build_string() {
if test `get_engine_build $1` != no ; then
if test $2 != no -a "$3" != wip; then
base="[ITE]"
fi
get_subengines_build_string $1 $2 "$base" $3
fi
echo "$subengine_string"
}
#
@ -945,6 +971,7 @@ for ac_option in $@; do
--disable-sparkle) _sparkle=no ;;
--enable-nasm) _nasm=yes ;;
--disable-nasm) _nasm=no ;;
#ResidualVM specific option:
--enable-mpeg2) _mpeg2=yes ;;
--disable-mpeg2) _mpeg2=no ;;
--disable-png) _png=no ;;
@ -988,6 +1015,7 @@ for ac_option in $@; do
FLUIDSYNTH_CFLAGS="-I$arg/include"
FLUIDSYNTH_LIBS="-L$arg/lib"
;;
#ResidualVM specific option:
--with-mpeg2-prefix=*)
arg=`echo $ac_option | cut -d '=' -f 2`
MPEG2_CFLAGS="-I$arg/include"
@ -1063,6 +1091,12 @@ for ac_option in $@; do
LIBUNITY_CFLAGS="-I$arg/include"
LIBUNITY_LIBS="-L$arg/lib"
;;
// ResidualVM don't use that
// --with-opengl-prefix=*)
// arg=`echo $ac_option | cut -d '=' -f 2`
// OPENGL_CFLAGS="-I$arg/include"
// OPENGL_LIBS="-L$arg/lib"
// ;;
--backend=*)
_backend=`echo $ac_option | cut -d '=' -f 2`
;;
@ -2484,10 +2518,13 @@ if test -n "$_host"; then
# Use -O3 on the OpenPandora for non-debug builds.
_optimization_level=-O3
fi
define_in_config_if_yes yes 'USE_ARM_NEON_ASPECT_CORRECTOR'
CXXFLAGS="$CXXFLAGS -march=armv7-a"
CXXFLAGS="$CXXFLAGS -mtune=cortex-a8"
CXXFLAGS="$CXXFLAGS -mfloat-abi=softfp"
CXXFLAGS="$CXXFLAGS -mfpu=neon"
ASFLAGS="$ASFLAGS -mfloat-abi=soft"
CXXFLAGS="$CXXFLAGS -fsingle-precision-constant"
ASFLAGS="$ASFLAGS -mfloat-abi=softfp"
_backend="openpandora"
_build_hq_scalers=yes
_vkeybd=no
@ -2496,6 +2533,16 @@ if test -n "$_host"; then
_port_mk="backends/platform/openpandora/op-bundle.mk"
;;
ppc-amigaos)
# Only static builds link successfully on buildbot
LDFLAGS=`echo $LDFLAGS | sed 's/-use-dynld//'`
LDFLAGS="$LDFLAGS -static"
# toolchain binaries prefixed by host
_ranlib=$_host-ranlib
_strip=$_host-strip
_ar="$_host-ar cru"
_as="$_host-as"
_windres=$_host-windres
;;
ps2)
DEFINES="$DEFINES -DDISABLE_TEXT_CONSOLE"
@ -2936,7 +2983,7 @@ PLUGIN_LDFLAGS += -Wl,-T$(srcdir)/backends/plugins/ds/plugin.ld -mthumb-interwo
freebsd*)
_plugin_prefix="lib"
_plugin_suffix=".so"
CXXFLAGS="$CXXFLAGS -fpic"
CXXFLAGS="$CXXFLAGS -fPIC"
_mak_plugins='
PLUGIN_EXTRA_DEPS =
PLUGIN_LDFLAGS += -shared
@ -2980,7 +3027,7 @@ POST_OBJS_FLAGS := -Wl,-no-whole-archive
linux*)
_plugin_prefix="lib"
_plugin_suffix=".so"
CXXFLAGS="$CXXFLAGS -fpic"
CXXFLAGS="$CXXFLAGS -fPIC"
LIBS="$LIBS -ldl"
_mak_plugins='
PLUGIN_EXTRA_DEPS =
@ -3391,6 +3438,7 @@ fi
echo "$_sparkle"
#
# ResidualVM specific
# Check for LibMPEG2
#
echocheck "libmpeg2 >= 0.3.2"
@ -4006,9 +4054,6 @@ for engine in $_engines; do
isbuilt=STATIC_PLUGIN
fi
fi
# Prepare the information to be shown
prepare_engine_build_strings $engine
else
# It's a subengine, just say yes or no
if test "`get_engine_build $engine`" = "no" ; then
@ -4027,6 +4072,14 @@ for engine in $_engines; do
fi
done
# Prepare the information to be shown
for engine in $_engines; do
if test "`get_engine_sub $engine`" = "no" ; then
# It's a main engine
prepare_engine_build_strings $engine
fi
done
#
# Detection of WIP/unstable engines
#
@ -4054,28 +4107,28 @@ fi
echo
if test -n "$_engines_built_static" ; then
echo "Engines (builtin):"
echo $_engines_built_static | sed 's/@/\
echo "$_engines_built_static" | sed 's/@/\
/g
s/#/ /g'
fi
if test -n "$_engines_built_dynamic" ; then
echo "Engines (plugins):"
echo $_engines_built_dynamic | sed 's/@/\
echo "$_engines_built_dynamic" | sed 's/@/\
/g
s/#/ /g'
fi
if test -n "$_engines_skipped" ; then
echo "Engines Skipped:"
echo $_engines_skipped | sed 's/@/\
echo "$_engines_skipped" | sed 's/@/\
/g
s/#/ /g'
fi
if test -n "$_engines_built_wip" ; then
echo "WARNING: This ResidualVM build contains the following UNSTABLE engines:"
echo $_engines_built_wip | sed 's/@/\
echo "$_engines_built_wip" | sed 's/@/\
/g
s/#/ /g'
fi

View File

@ -206,7 +206,7 @@ void XCodeProvider::writeFileListToProject(const FileNode &dir, std::ofstream &p
// Create group
std::string name = getLastPathComponent(dir.name);
Object *group = new Object(this, "PBXGroup_" + name , "PBXGroup", "PBXGroup", "", name);
// List of children
Property children;
children.hasOrder = true;
@ -225,7 +225,7 @@ void XCodeProvider::writeFileListToProject(const FileNode &dir, std::ofstream &p
ADD_SETTING_ORDER_NOVALUE(children, getHash(id), node->name, order++);
ADD_BUILD_FILE(id, node->name, node->name + " in Sources");
ADD_FILE_REFERENCE(node->name, property);
// Process child nodes
if (!node->children.empty())
writeFileListToProject(*node, projectFile, indentation + 1, duplicate, objPrefix + node->name + '_', filePrefix + node->name + '/');

View File

@ -194,7 +194,7 @@ protected:
/**
* A map containing all the extra game GUI options the engine supports.
*/
*/
const ADExtraGuiOptionsMap * const _extraGuiOptions;
/**
@ -212,7 +212,7 @@ protected:
*
* Used to override gameid.
* This is a recommended setting to prevent global gameid pollution.
* With this option set, the gameid effectively turns into engineid.
* With this option set, the gameid effectively turns into engineid.
*
* FIXME: This field actually removes a feature (gameid) in order to
* address a more generic problem. We should find a better way to

View File

@ -1,4 +1,5 @@
# This file is included from the main "configure" script
# add_engine [name] [desc] [build-by-default] [subengines] [base games] [deps]
add_engine grim "Grim" yes "monkey4" "zlib"
add_engine monkey4 "Escape from Monkey Island" yes
add_engine myst3 "Myst 3" yes

View File

@ -229,7 +229,7 @@ void MainMenuDialog::save() {
"Please consult the README for basic information, and for "
"instructions on how to obtain further assistance."), status.getDesc().c_str());
GUI::MessageDialog dialog(failMessage);
dialog.runModal();
dialog.runModal();
}
close();

View File

@ -276,6 +276,7 @@ public:
/**
* Run the Global Main Menu Dialog
* added 'virtual' ResidualVM specific
*/
virtual void openMainMenuDialog();

View File

@ -80,7 +80,7 @@ void VectorRenderer::stepGetPositions(const DrawStep &step, const Common::Rect &
case Graphics::DrawStep::kVectorAlignManual:
if (step.x >= 0)
in_x = area.left + step.x + step.padding.left;
else
else
in_x = area.left + area.width() + step.x + step.padding.left; // value relative to the opposite corner.
break;

View File

@ -55,7 +55,7 @@ struct DrawStep {
bool autoWidth, autoHeight;
int16 x, y, w, h; /**< width, height and position, if not measured automatically.
negative values mean counting from the opposite direction */
Common::Rect padding;
enum VectorAlignment {

View File

@ -369,12 +369,12 @@ gradientFill(PixelType *ptr, int width, int x, int y) {
int grad = (((y - _gradIndexes[curGrad]) % stripSize) << 2) / stripSize;
// Dithering:
// +--+ +--+ +--+ +--+
// | | | | | *| | *|
// | | | *| |* | |**|
// +--+ +--+ +--+ +--+
// +--+ +--+ +--+ +--+
// | | | | | *| | *|
// | | | *| |* | |**|
// +--+ +--+ +--+ +--+
// 0 1 2 3
if (grad == 0 ||
if (grad == 0 ||
_gradCache[curGrad] == _gradCache[curGrad + 1] || // no color change
stripSize < 2) { // the stip is small
colorFill<PixelType>(ptr, ptr + width, _gradCache[curGrad]);
@ -873,7 +873,7 @@ drawTriangle(int x, int y, int w, int h, TriangleOrientation orient) {
case kTriangleDown:
drawTriangleVertAlg(x, y, newW, newH, (orient == kTriangleDown), color, Base::_fillMode);
break;
case kTriangleLeft:
case kTriangleRight:
case kTriangleAuto:
@ -1206,14 +1206,14 @@ drawTriangleVertAlg(int x1, int y1, int w, int h, bool inverted, PixelType color
pitch = -pitch;
y1 += h;
}
PixelType *ptr_right = (PixelType *)_activeSurface->getBasePtr(x1, y1);
PixelType *floor = ptr_right - 1;
PixelType *ptr_left = (PixelType *)_activeSurface->getBasePtr(x1 + w, y1);
int x2 = x1 + w / 2;
int y2 = y1 + h;
#if FIXED_POINT
int dx = (x2 - x1) << 8;
int dy = (y2 - y1) << 8;
@ -1227,7 +1227,7 @@ drawTriangleVertAlg(int x1, int y1, int w, int h, bool inverted, PixelType color
#endif
while (floor++ != ptr_left)
blendPixelPtr(floor, color, 50);
#if FIXED_POINT
int gradient = (dy << 8) / dx;
int intery = (y1 << 8) + gradient;
@ -1250,7 +1250,7 @@ drawTriangleVertAlg(int x1, int y1, int w, int h, bool inverted, PixelType color
ptr_right += pitch;
intery += gradient;
switch (fill_m) {
case kFillDisabled:
*ptr_left = *ptr_right = color;
@ -1262,16 +1262,16 @@ drawTriangleVertAlg(int x1, int y1, int w, int h, bool inverted, PixelType color
blendPixelPtr(ptr_left, color, rfpart(intery));
break;
case kFillGradient:
colorFill<PixelType>(ptr_right, ptr_left, calcGradient(gradient_h++, h));
colorFill<PixelType>(ptr_right, ptr_left, calcGradient(gradient_h++, h));
blendPixelPtr(ptr_right, color, rfpart(intery));
blendPixelPtr(ptr_left, color, rfpart(intery));
break;
}
}
return;
}
#if FIXED_POINT
if (abs(dx) < abs(dy)) {
#else
@ -1280,7 +1280,7 @@ drawTriangleVertAlg(int x1, int y1, int w, int h, bool inverted, PixelType color
ptr_left--;
while (floor++ != ptr_left)
blendPixelPtr(floor, color, 50);
#if FIXED_POINT
int gradient = (dx << 8) / (dy + 0x100);
int interx = (x1 << 8) + gradient;
@ -1303,7 +1303,7 @@ drawTriangleVertAlg(int x1, int y1, int w, int h, bool inverted, PixelType color
ptr_right += pitch;
interx += gradient;
switch (fill_m) {
case kFillDisabled:
*ptr_left = *ptr_right = color;
@ -1315,18 +1315,18 @@ drawTriangleVertAlg(int x1, int y1, int w, int h, bool inverted, PixelType color
blendPixelPtr(ptr_left, color, rfpart(interx));
break;
case kFillGradient:
colorFill<PixelType>(ptr_right, ptr_left, calcGradient(gradient_h++, h));
colorFill<PixelType>(ptr_right, ptr_left, calcGradient(gradient_h++, h));
blendPixelPtr(ptr_right, color, rfpart(interx));
blendPixelPtr(ptr_left, color, rfpart(interx));
break;
}
}
return;
}
ptr_left--;
while (floor++ != ptr_left)
blendPixelPtr(floor, color, 50);
@ -1341,12 +1341,12 @@ drawTriangleVertAlg(int x1, int y1, int w, int h, bool inverted, PixelType color
for (int y = y1 + 1; y < y2; y++) {
ptr_right++;
ptr_left--;
ptr_left += pitch;
ptr_right += pitch;
interx += gradient;
switch (fill_m) {
case kFillDisabled:
*ptr_left = *ptr_right = color;
@ -1358,13 +1358,13 @@ drawTriangleVertAlg(int x1, int y1, int w, int h, bool inverted, PixelType color
blendPixelPtr(ptr_left, color, rfpart(interx));
break;
case kFillGradient:
colorFill<PixelType>(ptr_right, ptr_left, calcGradient(gradient_h++, h));
colorFill<PixelType>(ptr_right, ptr_left, calcGradient(gradient_h++, h));
blendPixelPtr(ptr_right, color, rfpart(interx));
blendPixelPtr(ptr_left, color, rfpart(interx));
break;
}
}
}
/** VERTICAL TRIANGLE DRAWING - FAST VERSION FOR SQUARED TRIANGLES */
@ -1372,12 +1372,12 @@ template<typename PixelType>
void VectorRendererSpec<PixelType>::
drawTriangleFast(int x1, int y1, int size, bool inverted, PixelType color, VectorRenderer::FillMode fill_m) {
int pitch = _activeSurface->pitch / _activeSurface->format.bytesPerPixel;
if (!inverted) {
pitch = -pitch;
y1 += size;
}
int gradient_h = 0;
PixelType *ptr_right = (PixelType *)_activeSurface->getBasePtr(x1, y1);
PixelType *ptr_left = (PixelType *)_activeSurface->getBasePtr(x1 + size, y1);
@ -1388,9 +1388,9 @@ drawTriangleFast(int x1, int y1, int size, bool inverted, PixelType color, Vecto
int signX = x1 < x2 ? 1 : -1;
int signY = y1 < y2 ? 1 : -1;
int error = deltaX - deltaY;
colorFill<PixelType>(ptr_right, ptr_left, color);
while (1) {
switch (fill_m) {
case kFillDisabled:
@ -1401,22 +1401,22 @@ drawTriangleFast(int x1, int y1, int size, bool inverted, PixelType color, Vecto
colorFill<PixelType>(ptr_right, ptr_left, color);
break;
case kFillGradient:
colorFill<PixelType>(ptr_right, ptr_left, calcGradient(gradient_h++, size));
colorFill<PixelType>(ptr_right, ptr_left, calcGradient(gradient_h++, size));
break;
}
if (x1 == x2 && y1 == y2)
break;
int error2 = error * 2;
if (error2 > -deltaY) {
error -= deltaY;
x1 += signX;
ptr_right += signX;
ptr_left += -signX;
}
if (error2 < deltaX) {
error += deltaX;
y1 += signY;

View File

@ -22,123 +22,143 @@
#include "graphics/conversion.h"
#include "graphics/pixelformat.h"
#include "common/endian.h"
namespace Graphics {
// TODO: YUV to RGB conversion function
namespace {
template<typename SrcColor, typename DstColor, bool backward>
inline void crossBlitLogic(byte *dst, const byte *src, const uint w, const uint h,
const PixelFormat &srcFmt, const PixelFormat &dstFmt,
const uint srcDelta, const uint dstDelta) {
for (uint y = 0; y < h; ++y) {
for (uint x = 0; x < w; ++x) {
const uint32 color = *(const SrcColor *)src;
byte a, r, g, b;
srcFmt.colorToARGB(color, a, r, g, b);
*(DstColor *)dst = dstFmt.ARGBToColor(a, r, g, b);
if (backward) {
src -= sizeof(SrcColor);
dst -= sizeof(DstColor);
} else {
src += sizeof(SrcColor);
dst += sizeof(DstColor);
}
}
if (backward) {
src -= srcDelta;
dst -= dstDelta;
} else {
src += srcDelta;
dst += dstDelta;
}
}
}
template<typename DstColor, bool backward>
inline void crossBlitLogic3BppSource(byte *dst, const byte *src, const uint w, const uint h,
const PixelFormat &srcFmt, const PixelFormat &dstFmt,
const uint srcDelta, const uint dstDelta) {
uint32 color;
byte r, g, b, a;
uint8 *col = (uint8 *)&color;
#ifdef SCUMM_BIG_ENDIAN
col++;
#endif
for (uint y = 0; y < h; ++y) {
for (uint x = 0; x < w; ++x) {
memcpy(col, src, 3);
srcFmt.colorToARGB(color, a, r, g, b);
*(DstColor *)dst = dstFmt.ARGBToColor(a, r, g, b);
if (backward) {
src -= 3;
dst -= sizeof(DstColor);
} else {
src += 3;
dst += sizeof(DstColor);
}
}
if (backward) {
src -= srcDelta;
dst -= dstDelta;
} else {
src += srcDelta;
dst += dstDelta;
}
}
}
} // End of anonymous namespace
// Function to blit a rect from one color format to another
bool crossBlit(byte *dst, const byte *src, int dstpitch, int srcpitch,
int w, int h, const Graphics::PixelFormat &dstFmt, const Graphics::PixelFormat &srcFmt) {
bool crossBlit(byte *dst, const byte *src,
const uint dstPitch, const uint srcPitch,
const uint w, const uint h,
const Graphics::PixelFormat &dstFmt, const Graphics::PixelFormat &srcFmt) {
// Error out if conversion is impossible
if ((srcFmt.bytesPerPixel == 1) || (dstFmt.bytesPerPixel == 1)
|| (!srcFmt.bytesPerPixel) || (!dstFmt.bytesPerPixel)
|| (srcFmt.bytesPerPixel > dstFmt.bytesPerPixel))
|| (dstFmt.bytesPerPixel == 3)
|| (!srcFmt.bytesPerPixel) || (!dstFmt.bytesPerPixel))
return false;
// Don't perform unnecessary conversion
if (srcFmt == dstFmt) {
if (dst == src)
return true;
if (dstpitch == srcpitch && ((w * dstFmt.bytesPerPixel) == dstpitch)) {
memcpy(dst,src,dstpitch * h);
return true;
} else {
for (int i = 0; i < h; i++) {
memcpy(dst,src,w * dstFmt.bytesPerPixel);
dst += dstpitch;
src += srcpitch;
if (dst != src) {
if (dstPitch == srcPitch && ((w * dstFmt.bytesPerPixel) == dstPitch)) {
memcpy(dst, src, dstPitch * h);
} else {
for (uint i = 0; i < h; ++i) {
memcpy(dst, src, w * dstFmt.bytesPerPixel);
dst += dstPitch;
src += srcPitch;
}
}
return true;
}
return true;
}
// Faster, but larger, to provide optimized handling for each case.
int srcDelta, dstDelta;
srcDelta = (srcpitch - w * srcFmt.bytesPerPixel);
dstDelta = (dstpitch - w * dstFmt.bytesPerPixel);
const uint srcDelta = (srcPitch - w * srcFmt.bytesPerPixel);
const uint dstDelta = (dstPitch - w * dstFmt.bytesPerPixel);
// TODO: optimized cases for dstDelta of 0
uint8 r, g, b, a;
if (dstFmt.bytesPerPixel == 2) {
uint16 color;
for (int y = 0; y < h; y++) {
for (int x = 0; x < w; x++, src += 2, dst += 2) {
color = *(const uint16 *)src;
srcFmt.colorToARGB(color, a, r, g, b);
color = dstFmt.ARGBToColor(a, r, g, b);
*(uint16 *)dst = color;
}
src += srcDelta;
dst += dstDelta;
}
} else if (dstFmt.bytesPerPixel == 3) {
uint32 color;
uint8 *col = (uint8 *) &color;
#ifdef SCUMM_BIG_ENDIAN
col++;
#endif
if (srcFmt.bytesPerPixel == 2) {
for (int y = 0; y < h; y++) {
for (int x = 0; x < w; x++, src += 2, dst += 3) {
color = *(const uint16 *)src;
srcFmt.colorToARGB(color, a, r, g, b);
color = dstFmt.ARGBToColor(a, r, g, b);
memcpy(dst, col, 3);
}
src += srcDelta;
dst += dstDelta;
}
crossBlitLogic<uint16, uint16, false>(dst, src, w, h, srcFmt, dstFmt, srcDelta, dstDelta);
} else if (srcFmt.bytesPerPixel == 3) {
crossBlitLogic3BppSource<uint16, false>(dst, src, w, h, srcFmt, dstFmt, srcDelta, dstDelta);
} else {
for (int y = 0; y < h; y++) {
for (int x = 0; x < w; x++, src += 3, dst += 3) {
memcpy(col, src, 3);
srcFmt.colorToARGB(color, a, r, g, b);
color = dstFmt.ARGBToColor(a, r, g, b);
memcpy(dst, col, 3);
}
src += srcDelta;
dst += dstDelta;
}
crossBlitLogic<uint32, uint16, false>(dst, src, w, h, srcFmt, dstFmt, srcDelta, dstDelta);
}
} else if (dstFmt.bytesPerPixel == 4) {
uint32 color;
if (srcFmt.bytesPerPixel == 2) {
for (int y = 0; y < h; y++) {
for (int x = 0; x < w; x++, src += 2, dst += 4) {
color = *(const uint16 *)src;
srcFmt.colorToARGB(color, a, r, g, b);
color = dstFmt.ARGBToColor(a, r, g, b);
*(uint32 *)dst = color;
}
src += srcDelta;
dst += dstDelta;
}
// We need to blit the surface from bottom right to top left here.
// This is neeeded, because when we convert to the same memory
// buffer copying the surface from top left to bottom right would
// overwrite the source, since we have more bits per destination
// color than per source color.
dst += h * dstPitch - dstDelta - dstFmt.bytesPerPixel;
src += h * srcPitch - srcDelta - srcFmt.bytesPerPixel;
crossBlitLogic<uint16, uint32, true>(dst, src, w, h, srcFmt, dstFmt, srcDelta, dstDelta);
} else if (srcFmt.bytesPerPixel == 3) {
uint8 *col = (uint8 *)&color;
#ifdef SCUMM_BIG_ENDIAN
col++;
#endif
for (int y = 0; y < h; y++) {
for (int x = 0; x < w; x++, src += 2, dst += 4) {
memcpy(col, src, 3);
srcFmt.colorToARGB(color, a, r, g, b);
color = dstFmt.ARGBToColor(a, r, g, b);
*(uint32 *)dst = color;
}
src += srcDelta;
dst += dstDelta;
}
// We need to blit the surface from bottom right to top left here.
// This is neeeded, because when we convert to the same memory
// buffer copying the surface from top left to bottom right would
// overwrite the source, since we have more bits per destination
// color than per source color.
dst += h * dstPitch - dstDelta - dstFmt.bytesPerPixel;
src += h * srcPitch - srcDelta - srcFmt.bytesPerPixel;
crossBlitLogic3BppSource<uint32, true>(dst, src, w, h, srcFmt, dstFmt, srcDelta, dstDelta);
} else {
for (int y = 0; y < h; y++) {
for (int x = 0; x < w; x++, src += 4, dst += 4) {
color = *(const uint32 *)src;
srcFmt.colorToARGB(color, a, r, g, b);
color = dstFmt.ARGBToColor(a, r, g, b);
*(uint32 *)dst = color;
}
src += srcDelta;
dst += dstDelta;
}
crossBlitLogic<uint32, uint32, false>(dst, src, w, h, srcFmt, dstFmt, srcDelta, dstDelta);
}
} else {
return false;

View File

@ -59,15 +59,18 @@ inline static void RGB2YUV(byte r, byte g, byte b, byte &y, byte &u, byte &v) {
* @return true if conversion completes successfully,
* false if there is an error.
*
* @note This implementation currently arbitrarily requires that the
* destination's format have at least as high a bytedepth as
* the source's.
* @note This can convert a rectangle in place, if the source and
* destination format have the same bytedepth.
*
* @note Blitting to a 3Bpp destination is not supported
* @note This can convert a surface in place, regardless of the
* source and destination format, as long as there is enough
* space for the destination. The dstPitch / srcPitch ratio
* must at least equal the dstBpp / srcBpp ratio for
* dstPitch >= srcPitch and at most dstBpp / srcBpp for
* dstPitch < srcPitch though.
*/
bool crossBlit(byte *dst, const byte *src, int dstpitch, int srcpitch,
int w, int h, const Graphics::PixelFormat &dstFmt, const Graphics::PixelFormat &srcFmt);
bool crossBlit(byte *dst, const byte *src,
const uint dstPitch, const uint srcPitch,
const uint w, const uint h,
const Graphics::PixelFormat &dstFmt, const Graphics::PixelFormat &srcFmt);
} // End of namespace Graphics

View File

@ -100,10 +100,10 @@ bool BitmapDecoder::loadStream(Common::SeekableReadStream &stream) {
_paletteColorCount = stream.readUint32LE();
/* uint32 colorsImportant = */ stream.readUint32LE();
if (_paletteColorCount == 0)
_paletteColorCount = 256;
if (bitsPerPixel == 8) {
if (_paletteColorCount == 0)
_paletteColorCount = 256;
// Read the palette
_palette = new byte[_paletteColorCount * 3];
for (uint16 i = 0; i < _paletteColorCount; i++) {
@ -155,7 +155,7 @@ bool BitmapDecoder::loadStream(Common::SeekableReadStream &stream) {
}
} else { // 32 bpp
byte *dst = (byte *)_surface->pixels + (height - 1) * _surface->pitch;
for (int32 i = 0; i < height; i++) {
for (uint32 j = 0; j < width; j++) {
byte b = stream.readByte();
@ -166,11 +166,11 @@ bool BitmapDecoder::loadStream(Common::SeekableReadStream &stream) {
// ref: http://msdn.microsoft.com/en-us/library/windows/desktop/dd183376%28v=vs.85%29.aspx
stream.readByte();
uint32 color = format.RGBToColor(r, g, b);
*((uint32 *)dst) = color;
dst += format.bytesPerPixel;
}
stream.skip(extraDataLength);
dst -= _surface->pitch * 2;
}

View File

@ -24,6 +24,7 @@
* Image decoder used in engines:
* - hugo
* - mohawk
* - wintermute
*/
#ifndef GRAPHICS_DECODERS_BMP_H

View File

@ -78,10 +78,15 @@ public:
* The palette's format is the same as PaletteManager's palette
* (interleaved RGB values).
*
* @return the decoded palette, or 0 if no palette is present
* @return the decoded palette, or undefined if no palette is present
*/
virtual const byte *getPalette() const { return 0; }
/**
* Query if the decoded image has a palette.
*/
virtual bool hasPalette() const { return getPaletteColorCount() != 0; }
/** Return the starting index of the palette. */
virtual byte getPaletteStartIndex() const { return 0; }
/** Return the number of colors in the palette. */

View File

@ -81,7 +81,7 @@ const Surface *JPEGDecoder::getSurface() const {
const Graphics::Surface *uComponent = getComponent(2);
const Graphics::Surface *vComponent = getComponent(3);
convertYUV444ToRGB(_rgbSurface, (byte *)yComponent->pixels, (byte *)uComponent->pixels, (byte *)vComponent->pixels, yComponent->w, yComponent->h, yComponent->pitch, uComponent->pitch);
YUVToRGBMan.convert444(_rgbSurface, Graphics::YUVToRGBManager::kScaleFull, (byte *)yComponent->pixels, (byte *)uComponent->pixels, (byte *)vComponent->pixels, yComponent->w, yComponent->h, yComponent->pitch, uComponent->pitch);
return _rgbSurface;
}
@ -452,7 +452,7 @@ bool JPEGDecoder::readSOS() {
_bitsNumber = 0;
for (byte i = 0; i < _numScanComp; i++)
_scanComp[i]->DCpredictor = 0;
_scanComp[i]->DCpredictor = 0;
}
}
}

View File

@ -25,6 +25,7 @@
* Image decoder used in engines:
* - groovie
* - mohawk
* - wintermute
*/
#ifndef GRAPHICS_JPEG_H

View File

@ -145,7 +145,10 @@ bool TGADecoder::readHeader(Common::SeekableReadStream &tga, byte &imageType, by
if (pixelDepth == 24) {
_format = PixelFormat(3, 8, 8, 8, 0, 16, 8, 0, 0);
} else if (pixelDepth == 32) {
_format = PixelFormat(4, 8, 8, 8, attributeBits, 16, 8, 0, 24);
// HACK: According to the spec, attributeBits should determine the amount
// of alpha-bits, however, as the game files that use this decoder seems
// to ignore that fact, we force the amount to 8 for 32bpp files for now.
_format = PixelFormat(4, 8, 8, 8, /* attributeBits */ 8, 16, 8, 0, 24);
} else if (pixelDepth == 16 && imageType == TYPE_TRUECOLOR) {
// 16bpp TGA is ARGB1555
_format = PixelFormat(2, 5, 5, 5, attributeBits, 10, 5, 0, 15);

View File

@ -25,7 +25,7 @@
/*
* TGA decoder used in engines:
* - none
* - wintermute
*/
#ifndef GRAPHICS_DECODERS_TGA_H

View File

@ -1,7 +1,7 @@
// Generated by convbdf on Fri Jan 6 14:32:21 2012
#include "graphics/fonts/bdf.h"
// Font information:
// Font information:
// Name: -Misc-Fixed-Medium-R-Normal--8-80-75-75-C-50-ISO8859-1
// Size: 5x8
// Box: 5 8 0 -1

View File

@ -1,7 +1,7 @@
// Generated by convbdf on Fri Jan 6 14:33:07 2012
#include "graphics/fonts/bdf.h"
// Font information:
// Font information:
// Name: -Schumacher-Clean-Medium-R-Normal--12-120-75-75-C-60-ISO8859-1
// Size: 6x12
// Box: 6 12 0 -3

View File

@ -1,7 +1,7 @@
// Generated by convbdf on Fri Jan 6 14:33:14 2012
#include "graphics/fonts/bdf.h"
// Font information:
// Font information:
// Name: -Adobe-Helvetica-Bold-R-Normal--12-120-75-75-P-70-ISO8859-1
// Size: 13x14
// Box: 13 15 -1 -3

View File

@ -61,59 +61,21 @@ void drawLine(int x0, int y0, int x1, int y1, int color, void (*plotProc)(int, i
}
}
void drawThickLine(int x0, int y0, int x1, int y1, int penX, int penY, int color, void (*plotProc)(int, int, int, void *), void *data) {
assert(penX > 0 && penY > 0);
// FIXME: This is a limited version of thick line drawing
// it draws striped lines at some angles. Better algorithm could
// be found here:
//
// http://homepages.enterprise.net/murphy/thickline/index.html
//
// Feel free to replace it with better implementation
void drawThickLine(int x0, int y0, int x1, int y1, int thickness, int color, void (*plotProc)(int, int, int, void *), void *data) {
const bool steep = ABS(y1 - y0) > ABS(x1 - x0);
if (steep) {
SWAP(x0, y0);
SWAP(x1, y1);
}
float dx = x1 - x0;
float dy = y1 - y0;
float d = (float)sqrt(dx * dx + dy * dy);
if (!d)
// Shortcut
if (penX == 1 && penY == 1) {
drawLine(x0, y0, x1, y1, color, plotProc, data);
return;
int thickX = (int)((float)thickness * dy / d / 2);
int thickY = (int)((float)thickness * dx / d / 2);
const int delta_x = ABS(x1 - x0);
const int delta_y = ABS(y1 - y0);
const int delta_err = delta_y;
int x = x0;
int y = y0;
int err = 0;
const int x_step = (x0 < x1) ? 1 : -1;
const int y_step = (y0 < y1) ? 1 : -1;
if (steep)
drawLine(y - thickY, x + thickX, y + thickY, x - thickX, color, plotProc, data);
else
drawLine(x - thickX, y + thickY, x + thickX, y - thickY, color, plotProc, data);
while (x != x1) {
x += x_step;
err += delta_err;
if (2 * err > delta_x) {
y += y_step;
err -= delta_x;
}
if (steep)
drawLine(y - thickY, x + thickX, y + thickY, x - thickX, color, plotProc, data);
else
drawLine(x - thickX, y + thickY, x + thickX, y - thickY, color, plotProc, data);
}
// TODO: Optimize this. It currently is a very naive way of handling
// thick lines since quite often it will be drawing to the same pixel
// multiple times.
for (int x = 0; x < penX; x++)
for (int y = 0; y < penY; y++)
drawLine(x0 + x, y0 + y, x1 + x, y1 + y, color, plotProc, data);
}
} // End of namespace Graphics

View File

@ -25,7 +25,7 @@
namespace Graphics {
void drawLine(int x0, int y0, int x1, int y1, int color, void (*plotProc)(int, int, int, void *), void *data);
void drawThickLine(int x0, int y0, int x1, int y1, int thickness, int color, void (*plotProc)(int, int, int, void *), void *data);
void drawThickLine(int x0, int y0, int x1, int y1, int penX, int penY, int color, void (*plotProc)(int, int, int, void *), void *data);
} // End of namespace Graphics

View File

@ -26,6 +26,7 @@
#include "common/textconsole.h"
#include "graphics/primitives.h"
#include "graphics/surface.h"
#include "graphics/conversion.h"
namespace Graphics {
@ -49,6 +50,17 @@ void Surface::drawLine(int x0, int y0, int x1, int y1, uint32 color) {
error("Surface::drawLine: bytesPerPixel must be 1, 2, or 4");
}
void Surface::drawThickLine(int x0, int y0, int x1, int y1, int penX, int penY, uint32 color) {
if (format.bytesPerPixel == 1)
Graphics::drawThickLine(x0, y0, x1, y1, penX, penY, color, plotPoint<byte>, this);
else if (format.bytesPerPixel == 2)
Graphics::drawThickLine(x0, y0, x1, y1, penX, penY, color, plotPoint<uint16>, this);
else if (format.bytesPerPixel == 4)
Graphics::drawThickLine(x0, y0, x1, y1, penX, penY, color, plotPoint<uint32>, this);
else
error("Surface::drawThickLine: bytesPerPixel must be 1, 2, or 4");
}
void Surface::create(uint16 width, uint16 height, const PixelFormat &f) {
free();
@ -271,6 +283,72 @@ void Surface::move(int dx, int dy, int height) {
}
}
void Surface::convertToInPlace(const PixelFormat &dstFormat, const byte *palette) {
// Do not convert to the same format and ignore empty surfaces.
if (format == dstFormat || pixels == 0) {
return;
}
if (format.bytesPerPixel == 0 || format.bytesPerPixel > 4)
error("Surface::convertToInPlace(): Can only convert from 1Bpp, 2Bpp, 3Bpp, and 4Bpp");
if (dstFormat.bytesPerPixel != 2 && dstFormat.bytesPerPixel != 4)
error("Surface::convertToInPlace(): Can only convert to 2Bpp and 4Bpp");
// In case the surface data needs more space allocate it.
if (dstFormat.bytesPerPixel > format.bytesPerPixel) {
void *const newPixels = realloc(pixels, w * h * dstFormat.bytesPerPixel);
if (!newPixels) {
error("Surface::convertToInPlace(): Out of memory");
}
pixels = newPixels;
}
// We take advantage of the fact that pitch is always w * format.bytesPerPixel.
// This is assured by the logic of Surface::create.
// We need to handle 1 Bpp surfaces special here.
if (format.bytesPerPixel == 1) {
assert(palette);
for (int y = h; y > 0; --y) {
const byte *srcRow = (const byte *)pixels + y * pitch - 1;
byte *dstRow = (byte *)pixels + y * w * dstFormat.bytesPerPixel - dstFormat.bytesPerPixel;
for (int x = 0; x < w; x++) {
byte index = *srcRow--;
byte r = palette[index * 3];
byte g = palette[index * 3 + 1];
byte b = palette[index * 3 + 2];
uint32 color = dstFormat.RGBToColor(r, g, b);
if (dstFormat.bytesPerPixel == 2)
*((uint16 *)dstRow) = color;
else
*((uint32 *)dstRow) = color;
dstRow -= dstFormat.bytesPerPixel;
}
}
} else {
crossBlit((byte *)pixels, (const byte *)pixels, w * dstFormat.bytesPerPixel, pitch, w, h, dstFormat, format);
}
// In case the surface data got smaller, free up some memory.
if (dstFormat.bytesPerPixel < format.bytesPerPixel) {
void *const newPixels = realloc(pixels, w * h * dstFormat.bytesPerPixel);
if (!newPixels) {
error("Surface::convertToInPlace(): Freeing memory failed");
}
pixels = newPixels;
}
// Update the surface specific data.
format = dstFormat;
pitch = w * dstFormat.bytesPerPixel;
}
Graphics::Surface *Surface::convertTo(const PixelFormat &dstFormat, const byte *palette) const {
assert(pixels);

View File

@ -134,6 +134,20 @@ struct Surface {
*/
void copyFrom(const Surface &surf);
/**
* Convert the data to another pixel format.
*
* This works in-place. This means it will not create an additional buffer
* for the conversion process. The value of pixels might change though.
*
* Note that you should only use this, when you created the Surface data via
* create! Otherwise this function has undefined behavior.
*
* @param dstFormat The desired format
* @param palette The palette (in RGB888), if the source format has a Bpp of 1
*/
void convertToInPlace(const PixelFormat &dstFormat, const byte *palette = 0);
/**
* Convert the data to another pixel format.
*
@ -153,9 +167,25 @@ struct Surface {
* @param x1 The x coordinate of the end point.
* @param y1 The y coordinate of the end point.
* @param color The color of the line.
* @note This is just a wrapper around Graphics::drawLine
*/
void drawLine(int x0, int y0, int x1, int y1, uint32 color);
/**
* Draw a thick line.
*
* @param x0 The x coordinate of the start point.
* @param y0 The y coordiante of the start point.
* @param x1 The x coordinate of the end point.
* @param y1 The y coordinate of the end point.
* @param penX The width of the pen (thickness in the x direction)
* @param penY The height of the pen (thickness in the y direction)
* @param color The color of the line.
* @note This is just a wrapper around Graphics::drawThickLine
* @note The x/y coordinates of the start and end points are the upper-left most part of the pen
*/
void drawThickLine(int x0, int y0, int x1, int y1, int penX, int penY, uint32 color);
/**
* Draw a horizontal line.
*

View File

@ -83,129 +83,127 @@
// BASIS, AND BROWN UNIVERSITY HAS NO OBLIGATION TO PROVIDE MAINTENANCE,
// SUPPORT, UPDATES, ENHANCEMENTS, OR MODIFICATIONS.
#include "common/scummsys.h"
#include "common/singleton.h"
#include "graphics/surface.h"
#include "graphics/yuv_to_rgb.h"
namespace Common {
DECLARE_SINGLETON(Graphics::YUVToRGBManager);
}
namespace Graphics {
class YUVToRGBLookup {
public:
YUVToRGBLookup(Graphics::PixelFormat format);
~YUVToRGBLookup();
YUVToRGBLookup(Graphics::PixelFormat format, YUVToRGBManager::LuminanceScale scale);
int16 *_colorTab;
uint32 *_rgbToPix;
Graphics::PixelFormat getFormat() const { return _format; }
YUVToRGBManager::LuminanceScale getScale() const { return _scale; }
const uint32 *getRGBToPix() const { return _rgbToPix; }
private:
Graphics::PixelFormat _format;
YUVToRGBManager::LuminanceScale _scale;
uint32 _rgbToPix[3 * 768]; // 9216 bytes
};
YUVToRGBLookup::YUVToRGBLookup(Graphics::PixelFormat format) {
_colorTab = new int16[4 * 256]; // 2048 bytes
YUVToRGBLookup::YUVToRGBLookup(Graphics::PixelFormat format, YUVToRGBManager::LuminanceScale scale) {
_format = format;
_scale = scale;
uint32 *r_2_pix_alloc = &_rgbToPix[0 * 768];
uint32 *g_2_pix_alloc = &_rgbToPix[1 * 768];
uint32 *b_2_pix_alloc = &_rgbToPix[2 * 768];
if (scale == YUVToRGBManager::kScaleFull) {
// Set up entries 0-255 in rgb-to-pixel value tables.
for (int i = 0; i < 256; i++) {
r_2_pix_alloc[i + 256] = format.RGBToColor(i, 0, 0);
g_2_pix_alloc[i + 256] = format.RGBToColor(0, i, 0);
b_2_pix_alloc[i + 256] = format.RGBToColor(0, 0, i);
}
// Spread out the values we have to the rest of the array so that we do
// not need to check for overflow.
for (int i = 0; i < 256; i++) {
r_2_pix_alloc[i] = r_2_pix_alloc[256];
r_2_pix_alloc[i + 512] = r_2_pix_alloc[511];
g_2_pix_alloc[i] = g_2_pix_alloc[256];
g_2_pix_alloc[i + 512] = g_2_pix_alloc[511];
b_2_pix_alloc[i] = b_2_pix_alloc[256];
b_2_pix_alloc[i + 512] = b_2_pix_alloc[511];
}
} else {
// Set up entries 16-235 in rgb-to-pixel value tables
for (int i = 16; i < 236; i++) {
int scaledValue = (i - 16) * 255 / 219;
r_2_pix_alloc[i + 256] = format.RGBToColor(scaledValue, 0, 0);
g_2_pix_alloc[i + 256] = format.RGBToColor(0, scaledValue, 0);
b_2_pix_alloc[i + 256] = format.RGBToColor(0, 0, scaledValue);
}
// Spread out the values we have to the rest of the array so that we do
// not need to check for overflow. We have to do it here in two steps.
for (int i = 0; i < 256 + 16; i++) {
r_2_pix_alloc[i] = r_2_pix_alloc[256 + 16];
g_2_pix_alloc[i] = g_2_pix_alloc[256 + 16];
b_2_pix_alloc[i] = b_2_pix_alloc[256 + 16];
}
for (int i = 256 + 236; i < 768; i++) {
r_2_pix_alloc[i] = r_2_pix_alloc[256 + 236 - 1];
g_2_pix_alloc[i] = g_2_pix_alloc[256 + 236 - 1];
b_2_pix_alloc[i] = b_2_pix_alloc[256 + 236 - 1];
}
}
}
YUVToRGBManager::YUVToRGBManager() {
_lookup = 0;
int16 *Cr_r_tab = &_colorTab[0 * 256];
int16 *Cr_g_tab = &_colorTab[1 * 256];
int16 *Cb_g_tab = &_colorTab[2 * 256];
int16 *Cb_b_tab = &_colorTab[3 * 256];
_rgbToPix = new uint32[3 * 768]; // 9216 bytes
uint32 *r_2_pix_alloc = &_rgbToPix[0 * 768];
uint32 *g_2_pix_alloc = &_rgbToPix[1 * 768];
uint32 *b_2_pix_alloc = &_rgbToPix[2 * 768];
int16 CR, CB;
int i;
// Generate the tables for the display surface
for (i = 0; i < 256; i++) {
for (int i = 0; i < 256; i++) {
// Gamma correction (luminescence table) and chroma correction
// would be done here. See the Berkeley mpeg_play sources.
CR = CB = (i - 128);
int16 CR = (i - 128), CB = CR;
Cr_r_tab[i] = (int16) ( (0.419 / 0.299) * CR) + 0 * 768 + 256;
Cr_g_tab[i] = (int16) (-(0.299 / 0.419) * CR) + 1 * 768 + 256;
Cb_g_tab[i] = (int16) (-(0.114 / 0.331) * CB);
Cb_b_tab[i] = (int16) ( (0.587 / 0.331) * CB) + 2 * 768 + 256;
}
// Set up entries 0-255 in rgb-to-pixel value tables.
for (i = 0; i < 256; i++) {
r_2_pix_alloc[i + 256] = format.RGBToColor(i, 0, 0);
g_2_pix_alloc[i + 256] = format.RGBToColor(0, i, 0);
b_2_pix_alloc[i + 256] = format.RGBToColor(0, 0, i);
}
// Spread out the values we have to the rest of the array so that we do
// not need to check for overflow.
for (i = 0; i < 256; i++) {
r_2_pix_alloc[i] = r_2_pix_alloc[256];
r_2_pix_alloc[i + 512] = r_2_pix_alloc[511];
g_2_pix_alloc[i] = g_2_pix_alloc[256];
g_2_pix_alloc[i + 512] = g_2_pix_alloc[511];
b_2_pix_alloc[i] = b_2_pix_alloc[256];
b_2_pix_alloc[i + 512] = b_2_pix_alloc[511];
}
}
YUVToRGBLookup::~YUVToRGBLookup() {
delete[] _rgbToPix;
delete[] _colorTab;
}
class YUVToRGBManager : public Common::Singleton<YUVToRGBManager> {
public:
const YUVToRGBLookup *getLookup(Graphics::PixelFormat format);
private:
friend class Common::Singleton<SingletonBaseType>;
YUVToRGBManager();
~YUVToRGBManager();
Graphics::PixelFormat _lastFormat;
YUVToRGBLookup *_lookup;
};
YUVToRGBManager::YUVToRGBManager() {
_lookup = 0;
}
YUVToRGBManager::~YUVToRGBManager() {
delete _lookup;
}
const YUVToRGBLookup *YUVToRGBManager::getLookup(Graphics::PixelFormat format) {
if (_lastFormat == format)
const YUVToRGBLookup *YUVToRGBManager::getLookup(Graphics::PixelFormat format, YUVToRGBManager::LuminanceScale scale) {
if (_lookup && _lookup->getFormat() == format && _lookup->getScale() == scale)
return _lookup;
delete _lookup;
_lookup = new YUVToRGBLookup(format);
_lastFormat = format;
_lookup = new YUVToRGBLookup(format, scale);
return _lookup;
}
} // End of namespace Graphics
namespace Common {
DECLARE_SINGLETON(Graphics::YUVToRGBManager);
}
#define YUVToRGBMan (Graphics::YUVToRGBManager::instance())
namespace Graphics {
#define PUT_PIXEL(s, d) \
L = &rgbToPix[(s)]; \
*((PixelInt *)(d)) = (L[cr_r] | L[crb_g] | L[cb_b])
template<typename PixelInt>
void convertYUV444ToRGB(byte *dstPtr, int dstPitch, const YUVToRGBLookup *lookup, const byte *ySrc, const byte *uSrc, const byte *vSrc, int yWidth, int yHeight, int yPitch, int uvPitch) {
void convertYUV444ToRGB(byte *dstPtr, int dstPitch, const YUVToRGBLookup *lookup, int16 *colorTab, const byte *ySrc, const byte *uSrc, const byte *vSrc, int yWidth, int yHeight, int yPitch, int uvPitch) {
// Keep the tables in pointers here to avoid a dereference on each pixel
const int16 *Cr_r_tab = lookup->_colorTab;
const int16 *Cr_r_tab = colorTab;
const int16 *Cr_g_tab = Cr_r_tab + 256;
const int16 *Cb_g_tab = Cr_g_tab + 256;
const int16 *Cb_b_tab = Cb_g_tab + 256;
const uint32 *rgbToPix = lookup->_rgbToPix;
const uint32 *rgbToPix = lookup->getRGBToPix();
for (int h = 0; h < yHeight; h++) {
for (int w = 0; w < yWidth; w++) {
@ -229,32 +227,32 @@ void convertYUV444ToRGB(byte *dstPtr, int dstPitch, const YUVToRGBLookup *lookup
}
}
void convertYUV444ToRGB(Graphics::Surface *dst, const byte *ySrc, const byte *uSrc, const byte *vSrc, int yWidth, int yHeight, int yPitch, int uvPitch) {
void YUVToRGBManager::convert444(Graphics::Surface *dst, YUVToRGBManager::LuminanceScale scale, const byte *ySrc, const byte *uSrc, const byte *vSrc, int yWidth, int yHeight, int yPitch, int uvPitch) {
// Sanity checks
assert(dst && dst->pixels);
assert(dst->format.bytesPerPixel == 2 || dst->format.bytesPerPixel == 4);
assert(ySrc && uSrc && vSrc);
const YUVToRGBLookup *lookup = YUVToRGBMan.getLookup(dst->format);
const YUVToRGBLookup *lookup = getLookup(dst->format, scale);
// Use a templated function to avoid an if check on every pixel
if (dst->format.bytesPerPixel == 2)
convertYUV444ToRGB<uint16>((byte *)dst->pixels, dst->pitch, lookup, ySrc, uSrc, vSrc, yWidth, yHeight, yPitch, uvPitch);
convertYUV444ToRGB<uint16>((byte *)dst->pixels, dst->pitch, lookup, _colorTab, ySrc, uSrc, vSrc, yWidth, yHeight, yPitch, uvPitch);
else
convertYUV444ToRGB<uint32>((byte *)dst->pixels, dst->pitch, lookup, ySrc, uSrc, vSrc, yWidth, yHeight, yPitch, uvPitch);
convertYUV444ToRGB<uint32>((byte *)dst->pixels, dst->pitch, lookup, _colorTab, ySrc, uSrc, vSrc, yWidth, yHeight, yPitch, uvPitch);
}
template<typename PixelInt>
void convertYUV420ToRGB(byte *dstPtr, int dstPitch, const YUVToRGBLookup *lookup, const byte *ySrc, const byte *uSrc, const byte *vSrc, int yWidth, int yHeight, int yPitch, int uvPitch) {
void convertYUV420ToRGB(byte *dstPtr, int dstPitch, const YUVToRGBLookup *lookup, int16 *colorTab, const byte *ySrc, const byte *uSrc, const byte *vSrc, int yWidth, int yHeight, int yPitch, int uvPitch) {
int halfHeight = yHeight >> 1;
int halfWidth = yWidth >> 1;
// Keep the tables in pointers here to avoid a dereference on each pixel
const int16 *Cr_r_tab = lookup->_colorTab;
const int16 *Cr_r_tab = colorTab;
const int16 *Cr_g_tab = Cr_r_tab + 256;
const int16 *Cb_g_tab = Cr_g_tab + 256;
const int16 *Cb_b_tab = Cb_g_tab + 256;
const uint32 *rgbToPix = lookup->_rgbToPix;
const uint32 *rgbToPix = lookup->getRGBToPix();
for (int h = 0; h < halfHeight; h++) {
for (int w = 0; w < halfWidth; w++) {
@ -283,7 +281,7 @@ void convertYUV420ToRGB(byte *dstPtr, int dstPitch, const YUVToRGBLookup *lookup
}
}
void convertYUV420ToRGB(Graphics::Surface *dst, const byte *ySrc, const byte *uSrc, const byte *vSrc, int yWidth, int yHeight, int yPitch, int uvPitch) {
void YUVToRGBManager::convert420(Graphics::Surface *dst, YUVToRGBManager::LuminanceScale scale, const byte *ySrc, const byte *uSrc, const byte *vSrc, int yWidth, int yHeight, int yPitch, int uvPitch) {
// Sanity checks
assert(dst && dst->pixels);
assert(dst->format.bytesPerPixel == 2 || dst->format.bytesPerPixel == 4);
@ -291,13 +289,13 @@ void convertYUV420ToRGB(Graphics::Surface *dst, const byte *ySrc, const byte *uS
assert((yWidth & 1) == 0);
assert((yHeight & 1) == 0);
const YUVToRGBLookup *lookup = YUVToRGBMan.getLookup(dst->format);
const YUVToRGBLookup *lookup = getLookup(dst->format, scale);
// Use a templated function to avoid an if check on every pixel
if (dst->format.bytesPerPixel == 2)
convertYUV420ToRGB<uint16>((byte *)dst->pixels, dst->pitch, lookup, ySrc, uSrc, vSrc, yWidth, yHeight, yPitch, uvPitch);
convertYUV420ToRGB<uint16>((byte *)dst->pixels, dst->pitch, lookup, _colorTab, ySrc, uSrc, vSrc, yWidth, yHeight, yPitch, uvPitch);
else
convertYUV420ToRGB<uint32>((byte *)dst->pixels, dst->pitch, lookup, ySrc, uSrc, vSrc, yWidth, yHeight, yPitch, uvPitch);
convertYUV420ToRGB<uint32>((byte *)dst->pixels, dst->pitch, lookup, _colorTab, ySrc, uSrc, vSrc, yWidth, yHeight, yPitch, uvPitch);
}
#define READ_QUAD(ptr, prefix) \
@ -325,13 +323,13 @@ void convertYUV420ToRGB(Graphics::Surface *dst, const byte *ySrc, const byte *uS
xDiff++
template<typename PixelInt>
void convertYUV410ToRGB(byte *dstPtr, int dstPitch, const YUVToRGBLookup *lookup, const byte *ySrc, const byte *uSrc, const byte *vSrc, int yWidth, int yHeight, int yPitch, int uvPitch) {
void convertYUV410ToRGB(byte *dstPtr, int dstPitch, const YUVToRGBLookup *lookup, int16 *colorTab, const byte *ySrc, const byte *uSrc, const byte *vSrc, int yWidth, int yHeight, int yPitch, int uvPitch) {
// Keep the tables in pointers here to avoid a dereference on each pixel
const int16 *Cr_r_tab = lookup->_colorTab;
const int16 *Cr_r_tab = colorTab;
const int16 *Cr_g_tab = Cr_r_tab + 256;
const int16 *Cb_g_tab = Cr_g_tab + 256;
const int16 *Cb_b_tab = Cb_g_tab + 256;
const uint32 *rgbToPix = lookup->_rgbToPix;
const uint32 *rgbToPix = lookup->getRGBToPix();
int quarterWidth = yWidth >> 2;
@ -368,7 +366,7 @@ void convertYUV410ToRGB(byte *dstPtr, int dstPitch, const YUVToRGBLookup *lookup
#undef DO_INTERPOLATION
#undef DO_YUV410_PIXEL
void convertYUV410ToRGB(Graphics::Surface *dst, const byte *ySrc, const byte *uSrc, const byte *vSrc, int yWidth, int yHeight, int yPitch, int uvPitch) {
void YUVToRGBManager::convert410(Graphics::Surface *dst, YUVToRGBManager::LuminanceScale scale, const byte *ySrc, const byte *uSrc, const byte *vSrc, int yWidth, int yHeight, int yPitch, int uvPitch) {
// Sanity checks
assert(dst && dst->pixels);
assert(dst->format.bytesPerPixel == 2 || dst->format.bytesPerPixel == 4);
@ -376,13 +374,13 @@ void convertYUV410ToRGB(Graphics::Surface *dst, const byte *ySrc, const byte *uS
assert((yWidth & 3) == 0);
assert((yHeight & 3) == 0);
const YUVToRGBLookup *lookup = YUVToRGBMan.getLookup(dst->format);
const YUVToRGBLookup *lookup = getLookup(dst->format, scale);
// Use a templated function to avoid an if check on every pixel
if (dst->format.bytesPerPixel == 2)
convertYUV410ToRGB<uint16>((byte *)dst->pixels, dst->pitch, lookup, ySrc, uSrc, vSrc, yWidth, yHeight, yPitch, uvPitch);
convertYUV410ToRGB<uint16>((byte *)dst->pixels, dst->pitch, lookup, _colorTab, ySrc, uSrc, vSrc, yWidth, yHeight, yPitch, uvPitch);
else
convertYUV410ToRGB<uint32>((byte *)dst->pixels, dst->pitch, lookup, ySrc, uSrc, vSrc, yWidth, yHeight, yPitch, uvPitch);
convertYUV410ToRGB<uint32>((byte *)dst->pixels, dst->pitch, lookup, _colorTab, ySrc, uSrc, vSrc, yWidth, yHeight, yPitch, uvPitch);
}
} // End of namespace Graphics

View File

@ -32,57 +32,85 @@
#define GRAPHICS_YUV_TO_RGB_H
#include "common/scummsys.h"
#include "common/singleton.h"
#include "graphics/surface.h"
namespace Graphics {
/**
* Convert a YUV444 image to an RGB surface
*
* @param dst the destination surface
* @param ySrc the source of the y component
* @param uSrc the source of the u component
* @param vSrc the source of the v component
* @param yWidth the width of the y surface
* @param yHeight the height of the y surface
* @param yPitch the pitch of the y surface
* @param uvPitch the pitch of the u and v surfaces
*/
void convertYUV444ToRGB(Graphics::Surface *dst, const byte *ySrc, const byte *uSrc, const byte *vSrc, int yWidth, int yHeight, int yPitch, int uvPitch);
class YUVToRGBLookup;
/**
* Convert a YUV420 image to an RGB surface
*
* @param dst the destination surface
* @param ySrc the source of the y component
* @param uSrc the source of the u component
* @param vSrc the source of the v component
* @param yWidth the width of the y surface (must be divisible by 2)
* @param yHeight the height of the y surface (must be divisible by 2)
* @param yPitch the pitch of the y surface
* @param uvPitch the pitch of the u and v surfaces
*/
void convertYUV420ToRGB(Graphics::Surface *dst, const byte *ySrc, const byte *uSrc, const byte *vSrc, int yWidth, int yHeight, int yPitch, int uvPitch);
class YUVToRGBManager : public Common::Singleton<YUVToRGBManager> {
public:
/** The scale of the luminance values */
enum LuminanceScale {
kScaleFull, /** Luminance values range from [0, 255] */
kScaleITU /** Luminance values range from [16, 235], the range from ITU-R BT.601 */
};
/**
* Convert a YUV410 image to an RGB surface
*
* Since the chroma has a very low resolution in 410, we perform bilinear scaling
* on the two chroma planes to produce the image. The chroma planes must have
* at least one extra row that can be read from in order to produce a proper
* image (filled with 0x80). This is required in order to speed up this function.
*
* @param dst the destination surface
* @param ySrc the source of the y component
* @param uSrc the source of the u component
* @param vSrc the source of the v component
* @param yWidth the width of the y surface (must be divisible by 4)
* @param yHeight the height of the y surface (must be divisible by 4)
* @param yPitch the pitch of the y surface
* @param uvPitch the pitch of the u and v surfaces
*/
void convertYUV410ToRGB(Graphics::Surface *dst, const byte *ySrc, const byte *uSrc, const byte *vSrc, int yWidth, int yHeight, int yPitch, int uvPitch);
/**
* Convert a YUV444 image to an RGB surface
*
* @param dst the destination surface
* @param scale the scale of the luminance values
* @param ySrc the source of the y component
* @param uSrc the source of the u component
* @param vSrc the source of the v component
* @param yWidth the width of the y surface
* @param yHeight the height of the y surface
* @param yPitch the pitch of the y surface
* @param uvPitch the pitch of the u and v surfaces
*/
void convert444(Graphics::Surface *dst, LuminanceScale scale, const byte *ySrc, const byte *uSrc, const byte *vSrc, int yWidth, int yHeight, int yPitch, int uvPitch);
/**
* Convert a YUV420 image to an RGB surface
*
* @param dst the destination surface
* @param scale the scale of the luminance values
* @param ySrc the source of the y component
* @param uSrc the source of the u component
* @param vSrc the source of the v component
* @param yWidth the width of the y surface (must be divisible by 2)
* @param yHeight the height of the y surface (must be divisible by 2)
* @param yPitch the pitch of the y surface
* @param uvPitch the pitch of the u and v surfaces
*/
void convert420(Graphics::Surface *dst, LuminanceScale scale, const byte *ySrc, const byte *uSrc, const byte *vSrc, int yWidth, int yHeight, int yPitch, int uvPitch);
/**
* Convert a YUV410 image to an RGB surface
*
* Since the chroma has a very low resolution in 410, we perform bilinear scaling
* on the two chroma planes to produce the image. The chroma planes must have
* at least one extra row and one extra column that can be read from in order to
* produce a proper image. It is suggested that you fill these in with the previous
* row and column's data. This is required in order to speed up this function.
*
* @param dst the destination surface
* @param scale the scale of the luminance values
* @param ySrc the source of the y component
* @param uSrc the source of the u component
* @param vSrc the source of the v component
* @param yWidth the width of the y surface (must be divisible by 4)
* @param yHeight the height of the y surface (must be divisible by 4)
* @param yPitch the pitch of the y surface
* @param uvPitch the pitch of the u and v surfaces
*/
void convert410(Graphics::Surface *dst, LuminanceScale scale, const byte *ySrc, const byte *uSrc, const byte *vSrc, int yWidth, int yHeight, int yPitch, int uvPitch);
private:
friend class Common::Singleton<SingletonBaseType>;
YUVToRGBManager();
~YUVToRGBManager();
const YUVToRGBLookup *getLookup(Graphics::PixelFormat format, LuminanceScale scale);
YUVToRGBLookup *_lookup;
int16 _colorTab[4 * 256]; // 2048 bytes
};
} // End of namespace Graphics
#define YUVToRGBMan (::Graphics::YUVToRGBManager::instance())
#endif

View File

@ -548,11 +548,11 @@ bool ThemeParser::parseDrawStep(ParserNode *stepNode, Graphics::DrawStep *drawst
else
return parserError("'" + stepNode->values["fill"] + "' is not a valid fill mode for a shape.");
}
if (stepNode->values.contains("padding")) {
val = stepNode->values["padding"];
int pr, pt, pl, pb;
if (parseIntegerKey(val, 4, &pl, &pt, &pr, &pb))
if (parseIntegerKey(val, 4, &pl, &pt, &pr, &pb))
drawstep->padding.left = pl,
drawstep->padding.top = pt,
drawstep->padding.right = pr,

View File

@ -139,7 +139,7 @@ protected:
XML_PROP(height, false)
XML_PROP(xpos, false)
XML_PROP(ypos, false)
XML_PROP(padding, false)
XML_PROP(padding, false)
XML_PROP(orientation, false)
XML_PROP(file, false)
KEY_END()

View File

@ -48,6 +48,7 @@ public:
protected:
#ifdef MACOSX
const void *_titleRef;
const void *_chooseRef;
#else
ListWidget *_fileList;
StaticTextWidget *_currentPath;

View File

@ -28,20 +28,39 @@
#include "common/config-manager.h"
#include "common/system.h"
#include "common/algorithm.h"
#include "common/translation.h"
#include <AppKit/NSOpenPanel.h>
#include <Foundation/NSString.h>
#include <Foundation/NSURL.h>
namespace GUI {
BrowserDialog::BrowserDialog(const char *title, bool dirBrowser)
: Dialog("Browser") {
_titleRef = CFStringCreateWithCString(0, title, CFStringGetSystemEncoding());
// remember whether this is a file browser or a directory browser.
_isDirBrowser = dirBrowser;
// Get current encoding
#ifdef USE_TRANSLATION
CFStringRef encStr = CFStringCreateWithCString(NULL, TransMan.getCurrentCharset().c_str(), kCFStringEncodingASCII);
CFStringEncoding stringEncoding = CFStringConvertIANACharSetNameToEncoding(encStr);
CFRelease(encStr);
#else
CFStringEncoding stringEncoding = kCFStringEncodingASCII;
#endif
// Convert title to NSString
_titleRef = CFStringCreateWithCString(0, title, stringEncoding);
// Convert button text to NSString
_chooseRef = CFStringCreateWithCString(0, _("Choose"), stringEncoding);
}
BrowserDialog::~BrowserDialog() {
CFRelease(_titleRef);
CFRelease(_chooseRef);
}
int BrowserDialog::runModal() {
@ -58,16 +77,20 @@ int BrowserDialog::runModal() {
// Temporarily show the real mouse
CGDisplayShowCursor(kCGDirectMainDisplay);
NSOpenPanel * panel = [NSOpenPanel openPanel];
[panel setCanChooseDirectories:YES];
if ([panel runModalForTypes:nil] == NSOKButton) {
const char *filename = [[panel filename] UTF8String];
_choice = Common::FSNode(filename);
choiceMade = true;
NSOpenPanel *panel = [NSOpenPanel openPanel];
[panel setCanChooseFiles:!_isDirBrowser];
[panel setCanChooseDirectories:_isDirBrowser];
[panel setTitle:(NSString *)_titleRef];
[panel setPrompt:(NSString *)_chooseRef];
if ([panel runModal] == NSOKButton) {
NSURL *url = [panel URL];
if ([url isFileURL]) {
const char *filename = [[url path] UTF8String];
_choice = Common::FSNode(filename);
choiceMade = true;
}
}
// If we were in fullscreen mode, switch back
if (wasFullscreen) {
g_system->beginGFXTransaction();

View File

@ -166,7 +166,7 @@ EditGameDialog::EditGameDialog(const String &domain, const String &desc)
} else {
warning("Plugin for target \"%s\" not found! Game specific settings might be missing", domain.c_str());
}
// GAME: Path to game data (r/o), extra data (r/o), and save data (r/w)
String gamePath(ConfMan.get("path", _domain));
String extraPath(ConfMan.get("extrapath", _domain));

View File

@ -898,7 +898,7 @@ void OptionsDialog::addEngineControls(GuiObject *boss, const Common::String &pre
ExtraGuiOptions::const_iterator iter;
for (iter = engineOptions.begin(); iter != engineOptions.end(); ++iter, ++i) {
Common::String id = Common::String::format("%d", i);
_engineCheckboxes.push_back(new CheckboxWidget(boss,
_engineCheckboxes.push_back(new CheckboxWidget(boss,
prefix + "customOption" + id + "Checkbox", _(iter->label), _(iter->tooltip)));
}
}

View File

@ -71,7 +71,7 @@ PredictiveDialog::PredictiveDialog() : Dialog("Predictive") {
_btns = (ButtonWidget **)calloc(1, sizeof(ButtonWidget *) * 16);
_btns[kCancelAct] = new ButtonWidget(this, "Predictive.Cancel", _("Cancel") , 0, kCancelCmd);
_btns[kCancelAct] = new ButtonWidget(this, "Predictive.Cancel", _("Cancel") , 0, kCancelCmd);
_btns[kOkAct] = new ButtonWidget(this, "Predictive.OK", _("Ok") , 0, kOkCmd);
_btns[kBtn1Act] = new ButtonWidget(this, "Predictive.Button1", "1 `-.&" , 0, kBut1Cmd);
_btns[kBtn2Act] = new ButtonWidget(this, "Predictive.Button2", "2 abc" , 0, kBut2Cmd);
@ -84,10 +84,10 @@ PredictiveDialog::PredictiveDialog() : Dialog("Predictive") {
_btns[kBtn9Act] = new ButtonWidget(this, "Predictive.Button9", "9 wxyz" , 0, kBut9Cmd);
_btns[kBtn0Act] = new ButtonWidget(this, "Predictive.Button0", "0" , 0, kBut0Cmd);
// I18N: You must leave "#" as is, only word 'next' is translatable
_btns[kNextAct] = new ButtonWidget(this, "Predictive.Next", _("# next") , 0, kNextCmd);
_btns[kNextAct] = new ButtonWidget(this, "Predictive.Next", _("# next") , 0, kNextCmd);
_btns[kAddAct] = new ButtonWidget(this, "Predictive.Add", _("add") , 0, kAddCmd);
_btns[kAddAct]->setEnabled(false);
#ifndef DISABLE_FANCY_THEMES
_btns[kDelAct] = new PicButtonWidget(this, "Predictive.Delete", _("Delete char"), kDelCmd);
((PicButtonWidget *)_btns[kDelAct])->useThemeTransparency(true);
@ -214,7 +214,7 @@ void PredictiveDialog::handleKeyDown(Common::KeyState state) {
_navigationwithkeys = true;
if (_lastbutton == kBtn1Act || _lastbutton == kBtn4Act || _lastbutton == kBtn7Act)
_currBtn = ButtonId(_lastbutton + 2);
else if (_lastbutton == kDelAct)
else if (_lastbutton == kDelAct)
_currBtn = kBtn1Act;
else if (_lastbutton == kModeAct)
_currBtn = kNextAct;
@ -227,7 +227,7 @@ void PredictiveDialog::handleKeyDown(Common::KeyState state) {
else
_currBtn = ButtonId(_lastbutton - 1);
if (_mode != kModeAbc && _lastbutton == kCancelAct)
_currBtn = kOkAct;
@ -237,7 +237,7 @@ void PredictiveDialog::handleKeyDown(Common::KeyState state) {
_navigationwithkeys = true;
if (_lastbutton == kBtn3Act || _lastbutton == kBtn6Act || _lastbutton == kBtn9Act || _lastbutton == kOkAct)
_currBtn = ButtonId(_lastbutton - 2);
else if (_lastbutton == kDelAct)
else if (_lastbutton == kDelAct)
_currBtn = kBtn3Act;
else if (_lastbutton == kBtn0Act)
_currBtn = kNextAct;
@ -249,7 +249,7 @@ void PredictiveDialog::handleKeyDown(Common::KeyState state) {
_currBtn = kAddAct;
else
_currBtn = ButtonId(_lastbutton + 1);
if (_mode != kModeAbc && _lastbutton == kOkAct)
_currBtn = kCancelAct;
_needRefresh = true;
@ -260,7 +260,7 @@ void PredictiveDialog::handleKeyDown(Common::KeyState state) {
_currBtn = kDelAct;
else if (_lastbutton == kDelAct)
_currBtn = kOkAct;
else if (_lastbutton == kModeAct)
else if (_lastbutton == kModeAct)
_currBtn = kBtn7Act;
else if (_lastbutton == kBtn0Act)
_currBtn = kBtn8Act;
@ -286,7 +286,7 @@ void PredictiveDialog::handleKeyDown(Common::KeyState state) {
_currBtn = kBtn0Act;
else if (_lastbutton == kBtn9Act)
_currBtn = kNextAct;
else if (_lastbutton == kModeAct)
else if (_lastbutton == kModeAct)
_currBtn = kAddAct;
else if (_lastbutton == kBtn0Act)
_currBtn = kCancelAct;

View File

@ -35,6 +35,14 @@ namespace GUI {
#ifndef DISABLE_SAVELOADCHOOSER_GRID
SaveLoadChooserType getRequestedSaveLoadDialog(const MetaEngine &metaEngine) {
const Common::String &userConfig = ConfMan.get("gui_saveload_chooser", Common::ConfigManager::kApplicationDomain);
// Check (and update if necessary) the theme config here. This catches
// resolution changes, which happened after the GUI was closed. This
// should assure that the correct GUI width/height are returned below and
// prevent the logic from picking the grid dialog, even though it is not
// possible to use it.
g_gui.checkScreenChange();
if (g_gui.getWidth() >= 640 && g_gui.getHeight() >= 400
&& metaEngine.hasFeature(MetaEngine::kSavesSupportMetaInfo)
&& metaEngine.hasFeature(MetaEngine::kSavesSupportThumbnail)
@ -422,7 +430,28 @@ void SaveLoadChooserSimple::updateSelection(bool redraw) {
}
}
void SaveLoadChooserSimple::open() {
SaveLoadChooserDialog::open();
// Scroll the list to the last used entry.
_list->scrollTo(ConfMan.getInt("gui_saveload_last_pos"));
}
void SaveLoadChooserSimple::close() {
// Save the current scroll position/used entry.
const int result = getResult();
if (result >= 0) {
ConfMan.setInt("gui_saveload_last_pos", result);
} else {
// Use the current scroll position here.
// TODO: This means we canceled the dialog (or switch to the grid). Do
// we want to save this position here? Does the user want that?
// TODO: Do we want to save the current scroll position or the
// currently selected item here? The scroll position is what the user
// currently sees and seems to make more sense.
ConfMan.setInt("gui_saveload_last_pos", _list->getCurrentScrollPos());
}
_metaEngine = 0;
_target.clear();
_saveList.clear();
@ -590,10 +619,29 @@ void SaveLoadChooserGrid::handleMouseWheel(int x, int y, int direction) {
void SaveLoadChooserGrid::open() {
SaveLoadChooserDialog::open();
_curPage = 0;
_saveList = _metaEngine->listSaves(_target.c_str());
_resultString.clear();
// Load information to restore the last page the user had open.
assert(_entriesPerPage != 0);
const uint lastPos = ConfMan.getInt("gui_saveload_last_pos");
const uint listSize = _saveList.size();
uint bestMatch = 0;
uint diff = 0xFFFFFFFF;
// We look for the nearest available slot, since a slot might be missing
// due to the user deleting it via the list based chooser, by deleting
// it by hand, etc.
for (uint i = 0; i < listSize; ++i) {
uint curDiff = ABS(_saveList[i].getSaveSlot() - (int)lastPos);
if (curDiff < diff) {
diff = curDiff;
bestMatch = i;
}
}
_curPage = bestMatch / _entriesPerPage;
// Determine the next free save slot for save mode
if (_saveMode) {
int lastSlot = -1;
@ -703,7 +751,7 @@ void SaveLoadChooserGrid::reflowLayout() {
if (!_saveMode) {
buttonCmd += 1;
}
PicButtonWidget *button = new PicButtonWidget(container, dstX, dstY, buttonWidth, buttonHeight, 0, buttonCmd);
dstY += buttonHeight;
@ -718,6 +766,24 @@ void SaveLoadChooserGrid::reflowLayout() {
}
void SaveLoadChooserGrid::close() {
// Save the current page.
const int result = getResult();
if (result >= 0 && result != _nextFreeSaveSlot) {
// If the user selected a slot we use that one. We ignore new slots
// here, since otherwise the dialog would reset to page 0 when the
// user cancels the savename dialog.
ConfMan.setInt("gui_saveload_last_pos", result);
} else {
// Otherwise save the first entry on the current page.
// This is less precise than the solution above, since the number of
// entries shown differs between save and load version of the dialog,
// thus it might wrap to a different page than expected.
// Similar things happen on resolution changes.
// TODO: Should we ignore this here? Is the user likely to be
// interested in having this page restored when he canceled?
ConfMan.setInt("gui_saveload_last_pos", !_saveList.empty() ? _saveList[_curPage * _entriesPerPage].getSaveSlot() : 0);
}
SaveLoadChooserDialog::close();
hideButtons();
}
@ -737,6 +803,12 @@ int SaveLoadChooserGrid::runIntern() {
slot = runModal();
} while (_saveMode && slot >= 0 && !selectDescription());
// Special case for new save games. We need to handle this here, since
// we cannot handle it in close() without problems.
if (slot == _nextFreeSaveSlot) {
ConfMan.setInt("gui_saveload_last_pos", slot);
}
return slot;
}
@ -826,7 +898,7 @@ void SaveLoadChooserGrid::updateSaves() {
}
}
const uint numPages = (_entriesPerPage != 0) ? (_saveList.size() / _entriesPerPage + 1) : 1;
const uint numPages = (_entriesPerPage != 0 && !_saveList.empty()) ? ((_saveList.size() + _entriesPerPage - 1) / _entriesPerPage) : 1;
_pageDisplay->setLabel(Common::String::format("%u/%u", _curPage + 1, numPages));
if (_curPage > 0)

View File

@ -103,6 +103,7 @@ public:
virtual SaveLoadChooserType getType() const { return kSaveLoadDialogList; }
#endif // !DISABLE_SAVELOADCHOOSER_GRID
virtual void open();
virtual void close();
private:
virtual int runIntern();

View File

@ -366,7 +366,7 @@ void ButtonWidget::startAnimatePressedState() {
}
void ButtonWidget::wantTickle(bool tickled) {
if (tickled)
if (tickled)
((GUI::Dialog *)_boss)->setTickleWidget(this);
else
((GUI::Dialog *)_boss)->unSetTickleWidget();
@ -376,7 +376,7 @@ void ButtonWidget::wantTickle(bool tickled) {
PicButtonWidget::PicButtonWidget(GuiObject *boss, int x, int y, int w, int h, const char *tooltip, uint32 cmd, uint8 hotkey)
: ButtonWidget(boss, x, y, w, h, "", tooltip, cmd, hotkey),
_gfx(new Graphics::Surface()), _alpha(256), _transparency(false) {
_gfx(), _alpha(256), _transparency(false) {
setFlags(WIDGET_ENABLED/* | WIDGET_BORDER*/ | WIDGET_CLEARBG);
_type = kButtonWidget;
@ -384,18 +384,17 @@ PicButtonWidget::PicButtonWidget(GuiObject *boss, int x, int y, int w, int h, co
PicButtonWidget::PicButtonWidget(GuiObject *boss, const Common::String &name, const char *tooltip, uint32 cmd, uint8 hotkey)
: ButtonWidget(boss, name, "", tooltip, cmd, hotkey),
_gfx(new Graphics::Surface()), _alpha(256), _transparency(false) {
_gfx(), _alpha(256), _transparency(false) {
setFlags(WIDGET_ENABLED/* | WIDGET_BORDER*/ | WIDGET_CLEARBG);
_type = kButtonWidget;
}
PicButtonWidget::~PicButtonWidget() {
_gfx->free();
delete _gfx;
_gfx.free();
}
void PicButtonWidget::setGfx(const Graphics::Surface *gfx) {
_gfx->free();
_gfx.free();
if (!gfx || !gfx->pixels)
return;
@ -411,7 +410,7 @@ void PicButtonWidget::setGfx(const Graphics::Surface *gfx) {
return;
}
_gfx->copyFrom(*gfx);
_gfx.copyFrom(*gfx);
}
void PicButtonWidget::setGfx(int w, int h, int r, int g, int b) {
@ -422,29 +421,26 @@ void PicButtonWidget::setGfx(int w, int h, int r, int g, int b) {
const Graphics::PixelFormat &requiredFormat = g_gui.theme()->getPixelFormat();
_gfx->free();
_gfx->create(w, h, requiredFormat);
_gfx->fillRect(Common::Rect(0, 0, w, h), _gfx->format.RGBToColor(r, g, b));
_gfx.free();
_gfx.create(w, h, requiredFormat);
_gfx.fillRect(Common::Rect(0, 0, w, h), _gfx.format.RGBToColor(r, g, b));
}
void PicButtonWidget::drawWidget() {
g_gui.theme()->drawButton(Common::Rect(_x, _y, _x+_w, _y+_h), "", _state, getFlags());
if (_gfx->pixels) {
if (_gfx.pixels) {
// Check whether the set up surface needs to be converted to the GUI
// color format.
const Graphics::PixelFormat &requiredFormat = g_gui.theme()->getPixelFormat();
if (_gfx->format != requiredFormat) {
Graphics::Surface *converted = _gfx->convertTo(requiredFormat);
_gfx->free();
delete _gfx;
_gfx = converted;
if (_gfx.format != requiredFormat) {
_gfx.convertToInPlace(requiredFormat);
}
const int x = _x + (_w - _gfx->w) / 2;
const int y = _y + (_h - _gfx->h) / 2;
const int x = _x + (_w - _gfx.w) / 2;
const int y = _y + (_h - _gfx.h) / 2;
g_gui.theme()->drawSurface(Common::Rect(x, y, x + _gfx->w, y + _gfx->h), *_gfx, _state, _alpha, _transparency);
g_gui.theme()->drawSurface(Common::Rect(x, y, x + _gfx.w, y + _gfx.h), _gfx, _state, _alpha, _transparency);
}
}
@ -632,24 +628,23 @@ int SliderWidget::posToValue(int pos) {
#pragma mark -
GraphicsWidget::GraphicsWidget(GuiObject *boss, int x, int y, int w, int h, const char *tooltip)
: Widget(boss, x, y, w, h, tooltip), _gfx(new Graphics::Surface()), _alpha(256), _transparency(false) {
: Widget(boss, x, y, w, h, tooltip), _gfx(), _alpha(256), _transparency(false) {
setFlags(WIDGET_ENABLED | WIDGET_CLEARBG);
_type = kGraphicsWidget;
}
GraphicsWidget::GraphicsWidget(GuiObject *boss, const Common::String &name, const char *tooltip)
: Widget(boss, name, tooltip), _gfx(new Graphics::Surface()), _alpha(256), _transparency(false) {
: Widget(boss, name, tooltip), _gfx(), _alpha(256), _transparency(false) {
setFlags(WIDGET_ENABLED | WIDGET_CLEARBG);
_type = kGraphicsWidget;
}
GraphicsWidget::~GraphicsWidget() {
_gfx->free();
delete _gfx;
_gfx.free();
}
void GraphicsWidget::setGfx(const Graphics::Surface *gfx) {
_gfx->free();
_gfx.free();
if (!gfx || !gfx->pixels)
return;
@ -664,7 +659,7 @@ void GraphicsWidget::setGfx(const Graphics::Surface *gfx) {
return;
}
_gfx->copyFrom(*gfx);
_gfx.copyFrom(*gfx);
}
void GraphicsWidget::setGfx(int w, int h, int r, int g, int b) {
@ -675,27 +670,24 @@ void GraphicsWidget::setGfx(int w, int h, int r, int g, int b) {
const Graphics::PixelFormat &requiredFormat = g_gui.theme()->getPixelFormat();
_gfx->free();
_gfx->create(w, h, requiredFormat);
_gfx->fillRect(Common::Rect(0, 0, w, h), _gfx->format.RGBToColor(r, g, b));
_gfx.free();
_gfx.create(w, h, requiredFormat);
_gfx.fillRect(Common::Rect(0, 0, w, h), _gfx.format.RGBToColor(r, g, b));
}
void GraphicsWidget::drawWidget() {
if (_gfx->pixels) {
if (_gfx.pixels) {
// Check whether the set up surface needs to be converted to the GUI
// color format.
const Graphics::PixelFormat &requiredFormat = g_gui.theme()->getPixelFormat();
if (_gfx->format != requiredFormat) {
Graphics::Surface *converted = _gfx->convertTo(requiredFormat);
_gfx->free();
delete _gfx;
_gfx = converted;
if (_gfx.format != requiredFormat) {
_gfx.convertToInPlace(requiredFormat);
}
const int x = _x + (_w - _gfx->w) / 2;
const int y = _y + (_h - _gfx->h) / 2;
const int x = _x + (_w - _gfx.w) / 2;
const int y = _y + (_h - _gfx.h) / 2;
g_gui.theme()->drawSurface(Common::Rect(x, y, x + _gfx->w, y + _gfx->h), *_gfx, _state, _alpha, _transparency);
g_gui.theme()->drawSurface(Common::Rect(x, y, x + _gfx.w, y + _gfx.h), _gfx, _state, _alpha, _transparency);
}
}

View File

@ -230,7 +230,7 @@ public:
protected:
void drawWidget();
Graphics::Surface *_gfx;
Graphics::Surface _gfx;
int _alpha;
bool _transparency;
};
@ -358,7 +358,7 @@ public:
protected:
void drawWidget();
Graphics::Surface *_gfx;
Graphics::Surface _gfx;
int _alpha;
bool _transparency;
};

View File

@ -77,10 +77,10 @@ public:
protected:
virtual void startEditMode() = 0;
virtual void endEditMode() = 0;
virtual void abortEditMode() = 0;
virtual void abortEditMode() = 0;
virtual Common::Rect getEditRect() const = 0;
virtual int getCaretOffset() const;
void drawCaret(bool erase);
void drawCaret(bool erase);
bool adjustOffset();
void makeCaretVisible();

View File

@ -105,6 +105,7 @@ public:
void scrollTo(int item);
void scrollToEnd();
int getCurrentScrollPos() const { return _currentPos; }
void enableQuickSelect(bool enable) { _quickSelect = enable; }
String getQuickSelectString() const { return _quickSelectStr; }

View File

@ -7,6 +7,22 @@
# POSIX specific
#
install:
$(INSTALL) -d "$(DESTDIR)$(bindir)"
$(INSTALL) -c -m 755 "./$(EXECUTABLE)" "$(DESTDIR)$(bindir)/$(EXECUTABLE)"
$(INSTALL) -d "$(DESTDIR)$(mandir)/man6/"
$(INSTALL) -c -m 644 "$(srcdir)/dists/residualvm.6" "$(DESTDIR)$(mandir)/man6/residualvm.6"
$(INSTALL) -d "$(DESTDIR)$(datarootdir)/pixmaps/"
$(INSTALL) -c -m 644 "$(srcdir)/icons/residualvm.xpm" "$(DESTDIR)$(datarootdir)/pixmaps/residualvm.xpm"
$(INSTALL) -d "$(DESTDIR)$(docdir)"
$(INSTALL) -c -m 644 $(DIST_FILES_DOCS) "$(DESTDIR)$(docdir)"
$(INSTALL) -d "$(DESTDIR)$(datadir)"
$(INSTALL) -c -m 644 $(DIST_FILES_THEMES) $(DIST_FILES_ENGINEDATA) "$(DESTDIR)$(datadir)/"
ifdef DYNAMIC_MODULES
$(INSTALL) -d "$(DESTDIR)$(libdir)/residualvm/"
$(INSTALL) -c -m 644 $(PLUGINS) "$(DESTDIR)$(libdir)/residualvm/"
endif
install-strip:
$(INSTALL) -d "$(DESTDIR)$(bindir)"
$(INSTALL) -c -s -m 755 "./$(EXECUTABLE)" "$(DESTDIR)$(bindir)/$(EXECUTABLE)"
$(INSTALL) -d "$(DESTDIR)$(mandir)/man6/"
@ -292,8 +308,10 @@ endif
# Special target to create an AmigaOS snapshot installation
aos4dist: $(EXECUTABLE)
mkdir -p $(AOS4PATH)
mkdir -p $(AOS4PATH)/themes
mkdir -p $(AOS4PATH)/extras
$(STRIP) $(EXECUTABLE) -o $(AOS4PATH)/$(EXECUTABLE)
cp icons/residualvm.info $(AOS4PATH)/$(EXECUTABLE).info
cp ${srcdir}/icons/residualvm.info $(AOS4PATH)/$(EXECUTABLE).info
cp $(DIST_FILES_THEMES) $(AOS4PATH)/themes/
ifdef DIST_FILES_ENGINEDATA
cp $(DIST_FILES_ENGINEDATA) $(AOS4PATH)/extras/
@ -303,8 +321,10 @@ endif
# Special target to cross create an AmigaOS snapshot installation
aos4dist-cross: $(EXECUTABLE)
mkdir -p ResidualVM
$(STRIP) $(EXECUTABLE) -o ResidualVM/ResidualVM
cp icons/residualvm.info ResidualVM/ResidualVM.info
mkdir -p $(AOS4PATH)/themes
mkdir -p $(AOS4PATH)/extras
$(STRIP) $(EXECUTABLE) -o $(AOS4PATH)/$(EXECUTABLE)
cp ${srcdir}/icons/residualvm.info $(AOS4PATH)/$(EXECUTABLE).info
cp $(DIST_FILES_THEMES) ResidualVM
ifdef DIST_FILES_ENGINEDATA
cp $(DIST_FILES_ENGINEDATA) ResidualVM

View File

@ -305,7 +305,7 @@ void BinkDecoder::videoPacket(VideoFrame &video) {
// Convert the YUV data we have to our format
// We're ignoring alpha for now
assert(_curPlanes[0] && _curPlanes[1] && _curPlanes[2]);
Graphics::convertYUV420ToRGB(&_surface, _curPlanes[0], _curPlanes[1], _curPlanes[2],
YUVToRGBMan.convert420(&_surface, Graphics::YUVToRGBManager::kScaleITU, _curPlanes[0], _curPlanes[1], _curPlanes[2],
_surface.w, _surface.h, _surface.w, _surface.w >> 1);
// And swap the planes with the reference planes