mirror of
https://github.com/libretro/scummvm.git
synced 2025-01-10 11:51:52 +00:00
DIRECTOR: Freeze LingoState instead of using callstack
Previously, the Lingo state would be frozen by persisting the frames on the bottom of the callstack with a special flag. This required extra logic to determine whether or not the callstack was intended to be empty after execution finished, with some complex edge cases. The new approach is to swap out the LingoState object on the window, meaning that an empty callstack always signifies execution has completed. It remains to be seen if we'll need to track more than one frozen context; for now it seems to be okay. Fixes driving to locations in DEVO Presents: Adventures of the Smart Patrol.
This commit is contained in:
parent
873095c87e
commit
3ea7f10467
@ -233,7 +233,6 @@ void Lingo::pushContext(const Symbol funcSym, bool allowRetVal, Datum defaultRet
|
||||
fp->retPC = _state->pc;
|
||||
fp->retScript = _state->script;
|
||||
fp->retContext = _state->context;
|
||||
fp->retFreezeContext = _freezeContext;
|
||||
fp->retLocalVars = _state->localVars;
|
||||
fp->retMe = _state->me;
|
||||
fp->sp = funcSym;
|
||||
@ -249,7 +248,6 @@ void Lingo::pushContext(const Symbol funcSym, bool allowRetVal, Datum defaultRet
|
||||
_state->context = funcSym.ctx;
|
||||
*_state->context->_refCount += 1;
|
||||
}
|
||||
_freezeContext = false;
|
||||
|
||||
DatumHash *localvars = _state->localVars;
|
||||
if (!funcSym.anonymous) {
|
||||
@ -345,7 +343,6 @@ void Lingo::popContext(bool aborting) {
|
||||
|
||||
_state->script = fp->retScript;
|
||||
_state->context = fp->retContext;
|
||||
_freezeContext = fp->retFreezeContext;
|
||||
_state->pc = fp->retPC;
|
||||
_state->me = fp->retMe;
|
||||
|
||||
@ -364,17 +361,15 @@ void Lingo::popContext(bool aborting) {
|
||||
g_debugger->popContextHook();
|
||||
}
|
||||
|
||||
bool Lingo::hasFrozenContext() {
|
||||
if (_freezeContext)
|
||||
return true;
|
||||
bool Lingo::hasFrozenState() {
|
||||
Window *window = _vm->getCurrentWindow();
|
||||
return window->hasFrozenLingoState();
|
||||
}
|
||||
|
||||
Common::Array<CFrame *> &callstack = _state->callstack;
|
||||
for (uint i = 0; i < callstack.size(); i++) {
|
||||
if (callstack[i]->retFreezeContext)
|
||||
return true;
|
||||
}
|
||||
|
||||
return false;
|
||||
void Lingo::freezeState() {
|
||||
Window *window = _vm->getCurrentWindow();
|
||||
window->freezeLingoState();
|
||||
switchStateFromWindow();
|
||||
}
|
||||
|
||||
void LC::c_constpush() {
|
||||
|
@ -197,8 +197,8 @@ void Lingo::func_goto(Datum &frame, Datum &movie) {
|
||||
|
||||
// If there isn't already frozen Lingo (e.g. from a previous func_goto we haven't yet unfrozen),
|
||||
// freeze this script context. We'll return to it after entering the next frame.
|
||||
if (!g_lingo->hasFrozenContext()) {
|
||||
g_lingo->_freezeContext = true;
|
||||
if (!g_lingo->hasFrozenState()) {
|
||||
g_lingo->_freezeState = true;
|
||||
}
|
||||
|
||||
if (movie.type != VOID) {
|
||||
|
@ -155,7 +155,7 @@ Lingo::Lingo(DirectorEngine *vm) : _vm(vm) {
|
||||
_state = nullptr;
|
||||
_currentChannelId = -1;
|
||||
_globalCounter = 0;
|
||||
_freezeContext = false;
|
||||
_freezeState = false;
|
||||
_abort = false;
|
||||
_expectError = false;
|
||||
_caughtError = false;
|
||||
@ -541,7 +541,7 @@ Common::String Lingo::formatFunctionBody(Symbol &sym) {
|
||||
void Lingo::execute() {
|
||||
uint localCounter = 0;
|
||||
|
||||
while (!_abort && !_freezeContext && _state->script && (*_state->script)[_state->pc] != STOP) {
|
||||
while (!_abort && !_freezeState && _state->script && (*_state->script)[_state->pc] != STOP) {
|
||||
if (_globalCounter > 1000 && debugChannelSet(-1, kDebugFewFramesOnly)) {
|
||||
warning("Lingo::execute(): Stopping due to debug few frames only");
|
||||
_vm->getCurrentMovie()->getScore()->_playState = kPlayStopped;
|
||||
@ -595,17 +595,18 @@ void Lingo::execute() {
|
||||
}
|
||||
}
|
||||
|
||||
if (_abort || _vm->getCurrentMovie()->getScore()->_playState == kPlayStopped) {
|
||||
if (_freezeState) {
|
||||
debugC(5, kDebugLingoExec, "Lingo::execute(): Context is frozen, pausing execution");
|
||||
freezeState();
|
||||
} else if (_abort || _vm->getCurrentMovie()->getScore()->_playState == kPlayStopped) {
|
||||
// Clean up call stack
|
||||
while (_state->callstack.size()) {
|
||||
popContext(true);
|
||||
}
|
||||
}
|
||||
_abort = false;
|
||||
_freezeState = false;
|
||||
|
||||
if (_freezeContext) {
|
||||
debugC(1, kDebugLingoExec, "Lingo::execute(): Context is frozen, pausing execution");
|
||||
}
|
||||
g_debugger->stepHook();
|
||||
}
|
||||
|
||||
|
@ -241,7 +241,6 @@ struct CFrame { /* proc/func call stack frame */
|
||||
int retPC; /* where to resume after return */
|
||||
ScriptData *retScript; /* which script to resume after return */
|
||||
ScriptContext *retContext; /* which script context to use after return */
|
||||
bool retFreezeContext; /* whether the context should be frozen after return */
|
||||
DatumHash *retLocalVars;
|
||||
Datum retMe; /* which me obj to use after return */
|
||||
uint stackSizeBefore;
|
||||
@ -358,9 +357,10 @@ public:
|
||||
public:
|
||||
void execute();
|
||||
void switchStateFromWindow();
|
||||
void freezeState();
|
||||
bool hasFrozenState();
|
||||
void pushContext(const Symbol funcSym, bool allowRetVal, Datum defaultRetVal);
|
||||
void popContext(bool aborting = false);
|
||||
bool hasFrozenContext();
|
||||
void cleanLocalVars();
|
||||
void varAssign(const Datum &var, const Datum &value);
|
||||
Datum varFetch(const Datum &var, bool silent = false);
|
||||
@ -448,7 +448,7 @@ public:
|
||||
|
||||
int _currentChannelId;
|
||||
|
||||
bool _freezeContext;
|
||||
bool _freezeState;
|
||||
bool _abort;
|
||||
bool _expectError;
|
||||
bool _caughtError;
|
||||
|
@ -471,7 +471,11 @@ void Score::update() {
|
||||
debugC(1, kDebugLoading, "****************************** Current frame: %d, time: %d", _currentFrame, g_system->getMillis(false));
|
||||
g_debugger->frameHook();
|
||||
|
||||
uint initialCallStackSize = g_lingo->_state->callstack.size();
|
||||
if (_window->hasFrozenLingoState()) {
|
||||
_window->thawLingoState();
|
||||
g_lingo->switchStateFromWindow();
|
||||
g_lingo->execute();
|
||||
}
|
||||
|
||||
_lingo->executeImmediateScripts(_frames[_currentFrame]);
|
||||
|
||||
@ -506,18 +510,6 @@ void Score::update() {
|
||||
_movie->_lastTimeOut = _vm->getMacTicks();
|
||||
}
|
||||
|
||||
// If we have more call stack frames than we started with, then we have a newly
|
||||
// added frozen context. We'll deal with that later.
|
||||
if (g_lingo->_state->callstack.size() == initialCallStackSize) {
|
||||
// We may have a frozen Lingo context from func_goto.
|
||||
// Now that we've entered a new frame, let's unfreeze that context.
|
||||
if (g_lingo->_freezeContext) {
|
||||
debugC(1, kDebugLingoExec, "Score::update(): Unfreezing Lingo context");
|
||||
g_lingo->_freezeContext = false;
|
||||
g_lingo->execute();
|
||||
}
|
||||
}
|
||||
|
||||
}
|
||||
|
||||
void Score::renderFrame(uint16 frameId, RenderMode mode) {
|
||||
|
@ -49,6 +49,7 @@ Window::Window(int id, bool scrollable, bool resizable, bool editable, Graphics:
|
||||
_puppetTransition = nullptr;
|
||||
_soundManager = new DirectorSound(this);
|
||||
_lingoState = new LingoState;
|
||||
_frozenLingoState = nullptr;
|
||||
|
||||
_currentMovie = nullptr;
|
||||
_mainArchive = nullptr;
|
||||
@ -428,4 +429,29 @@ Common::String Window::getSharedCastPath() {
|
||||
return Common::String();
|
||||
}
|
||||
|
||||
void Window::freezeLingoState() {
|
||||
if (_frozenLingoState) {
|
||||
warning("State already frozen! Ditching callstack with %d frames", _frozenLingoState->callstack.size());
|
||||
delete _frozenLingoState;
|
||||
}
|
||||
_frozenLingoState = _lingoState;
|
||||
_lingoState = new LingoState;
|
||||
debugC(kDebugLingoExec, 5, "Freezing Lingo state");
|
||||
}
|
||||
|
||||
void Window::thawLingoState() {
|
||||
if (!_frozenLingoState) {
|
||||
warning("Tried to thaw when there's no frozen state, ignoring");
|
||||
return;
|
||||
}
|
||||
if (!_lingoState->callstack.empty()) {
|
||||
warning("Can't thaw a Lingo state in mid-execution, ignoring");
|
||||
return;
|
||||
}
|
||||
delete _lingoState;
|
||||
_lingoState = _frozenLingoState;
|
||||
_frozenLingoState = nullptr;
|
||||
debugC(kDebugLingoExec, 5, "Thawing Lingo state");
|
||||
}
|
||||
|
||||
} // End of namespace Director
|
||||
|
@ -125,6 +125,7 @@ public:
|
||||
void transMultiPass(TransParams &t, Common::Rect &clipRect, Graphics::ManagedSurface *tmpSurface);
|
||||
void transZoom(TransParams &t, Common::Rect &clipRect, Graphics::ManagedSurface *tmpSurface);
|
||||
|
||||
// window.cpp
|
||||
Common::Point getMousePos();
|
||||
|
||||
DirectorEngine *getVM() const { return _vm; }
|
||||
@ -140,7 +141,6 @@ public:
|
||||
int getWindowType() const { return _windowType; }
|
||||
void setTitleVisible(bool titleVisible) { _titleVisible = titleVisible; updateBorderType(); };
|
||||
bool isTitleVisible() { return _titleVisible; };
|
||||
LingoState *getLingoState() { return _lingoState; };
|
||||
Datum getStageRect();
|
||||
|
||||
void updateBorderType();
|
||||
@ -151,6 +151,11 @@ public:
|
||||
|
||||
Common::String getSharedCastPath();
|
||||
|
||||
LingoState *getLingoState() { return _lingoState; };
|
||||
bool hasFrozenLingoState() { return _frozenLingoState != nullptr; };
|
||||
void freezeLingoState();
|
||||
void thawLingoState();
|
||||
|
||||
// events.cpp
|
||||
bool processEvent(Common::Event &event) override;
|
||||
|
||||
@ -199,6 +204,7 @@ private:
|
||||
DirectorEngine *_vm;
|
||||
DirectorSound *_soundManager;
|
||||
LingoState *_lingoState;
|
||||
LingoState *_frozenLingoState;
|
||||
bool _isStage;
|
||||
Archive *_mainArchive;
|
||||
Movie *_currentMovie;
|
||||
|
Loading…
Reference in New Issue
Block a user