From 5f9ceb7f5d642b849692c4fda4b99df4b09484bc Mon Sep 17 00:00:00 2001 From: Alexander Batalov Date: Mon, 3 Oct 2022 09:37:05 +0300 Subject: [PATCH] Extract vcr --- CMakeLists.txt | 2 + src/animation.cc | 1 + src/core.cc | 424 +------------------------------------ src/core.h | 87 -------- src/interpreter_extra.cc | 1 + src/selfrun.cc | 1 + src/vcr.cc | 438 +++++++++++++++++++++++++++++++++++++++ src/vcr.h | 88 ++++++++ src/window_manager.cc | 1 + 9 files changed, 533 insertions(+), 510 deletions(-) create mode 100644 src/vcr.cc create mode 100644 src/vcr.h diff --git a/CMakeLists.txt b/CMakeLists.txt index 5215429..07f8ec8 100644 --- a/CMakeLists.txt +++ b/CMakeLists.txt @@ -225,6 +225,8 @@ target_sources(${EXECUTABLE_NAME} PUBLIC "src/trait.h" "src/trap.cc" "src/trap.h" + "src/vcr.cc" + "src/vcr.h" "src/version.cc" "src/version.h" "src/widget.cc" diff --git a/src/animation.cc b/src/animation.cc index e91968d..7b34cd1 100644 --- a/src/animation.cc +++ b/src/animation.cc @@ -30,6 +30,7 @@ #include "text_object.h" #include "tile.h" #include "trait.h" +#include "vcr.h" namespace fallout { diff --git a/src/core.cc b/src/core.cc index 9246142..adcc0a8 100644 --- a/src/core.cc +++ b/src/core.cc @@ -14,6 +14,7 @@ #include "memory.h" #include "mmx.h" #include "text_font.h" +#include "vcr.h" #include "win32.h" #include "window_manager.h" #include "window_manager_private.h" @@ -120,49 +121,6 @@ int gModifierKeysState = 0; // 0x51E2EC int (*_kb_scan_to_ascii)() = keyboardDequeueLogicalKeyCode; -// 0x51E2F0 -VcrEntry* _vcr_buffer = NULL; - -// number of entries in _vcr_buffer -// 0x51E2F4 -int _vcr_buffer_index = 0; - -// 0x51E2F8 -unsigned int gVcrState = VCR_STATE_TURNED_OFF; - -// 0x51E2FC -unsigned int _vcr_time = 0; - -// 0x51E300 -unsigned int _vcr_counter = 0; - -// 0x51E304 -unsigned int gVcrTerminateFlags = 0; - -// 0x51E308 -int gVcrPlaybackCompletionReason = VCR_PLAYBACK_COMPLETION_REASON_NONE; - -// 0x51E30C -unsigned int _vcr_start_time = 0; - -// 0x51E310 -int _vcr_registered_atexit = 0; - -// 0x51E314 -File* gVcrFile = NULL; - -// 0x51E318 -int _vcr_buffer_end = 0; - -// 0x51E31C -VcrPlaybackCompletionCallback* gVcrPlaybackCompletionCallback = NULL; - -// 0x51E320 -unsigned int gVcrRequestedTerminationFlags = 0; - -// 0x51E324 -int gVcrOldKeyboardLayout = 0; - // A map of SDL_SCANCODE_* constants normalized for QWERTY keyboard. // // 0x6ABC70 @@ -369,9 +327,6 @@ int gKeyboardLayout; // 0x6AD93C unsigned char gPressedPhysicalKeysCount; -// 0x6AD940 -VcrEntry stru_6AD940; - SDL_Window* gSdlWindow = NULL; SDL_Surface* gSdlSurface = NULL; SDL_Renderer* gSdlRenderer = NULL; @@ -4468,383 +4423,6 @@ int keyboardPeekEvent(int index, KeyboardEvent** keyboardEventPtr) return rc; } -// 0x4D2680 -bool vcrRecord(const char* fileName) -{ - if (gVcrState != VCR_STATE_TURNED_OFF) { - return false; - } - - if (fileName == NULL) { - return false; - } - - // NOTE: Uninline. - if (!vcrInitBuffer()) { - return false; - } - - gVcrFile = fileOpen(fileName, "wb"); - if (gVcrFile == NULL) { - // NOTE: Uninline. - vcrFreeBuffer(); - return false; - } - - if (_vcr_registered_atexit == 0) { - _vcr_registered_atexit = atexit(vcrStop); - } - - VcrEntry* vcrEntry = &(_vcr_buffer[_vcr_buffer_index]); - vcrEntry->type = VCR_ENTRY_TYPE_INITIAL_STATE; - vcrEntry->time = 0; - vcrEntry->counter = 0; - vcrEntry->initial.keyboardLayout = keyboardGetLayout(); - - while (mouseGetEvent() != 0) { - _mouse_info(); - } - - mouseGetPosition(&(vcrEntry->initial.mouseX), &(vcrEntry->initial.mouseY)); - - _vcr_counter = 1; - _vcr_buffer_index++; - _vcr_start_time = _get_time(); - keyboardReset(); - gVcrState = VCR_STATE_RECORDING; - - return true; -} - -// 0x4D27EC -bool vcrPlay(const char* fileName, unsigned int terminationFlags, VcrPlaybackCompletionCallback* callback) -{ - if (gVcrState != VCR_STATE_TURNED_OFF) { - return false; - } - - if (fileName == NULL) { - return false; - } - - // NOTE: Uninline. - if (!vcrInitBuffer()) { - return false; - } - - gVcrFile = fileOpen(fileName, "rb"); - if (gVcrFile == NULL) { - // NOTE: Uninline. - vcrFreeBuffer(); - return false; - } - - if (!vcrLoad()) { - fileClose(gVcrFile); - // NOTE: Uninline. - vcrFreeBuffer(); - return false; - } - - while (mouseGetEvent() != 0) { - _mouse_info(); - } - - keyboardReset(); - - gVcrRequestedTerminationFlags = terminationFlags; - gVcrPlaybackCompletionCallback = callback; - gVcrPlaybackCompletionReason = VCR_PLAYBACK_COMPLETION_REASON_COMPLETED; - gVcrTerminateFlags = 0; - _vcr_counter = 0; - _vcr_time = 0; - _vcr_start_time = _get_time(); - gVcrState = VCR_STATE_PLAYING; - stru_6AD940.time = 0; - stru_6AD940.counter = 0; - - return true; -} - -// 0x4D28F4 -void vcrStop() -{ - if (gVcrState == VCR_STATE_RECORDING || gVcrState == VCR_STATE_PLAYING) { - gVcrState |= VCR_STATE_STOP_REQUESTED; - } - - keyboardReset(); -} - -// 0x4D2918 -int vcrGetState() -{ - return gVcrState; -} - -// 0x4D2930 -int vcrUpdate() -{ - if ((gVcrState & VCR_STATE_STOP_REQUESTED) != 0) { - gVcrState &= ~VCR_STATE_STOP_REQUESTED; - - switch (gVcrState) { - case VCR_STATE_RECORDING: - vcrDump(); - - fileClose(gVcrFile); - gVcrFile = NULL; - - // NOTE: Uninline. - vcrFreeBuffer(); - - break; - case VCR_STATE_PLAYING: - fileClose(gVcrFile); - gVcrFile = NULL; - - // NOTE: Uninline. - vcrFreeBuffer(); - - keyboardSetLayout(gVcrOldKeyboardLayout); - - if (gVcrPlaybackCompletionCallback != NULL) { - gVcrPlaybackCompletionCallback(gVcrPlaybackCompletionReason); - } - break; - } - - gVcrState = VCR_STATE_TURNED_OFF; - } - - switch (gVcrState) { - case VCR_STATE_RECORDING: - _vcr_counter++; - _vcr_time = getTicksSince(_vcr_start_time); - if (_vcr_buffer_index == VCR_BUFFER_CAPACITY - 1) { - vcrDump(); - } - break; - case VCR_STATE_PLAYING: - if (_vcr_buffer_index < _vcr_buffer_end || vcrLoad()) { - VcrEntry* vcrEntry = &(_vcr_buffer[_vcr_buffer_index]); - if (stru_6AD940.counter < vcrEntry->counter) { - if (vcrEntry->time > stru_6AD940.time) { - unsigned int delay = stru_6AD940.time; - delay += (_vcr_counter - stru_6AD940.counter) - * (vcrEntry->time - stru_6AD940.time) - / (vcrEntry->counter - stru_6AD940.counter); - - while (getTicksSince(_vcr_start_time) < delay) { - } - } - } - - _vcr_counter++; - - int rc = 0; - while (_vcr_counter >= _vcr_buffer[_vcr_buffer_index].counter) { - _vcr_time = getTicksSince(_vcr_start_time); - if (_vcr_time > _vcr_buffer[_vcr_buffer_index].time + 5 - || _vcr_time < _vcr_buffer[_vcr_buffer_index].time - 5) { - _vcr_start_time += _vcr_time - _vcr_buffer[_vcr_buffer_index].time; - } - - switch (_vcr_buffer[_vcr_buffer_index].type) { - case VCR_ENTRY_TYPE_INITIAL_STATE: - gVcrState = VCR_STATE_TURNED_OFF; - gVcrOldKeyboardLayout = keyboardGetLayout(); - keyboardSetLayout(_vcr_buffer[_vcr_buffer_index].initial.keyboardLayout); - while (mouseGetEvent() != 0) { - _mouse_info(); - } - gVcrState = VCR_ENTRY_TYPE_INITIAL_STATE; - mouseHideCursor(); - _mouse_set_position(_vcr_buffer[_vcr_buffer_index].initial.mouseX, _vcr_buffer[_vcr_buffer_index].initial.mouseY); - mouseShowCursor(); - keyboardReset(); - gVcrTerminateFlags = gVcrRequestedTerminationFlags; - _vcr_start_time = _get_time(); - _vcr_counter = 0; - break; - case VCR_ENTRY_TYPE_KEYBOARD_EVENT: - if (1) { - KeyboardData keyboardData; - keyboardData.key = _vcr_buffer[_vcr_buffer_index].keyboardEvent.key; - _kb_simulate_key(&keyboardData); - } - break; - case VCR_ENTRY_TYPE_MOUSE_EVENT: - rc = 3; - _mouse_simulate_input(_vcr_buffer[_vcr_buffer_index].mouseEvent.dx, _vcr_buffer[_vcr_buffer_index].mouseEvent.dy, _vcr_buffer[_vcr_buffer_index].mouseEvent.buttons); - break; - } - - memcpy(&stru_6AD940, &(_vcr_buffer[_vcr_buffer_index]), sizeof(stru_6AD940)); - _vcr_buffer_index++; - } - - return rc; - } else { - // NOTE: Uninline. - vcrStop(); - } - break; - } - - return 0; -} - -// NOTE: Inlined. -// -// 0x4D2C64 -bool vcrInitBuffer() -{ - if (_vcr_buffer == NULL) { - _vcr_buffer = (VcrEntry*)internal_malloc(sizeof(*_vcr_buffer) * VCR_BUFFER_CAPACITY); - if (_vcr_buffer == NULL) { - return false; - } - } - - // NOTE: Uninline. - vcrClear(); - - return true; -} - -// NOTE: Inlined. -// -// 0x4D2C98 -bool vcrFreeBuffer() -{ - if (_vcr_buffer == NULL) { - return false; - } - - // NOTE: Uninline. - vcrClear(); - - internal_free(_vcr_buffer); - _vcr_buffer = NULL; - - return true; -} - -// 0x4D2CD0 -bool vcrClear() -{ - if (_vcr_buffer == NULL) { - return false; - } - - _vcr_buffer_index = 0; - - return true; -} - -// 0x4D2CF0 -bool vcrDump() -{ - if (_vcr_buffer == NULL) { - return false; - } - - if (gVcrFile == NULL) { - return false; - } - - for (int index = 0; index < _vcr_buffer_index; index++) { - if (!vcrWriteEntry(&(_vcr_buffer[index]), gVcrFile)) { - return false; - } - } - - // NOTE: Uninline. - if (!vcrClear()) { - return false; - } - - return true; -} - -// 0x4D2D74 -bool vcrLoad() -{ - if (gVcrFile == NULL) { - return false; - } - - // NOTE: Uninline. - if (!vcrClear()) { - return false; - } - - for (_vcr_buffer_end = 0; _vcr_buffer_end < VCR_BUFFER_CAPACITY; _vcr_buffer_end++) { - if (!vcrReadEntry(&(_vcr_buffer[_vcr_buffer_end]), gVcrFile)) { - break; - } - } - - if (_vcr_buffer_end == 0) { - return false; - } - - return true; -} - -// 0x4D2E00 -bool vcrWriteEntry(VcrEntry* vcrEntry, File* stream) -{ - if (fileWriteUInt32(stream, vcrEntry->type) == -1) return false; - if (fileWriteUInt32(stream, vcrEntry->time) == -1) return false; - if (fileWriteUInt32(stream, vcrEntry->counter) == -1) return false; - - switch (vcrEntry->type) { - case VCR_ENTRY_TYPE_INITIAL_STATE: - if (fileWriteInt32(stream, vcrEntry->initial.mouseX) == -1) return false; - if (fileWriteInt32(stream, vcrEntry->initial.mouseY) == -1) return false; - if (fileWriteInt32(stream, vcrEntry->initial.keyboardLayout) == -1) return false; - return true; - case VCR_ENTRY_TYPE_KEYBOARD_EVENT: - if (fileWriteInt16(stream, vcrEntry->keyboardEvent.key) == -1) return false; - return true; - case VCR_ENTRY_TYPE_MOUSE_EVENT: - if (fileWriteInt32(stream, vcrEntry->mouseEvent.dx) == -1) return false; - if (fileWriteInt32(stream, vcrEntry->mouseEvent.dy) == -1) return false; - if (fileWriteInt32(stream, vcrEntry->mouseEvent.buttons) == -1) return false; - return true; - } - - return false; -} - -// 0x4D2EE4 -bool vcrReadEntry(VcrEntry* vcrEntry, File* stream) -{ - if (fileReadUInt32(stream, &(vcrEntry->type)) == -1) return false; - if (fileReadUInt32(stream, &(vcrEntry->time)) == -1) return false; - if (fileReadUInt32(stream, &(vcrEntry->counter)) == -1) return false; - - switch (vcrEntry->type) { - case VCR_ENTRY_TYPE_INITIAL_STATE: - if (fileReadInt32(stream, &(vcrEntry->initial.mouseX)) == -1) return false; - if (fileReadInt32(stream, &(vcrEntry->initial.mouseY)) == -1) return false; - if (fileReadInt32(stream, &(vcrEntry->initial.keyboardLayout)) == -1) return false; - return true; - case VCR_ENTRY_TYPE_KEYBOARD_EVENT: - if (fileReadInt16(stream, &(vcrEntry->keyboardEvent.key)) == -1) return false; - return true; - case VCR_ENTRY_TYPE_MOUSE_EVENT: - if (fileReadInt32(stream, &(vcrEntry->mouseEvent.dx)) == -1) return false; - if (fileReadInt32(stream, &(vcrEntry->mouseEvent.dy)) == -1) return false; - if (fileReadInt32(stream, &(vcrEntry->mouseEvent.buttons)) == -1) return false; - return true; - } - - return false; -} - int screenGetWidth() { // TODO: Make it on par with _xres; diff --git a/src/core.h b/src/core.h index 35ed6b0..4a9044c 100644 --- a/src/core.h +++ b/src/core.h @@ -363,44 +363,6 @@ typedef enum KeyboardLayout { KEYBOARD_LAYOUT_SPANISH, } KeyboardLayout; -#define VCR_BUFFER_CAPACITY 4096 - -typedef enum VcrState { - VCR_STATE_RECORDING, - VCR_STATE_PLAYING, - VCR_STATE_TURNED_OFF, -} VcrState; - -#define VCR_STATE_STOP_REQUESTED 0x80000000 - -typedef enum VcrTerminationFlags { - // Specifies that VCR playback should stop if any key is pressed. - VCR_TERMINATE_ON_KEY_PRESS = 0x01, - - // Specifies that VCR playback should stop if mouse is mouved. - VCR_TERMINATE_ON_MOUSE_MOVE = 0x02, - - // Specifies that VCR playback should stop if any mouse button is pressed. - VCR_TERMINATE_ON_MOUSE_PRESS = 0x04, -} VcrTerminationFlags; - -typedef enum VcrPlaybackCompletionReason { - VCR_PLAYBACK_COMPLETION_REASON_NONE = 0, - - // Indicates that VCR playback completed normally. - VCR_PLAYBACK_COMPLETION_REASON_COMPLETED = 1, - - // Indicates that VCR playback terminated according to termination flags. - VCR_PLAYBACK_COMPLETION_REASON_TERMINATED = 2, -} VcrPlaybackCompletionReason; - -typedef enum VcrEntryType { - VCR_ENTRY_TYPE_NONE = 0, - VCR_ENTRY_TYPE_INITIAL_STATE = 1, - VCR_ENTRY_TYPE_KEYBOARD_EVENT = 2, - VCR_ENTRY_TYPE_MOUSE_EVENT = 3, -} VcrEntryType; - typedef struct STRUCT_6ABF50 { // Time when appropriate key was pressed down or -1 if it's up. int tick; @@ -426,27 +388,6 @@ typedef struct TickerListNode { struct TickerListNode* next; } TickerListNode; -typedef struct VcrEntry { - unsigned int type; - unsigned int time; - unsigned int counter; - union { - struct { - int mouseX; - int mouseY; - int keyboardLayout; - } initial; - struct { - short key; - } keyboardEvent; - struct { - int dx; - int dy; - int buttons; - } mouseEvent; - }; -} VcrEntry; - typedef struct LogicalKeyEntry { short field_0; short unmodified; @@ -463,7 +404,6 @@ typedef struct KeyboardEvent { typedef int(PauseHandler)(); typedef int(ScreenshotHandler)(int width, int height, unsigned char* buffer, unsigned char* palette); -typedef void(VcrPlaybackCompletionCallback)(int reason); extern IdleFunc* _idle_func; extern FocusFunc* _focus_func; @@ -492,20 +432,6 @@ extern int gKeyboardEventQueueReadIndex; extern short word_51E2E8; extern int gModifierKeysState; extern int (*_kb_scan_to_ascii)(); -extern VcrEntry* _vcr_buffer; -extern int _vcr_buffer_index; -extern unsigned int gVcrState; -extern unsigned int _vcr_time; -extern unsigned int _vcr_counter; -extern unsigned int gVcrTerminateFlags; -extern int gVcrPlaybackCompletionReason; -extern unsigned int _vcr_start_time; -extern int _vcr_registered_atexit; -extern File* gVcrFile; -extern int _vcr_buffer_end; -extern VcrPlaybackCompletionCallback* gVcrPlaybackCompletionCallback; -extern unsigned int gVcrRequestedTerminationFlags; -extern int gVcrOldKeyboardLayout; extern int gNormalizedQwertyKeys[SDL_NUM_SCANCODES]; extern InputEvent gInputEventQueue[40]; @@ -567,7 +493,6 @@ extern unsigned int _kb_idle_start_time; extern KeyboardEvent gLastKeyboardEvent; extern int gKeyboardLayout; extern unsigned char gPressedPhysicalKeysCount; -extern VcrEntry stru_6AD940; extern SDL_Window* gSdlWindow; extern SDL_Surface* gSdlSurface; @@ -677,18 +602,6 @@ void keyboardBuildItalianConfiguration(); void keyboardBuildSpanishConfiguration(); void _kb_init_lock_status(); int keyboardPeekEvent(int index, KeyboardEvent** keyboardEventPtr); -bool vcrRecord(const char* fileName); -bool vcrPlay(const char* fileName, unsigned int terminationFlags, VcrPlaybackCompletionCallback* callback); -void vcrStop(); -int vcrGetState(); -int vcrUpdate(); -bool vcrInitBuffer(); -bool vcrFreeBuffer(); -bool vcrClear(); -bool vcrDump(); -bool vcrLoad(); -bool vcrWriteEntry(VcrEntry* ptr, File* stream); -bool vcrReadEntry(VcrEntry* ptr, File* stream); int screenGetWidth(); int screenGetHeight(); diff --git a/src/interpreter_extra.cc b/src/interpreter_extra.cc index 05df191..575f389 100644 --- a/src/interpreter_extra.cc +++ b/src/interpreter_extra.cc @@ -42,6 +42,7 @@ #include "text_object.h" #include "tile.h" #include "trait.h" +#include "vcr.h" #include "worldmap.h" namespace fallout { diff --git a/src/selfrun.cc b/src/selfrun.cc index 2d21ba3..6f59106 100644 --- a/src/selfrun.cc +++ b/src/selfrun.cc @@ -7,6 +7,7 @@ #include "game.h" #include "game_config.h" #include "platform_compat.h" +#include "vcr.h" namespace fallout { diff --git a/src/vcr.cc b/src/vcr.cc new file mode 100644 index 0000000..8b86ad7 --- /dev/null +++ b/src/vcr.cc @@ -0,0 +1,438 @@ +#include "vcr.h" + +#include + +#include "core.h" +#include "memory.h" + +namespace fallout { + +static bool vcrInitBuffer(); +static bool vcrFreeBuffer(); +static bool vcrClear(); +static bool vcrLoad(); + +// 0x51E2F0 +VcrEntry* _vcr_buffer = NULL; + +// number of entries in _vcr_buffer +// 0x51E2F4 +int _vcr_buffer_index = 0; + +// 0x51E2F8 +unsigned int gVcrState = VCR_STATE_TURNED_OFF; + +// 0x51E2FC +unsigned int _vcr_time = 0; + +// 0x51E300 +unsigned int _vcr_counter = 0; + +// 0x51E304 +unsigned int gVcrTerminateFlags = 0; + +// 0x51E308 +int gVcrPlaybackCompletionReason = VCR_PLAYBACK_COMPLETION_REASON_NONE; + +// 0x51E30C +static unsigned int _vcr_start_time = 0; + +// 0x51E310 +static int _vcr_registered_atexit = 0; + +// 0x51E314 +static File* gVcrFile = NULL; + +// 0x51E318 +static int _vcr_buffer_end = 0; + +// 0x51E31C +static VcrPlaybackCompletionCallback* gVcrPlaybackCompletionCallback = NULL; + +// 0x51E320 +static unsigned int gVcrRequestedTerminationFlags = 0; + +// 0x51E324 +static int gVcrOldKeyboardLayout = 0; + +// 0x6AD940 +static VcrEntry stru_6AD940; + +// 0x4D2680 +bool vcrRecord(const char* fileName) +{ + if (gVcrState != VCR_STATE_TURNED_OFF) { + return false; + } + + if (fileName == NULL) { + return false; + } + + // NOTE: Uninline. + if (!vcrInitBuffer()) { + return false; + } + + gVcrFile = fileOpen(fileName, "wb"); + if (gVcrFile == NULL) { + // NOTE: Uninline. + vcrFreeBuffer(); + return false; + } + + if (_vcr_registered_atexit == 0) { + _vcr_registered_atexit = atexit(vcrStop); + } + + VcrEntry* vcrEntry = &(_vcr_buffer[_vcr_buffer_index]); + vcrEntry->type = VCR_ENTRY_TYPE_INITIAL_STATE; + vcrEntry->time = 0; + vcrEntry->counter = 0; + vcrEntry->initial.keyboardLayout = keyboardGetLayout(); + + while (mouseGetEvent() != 0) { + _mouse_info(); + } + + mouseGetPosition(&(vcrEntry->initial.mouseX), &(vcrEntry->initial.mouseY)); + + _vcr_counter = 1; + _vcr_buffer_index++; + _vcr_start_time = _get_time(); + keyboardReset(); + gVcrState = VCR_STATE_RECORDING; + + return true; +} + +// 0x4D27EC +bool vcrPlay(const char* fileName, unsigned int terminationFlags, VcrPlaybackCompletionCallback* callback) +{ + if (gVcrState != VCR_STATE_TURNED_OFF) { + return false; + } + + if (fileName == NULL) { + return false; + } + + // NOTE: Uninline. + if (!vcrInitBuffer()) { + return false; + } + + gVcrFile = fileOpen(fileName, "rb"); + if (gVcrFile == NULL) { + // NOTE: Uninline. + vcrFreeBuffer(); + return false; + } + + if (!vcrLoad()) { + fileClose(gVcrFile); + // NOTE: Uninline. + vcrFreeBuffer(); + return false; + } + + while (mouseGetEvent() != 0) { + _mouse_info(); + } + + keyboardReset(); + + gVcrRequestedTerminationFlags = terminationFlags; + gVcrPlaybackCompletionCallback = callback; + gVcrPlaybackCompletionReason = VCR_PLAYBACK_COMPLETION_REASON_COMPLETED; + gVcrTerminateFlags = 0; + _vcr_counter = 0; + _vcr_time = 0; + _vcr_start_time = _get_time(); + gVcrState = VCR_STATE_PLAYING; + stru_6AD940.time = 0; + stru_6AD940.counter = 0; + + return true; +} + +// 0x4D28F4 +void vcrStop() +{ + if (gVcrState == VCR_STATE_RECORDING || gVcrState == VCR_STATE_PLAYING) { + gVcrState |= VCR_STATE_STOP_REQUESTED; + } + + keyboardReset(); +} + +// 0x4D2918 +int vcrGetState() +{ + return gVcrState; +} + +// 0x4D2930 +int vcrUpdate() +{ + if ((gVcrState & VCR_STATE_STOP_REQUESTED) != 0) { + gVcrState &= ~VCR_STATE_STOP_REQUESTED; + + switch (gVcrState) { + case VCR_STATE_RECORDING: + vcrDump(); + + fileClose(gVcrFile); + gVcrFile = NULL; + + // NOTE: Uninline. + vcrFreeBuffer(); + + break; + case VCR_STATE_PLAYING: + fileClose(gVcrFile); + gVcrFile = NULL; + + // NOTE: Uninline. + vcrFreeBuffer(); + + keyboardSetLayout(gVcrOldKeyboardLayout); + + if (gVcrPlaybackCompletionCallback != NULL) { + gVcrPlaybackCompletionCallback(gVcrPlaybackCompletionReason); + } + break; + } + + gVcrState = VCR_STATE_TURNED_OFF; + } + + switch (gVcrState) { + case VCR_STATE_RECORDING: + _vcr_counter++; + _vcr_time = getTicksSince(_vcr_start_time); + if (_vcr_buffer_index == VCR_BUFFER_CAPACITY - 1) { + vcrDump(); + } + break; + case VCR_STATE_PLAYING: + if (_vcr_buffer_index < _vcr_buffer_end || vcrLoad()) { + VcrEntry* vcrEntry = &(_vcr_buffer[_vcr_buffer_index]); + if (stru_6AD940.counter < vcrEntry->counter) { + if (vcrEntry->time > stru_6AD940.time) { + unsigned int delay = stru_6AD940.time; + delay += (_vcr_counter - stru_6AD940.counter) + * (vcrEntry->time - stru_6AD940.time) + / (vcrEntry->counter - stru_6AD940.counter); + + while (getTicksSince(_vcr_start_time) < delay) { + } + } + } + + _vcr_counter++; + + int rc = 0; + while (_vcr_counter >= _vcr_buffer[_vcr_buffer_index].counter) { + _vcr_time = getTicksSince(_vcr_start_time); + if (_vcr_time > _vcr_buffer[_vcr_buffer_index].time + 5 + || _vcr_time < _vcr_buffer[_vcr_buffer_index].time - 5) { + _vcr_start_time += _vcr_time - _vcr_buffer[_vcr_buffer_index].time; + } + + switch (_vcr_buffer[_vcr_buffer_index].type) { + case VCR_ENTRY_TYPE_INITIAL_STATE: + gVcrState = VCR_STATE_TURNED_OFF; + gVcrOldKeyboardLayout = keyboardGetLayout(); + keyboardSetLayout(_vcr_buffer[_vcr_buffer_index].initial.keyboardLayout); + while (mouseGetEvent() != 0) { + _mouse_info(); + } + gVcrState = VCR_ENTRY_TYPE_INITIAL_STATE; + mouseHideCursor(); + _mouse_set_position(_vcr_buffer[_vcr_buffer_index].initial.mouseX, _vcr_buffer[_vcr_buffer_index].initial.mouseY); + mouseShowCursor(); + keyboardReset(); + gVcrTerminateFlags = gVcrRequestedTerminationFlags; + _vcr_start_time = _get_time(); + _vcr_counter = 0; + break; + case VCR_ENTRY_TYPE_KEYBOARD_EVENT: + if (1) { + KeyboardData keyboardData; + keyboardData.key = _vcr_buffer[_vcr_buffer_index].keyboardEvent.key; + _kb_simulate_key(&keyboardData); + } + break; + case VCR_ENTRY_TYPE_MOUSE_EVENT: + rc = 3; + _mouse_simulate_input(_vcr_buffer[_vcr_buffer_index].mouseEvent.dx, _vcr_buffer[_vcr_buffer_index].mouseEvent.dy, _vcr_buffer[_vcr_buffer_index].mouseEvent.buttons); + break; + } + + memcpy(&stru_6AD940, &(_vcr_buffer[_vcr_buffer_index]), sizeof(stru_6AD940)); + _vcr_buffer_index++; + } + + return rc; + } else { + // NOTE: Uninline. + vcrStop(); + } + break; + } + + return 0; +} + +// NOTE: Inlined. +// +// 0x4D2C64 +static bool vcrInitBuffer() +{ + if (_vcr_buffer == NULL) { + _vcr_buffer = (VcrEntry*)internal_malloc(sizeof(*_vcr_buffer) * VCR_BUFFER_CAPACITY); + if (_vcr_buffer == NULL) { + return false; + } + } + + // NOTE: Uninline. + vcrClear(); + + return true; +} + +// NOTE: Inlined. +// +// 0x4D2C98 +static bool vcrFreeBuffer() +{ + if (_vcr_buffer == NULL) { + return false; + } + + // NOTE: Uninline. + vcrClear(); + + internal_free(_vcr_buffer); + _vcr_buffer = NULL; + + return true; +} + +// 0x4D2CD0 +static bool vcrClear() +{ + if (_vcr_buffer == NULL) { + return false; + } + + _vcr_buffer_index = 0; + + return true; +} + +// 0x4D2CF0 +bool vcrDump() +{ + if (_vcr_buffer == NULL) { + return false; + } + + if (gVcrFile == NULL) { + return false; + } + + for (int index = 0; index < _vcr_buffer_index; index++) { + if (!vcrWriteEntry(&(_vcr_buffer[index]), gVcrFile)) { + return false; + } + } + + // NOTE: Uninline. + if (!vcrClear()) { + return false; + } + + return true; +} + +// 0x4D2D74 +static bool vcrLoad() +{ + if (gVcrFile == NULL) { + return false; + } + + // NOTE: Uninline. + if (!vcrClear()) { + return false; + } + + for (_vcr_buffer_end = 0; _vcr_buffer_end < VCR_BUFFER_CAPACITY; _vcr_buffer_end++) { + if (!vcrReadEntry(&(_vcr_buffer[_vcr_buffer_end]), gVcrFile)) { + break; + } + } + + if (_vcr_buffer_end == 0) { + return false; + } + + return true; +} + +// 0x4D2E00 +bool vcrWriteEntry(VcrEntry* vcrEntry, File* stream) +{ + if (fileWriteUInt32(stream, vcrEntry->type) == -1) return false; + if (fileWriteUInt32(stream, vcrEntry->time) == -1) return false; + if (fileWriteUInt32(stream, vcrEntry->counter) == -1) return false; + + switch (vcrEntry->type) { + case VCR_ENTRY_TYPE_INITIAL_STATE: + if (fileWriteInt32(stream, vcrEntry->initial.mouseX) == -1) return false; + if (fileWriteInt32(stream, vcrEntry->initial.mouseY) == -1) return false; + if (fileWriteInt32(stream, vcrEntry->initial.keyboardLayout) == -1) return false; + return true; + case VCR_ENTRY_TYPE_KEYBOARD_EVENT: + if (fileWriteInt16(stream, vcrEntry->keyboardEvent.key) == -1) return false; + return true; + case VCR_ENTRY_TYPE_MOUSE_EVENT: + if (fileWriteInt32(stream, vcrEntry->mouseEvent.dx) == -1) return false; + if (fileWriteInt32(stream, vcrEntry->mouseEvent.dy) == -1) return false; + if (fileWriteInt32(stream, vcrEntry->mouseEvent.buttons) == -1) return false; + return true; + } + + return false; +} + +// 0x4D2EE4 +bool vcrReadEntry(VcrEntry* vcrEntry, File* stream) +{ + if (fileReadUInt32(stream, &(vcrEntry->type)) == -1) return false; + if (fileReadUInt32(stream, &(vcrEntry->time)) == -1) return false; + if (fileReadUInt32(stream, &(vcrEntry->counter)) == -1) return false; + + switch (vcrEntry->type) { + case VCR_ENTRY_TYPE_INITIAL_STATE: + if (fileReadInt32(stream, &(vcrEntry->initial.mouseX)) == -1) return false; + if (fileReadInt32(stream, &(vcrEntry->initial.mouseY)) == -1) return false; + if (fileReadInt32(stream, &(vcrEntry->initial.keyboardLayout)) == -1) return false; + return true; + case VCR_ENTRY_TYPE_KEYBOARD_EVENT: + if (fileReadInt16(stream, &(vcrEntry->keyboardEvent.key)) == -1) return false; + return true; + case VCR_ENTRY_TYPE_MOUSE_EVENT: + if (fileReadInt32(stream, &(vcrEntry->mouseEvent.dx)) == -1) return false; + if (fileReadInt32(stream, &(vcrEntry->mouseEvent.dy)) == -1) return false; + if (fileReadInt32(stream, &(vcrEntry->mouseEvent.buttons)) == -1) return false; + return true; + } + + return false; +} + +} // fallout diff --git a/src/vcr.h b/src/vcr.h new file mode 100644 index 0000000..b433605 --- /dev/null +++ b/src/vcr.h @@ -0,0 +1,88 @@ +#ifndef FALLOUT_VCR_H_ +#define FALLOUT_VCR_H_ + +#include "db.h" + +namespace fallout { + +#define VCR_BUFFER_CAPACITY 4096 + +typedef enum VcrState { + VCR_STATE_RECORDING, + VCR_STATE_PLAYING, + VCR_STATE_TURNED_OFF, +} VcrState; + +#define VCR_STATE_STOP_REQUESTED 0x80000000 + +typedef enum VcrTerminationFlags { + // Specifies that VCR playback should stop if any key is pressed. + VCR_TERMINATE_ON_KEY_PRESS = 0x01, + + // Specifies that VCR playback should stop if mouse is mouved. + VCR_TERMINATE_ON_MOUSE_MOVE = 0x02, + + // Specifies that VCR playback should stop if any mouse button is pressed. + VCR_TERMINATE_ON_MOUSE_PRESS = 0x04, +} VcrTerminationFlags; + +typedef enum VcrPlaybackCompletionReason { + VCR_PLAYBACK_COMPLETION_REASON_NONE = 0, + + // Indicates that VCR playback completed normally. + VCR_PLAYBACK_COMPLETION_REASON_COMPLETED = 1, + + // Indicates that VCR playback terminated according to termination flags. + VCR_PLAYBACK_COMPLETION_REASON_TERMINATED = 2, +} VcrPlaybackCompletionReason; + +typedef enum VcrEntryType { + VCR_ENTRY_TYPE_NONE = 0, + VCR_ENTRY_TYPE_INITIAL_STATE = 1, + VCR_ENTRY_TYPE_KEYBOARD_EVENT = 2, + VCR_ENTRY_TYPE_MOUSE_EVENT = 3, +} VcrEntryType; + +typedef void(VcrPlaybackCompletionCallback)(int reason); + +typedef struct VcrEntry { + unsigned int type; + unsigned int time; + unsigned int counter; + union { + struct { + int mouseX; + int mouseY; + int keyboardLayout; + } initial; + struct { + short key; + } keyboardEvent; + struct { + int dx; + int dy; + int buttons; + } mouseEvent; + }; +} VcrEntry; + +extern VcrEntry* _vcr_buffer; +extern int _vcr_buffer_index; +extern unsigned int gVcrState; +extern unsigned int _vcr_time; +extern unsigned int _vcr_counter; +extern unsigned int gVcrTerminateFlags; +extern int gVcrPlaybackCompletionReason; + +bool vcrRecord(const char* fileName); +bool vcrPlay(const char* fileName, unsigned int terminationFlags, VcrPlaybackCompletionCallback* callback); +void vcrStop(); +int vcrGetState(); +int vcrUpdate(); +bool vcrDump(); +bool vcrWriteEntry(VcrEntry* ptr, File* stream); +bool vcrReadEntry(VcrEntry* ptr, File* stream); + +} // namespace fallout + +#endif /* FALLOUT_VCR_H_ */ diff --git a/src/window_manager.cc b/src/window_manager.cc index bf50212..9c0be31 100644 --- a/src/window_manager.cc +++ b/src/window_manager.cc @@ -14,6 +14,7 @@ #include "palette.h" #include "pointer_registry.h" #include "text_font.h" +#include "vcr.h" #include "win32.h" #include "window_manager_private.h"