Extract vcr

This commit is contained in:
Alexander Batalov 2022-10-03 09:37:05 +03:00
parent a1c1e03da0
commit 5f9ceb7f5d
9 changed files with 533 additions and 510 deletions

View File

@ -225,6 +225,8 @@ target_sources(${EXECUTABLE_NAME} PUBLIC
"src/trait.h" "src/trait.h"
"src/trap.cc" "src/trap.cc"
"src/trap.h" "src/trap.h"
"src/vcr.cc"
"src/vcr.h"
"src/version.cc" "src/version.cc"
"src/version.h" "src/version.h"
"src/widget.cc" "src/widget.cc"

View File

@ -30,6 +30,7 @@
#include "text_object.h" #include "text_object.h"
#include "tile.h" #include "tile.h"
#include "trait.h" #include "trait.h"
#include "vcr.h"
namespace fallout { namespace fallout {

View File

@ -14,6 +14,7 @@
#include "memory.h" #include "memory.h"
#include "mmx.h" #include "mmx.h"
#include "text_font.h" #include "text_font.h"
#include "vcr.h"
#include "win32.h" #include "win32.h"
#include "window_manager.h" #include "window_manager.h"
#include "window_manager_private.h" #include "window_manager_private.h"
@ -120,49 +121,6 @@ int gModifierKeysState = 0;
// 0x51E2EC // 0x51E2EC
int (*_kb_scan_to_ascii)() = keyboardDequeueLogicalKeyCode; 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. // A map of SDL_SCANCODE_* constants normalized for QWERTY keyboard.
// //
// 0x6ABC70 // 0x6ABC70
@ -369,9 +327,6 @@ int gKeyboardLayout;
// 0x6AD93C // 0x6AD93C
unsigned char gPressedPhysicalKeysCount; unsigned char gPressedPhysicalKeysCount;
// 0x6AD940
VcrEntry stru_6AD940;
SDL_Window* gSdlWindow = NULL; SDL_Window* gSdlWindow = NULL;
SDL_Surface* gSdlSurface = NULL; SDL_Surface* gSdlSurface = NULL;
SDL_Renderer* gSdlRenderer = NULL; SDL_Renderer* gSdlRenderer = NULL;
@ -4468,383 +4423,6 @@ int keyboardPeekEvent(int index, KeyboardEvent** keyboardEventPtr)
return rc; 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() int screenGetWidth()
{ {
// TODO: Make it on par with _xres; // TODO: Make it on par with _xres;

View File

@ -363,44 +363,6 @@ typedef enum KeyboardLayout {
KEYBOARD_LAYOUT_SPANISH, KEYBOARD_LAYOUT_SPANISH,
} KeyboardLayout; } 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 { typedef struct STRUCT_6ABF50 {
// Time when appropriate key was pressed down or -1 if it's up. // Time when appropriate key was pressed down or -1 if it's up.
int tick; int tick;
@ -426,27 +388,6 @@ typedef struct TickerListNode {
struct TickerListNode* next; struct TickerListNode* next;
} TickerListNode; } 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 { typedef struct LogicalKeyEntry {
short field_0; short field_0;
short unmodified; short unmodified;
@ -463,7 +404,6 @@ typedef struct KeyboardEvent {
typedef int(PauseHandler)(); typedef int(PauseHandler)();
typedef int(ScreenshotHandler)(int width, int height, unsigned char* buffer, unsigned char* palette); typedef int(ScreenshotHandler)(int width, int height, unsigned char* buffer, unsigned char* palette);
typedef void(VcrPlaybackCompletionCallback)(int reason);
extern IdleFunc* _idle_func; extern IdleFunc* _idle_func;
extern FocusFunc* _focus_func; extern FocusFunc* _focus_func;
@ -492,20 +432,6 @@ extern int gKeyboardEventQueueReadIndex;
extern short word_51E2E8; extern short word_51E2E8;
extern int gModifierKeysState; extern int gModifierKeysState;
extern int (*_kb_scan_to_ascii)(); 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 int gNormalizedQwertyKeys[SDL_NUM_SCANCODES];
extern InputEvent gInputEventQueue[40]; extern InputEvent gInputEventQueue[40];
@ -567,7 +493,6 @@ extern unsigned int _kb_idle_start_time;
extern KeyboardEvent gLastKeyboardEvent; extern KeyboardEvent gLastKeyboardEvent;
extern int gKeyboardLayout; extern int gKeyboardLayout;
extern unsigned char gPressedPhysicalKeysCount; extern unsigned char gPressedPhysicalKeysCount;
extern VcrEntry stru_6AD940;
extern SDL_Window* gSdlWindow; extern SDL_Window* gSdlWindow;
extern SDL_Surface* gSdlSurface; extern SDL_Surface* gSdlSurface;
@ -677,18 +602,6 @@ void keyboardBuildItalianConfiguration();
void keyboardBuildSpanishConfiguration(); void keyboardBuildSpanishConfiguration();
void _kb_init_lock_status(); void _kb_init_lock_status();
int keyboardPeekEvent(int index, KeyboardEvent** keyboardEventPtr); 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 screenGetWidth();
int screenGetHeight(); int screenGetHeight();

View File

@ -42,6 +42,7 @@
#include "text_object.h" #include "text_object.h"
#include "tile.h" #include "tile.h"
#include "trait.h" #include "trait.h"
#include "vcr.h"
#include "worldmap.h" #include "worldmap.h"
namespace fallout { namespace fallout {

View File

@ -7,6 +7,7 @@
#include "game.h" #include "game.h"
#include "game_config.h" #include "game_config.h"
#include "platform_compat.h" #include "platform_compat.h"
#include "vcr.h"
namespace fallout { namespace fallout {

438
src/vcr.cc Normal file
View File

@ -0,0 +1,438 @@
#include "vcr.h"
#include <stdlib.h>
#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

88
src/vcr.h Normal file
View File

@ -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_ */

View File

@ -14,6 +14,7 @@
#include "palette.h" #include "palette.h"
#include "pointer_registry.h" #include "pointer_registry.h"
#include "text_font.h" #include "text_font.h"
#include "vcr.h"
#include "win32.h" #include "win32.h"
#include "window_manager_private.h" #include "window_manager_private.h"