diff --git a/backends/modular-backend.cpp b/backends/modular-backend.cpp index 7950018d574..715e5199141 100644 --- a/backends/modular-backend.cpp +++ b/backends/modular-backend.cpp @@ -186,6 +186,8 @@ void ModularGraphicsBackend::fillScreen(uint32 col) { void ModularGraphicsBackend::updateScreen() { #ifdef ENABLE_EVENTRECORDER + g_system->getMillis(); // force event recorder to update the tick count + g_eventRec.processScreenUpdate(); g_eventRec.preDrawOverlayGui(); #endif diff --git a/common/recorderfile.cpp b/common/recorderfile.cpp index e7f85ef3447..443db08fdf0 100644 --- a/common/recorderfile.cpp +++ b/common/recorderfile.cpp @@ -30,7 +30,7 @@ #include "graphics/surface.h" #include "graphics/scaler.h" -#define RECORD_VERSION 1 +#define RECORD_VERSION 2 namespace Common { @@ -47,6 +47,8 @@ PlaybackFile::PlaybackFile() _headerDumped = false; _recordCount = 0; _eventsSize = 0; + _trackScreenUpdate = true; + _version = RECORD_VERSION; memset(_tmpBuffer.data(), 1, kRecordBuffSize); _playbackParseState = kFileStateCheckFormat; @@ -130,12 +132,20 @@ bool PlaybackFile::parseHeader() { } bool PlaybackFile::checkPlaybackFileVersion() { - uint32 version; - version = _readStream->readUint32LE(); - if (version != RECORD_VERSION) { - warning("Incorrect playback file version. Expected version %d, but got %d.", RECORD_VERSION, version); + _version = _readStream->readUint32LE(); + switch (_version) { + case 2: + break; + case 1: + _trackScreenUpdate = false; + break; + default: + warning("Unknown playback file version %d. Maximum supported version is %d.", _version, RECORD_VERSION); return false; } + if (_version != RECORD_VERSION) { + warning("This playback file uses version %d, and may not be accurate. You can re-record the file in version %d format using --record-mode=update", _version, RECORD_VERSION); + } return true; } @@ -382,6 +392,9 @@ void PlaybackFile::readEvent(RecorderEvent& event) { event.timeDate.tm_year = _tmpPlaybackFile.readSint32LE(); event.timeDate.tm_wday = _tmpPlaybackFile.readSint32LE(); break; + case kRecorderEventTypeScreenUpdate: + event.time = _tmpPlaybackFile.readUint32LE(); + break; default: // fallthrough intended case kRecorderEventTypeNormal: @@ -567,6 +580,9 @@ void PlaybackFile::writeEvent(const RecorderEvent &event) { _tmpRecordFile.writeSint32LE(event.timeDate.tm_year); _tmpRecordFile.writeSint32LE(event.timeDate.tm_wday); break; + case kRecorderEventTypeScreenUpdate: + _tmpRecordFile.writeUint32LE(event.time); + break; default: // fallthrough intended case kRecorderEventTypeNormal: diff --git a/common/recorderfile.h b/common/recorderfile.h index ad501ce1c6a..fd8a514fd74 100644 --- a/common/recorderfile.h +++ b/common/recorderfile.h @@ -39,7 +39,8 @@ namespace Common { enum RecorderEventType { kRecorderEventTypeNormal = 0, kRecorderEventTypeTimer = 1, - kRecorderEventTypeTimeDate = 2 + kRecorderEventTypeTimeDate = 2, + kRecorderEventTypeScreenUpdate = 3, }; struct RecorderEvent : Event { @@ -157,6 +158,9 @@ public: PlaybackFileHeader &getHeader() {return _header;} void updateHeader(); void addSaveFile(const String &fileName, InSaveFile *saveStream); + + uint32 getVersion() {return _version;} + bool hasTrackScreenUpdate() {return _trackScreenUpdate;} private: Array _tmpBuffer; WriteStream *_recordFile; @@ -172,6 +176,8 @@ private: uint32 _eventsSize; PlaybackFileHeader _header; PlaybackFileState _playbackParseState; + bool _trackScreenUpdate; + uint32 _version; void skipHeader(); bool parseHeader(); diff --git a/gui/EventRecorder.cpp b/gui/EventRecorder.cpp index 8336f932744..f690c38e170 100644 --- a/gui/EventRecorder.cpp +++ b/gui/EventRecorder.cpp @@ -126,6 +126,13 @@ void EventRecorder::deinit() { DebugMan.disableDebugChannel("EventRec"); } +void EventRecorder::updateFakeTimer(uint32 millis) { + uint32 millisDelay = millis - _lastMillis; + _lastMillis = millis; + _fakeTimer += millisDelay; + _controlPanel->setReplayedTime(_fakeTimer); +} + void EventRecorder::processTimeAndDate(TimeDate &td, bool skipRecord) { if (!_initialized) { return; @@ -174,19 +181,14 @@ void EventRecorder::processMillis(uint32 &millis, bool skipRecord) { if (_recordMode == kRecorderPlaybackPause) { millis = _fakeTimer; } - uint32 millisDelay; Common::RecorderEvent timerEvent; switch (_recordMode) { case kRecorderRecord: updateSubsystems(); - millisDelay = millis - _lastMillis; - _lastMillis = millis; - _fakeTimer += millisDelay; - _controlPanel->setReplayedTime(_fakeTimer); + updateFakeTimer(millis); timerEvent.recordedtype = Common::kRecorderEventTypeTimer; timerEvent.time = _fakeTimer; - _playbackFile->writeEvent(timerEvent); - takeScreenshot(); + _recordFile->writeEvent(timerEvent); _timerManager->handler(); break; case kRecorderPlayback: @@ -222,6 +224,51 @@ bool EventRecorder::processDelayMillis() { return _fastPlayback; } +void EventRecorder::processScreenUpdate() { + if (!_initialized) { + return; + } + + Common::RecorderEvent screenUpdateEvent; + switch (_recordMode) { + case kRecorderRecord: + updateSubsystems(); + screenUpdateEvent.recordedtype = Common::kRecorderEventTypeScreenUpdate; + screenUpdateEvent.time = _fakeTimer; + _recordFile->writeEvent(screenUpdateEvent); + takeScreenshot(); + _timerManager->handler(); + break; + case kRecorderPlayback: + if (_playbackFile->hasTrackScreenUpdate()) { + // if the file has screen update support, but the next event + // isn't a screen update, fast forward until we find one. + if (_nextEvent.recordedtype != Common::kRecorderEventTypeScreenUpdate) { + int numSkipped = 0; + while (true) { + _nextEvent = _playbackFile->getNextEvent(); + numSkipped += 1; + if (_nextEvent.recordedtype == Common::kRecorderEventTypeScreenUpdate) { + warning("Skipped %d events to get to the next screen update at %d", numSkipped, _nextEvent.time); + break; + } + } + } + _processingMillis = true; + _fakeTimer = _nextEvent.time; + debug(3, "screenUpdate event: %u", _fakeTimer); + updateSubsystems(); + _nextEvent = _playbackFile->getNextEvent(); + _timerManager->handler(); + _controlPanel->setReplayedTime(_fakeTimer); + _processingMillis = false; + } + break; + default: + break; + } +} + void EventRecorder::checkForKeyCode(const Common::Event &event) { if ((event.type == Common::EVENT_KEYDOWN) && (event.kbd.flags & Common::KBD_CTRL) && (event.kbd.keycode == Common::KEYCODE_p) && (!event.kbdRepeat)) { togglePause(); @@ -234,6 +281,7 @@ bool EventRecorder::pollEvent(Common::Event &ev) { if (_nextEvent.recordedtype == Common::kRecorderEventTypeTimer || _nextEvent.recordedtype == Common::kRecorderEventTypeTimeDate + || _nextEvent.recordedtype == Common::kRecorderEventTypeScreenUpdate || _nextEvent.type == Common::EVENT_INVALID) { return false; } diff --git a/gui/EventRecorder.h b/gui/EventRecorder.h index 614fdcc9ff7..cbb7119f9e1 100644 --- a/gui/EventRecorder.h +++ b/gui/EventRecorder.h @@ -83,6 +83,7 @@ public: uint32 getRandomSeed(const Common::String &name); void processTimeAndDate(TimeDate &td, bool skipRecord); void processMillis(uint32 &millis, bool skipRecord); + void processScreenUpdate(); void processGameDescription(const ADGameDescription *desc); Common::SeekableReadStream *processSaveStream(const Common::String & fileName); @@ -228,6 +229,7 @@ private: void saveScreenShot(); void checkRecordedMD5(); void deleteTemporarySave(); + void updateFakeTimer(uint32 millis); volatile RecordMode _recordMode; Common::String _recordFileName; bool _fastPlayback;