MTROPOLIS: Annotate fields of STransCt (plug-in scene transition) modifier.

This commit is contained in:
elasota 2022-05-02 01:43:56 -04:00 committed by Eugene Sandulenko
parent 367afa4b53
commit 5e9f44d731
6 changed files with 322 additions and 25 deletions

View File

@ -22,6 +22,7 @@
#include "mtropolis/miniscript.h"
#include "common/config-manager.h"
#include "common/random.h"
#include "common/memstream.h"
namespace MTropolis {
@ -858,6 +859,245 @@ MiniscriptInstructionOutcome OrderedCompareInstruction::execute(MiniscriptThread
BuiltinFunc::BuiltinFunc(BuiltinFunctionID bfid) : _funcID(bfid) {
}
MiniscriptInstructionOutcome BuiltinFunc::execute(MiniscriptThread *thread) const {
size_t stackArgsNeeded = 1;
bool returnsValue = true;
if (thread->getStackSize() < stackArgsNeeded) {
thread->error("Stack underflow");
return kMiniscriptInstructionOutcomeFailed;
}
for (size_t i = 0; i < stackArgsNeeded; i++) {
MiniscriptInstructionOutcome outcome = thread->dereferenceRValue(i, false);
if (outcome != kMiniscriptInstructionOutcomeContinue)
return outcome;
}
DynamicValue staticDest;
DynamicValue *dest = nullptr;
if (returnsValue) {
if (stackArgsNeeded > 0)
dest = &thread->getStackValueFromTop(stackArgsNeeded - 1).value;
else
dest = &staticDest;
}
MiniscriptInstructionOutcome outcome = executeFunction(thread, dest);
if (outcome != kMiniscriptInstructionOutcomeContinue)
return outcome;
if (stackArgsNeeded > 0) {
size_t valuesToPop = stackArgsNeeded;
if (returnsValue)
valuesToPop--;
if (valuesToPop > 0)
thread->popValues(valuesToPop);
} else {
if (returnsValue)
thread->pushValue(staticDest);
}
return kMiniscriptInstructionOutcomeContinue;
}
MiniscriptInstructionOutcome BuiltinFunc::executeFunction(MiniscriptThread *thread, DynamicValue *returnValue) const {
switch (_funcID) {
case kSin:
case kCos:
case kRandom:
case kSqrt:
case kTan:
case kAbs:
case kSign:
case kArctangent:
case kExp:
case kLn:
case kLog:
case kCosH:
case kSinH:
case kTanH:
case kTrunc:
case kRound:
return executeSimpleNumericInstruction(thread, returnValue);
case kRect2Polar:
return executeRectToPolar(thread, returnValue);
case kPolar2Rect:
return executePolarToRect(thread, returnValue);
case kNum2Str:
return executeNum2Str(thread, returnValue);
case kStr2Num:
return executeStr2Num(thread, returnValue);
default:
thread->error("Unimplemented built-in function");
return kMiniscriptInstructionOutcomeFailed;
}
}
MiniscriptInstructionOutcome BuiltinFunc::executeSimpleNumericInstruction(MiniscriptThread *thread, DynamicValue *returnValue) const {
double result = 0.0;
double input = 0.0;
const DynamicValue &inputDynamicValue = thread->getStackValueFromTop(0).value;
switch (inputDynamicValue.getType()) {
case DynamicValueTypes::kInteger:
input = inputDynamicValue.getInt();
break;
case DynamicValueTypes::kFloat:
input = inputDynamicValue.getFloat();
break;
default:
thread->error("Invalid numeric function input type");
return kMiniscriptInstructionOutcomeFailed;
}
switch (_funcID) {
case kSin:
result = sin(input * (M_PI / 180.0));
break;
case kCos:
result = cos(input * (M_PI / 180.0));
break;
case kRandom:
if (input < 1.5)
result = 0.0;
else {
uint rngMax = static_cast<uint>(floor(input + 0.5)) - 1;
result = thread->getRuntime()->getRandom()->getRandomNumber(rngMax);
}
break;
case kSqrt:
result = sqrt(input);
break;
case kTan:
result = tan(input * (M_PI / 180.0));
break;
case kAbs:
result = fabs(input);
break;
case kSign:
if (input < 0.0)
result = -1;
else if (input > 0.0)
result = 1;
else
result = 0;
break;
case kArctangent:
result = atan(input) * (180.0 / M_PI);
break;
case kExp:
result = exp(input);
break;
case kLn:
result = log(input);
break;
case kLog:
result = log10(input);
break;
case kCosH:
result = cosh(input * (M_PI / 180.0));
break;
case kSinH:
result = sinh(input * (M_PI / 180.0));
break;
case kTanH:
result = tanh(input * (M_PI / 180.0));
break;
case kTrunc:
result = trunc(input);
break;
case kRound:
result = round(input);
break;
default:
thread->error("Unimplemented numeric function");
return kMiniscriptInstructionOutcomeFailed;
}
returnValue->setFloat(result);
return kMiniscriptInstructionOutcomeContinue;
}
MiniscriptInstructionOutcome BuiltinFunc::executeRectToPolar(MiniscriptThread *thread, DynamicValue *returnValue) const {
const DynamicValue &inputDynamicValue = thread->getStackValueFromTop(0).value;
if (inputDynamicValue.getType() != DynamicValueTypes::kPoint) {
thread->error("Polar to rect input must be a vector");
return kMiniscriptInstructionOutcomeFailed;
}
const Point16 &pt = inputDynamicValue.getPoint();
double angle = atan2(pt.x, pt.y);
double magnitude = sqrt(pt.x * pt.x + pt.y * pt.y);
returnValue->setVector(AngleMagVector::create(angle, magnitude));
return kMiniscriptInstructionOutcomeContinue;
}
MiniscriptInstructionOutcome BuiltinFunc::executePolarToRect(MiniscriptThread *thread, DynamicValue *returnValue) const {
const DynamicValue &inputDynamicValue = thread->getStackValueFromTop(0).value;
if (inputDynamicValue.getType() != DynamicValueTypes::kVector) {
thread->error("Polar to rect input must be a vector");
return kMiniscriptInstructionOutcomeFailed;
}
const AngleMagVector &vec = inputDynamicValue.getVector();
double x = cos(vec.angleRadians) * vec.magnitude;
double y = sin(vec.angleRadians) * vec.magnitude;
returnValue->setPoint(Point16::create(static_cast<int16>(round(x)), static_cast<int16>(round(y))));
return kMiniscriptInstructionOutcomeContinue;
}
MiniscriptInstructionOutcome BuiltinFunc::executeNum2Str(MiniscriptThread *thread, DynamicValue *returnValue) const {
Common::String result;
const DynamicValue &inputDynamicValue = thread->getStackValueFromTop(0).value;
switch (inputDynamicValue.getType()) {
case DynamicValueTypes::kInteger:
result.format("%i", static_cast<int>(inputDynamicValue.getInt()));
break;
case DynamicValueTypes::kFloat:
result.format("%g", static_cast<double>(inputDynamicValue.getFloat()));
break;
default:
thread->error("Invalid input value to num2str");
return kMiniscriptInstructionOutcomeFailed;
}
return kMiniscriptInstructionOutcomeContinue;
}
MiniscriptInstructionOutcome BuiltinFunc::executeStr2Num(MiniscriptThread *thread, DynamicValue *returnValue) const {
double result = 0.0;
const DynamicValue &inputDynamicValue = thread->getStackValueFromTop(0).value;
if (inputDynamicValue.getType() != DynamicValueTypes::kString) {
thread->error("Invalid input value to str2num");
return kMiniscriptInstructionOutcomeFailed;
}
const Common::String &str = inputDynamicValue.getString();
if (str.size() == 0 || !sscanf(str.c_str(), "%lf", &result)) {
thread->error("Couldn't parse number");
return kMiniscriptInstructionOutcomeFailed;
}
returnValue->setFloat(result);
return kMiniscriptInstructionOutcomeContinue;
}
MiniscriptInstructionOutcome StrConcat::execute(MiniscriptThread *thread) const {
if (thread->getStackSize() < 2) {
thread->error("Stack underflow");

View File

@ -246,6 +246,15 @@ namespace MiniscriptInstructions {
explicit BuiltinFunc(BuiltinFunctionID bfid);
private:
MiniscriptInstructionOutcome execute(MiniscriptThread *thread) const override;
MiniscriptInstructionOutcome executeFunction(MiniscriptThread *thread, DynamicValue *returnValue) const;
MiniscriptInstructionOutcome executeSimpleNumericInstruction(MiniscriptThread *thread, DynamicValue *returnValue) const;
MiniscriptInstructionOutcome executeRectToPolar(MiniscriptThread *thread, DynamicValue *returnValue) const;
MiniscriptInstructionOutcome executePolarToRect(MiniscriptThread *thread, DynamicValue *returnValue) const;
MiniscriptInstructionOutcome executeNum2Str(MiniscriptThread *thread, DynamicValue *returnValue) const;
MiniscriptInstructionOutcome executeStr2Num(MiniscriptThread *thread, DynamicValue *returnValue) const;
BuiltinFunctionID _funcID;
};

View File

@ -43,10 +43,9 @@ DataReadErrorCode STransCtModifier::load(PlugIn &plugIn, const PlugInModifier &p
if (prefix.plugInRevision != 0)
return kDataReadErrorUnsupportedRevision;
if (!reader.readU16(unknown1) || !unknown2.load(reader) || !reader.readU16(unknown3) || !unknown4.load(reader)
|| !reader.readU16(unknown5) || !reader.readU32(unknown6) || !reader.readU16(unknown7) || !reader.readU32(unknown8)
|| !reader.readU16(unknown9) || !reader.readU32(unknown10) || !reader.readU16(unknown11) || !reader.readU32(unknown12)
|| !reader.readU16(unknown13) || !reader.readU32(unknown14) || !reader.readU16(unknown15) || !reader.readBytes(unknown16))
if (!enableWhen.load(reader) || !disableWhen.load(reader) || !transitionType.load(reader) ||
!transitionDirection.load(reader) || !unknown1.load(reader) || !steps.load(reader) ||
!duration.load(reader) || !fullScreen.load(reader))
return kDataReadErrorReadFailed;
return kDataReadErrorNone;

View File

@ -44,22 +44,14 @@ protected:
};
struct STransCtModifier : public PlugInModifierData {
uint16 unknown1; // Type tag? (0x17)
Event unknown2; // Probably "apply when"
uint16 unknown3; // Type tag? (0x17)
Event unknown4; // Probably "remove when"
uint16 unknown5; // Type tag? (1)
uint32 unknown6;
uint16 unknown7; // Type tag? (1)
uint32 unknown8;
uint16 unknown9; // Type tag? (1)
uint32 unknown10;
uint16 unknown11; // Type tag? (1)
uint32 unknown12;
uint16 unknown13; // Type tag? (1)
uint32 unknown14;
uint16 unknown15; // Type tag? (0x14)
uint8 unknown16[2];
PlugInTypeTaggedValue enableWhen; // Event
PlugInTypeTaggedValue disableWhen; // Event
PlugInTypeTaggedValue transitionType; // int
PlugInTypeTaggedValue transitionDirection; // int
PlugInTypeTaggedValue unknown1; // int, seems to always be 1
PlugInTypeTaggedValue steps; // int, seems to always be 32
PlugInTypeTaggedValue duration; // int, always observed as 60000
PlugInTypeTaggedValue fullScreen; // bool
protected:
DataReadErrorCode load(PlugIn &plugIn, const PlugInModifier &prefix, DataReader &reader) override;

View File

@ -30,6 +30,7 @@
#include "common/debug.h"
#include "common/file.h"
#include "common/random.h"
#include "common/substream.h"
#include "common/system.h"
@ -2483,6 +2484,8 @@ Runtime::SceneStackEntry::SceneStackEntry() {
Runtime::Runtime(OSystem *system) : _nextRuntimeGUID(1), _realDisplayMode(kColorDepthModeInvalid), _fakeDisplayMode(kColorDepthModeInvalid),
_displayWidth(1024), _displayHeight(768), _realTimeBase(0), _playTimeBase(0), _sceneTransitionState(kSceneTransitionStateNotTransitioning),
_system(system), _lastFrameCursor(nullptr), _defaultCursor(new DefaultCursor()) {
_random.reset(new Common::RandomSource("mtropolis"));
_vthread.reset(new VThread());
for (int i = 0; i < kColorDepthModeCount; i++) {
@ -3187,7 +3190,9 @@ void Runtime::onMouseUp(int32 x, int32 y, Actions::MouseButton mButton) {
_mouseFocusWindow.reset();
}
Common::RandomSource* Runtime::getRandom() const {
return _random.get();
}
void Runtime::ensureMainWindowExists() {
// Maybe there's a better spot for this
@ -4319,10 +4324,12 @@ bool VisualElement::readAttribute(MiniscriptThread *thread, DynamicValue &result
if (attrib == "visible") {
result.setBool(_visible);
return true;
}
if (attrib == "direct") {
} else if (attrib == "direct") {
result.setBool(_directToScreen);
return true;
} else if (attrib == "position") {
result.setPoint(Point16::create(_rect.left, _rect.top));
return true;
}
return Element::readAttribute(thread, result, attrib);
@ -4332,10 +4339,12 @@ bool VisualElement::writeRefAttribute(MiniscriptThread *thread, DynamicValueWrit
if (attrib == "visible") {
DynamicValueWriteFuncHelper<VisualElement, &VisualElement::scriptSetVisibility>::create(this, writeProxy);
return true;
}
if (attrib == "direct") {
} else if (attrib == "direct") {
DynamicValueWriteFuncHelper<VisualElement, &VisualElement::scriptSetDirect>::create(this, writeProxy);
return true;
} else if (attrib == "position") {
DynamicValueWriteFuncHelper<VisualElement, &VisualElement::scriptSetPosition>::create(this, writeProxy);
return true;
}
return Element::writeRefAttribute(thread, writeProxy, attrib);
@ -4374,6 +4383,34 @@ bool VisualElement::scriptSetDirect(const DynamicValue &dest) {
return false;
}
bool VisualElement::scriptSetPosition(const DynamicValue &dest) {
if (dest.getType() == DynamicValueTypes::kPoint) {
const Point16 &destPoint = dest.getPoint();
int32 xDelta = destPoint.x - _rect.left;
int32 yDelta = destPoint.y - _rect.right;
offsetTranslate(xDelta, yDelta);
return true;
}
return false;
}
void VisualElement::offsetTranslate(int32 xDelta, int32 yDelta) {
_rect.left += xDelta;
_rect.right += xDelta;
_rect.top += yDelta;
_rect.bottom += yDelta;
for (const Common::SharedPtr<Structural> &child : _children) {
if (child->isElement()) {
Element *element = static_cast<Element *>(child.get());
if (element->isVisual())
static_cast<VisualElement *>(element)->offsetTranslate(xDelta, yDelta);
}
}
}
VThreadState VisualElement::changeVisibilityTask(const ChangeFlagTaskData &taskData) {
if (_visible != taskData.desiredFlag) {
_visible = taskData.desiredFlag;

View File

@ -39,6 +39,12 @@
class OSystem;
namespace Common {
class RandomSource;
} // End of namespace Common
namespace Graphics {
struct WinCursorGroup;
@ -398,6 +404,13 @@ struct AngleMagVector {
return !((*this) == other);
}
inline static AngleMagVector create(double angleRadians, double magnitude) {
AngleMagVector result;
result.angleRadians = angleRadians;
result.magnitude = magnitude;
return result;
}
bool dynSetAngleDegrees(const DynamicValue &value);
void dynGetAngleDegrees(DynamicValue &value) const;
@ -1267,6 +1280,8 @@ public:
void onMouseMove(int32 x, int32 y);
void onMouseUp(int32 x, int32 y, Actions::MouseButton mButton);
Common::RandomSource *getRandom() const;
#ifdef MTROPOLIS_DEBUG_ENABLE
void debugSetEnabled(bool enabled);
void debugBreak();
@ -1360,6 +1375,8 @@ private:
Common::SharedPtr<Graphics::MacFontManager> _macFontMan;
Common::SharedPtr<Common::RandomSource> _random;
uint32 _nextRuntimeGUID;
bool _displayModeSupported[kColorDepthModeCount];
@ -1823,6 +1840,9 @@ protected:
bool loadCommon(const Common::String &name, uint32 guid, const Data::Rect &rect, uint32 elementFlags, uint16 layer, uint32 streamLocator, uint16 sectionID);
bool scriptSetDirect(const DynamicValue &dest);
bool scriptSetPosition(const DynamicValue &dest);
void offsetTranslate(int32 xDelta, int32 yDelta);
struct ChangeFlagTaskData {
bool desiredFlag;