[Scummvm-git-logs] scummvm master -> 07726745d47b1a1468abf7befb1f04756c908f7f
elasota
noreply at scummvm.org
Tue Jun 13 02:45:26 UTC 2023
This automated email contains information about 2 new commits which have been
pushed to the 'scummvm' repo located at https://github.com/scummvm/scummvm .
Summary:
89c8f002c0 MTROPOLIS: Better fix for corrupted mToon graphics in MTI
07726745d4 MTROPOLIS: Implement Miniscript global references and MTI Shanghai minigame
Commit: 89c8f002c0a1a4ce1058214c35300b9d553765ca
https://github.com/scummvm/scummvm/commit/89c8f002c0a1a4ce1058214c35300b9d553765ca
Author: elasota (ejlasota at gmail.com)
Date: 2023-06-12T22:44:59-04:00
Commit Message:
MTROPOLIS: Better fix for corrupted mToon graphics in MTI
Changed paths:
engines/mtropolis/assets.cpp
engines/mtropolis/assets.h
diff --git a/engines/mtropolis/assets.cpp b/engines/mtropolis/assets.cpp
index 0284cf58bf6..896c57bd2c1 100644
--- a/engines/mtropolis/assets.cpp
+++ b/engines/mtropolis/assets.cpp
@@ -208,7 +208,7 @@ void CachedMToon::decompressFrames(const Common::Array<uint8> &data) {
}
template<class TNumber, uint32 TLiteralMask, uint32 TTransparentRowSkipMask>
-bool CachedMToon::decompressMToonRLE(const RleFrame &frame, const Common::Array<TNumber> &coefsArray, Graphics::ManagedSurface &surface, bool isBottomUp, uint hackFlags) {
+bool CachedMToon::decompressMToonRLE(const RleFrame &frame, const Common::Array<TNumber> &coefsArray, Graphics::ManagedSurface &surface, bool isBottomUp, bool isKeyFrame, uint hackFlags) {
assert(sizeof(TNumber) == surface.format.bytesPerPixel);
size_t size = coefsArray.size();
@@ -247,19 +247,13 @@ bool CachedMToon::decompressMToonRLE(const RleFrame &frame, const Common::Array<
// Vertical skip
uint32 skipAmount = transparentCountCode - TTransparentRowSkipMask;
- // Not sure why this special code exists, but several mToons in MTI use it, such as the pants and shoe pull-outs
- // in the treasure chest in the first area, and the Hispaniola TV in the MPZ-1000.
- //
- // The pants graphic depends on avoiding the x=0 here.
- if (skipAmount != (TTransparentRowSkipMask - 2)) {
- y += skipAmount;
- x = 0;
- if (y < h) {
- rowData = static_cast<TNumber *>(surface.getBasePtr(0, isBottomUp ? (h - 1 - y) : y));
- continue;
- } else {
- break;
- }
+ y += skipAmount;
+ x = 0;
+ if (y < h) {
+ rowData = static_cast<TNumber *>(surface.getBasePtr(0, isBottomUp ? (h - 1 - y) : y));
+ continue;
+ } else {
+ break;
}
} else {
// Horizontal skip
@@ -278,7 +272,7 @@ bool CachedMToon::decompressMToonRLE(const RleFrame &frame, const Common::Array<
size -= numLiterals;
x += numLiterals;
} else {
- // Literals
+ // Run
const size_t numCopies = rleCode;
if (numCopies > remainingInRow || size == 0)
return false;
@@ -288,6 +282,21 @@ bool CachedMToon::decompressMToonRLE(const RleFrame &frame, const Common::Array<
coefs++;
size--;
x += numCopies;
+
+ if (size >= 2) {
+ // Handle some strange cases in MTI that appear to be caused by some kind of mToon
+ // encoder RLE flush problem: Numerous mToons have a 0-length RLE run after a max-length
+ // run. In most cases, the repeated value is 0, which has no effect, but in some cases
+ // this causes decode problems because the value is non-zero and gets decoded as a skip.
+ //
+ // In particular, it causes problems with the MPZ-1000 Hispaniola TV, the shoe and pants
+ // pull-outs in the chest in the first area, and the target animations in the Hispaniola
+ // cannon minigame.
+ if (numCopies == (TLiteralMask - 1) && coefs[0] == 0 && coefs[1] == repeatedValue) {
+ coefs += 2;
+ size -= 2;
+ }
+ }
}
if (x == w) {
@@ -310,13 +319,15 @@ void CachedMToon::decompressRLEFrameToImage(size_t frameIndex, Graphics::Managed
bool isBottomUp = (_metadata->imageFormat == MToonMetadata::kImageFormatWindows);
+ bool isKeyFrame = _metadata->frames[frameIndex].isKeyFrame;
+
bool decompressedOK = false;
if (_rleOptimizedFormat.bytesPerPixel == 4) {
- decompressedOK = decompressMToonRLE<uint32, 0x80000000u, 0x80000000u>(_rleData[frameIndex], _rleData[frameIndex].data32, surface, isBottomUp, _hackFlags);
+ decompressedOK = decompressMToonRLE<uint32, 0x80000000u, 0x80000000u>(_rleData[frameIndex], _rleData[frameIndex].data32, surface, isBottomUp, isKeyFrame, _hackFlags);
} else if (_rleOptimizedFormat.bytesPerPixel == 2) {
- decompressedOK = decompressMToonRLE<uint16, 0x8000u, 0x8000u>(_rleData[frameIndex], _rleData[frameIndex].data16, surface, isBottomUp, _hackFlags);
+ decompressedOK = decompressMToonRLE<uint16, 0x8000u, 0x8000u>(_rleData[frameIndex], _rleData[frameIndex].data16, surface, isBottomUp, isKeyFrame, _hackFlags);
} else if (_rleOptimizedFormat.bytesPerPixel == 1) {
- decompressedOK = decompressMToonRLE<uint8, 0x80u, 0x80u>(_rleData[frameIndex], _rleData[frameIndex].data8, surface, isBottomUp, _hackFlags);
+ decompressedOK = decompressMToonRLE<uint8, 0x80u, 0x80u>(_rleData[frameIndex], _rleData[frameIndex].data8, surface, isBottomUp, isKeyFrame, _hackFlags);
} else
error("Unknown mToon encoding");
@@ -652,12 +663,14 @@ void CachedMToon::getOrRenderFrame(uint32 prevFrame, uint32 targetFrame, Common:
bool isBottomUp = (_metadata->imageFormat == MToonMetadata::kImageFormatWindows);
for (size_t i = firstFrameToRender; i <= targetFrame; i++) {
+ bool isKeyFrame = _metadata->frames[i].isKeyFrame;
+
if (_rleOptimizedFormat.bytesPerPixel == 1)
- decompressMToonRLE<uint8, 0x80u, 0x80u>(_rleData[i], _rleData[i].data8, *surface, isBottomUp, _hackFlags);
+ decompressMToonRLE<uint8, 0x80u, 0x80u>(_rleData[i], _rleData[i].data8, *surface, isBottomUp, isKeyFrame, _hackFlags);
else if (_rleOptimizedFormat.bytesPerPixel == 2)
- decompressMToonRLE<uint16, 0x8000u, 0x8000u>(_rleData[i], _rleData[i].data16, *surface, isBottomUp, _hackFlags);
+ decompressMToonRLE<uint16, 0x8000u, 0x8000u>(_rleData[i], _rleData[i].data16, *surface, isBottomUp, isKeyFrame, _hackFlags);
else if (_rleOptimizedFormat.bytesPerPixel == 4)
- decompressMToonRLE<uint32, 0x80000000u, 0x80000000u>(_rleData[i], _rleData[i].data32, *surface, isBottomUp, _hackFlags);
+ decompressMToonRLE<uint32, 0x80000000u, 0x80000000u>(_rleData[i], _rleData[i].data32, *surface, isBottomUp, isKeyFrame, _hackFlags);
}
}
}
diff --git a/engines/mtropolis/assets.h b/engines/mtropolis/assets.h
index 1a4b7218acb..85e17331eb3 100644
--- a/engines/mtropolis/assets.h
+++ b/engines/mtropolis/assets.h
@@ -152,7 +152,7 @@ private:
void rleReformat(RleFrame &frame, const Common::Array<TSrcNumber> &srcData, const Graphics::PixelFormat &srcFormatRef, Common::Array<TDestNumber> &destData, const Graphics::PixelFormat &destFormatRef, uint hackFlags);
template<class TNumber, uint32 TLiteralMask, uint32 TTransparentRowSkipMask>
- static bool decompressMToonRLE(const RleFrame &frame, const Common::Array<TNumber> &coefsArray, Graphics::ManagedSurface &surface, bool isBottomUp, uint hackFlags);
+ static bool decompressMToonRLE(const RleFrame &frame, const Common::Array<TNumber> &coefsArray, Graphics::ManagedSurface &surface, bool isBottomUp, bool isKeyFrame, uint hackFlags);
Common::Array<RleFrame> _rleData;
bool _isRLETemporalCompressed;
Commit: 07726745d47b1a1468abf7befb1f04756c908f7f
https://github.com/scummvm/scummvm/commit/07726745d47b1a1468abf7befb1f04756c908f7f
Author: elasota (ejlasota at gmail.com)
Date: 2023-06-12T22:44:59-04:00
Commit Message:
MTROPOLIS: Implement Miniscript global references and MTI Shanghai minigame
Changed paths:
engines/mtropolis/miniscript.cpp
engines/mtropolis/miniscript.h
engines/mtropolis/plugin/mti.cpp
engines/mtropolis/plugin/mti.h
engines/mtropolis/plugin/mti_data.cpp
engines/mtropolis/plugin/mti_data.h
diff --git a/engines/mtropolis/miniscript.cpp b/engines/mtropolis/miniscript.cpp
index 122f719bdf0..50a2afa3dc5 100644
--- a/engines/mtropolis/miniscript.cpp
+++ b/engines/mtropolis/miniscript.cpp
@@ -28,6 +28,35 @@
namespace MTropolis {
+class MiniscriptInstructionParserFeedback : public IMiniscriptInstructionParserFeedback {
+public:
+ explicit MiniscriptInstructionParserFeedback(Common::Array<MiniscriptReferences::GlobalRef> *globalRefs);
+
+ uint registerGlobalGUIDIndex(uint32 guid) override;
+
+private:
+ Common::Array<MiniscriptReferences::GlobalRef> *_globalRefs;
+};
+
+MiniscriptInstructionParserFeedback::MiniscriptInstructionParserFeedback(Common::Array<MiniscriptReferences::GlobalRef> *globalRefs) : _globalRefs(globalRefs) {
+}
+
+uint MiniscriptInstructionParserFeedback::registerGlobalGUIDIndex(uint32 guid) {
+ for (uint i = 0; i < _globalRefs->size(); i++) {
+ if ((*_globalRefs)[i].guid == guid)
+ return i;
+ }
+
+ uint newIndex = _globalRefs->size();
+
+ MiniscriptReferences::GlobalRef globalRef;
+ globalRef.guid = guid;
+
+ _globalRefs->push_back(globalRef);
+
+ return newIndex;
+}
+
bool miniscriptEvaluateTruth(const DynamicValue &value) {
// NOTE: Comparing equal to "true" only passes for 1 exactly, but for conditions,
// any non-zero value is true.
@@ -45,21 +74,29 @@ bool miniscriptEvaluateTruth(const DynamicValue &value) {
}
}
+IMiniscriptInstructionParserFeedback::~IMiniscriptInstructionParserFeedback() {
+}
+
MiniscriptInstruction::~MiniscriptInstruction() {
}
MiniscriptReferences::LocalRef::LocalRef() : guid(0) {
}
-MiniscriptReferences::MiniscriptReferences(const Common::Array<LocalRef> &localRefs) : _localRefs(localRefs) {
+MiniscriptReferences::GlobalRef::GlobalRef() : guid(0) {
+}
+
+MiniscriptReferences::MiniscriptReferences(const Common::Array<LocalRef> &localRefs, const Common::Array<GlobalRef> &globalRefs) : _localRefs(localRefs), _globalRefs(globalRefs) {
}
void MiniscriptReferences::linkInternalReferences(ObjectLinkingScope *scope) {
// Resolve using name lookups since there are some known cases where the GUID is broken
// e.g. "bArriveFromCutScene" in "Set bArriveFromCutScene on PE" in Obsidian
- for (Common::Array<LocalRef>::iterator it = _localRefs.begin(), itEnd = _localRefs.end(); it != itEnd; ++it) {
+ for (Common::Array<LocalRef>::iterator it = _localRefs.begin(), itEnd = _localRefs.end(); it != itEnd; ++it)
it->resolution = scope->resolve(it->guid, it->name, false);
- }
+
+ for (Common::Array<GlobalRef>::iterator it = _globalRefs.begin(), itEnd = _globalRefs.end(); it != itEnd; ++it)
+ it->resolution = scope->resolve(it->guid, "", true);
}
void MiniscriptReferences::visitInternalReferences(IStructuralReferenceVisitor *visitor) {
@@ -77,6 +114,21 @@ void MiniscriptReferences::visitInternalReferences(IStructuralReferenceVisitor *
}
}
}
+
+ for (GlobalRef &ref : _globalRefs) {
+ Common::SharedPtr<RuntimeObject> obj = ref.resolution.lock();
+ if (obj) {
+ if (obj->isModifier()) {
+ Common::WeakPtr<Modifier> mod = obj.staticCast<Modifier>();
+ visitor->visitWeakModifierRef(mod);
+ ref.resolution = mod;
+ } else if (obj->isStructural()) {
+ Common::WeakPtr<Structural> struc = obj.staticCast<Structural>();
+ visitor->visitWeakStructuralRef(struc);
+ ref.resolution = struc;
+ }
+ }
+ }
}
Common::WeakPtr<RuntimeObject> MiniscriptReferences::getRefByIndex(uint index) const {
@@ -85,6 +137,13 @@ Common::WeakPtr<RuntimeObject> MiniscriptReferences::getRefByIndex(uint index) c
return _localRefs[index].resolution;
}
+Common::WeakPtr<RuntimeObject> MiniscriptReferences::getGlobalRefByIndex(uint index) const {
+ if (index >= _globalRefs.size())
+ return Common::WeakPtr<RuntimeObject>();
+ return _globalRefs[index].resolution;
+
+}
+
MiniscriptProgram::MiniscriptProgram(const Common::SharedPtr<Common::Array<uint8> > &programData, const Common::Array<MiniscriptInstruction *> &instructions, const Common::Array<Attribute> &attributes)
: _programData(programData), _instructions(instructions), _attributes(attributes) {
}
@@ -105,18 +164,18 @@ const Common::Array<MiniscriptProgram::Attribute> &MiniscriptProgram::getAttribu
template<class T>
struct MiniscriptInstructionLoader {
- static bool loadInstruction(void *dest, uint32 instrFlags, Data::DataReader &instrDataReader);
+ static bool loadInstruction(void *dest, uint32 instrFlags, Data::DataReader &instrDataReader, IMiniscriptInstructionParserFeedback &feedback);
};
template<class T>
-bool MiniscriptInstructionLoader<T>::loadInstruction(void *dest, uint32 instrFlags, Data::DataReader &instrDataReader) {
+bool MiniscriptInstructionLoader<T>::loadInstruction(void *dest, uint32 instrFlags, Data::DataReader &instrDataReader, IMiniscriptInstructionParserFeedback &feedback) {
// Default loader for simple instructions with no private data
new (dest) T();
return true;
}
template<>
-bool MiniscriptInstructionLoader<MiniscriptInstructions::Send>::loadInstruction(void *dest, uint32 instrFlags, Data::DataReader &instrDataReader) {
+bool MiniscriptInstructionLoader<MiniscriptInstructions::Send>::loadInstruction(void *dest, uint32 instrFlags, Data::DataReader &instrDataReader, IMiniscriptInstructionParserFeedback &feedback) {
Data::Event dataEvent;
if (!dataEvent.load(instrDataReader))
return false;
@@ -135,7 +194,7 @@ bool MiniscriptInstructionLoader<MiniscriptInstructions::Send>::loadInstruction(
}
template<>
-bool MiniscriptInstructionLoader<MiniscriptInstructions::BuiltinFunc>::loadInstruction(void *dest, uint32 instrFlags, Data::DataReader &instrDataReader) {
+bool MiniscriptInstructionLoader<MiniscriptInstructions::BuiltinFunc>::loadInstruction(void *dest, uint32 instrFlags, Data::DataReader &instrDataReader, IMiniscriptInstructionParserFeedback &feedback) {
uint32 functionID;
if (!instrDataReader.readU32(functionID))
return false;
@@ -148,7 +207,7 @@ bool MiniscriptInstructionLoader<MiniscriptInstructions::BuiltinFunc>::loadInstr
}
template<>
-bool MiniscriptInstructionLoader<MiniscriptInstructions::GetChild>::loadInstruction(void *dest, uint32 instrFlags, Data::DataReader &instrDataReader) {
+bool MiniscriptInstructionLoader<MiniscriptInstructions::GetChild>::loadInstruction(void *dest, uint32 instrFlags, Data::DataReader &instrDataReader, IMiniscriptInstructionParserFeedback &feedback) {
uint32 childAttribute;
if (!instrDataReader.readU32(childAttribute))
return false;
@@ -158,7 +217,7 @@ bool MiniscriptInstructionLoader<MiniscriptInstructions::GetChild>::loadInstruct
}
template<>
-bool MiniscriptInstructionLoader<MiniscriptInstructions::PushGlobal>::loadInstruction(void *dest, uint32 instrFlags, Data::DataReader &instrDataReader) {
+bool MiniscriptInstructionLoader<MiniscriptInstructions::PushGlobal>::loadInstruction(void *dest, uint32 instrFlags, Data::DataReader &instrDataReader, IMiniscriptInstructionParserFeedback &feedback) {
uint32 globalID;
if (!instrDataReader.readU32(globalID))
return false;
@@ -168,7 +227,7 @@ bool MiniscriptInstructionLoader<MiniscriptInstructions::PushGlobal>::loadInstru
}
template<>
-bool MiniscriptInstructionLoader<MiniscriptInstructions::Jump>::loadInstruction(void *dest, uint32 instrFlags, Data::DataReader &instrDataReader) {
+bool MiniscriptInstructionLoader<MiniscriptInstructions::Jump>::loadInstruction(void *dest, uint32 instrFlags, Data::DataReader &instrDataReader, IMiniscriptInstructionParserFeedback &feedback) {
uint32 jumpFlags, unknown, instrOffset;
if (!instrDataReader.readU32(jumpFlags) || !instrDataReader.readU32(unknown) || !instrDataReader.readU32(instrOffset))
return false;
@@ -185,7 +244,7 @@ bool MiniscriptInstructionLoader<MiniscriptInstructions::Jump>::loadInstruction(
}
template<>
-bool MiniscriptInstructionLoader<MiniscriptInstructions::PushValue>::loadInstruction(void *dest, uint32 instrFlags, Data::DataReader &instrDataReader) {
+bool MiniscriptInstructionLoader<MiniscriptInstructions::PushValue>::loadInstruction(void *dest, uint32 instrFlags, Data::DataReader &instrDataReader, IMiniscriptInstructionParserFeedback &feedback) {
uint16 dataType;
if (!instrDataReader.readU16(dataType))
return false;
@@ -217,7 +276,9 @@ bool MiniscriptInstructionLoader<MiniscriptInstructions::PushValue>::loadInstruc
if (!instrDataReader.readU32(refValue))
return false;
- new (dest) MiniscriptInstructions::PushValue(MiniscriptInstructions::PushValue::kDataTypeGlobalRef, &refValue, (instrFlags & 1) != 0);
+ uint32 indexedRef = feedback.registerGlobalGUIDIndex(refValue);
+
+ new (dest) MiniscriptInstructions::PushValue(MiniscriptInstructions::PushValue::kDataTypeGlobalRef, &indexedRef, (instrFlags & 1) != 0);
} else if (dataType == 0x1d) {
MiniscriptInstructions::PushValue::Label label;
if (!instrDataReader.readU32(label.superGroup) || !instrDataReader.readU32(label.id))
@@ -231,7 +292,7 @@ bool MiniscriptInstructionLoader<MiniscriptInstructions::PushValue>::loadInstruc
}
template<>
-bool MiniscriptInstructionLoader<MiniscriptInstructions::PushString>::loadInstruction(void *dest, uint32 instrFlags, Data::DataReader &instrDataReader) {
+bool MiniscriptInstructionLoader<MiniscriptInstructions::PushString>::loadInstruction(void *dest, uint32 instrFlags, Data::DataReader &instrDataReader, IMiniscriptInstructionParserFeedback &feedback) {
uint16 strLength;
if (!instrDataReader.readU16(strLength))
return false;
@@ -247,14 +308,14 @@ bool MiniscriptInstructionLoader<MiniscriptInstructions::PushString>::loadInstru
}
struct SIMiniscriptInstructionFactory {
- bool (*create)(void *dest, uint32 instrFlags, Data::DataReader &instrDataReader, MiniscriptInstruction *&outMiniscriptInstructionPtr);
+ bool (*create)(void *dest, uint32 instrFlags, Data::DataReader &instrDataReader, MiniscriptInstruction *&outMiniscriptInstructionPtr, IMiniscriptInstructionParserFeedback &feedback);
void (*getSizeAndAlignment)(size_t &outSize, size_t &outAlignment);
};
template<class T>
class MiniscriptInstructionFactory {
public:
- static bool create(void *dest, uint32 instrFlags, Data::DataReader &instrDataReader, MiniscriptInstruction *&outMiniscriptInstructionPtr);
+ static bool create(void *dest, uint32 instrFlags, Data::DataReader &instrDataReader, MiniscriptInstruction *&outMiniscriptInstructionPtr, IMiniscriptInstructionParserFeedback &feedback);
static void getSizeAndAlignment(size_t &outSize, size_t &outAlignment);
static SIMiniscriptInstructionFactory *getInstance();
@@ -264,8 +325,8 @@ private:
};
template<class T>
-bool MiniscriptInstructionFactory<T>::create(void *dest, uint32 instrFlags, Data::DataReader &instrDataReader, MiniscriptInstruction *&outMiniscriptInstructionPtr) {
- if (!MiniscriptInstructionLoader<T>::loadInstruction(dest, instrFlags, instrDataReader))
+bool MiniscriptInstructionFactory<T>::create(void *dest, uint32 instrFlags, Data::DataReader &instrDataReader, MiniscriptInstruction *&outMiniscriptInstructionPtr, IMiniscriptInstructionParserFeedback &feedback) {
+ if (!MiniscriptInstructionLoader<T>::loadInstruction(dest, instrFlags, instrDataReader, feedback))
return false;
outMiniscriptInstructionPtr = static_cast<MiniscriptInstruction *>(static_cast<T *>(dest));
@@ -295,6 +356,7 @@ MiniscriptParser::InstructionData::InstructionData()
bool MiniscriptParser::parse(const Data::MiniscriptProgram &program, Common::SharedPtr<MiniscriptProgram> &outProgram, Common::SharedPtr<MiniscriptReferences> &outReferences) {
Common::Array<MiniscriptReferences::LocalRef> localRefs;
+ Common::Array<MiniscriptReferences::GlobalRef> globalRefs;
Common::Array<MiniscriptProgram::Attribute> attributes;
Common::SharedPtr<Common::Array<uint8> > programDataPtr;
Common::Array<MiniscriptInstruction *> miniscriptInstructions;
@@ -302,7 +364,7 @@ bool MiniscriptParser::parse(const Data::MiniscriptProgram &program, Common::Sha
// If the program is empty then just return an empty program
if (program.bytecode.size() == 0 || program.numOfInstructions == 0) {
outProgram = Common::SharedPtr<MiniscriptProgram>(new MiniscriptProgram(programDataPtr, miniscriptInstructions, attributes));
- outReferences = Common::SharedPtr<MiniscriptReferences>(new MiniscriptReferences(localRefs));
+ outReferences = Common::SharedPtr<MiniscriptReferences>(new MiniscriptReferences(localRefs, globalRefs));
return true;
}
@@ -377,6 +439,8 @@ bool MiniscriptParser::parse(const Data::MiniscriptProgram &program, Common::Sha
miniscriptInstructions.resize(program.numOfInstructions);
+ MiniscriptInstructionParserFeedback parserFeedback(&globalRefs);
+
// Create instructions
for (size_t i = 0; i < program.numOfInstructions; i++) {
const InstructionData &rawInstruction = rawInstructions[i];
@@ -388,7 +452,7 @@ bool MiniscriptParser::parse(const Data::MiniscriptProgram &program, Common::Sha
Common::MemoryReadStreamEndian instrContentsStream(static_cast<const byte *>(dataLoc), rawInstruction.contents.size(), reader.isBigEndian());
Data::DataReader instrContentsReader(0, instrContentsStream, reader.getProjectFormat());
- if (!rawInstruction.instrFactory->create(&programData[baseOffset + rawInstruction.pdPosition], rawInstruction.flags, instrContentsReader, miniscriptInstructions[i])) {
+ if (!rawInstruction.instrFactory->create(&programData[baseOffset + rawInstruction.pdPosition], rawInstruction.flags, instrContentsReader, miniscriptInstructions[i], parserFeedback)) {
// Destroy any already-created instructions
for (size_t di = 0; di < i; di++) {
miniscriptInstructions[i - 1 - di]->~MiniscriptInstruction();
@@ -400,7 +464,7 @@ bool MiniscriptParser::parse(const Data::MiniscriptProgram &program, Common::Sha
// Done
outProgram = Common::SharedPtr<MiniscriptProgram>(new MiniscriptProgram(programDataPtr, miniscriptInstructions, attributes));
- outReferences = Common::SharedPtr<MiniscriptReferences>(new MiniscriptReferences(localRefs));
+ outReferences = Common::SharedPtr<MiniscriptReferences>(new MiniscriptReferences(localRefs, globalRefs));
return true;
}
@@ -1662,8 +1726,8 @@ MiniscriptInstructionOutcome PushValue::execute(MiniscriptThread *thread) const
value.setObject(ObjectReference(thread->getRefs()->getRefByIndex(_value.ref)));
break;
case DataType::kDataTypeGlobalRef:
- thread->error("Global references are not implemented");
- return kMiniscriptInstructionOutcomeFailed;
+ value.setObject(ObjectReference(thread->getRefs()->getGlobalRefByIndex(_value.ref)));
+ break;
case DataType::kDataTypeLabel: {
MTropolis::Label label;
label.id = _value.lbl.id;
diff --git a/engines/mtropolis/miniscript.h b/engines/mtropolis/miniscript.h
index 7ff7c1a0193..49c06fddfea 100644
--- a/engines/mtropolis/miniscript.h
+++ b/engines/mtropolis/miniscript.h
@@ -40,6 +40,13 @@ public:
virtual MiniscriptInstructionOutcome execute(MiniscriptThread *thread) const = 0;
};
+class IMiniscriptInstructionParserFeedback {
+public:
+ virtual ~IMiniscriptInstructionParserFeedback();
+
+ virtual uint registerGlobalGUIDIndex(uint32 guid) = 0;
+};
+
class MiniscriptReferences {
public:
struct LocalRef {
@@ -50,15 +57,24 @@ public:
Common::WeakPtr<RuntimeObject> resolution;
};
- explicit MiniscriptReferences(const Common::Array<LocalRef> &localRefs);
+ struct GlobalRef {
+ GlobalRef();
+
+ uint32 guid;
+ Common::WeakPtr<RuntimeObject> resolution;
+ };
+
+ explicit MiniscriptReferences(const Common::Array<LocalRef> &localRefs, const Common::Array<GlobalRef> &globalRefs);
void linkInternalReferences(ObjectLinkingScope *scope);
void visitInternalReferences(IStructuralReferenceVisitor *visitor);
Common::WeakPtr<RuntimeObject> getRefByIndex(uint index) const;
+ Common::WeakPtr<RuntimeObject> getGlobalRefByIndex(uint index) const;
private:
Common::Array<LocalRef> _localRefs;
+ Common::Array<GlobalRef> _globalRefs;
};
diff --git a/engines/mtropolis/plugin/mti.cpp b/engines/mtropolis/plugin/mti.cpp
index 674442c563f..3315f4e9f12 100644
--- a/engines/mtropolis/plugin/mti.cpp
+++ b/engines/mtropolis/plugin/mti.cpp
@@ -26,21 +26,147 @@
#include "mtropolis/miniscript.h"
+#include "common/random.h"
+
namespace MTropolis {
namespace MTI {
+
+
+/*
+Board layout:
+
+Layer 0:
+ 0 1 2 3 4 5 6 7 8 9 10 11 12
+ +-----+-----+-----+-----+-----+-----+
+0 + 0 | 1 | 2 | 3 | 4 | 5 |
+1 +-----+-----+-----+-----+-----+-----+
+2 +-----+ 7 | 8 | 9 | 10 +-----+-----+
+3 | 6 +-----+-----+-----+-----+ 11 | 12 |
+4 +-----+ 13 | 14 | 15 | 16 +-----+-----+
+5 +-----+-----+-----+-----+-----+-----+
+6 + 17 | 18 | 19 | 20 | 21 | 22 |
+ +-----+-----+-----+-----+-----+-----+
+
+Layer 1:
+ 0 1 2 3 4 5 6 7 8 9 10 11 12
+
+0
+1 +-----+-----+
+2 | 23 | 24 |
+3 +-----+-----+
+4 | 25 | 26 |
+5 +-----+-----+
+6
+
+Layer 2:
+ 0 1 2 3 4 5 6 7 8 9 10 11 12
+
+0
+1
+2 +-----+
+3 | 27 |
+4 +-----+
+5
+6
+
+*/
+
+ShanghaiModifier::TileCoordinate ShanghaiModifier::_tileCoordinates[ShanghaiModifier::kNumTiles] = {
+ {0, 0, 0},
+ {2, 0, 0},
+ {4, 0, 0},
+ {6, 0, 0},
+ {8, 0, 0},
+ {10, 0, 0},
+
+ {0, 3, 0},
+ {2, 2, 0},
+ {4, 2, 0},
+ {6, 2, 0},
+ {8, 2, 0},
+ {10, 3, 0},
+ {12, 3, 0},
+
+ {2, 4, 0},
+ {4, 4, 0},
+ {6, 4, 0},
+ {8, 4, 0},
+
+ {0, 6, 0},
+ {2, 6, 0},
+ {4, 6, 0},
+ {6, 6, 0},
+ {8, 6, 0},
+ {10, 6, 0},
+
+ {4, 2, 0},
+ {6, 2, 0},
+
+ {4, 4, 1},
+ {6, 4, 1},
+
+ {5, 3, 2},
+};
ShanghaiModifier::ShanghaiModifier() {
+ for (uint x = 0; x < kBoardSizeX; x++)
+ for (uint y = 0; y < kBoardSizeY; y++)
+ for (uint z = 0; z < kBoardSizeZ; z++)
+ _tileAtCoordinate[x][y][z] = -1;
+
+ for (uint i = 0; i < kNumTiles; i++) {
+ const TileCoordinate &coord = _tileCoordinates[i];
+ assert(coord.x < kBoardSizeX);
+ assert(coord.y < kBoardSizeY);
+ assert(coord.z < kBoardSizeZ);
+ _tileAtCoordinate[coord.x][coord.y][coord.z] = i;
+ }
}
ShanghaiModifier::~ShanghaiModifier() {
}
bool ShanghaiModifier::respondsToEvent(const Event &evt) const {
+ if (_resetTileSetWhen.respondsTo(evt))
+ return true;
+
return false;
}
VThreadState ShanghaiModifier::consumeMessage(Runtime *runtime, const Common::SharedPtr<MessageProperties> &msg) {
+ if (_resetTileSetWhen.respondsTo(msg->getEvent())) {
+ uint tileFaces[kNumTiles];
+
+ resetTiles(*runtime->getRandom(), tileFaces);
+
+ Modifier *varMod = this->_tileSetRef.resolution.lock().get();
+
+ if (varMod == nullptr || !varMod->isVariable()) {
+ warning("Shanghai reset var ref was unavailable");
+ return kVThreadError;
+ }
+
+ VariableModifier *var = static_cast<VariableModifier *>(varMod);
+
+ Common::SharedPtr<DynamicList> list(new DynamicList());
+
+ for (uint i = 0; i < kNumTiles; i++) {
+ DynamicValue tileValue;
+ tileValue.setInt(tileFaces[i]);
+
+ list->setAtIndex(i, tileValue);
+ }
+
+ DynamicValue listValue;
+ listValue.setList(list);
+
+ MiniscriptThread thread(runtime, nullptr, nullptr, nullptr, this);
+ var->varSetValue(&thread, listValue);
+
+ return kVThreadReturn;
+ }
+
return kVThreadReturn;
}
@@ -48,9 +174,217 @@ void ShanghaiModifier::disable(Runtime *runtime) {
}
bool ShanghaiModifier::load(const PlugInModifierLoaderContext &context, const Data::MTI::ShanghaiModifier &data) {
+ if (data.resetWhen.type != Data::PlugInTypeTaggedValue::kEvent)
+ return false;
+
+ if (!_resetTileSetWhen.load(data.resetWhen.value.asEvent))
+ return false;
+
+ if (data.tileSetVar.type != Data::PlugInTypeTaggedValue::kVariableReference)
+ return false;
+
+ _tileSetRef = VarReference(data.tileSetVar.value.asVarRefGUID, "");
+
+ return true;
+}
+
+void ShanghaiModifier::linkInternalReferences(ObjectLinkingScope *scope) {
+ _tileSetRef.linkInternalReferences(scope);
+}
+
+void ShanghaiModifier::visitInternalReferences(IStructuralReferenceVisitor *visitor) {
+ _tileSetRef.visitInternalReferences(visitor);
+}
+
+void ShanghaiModifier::resetTiles(Common::RandomSource &rng, uint (&tileFaces)[kNumTiles]) const {
+ uint possibleFaces[kNumFaces];
+ uint numPossibleFaces = kNumFaces;
+
+ for (uint i = 0; i < kNumFaces; i++)
+ possibleFaces[i] = i + 1;
+
+ uint facesToInsert[kNumTiles / 2];
+ uint numFacesToInsert = kNumTiles / 2;
+
+ // Pick random faces, each one gets inserted twice
+ for (uint i = 0; i < kNumTiles / 4u; i++) {
+ uint faceToInsert = selectAndRemoveOne(rng, possibleFaces, numPossibleFaces);
+ facesToInsert[i * 2 + 0] = faceToInsert;
+ facesToInsert[i * 2 + 1] = faceToInsert;
+ }
+
+ // We build the board by adding all tiles and then randomly picking 2 exposed tiles and
+ // assigning them a matching pair. A pair is only valid if the resulting board state has
+ // valid moves.
+ BoardState_t boardState = emptyBoardState();
+ for (uint i = 0; i < kNumTiles; i++)
+ boardState = boardState | boardStateBit(i);
+
+ for (uint pair = 0; pair < kNumTiles / 2u; pair++) {
+ uint exposedTiles[kNumTiles];
+ uint numExposedTiles = 0;
+
+ for (uint i = 0; i < kNumTiles; i++) {
+ if (boardState & boardStateBit(i)) {
+ if (tileIsExposed(boardState, i))
+ exposedTiles[numExposedTiles++] = i;
+ }
+ }
+
+ uint firstExposedTile = selectAndRemoveOne(rng, exposedTiles, numExposedTiles);
+
+ BoardState_t withFirstRemoved = boardState ^ boardStateBit(firstExposedTile);
+
+ uint secondExposedTile = selectAndRemoveOne(rng, exposedTiles, numExposedTiles);
+ BoardState_t withBothRemoved = withFirstRemoved ^ boardStateBit(secondExposedTile);
+
+ if (numExposedTiles > 0) {
+ // If this isn't the last move, validate that this won't result in a stuck board state (e.g. only one tile exposed)
+ // If it would result in such a state, pick a different move.
+ for (;;) {
+ if (boardStateHasValidMove(withBothRemoved))
+ break;
+
+ if (numExposedTiles == 0) {
+ error("Shanghai board creation failed, board state was %x, removed %u to produce board state %x", static_cast<uint>(boardState), firstExposedTile, static_cast<uint>(withFirstRemoved));
+ break;
+ }
+
+ secondExposedTile = selectAndRemoveOne(rng, exposedTiles, numExposedTiles);
+ withBothRemoved = withFirstRemoved ^ boardStateBit(secondExposedTile);
+ }
+ }
+
+ boardState = withBothRemoved;
+
+ uint faceToInsert = selectAndRemoveOne(rng, facesToInsert, numFacesToInsert);
+ tileFaces[firstExposedTile] = faceToInsert;
+ tileFaces[secondExposedTile] = faceToInsert;
+ }
+}
+
+uint ShanghaiModifier::selectAndRemoveOne(Common::RandomSource &rng, uint *valuesList, uint &listSize) {
+ if (listSize == 0) {
+ error("Internal error: selectAndRemoveOne ran out of values");
+ return 0;
+ }
+
+ if (listSize == 1) {
+ listSize = 0;
+ return valuesList[0];
+ }
+
+ uint selectedIndex = rng.getRandomNumber(listSize - 1);
+ uint selectedValue = valuesList[selectedIndex];
+
+ valuesList[selectedIndex] = valuesList[listSize - 1];
+ listSize--;
+
+ return selectedValue;
+}
+
+bool ShanghaiModifier::boardStateHasValidMove(BoardState_t boardState) const {
+ uint numExposedTiles = 0;
+ for (uint i = 0; i < kNumTiles; i++) {
+ if (boardState & boardStateBit(i)) {
+ if (tileIsExposed(boardState, i)) {
+ numExposedTiles++;
+ if (numExposedTiles == 2)
+ return true;
+ }
+ }
+ }
+
+ return false;
+}
+
+bool ShanghaiModifier::tileIsExposed(BoardState_t boardState, uint tile) const {
+ uint tileX = _tileCoordinates[tile].x;
+ uint tileY = _tileCoordinates[tile].y;
+ uint tileZ = _tileCoordinates[tile].z;
+
+ uint blockMinY = tileY;
+ uint blockMaxY = tileY;
+ if (blockMinY > 0)
+ blockMinY--;
+ if (blockMaxY < kBoardSizeY - 1u)
+ blockMaxY++;
+
+ bool blockedOnLeft = false;
+
+ if (tileX >= 2) {
+ // Check for left-side blocks
+ for (uint y = blockMinY; y <= blockMaxY; y++) {
+ if (tileExistsAtCoordinate(boardState, tileX - 2, y, tileZ)) {
+ blockedOnLeft = true;
+ break;
+ }
+ }
+ }
+
+ if (blockedOnLeft) {
+ bool blockedOnRight = false;
+
+ // Check for right-side blocks
+ if (tileX < kBoardSizeX - 2u) {
+ for (uint y = blockMinY; y <= blockMaxY; y++) {
+ if (tileExistsAtCoordinate(boardState, tileX + 2, y, tileZ)) {
+ blockedOnRight = true;
+ break;
+ }
+ }
+ }
+
+ // Tile is blocked on left and right
+ if (blockedOnRight)
+ return false;
+ }
+
+ // Check upper blocks
+ uint blockMinX = tileX;
+ uint blockMaxX = tileX;
+ if (blockMinX > 0)
+ blockMinX--;
+ if (blockMaxX < kBoardSizeX - 1u)
+ blockMaxX++;
+
+ for (uint z = tileZ + 1; z < kBoardSizeZ; z++) {
+ for (uint x = blockMinX; x <= blockMaxX; x++) {
+ for (uint y = blockMinY; y <= blockMaxY; y++) {
+ if (tileExistsAtCoordinate(boardState, x, y, z))
+ return false;
+ }
+ }
+ }
+
return true;
}
+bool ShanghaiModifier::tileExistsAtCoordinate(BoardState_t boardState, uint x, uint y, uint z) const {
+ assert(x < kBoardSizeX);
+ assert(y < kBoardSizeY);
+ assert(z < kBoardSizeZ);
+
+ int8 tile = _tileAtCoordinate[x][y][z];
+
+ if (tile < 0)
+ return false;
+
+ if (boardState & boardStateBit(static_cast<uint>(tile)))
+ return true;
+
+ return false;
+}
+
+ShanghaiModifier::BoardState_t ShanghaiModifier::boardStateBit(uint bit) {
+ return static_cast<BoardState_t>(1) << bit;
+}
+
+ShanghaiModifier::BoardState_t ShanghaiModifier::emptyBoardState() {
+ return 0;
+}
+
+
#ifdef MTROPOLIS_DEBUG_ENABLE
void ShanghaiModifier::debugInspect(IDebugInspectionReport *report) const {
}
@@ -64,7 +398,6 @@ const char *ShanghaiModifier::getDefaultName() const {
return "Shanghai Modifier"; // ???
}
-
PrintModifier::PrintModifier() {
}
diff --git a/engines/mtropolis/plugin/mti.h b/engines/mtropolis/plugin/mti.h
index f48a65df086..1b49e60451a 100644
--- a/engines/mtropolis/plugin/mti.h
+++ b/engines/mtropolis/plugin/mti.h
@@ -27,6 +27,12 @@
#include "mtropolis/plugin/mti_data.h"
#include "mtropolis/runtime.h"
+namespace Common {
+
+class RandomSource;
+
+} // End of namespace Common
+
namespace MTropolis {
namespace MTI {
@@ -44,14 +50,47 @@ public:
bool load(const PlugInModifierLoaderContext &context, const Data::MTI::ShanghaiModifier &data);
+ void linkInternalReferences(ObjectLinkingScope *scope) override;
+ void visitInternalReferences(IStructuralReferenceVisitor *visitor) override;
+
#ifdef MTROPOLIS_DEBUG_ENABLE
const char *debugGetTypeName() const override { return "Shanghai Modifier"; }
void debugInspect(IDebugInspectionReport *report) const override;
#endif
private:
+ static const uint kNumTiles = 28;
+ static const uint kNumFaces = 26;
+
+ typedef uint32 BoardState_t;
+
+ static const uint kBoardSizeX = 13;
+ static const uint kBoardSizeY = 7;
+ static const uint kBoardSizeZ = 3;
+
+ struct TileCoordinate {
+ uint x;
+ uint y;
+ uint z;
+ };
+
Common::SharedPtr<Modifier> shallowClone() const override;
const char *getDefaultName() const override;
+
+ void resetTiles(Common::RandomSource &rng, uint (&tileFaces)[kNumTiles]) const;
+ static uint selectAndRemoveOne(Common::RandomSource &rng, uint *valuesList, uint &listSize);
+ bool boardStateHasValidMove(BoardState_t boardState) const;
+ bool tileIsExposed(BoardState_t boardState, uint tile) const;
+ bool tileExistsAtCoordinate(BoardState_t boardState, uint x, uint y, uint z) const;
+
+ static BoardState_t boardStateBit(uint bit);
+ static BoardState_t emptyBoardState();
+
+ Event _resetTileSetWhen;
+ VarReference _tileSetRef;
+
+ static TileCoordinate _tileCoordinates[kNumTiles];
+ int8 _tileAtCoordinate[kBoardSizeX][kBoardSizeY][kBoardSizeZ];
};
class PrintModifier : public Modifier {
diff --git a/engines/mtropolis/plugin/mti_data.cpp b/engines/mtropolis/plugin/mti_data.cpp
index ddfbef9cf35..2c2f9bf0751 100644
--- a/engines/mtropolis/plugin/mti_data.cpp
+++ b/engines/mtropolis/plugin/mti_data.cpp
@@ -31,7 +31,7 @@ DataReadErrorCode ShanghaiModifier::load(PlugIn &plugIn, const PlugInModifier &p
if (prefix.plugInRevision != 0)
return kDataReadErrorUnsupportedRevision;
- if (!unknown1Event.load(reader) || !unknown2VarRef.load(reader))
+ if (!resetWhen.load(reader) || !tileSetVar.load(reader))
return kDataReadErrorReadFailed;
return kDataReadErrorNone;
diff --git a/engines/mtropolis/plugin/mti_data.h b/engines/mtropolis/plugin/mti_data.h
index f1f94188b52..dc7912da69c 100644
--- a/engines/mtropolis/plugin/mti_data.h
+++ b/engines/mtropolis/plugin/mti_data.h
@@ -34,8 +34,8 @@ namespace MTI {
// Shanghai - ???
struct ShanghaiModifier : public PlugInModifierData {
- PlugInTypeTaggedValue unknown1Event; // Probably "Enable When"
- PlugInTypeTaggedValue unknown2VarRef; // VarRef (Probably tile set)
+ PlugInTypeTaggedValue resetWhen; // Reset When
+ PlugInTypeTaggedValue tileSetVar; // VarRef
protected:
DataReadErrorCode load(PlugIn &plugIn, const PlugInModifier &prefix, DataReader &reader) override;
More information about the Scummvm-git-logs
mailing list