first working code
This commit is contained in:
commit
0b01cfc53b
76
.clang-format
Normal file
76
.clang-format
Normal file
|
@ -0,0 +1,76 @@
|
||||||
|
---
|
||||||
|
AccessModifierOffset: '-4'
|
||||||
|
AlignAfterOpenBracket: Align
|
||||||
|
AlignConsecutiveMacros: 'false'
|
||||||
|
AlignConsecutiveAssignments: 'true'
|
||||||
|
AlignConsecutiveDeclarations: 'false'
|
||||||
|
AlignEscapedNewlines: Right
|
||||||
|
AlignOperands: 'true'
|
||||||
|
AlignTrailingComments: 'true'
|
||||||
|
AllowAllArgumentsOnNextLine: 'false'
|
||||||
|
AllowAllConstructorInitializersOnNextLine: 'false'
|
||||||
|
AllowAllParametersOfDeclarationOnNextLine: 'false'
|
||||||
|
AllowShortBlocksOnASingleLine: 'false'
|
||||||
|
AllowShortCaseLabelsOnASingleLine: 'false'
|
||||||
|
AllowShortFunctionsOnASingleLine: Empty
|
||||||
|
AllowShortIfStatementsOnASingleLine: Never
|
||||||
|
AllowShortLambdasOnASingleLine: All
|
||||||
|
AllowShortLoopsOnASingleLine: 'false'
|
||||||
|
AlwaysBreakAfterReturnType: None
|
||||||
|
AlwaysBreakBeforeMultilineStrings: 'false'
|
||||||
|
AlwaysBreakTemplateDeclarations: 'Yes'
|
||||||
|
BinPackArguments: false
|
||||||
|
BinPackParameters: false
|
||||||
|
BraceWrapping:
|
||||||
|
AfterCaseLabel: false
|
||||||
|
AfterClass: true
|
||||||
|
AfterControlStatement: false
|
||||||
|
AfterEnum: true
|
||||||
|
AfterFunction: true
|
||||||
|
AfterNamespace: true
|
||||||
|
AfterObjCDeclaration: true
|
||||||
|
AfterStruct: true
|
||||||
|
AfterUnion: true
|
||||||
|
AfterExternBlock: true
|
||||||
|
BeforeCatch: true
|
||||||
|
BeforeElse: true
|
||||||
|
IndentBraces: false
|
||||||
|
SplitEmptyFunction: false
|
||||||
|
SplitEmptyRecord: false
|
||||||
|
SplitEmptyNamespace: false
|
||||||
|
BreakBeforeBraces: Custom
|
||||||
|
BreakBeforeTernaryOperators: 'true'
|
||||||
|
BreakConstructorInitializers: BeforeColon
|
||||||
|
BreakStringLiterals: 'false'
|
||||||
|
ColumnLimit: '120'
|
||||||
|
CompactNamespaces: 'false'
|
||||||
|
ConstructorInitializerAllOnOneLineOrOnePerLine: 'true'
|
||||||
|
FixNamespaceComments: 'false'
|
||||||
|
IndentCaseLabels: 'false'
|
||||||
|
IndentPPDirectives: BeforeHash
|
||||||
|
IndentWidth: '4'
|
||||||
|
IndentWrappedFunctionNames: 'false'
|
||||||
|
Language: Cpp
|
||||||
|
MaxEmptyLinesToKeep: '1'
|
||||||
|
NamespaceIndentation: None
|
||||||
|
PointerAlignment: Left
|
||||||
|
SortIncludes: 'false'
|
||||||
|
SpaceAfterCStyleCast: 'false'
|
||||||
|
SpaceAfterLogicalNot: 'false'
|
||||||
|
SpaceAfterTemplateKeyword: 'false'
|
||||||
|
SpaceBeforeAssignmentOperators: 'true'
|
||||||
|
SpaceBeforeCpp11BracedList: 'false'
|
||||||
|
SpaceBeforeCtorInitializerColon: 'true'
|
||||||
|
SpaceBeforeInheritanceColon: 'true'
|
||||||
|
SpaceBeforeParens: ControlStatements
|
||||||
|
SpaceBeforeRangeBasedForLoopColon: 'true'
|
||||||
|
SpaceInEmptyParentheses: 'false'
|
||||||
|
SpacesInAngles: 'false'
|
||||||
|
SpacesInCStyleCastParentheses: 'false'
|
||||||
|
SpacesInContainerLiterals: 'true'
|
||||||
|
SpacesInParentheses: 'false'
|
||||||
|
SpacesInSquareBrackets: 'false'
|
||||||
|
Standard: Cpp11
|
||||||
|
UseTab: Never
|
||||||
|
PenaltyReturnTypeOnItsOwnLine: 1000
|
||||||
|
...
|
8
build_test.bat
Normal file
8
build_test.bat
Normal file
|
@ -0,0 +1,8 @@
|
||||||
|
@echo off
|
||||||
|
|
||||||
|
mkdir build
|
||||||
|
pushd build
|
||||||
|
|
||||||
|
clang -o test.exe -Wall -Wextra -Wpedantic -std=c99 -O0 -g ..\test.c
|
||||||
|
|
||||||
|
popd
|
397
rt_replay.h
Normal file
397
rt_replay.h
Normal file
|
@ -0,0 +1,397 @@
|
||||||
|
#ifndef RT_REPLAY_H
|
||||||
|
#define RT_REPLAY_H
|
||||||
|
|
||||||
|
/* rt_replay.h
|
||||||
|
* Input replay system for game development.
|
||||||
|
*
|
||||||
|
* This library is C99 compliant and builds without warnings with
|
||||||
|
* (clang) -Wall -Wextra -Wpedantic.
|
||||||
|
*
|
||||||
|
* To integrate it into your codebase, define RT_REPLAY_IMPLEMENTATION
|
||||||
|
* before including it in one source file.
|
||||||
|
* By default, the library depends on stdint.h (for fixed size integers),
|
||||||
|
* and on stdlib.h (for malloc & free).
|
||||||
|
* You can provide replacements for these by defining macros as described
|
||||||
|
* below.
|
||||||
|
*
|
||||||
|
* It's provided under the terms of the MIT license (found below)
|
||||||
|
*/
|
||||||
|
|
||||||
|
/* The MIT License (MIT)
|
||||||
|
* Copyright © 2023 Kevin Trogant
|
||||||
|
*
|
||||||
|
* Permission is hereby granted, free of charge, to any person obtaining a copy
|
||||||
|
* of this software and associated documentation files (the “Software”), to deal
|
||||||
|
* in the Software without restriction, including without limitation the rights
|
||||||
|
* to use, copy, modify, merge, publish, distribute, sublicense, and/or sell
|
||||||
|
* copies of the Software, and to permit persons to whom the Software is
|
||||||
|
* furnished to do so, subject to the following conditions:
|
||||||
|
*
|
||||||
|
* The above copyright notice and this permission notice shall be
|
||||||
|
* included in all copies or substantial portions of the Software.
|
||||||
|
*
|
||||||
|
* THE SOFTWARE IS PROVIDED “AS IS”, WITHOUT WARRANTY OF ANY KIND, EXPRESS OR
|
||||||
|
* IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY,
|
||||||
|
* FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT.
|
||||||
|
* IN NO EVENT SHALL THE AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM,
|
||||||
|
* DAMAGES OR OTHER LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR
|
||||||
|
* OTHERWISE, ARISING FROM, OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE
|
||||||
|
* USE OR OTHER DEALINGS IN THE SOFTWARE.
|
||||||
|
*/
|
||||||
|
|
||||||
|
/* Fixed size integers.
|
||||||
|
*
|
||||||
|
* You can replace these by defining the macros below.
|
||||||
|
* HOWEVER: Please note that this might break previous recordings if you
|
||||||
|
* inadvertently end up changing the size of the integers.
|
||||||
|
*/
|
||||||
|
#if !defined(RTR_INT32) || !defined(RTR_UINT32) || !defined(RTR_UINT64)
|
||||||
|
#include <stdint.h>
|
||||||
|
#endif
|
||||||
|
|
||||||
|
#if !defined(RTR_INT32)
|
||||||
|
#define RTR_INT32 int32_t
|
||||||
|
#endif
|
||||||
|
|
||||||
|
#if !defined(RTR_UINT32)
|
||||||
|
#define RTR_UINT32 uint32_t
|
||||||
|
#endif
|
||||||
|
|
||||||
|
#if !defined(RTR_UINT64)
|
||||||
|
#define RTR_UINT64 uint64_t
|
||||||
|
#endif
|
||||||
|
|
||||||
|
typedef RTR_INT32 rtr_int32;
|
||||||
|
typedef RTR_UINT32 rtr_uint32;
|
||||||
|
typedef RTR_UINT64 rtr_uint64;
|
||||||
|
|
||||||
|
/* Allocators.
|
||||||
|
*
|
||||||
|
* You can replace the allocation functions by defining RTR_MALLOC
|
||||||
|
* and RTR_FREE. Both take a void* context parameter, that is user defined
|
||||||
|
* and can be used by your functions however you need.
|
||||||
|
*/
|
||||||
|
#if !defined(RTR_MALLOC) && !defined(RTR_FREE)
|
||||||
|
|
||||||
|
#include <stdlib.h>
|
||||||
|
#define RTR_MALLOC(ctx, sz) malloc(sz)
|
||||||
|
#define RTR_FREE(ctx, ptr) free(ptr)
|
||||||
|
|
||||||
|
#elif !defined(RTR_MALLOC) || !defined(RTR_FREE)
|
||||||
|
#error "You must define RTR_MALLOC and RTR_FREE or none"
|
||||||
|
#endif
|
||||||
|
|
||||||
|
/* Memcpy & memset
|
||||||
|
*
|
||||||
|
* You can replace these by defining the macros below.
|
||||||
|
*/
|
||||||
|
#if !defined(RTR_MEMCPY) || !defined(RTR_MEMSET)
|
||||||
|
#include <string.h>
|
||||||
|
#endif
|
||||||
|
|
||||||
|
#if !defined(RTR_MEMCPY)
|
||||||
|
#define RTR_MEMCPY(dst, src, n) memcpy((void*)(dst), (const void*)(src), (size_t)(n))
|
||||||
|
#endif
|
||||||
|
|
||||||
|
#if !defined(RTR_MEMSET)
|
||||||
|
#define RTR_MEMSET(dst, val, n) memset((void*)(dst), (int)(val), (size_t)(n))
|
||||||
|
#endif
|
||||||
|
|
||||||
|
enum
|
||||||
|
{
|
||||||
|
RTR_SUCCESS,
|
||||||
|
RTR_ERROR_OUT_OF_MEMORY,
|
||||||
|
RTR_ERROR_NO_FRAME,
|
||||||
|
RTR_ERROR_INVALID_RECORDING,
|
||||||
|
};
|
||||||
|
|
||||||
|
enum
|
||||||
|
{
|
||||||
|
RTR_RECORDING_MODE_RECORD,
|
||||||
|
RTR_RECORDING_MODE_PLAYBACK,
|
||||||
|
};
|
||||||
|
|
||||||
|
enum
|
||||||
|
{
|
||||||
|
RTR_EVENT_TYPE_KEY,
|
||||||
|
RTR_EVENT_TYPE_ANALOG,
|
||||||
|
};
|
||||||
|
|
||||||
|
typedef struct rtr_record_frame rtr_record_frame;
|
||||||
|
|
||||||
|
/* A recording keeps a list of record frames, where
|
||||||
|
* each frame contains a timestamp and a number of input events.
|
||||||
|
*
|
||||||
|
* During replay, events are fed back to the game once the timestamp is reached.
|
||||||
|
*/
|
||||||
|
typedef struct
|
||||||
|
{
|
||||||
|
unsigned int mode;
|
||||||
|
double timestamp;
|
||||||
|
rtr_record_frame* frames;
|
||||||
|
rtr_record_frame* last_frame;
|
||||||
|
rtr_uint32 num_frames;
|
||||||
|
void* ctx;
|
||||||
|
} rtr_recording;
|
||||||
|
|
||||||
|
typedef struct
|
||||||
|
{
|
||||||
|
rtr_uint32 type;
|
||||||
|
union
|
||||||
|
{
|
||||||
|
struct
|
||||||
|
{
|
||||||
|
rtr_uint32 keycode;
|
||||||
|
rtr_uint32 state;
|
||||||
|
} key;
|
||||||
|
|
||||||
|
struct
|
||||||
|
{
|
||||||
|
rtr_uint32 axis;
|
||||||
|
double value;
|
||||||
|
} analog;
|
||||||
|
} data;
|
||||||
|
} rtr_event;
|
||||||
|
|
||||||
|
typedef struct
|
||||||
|
{
|
||||||
|
rtr_record_frame* frame;
|
||||||
|
int is_finished;
|
||||||
|
} rtr_playback_cursor;
|
||||||
|
|
||||||
|
/*
|
||||||
|
* Recording api
|
||||||
|
*/
|
||||||
|
|
||||||
|
void rtr_begin_recording(void* user_ctx, rtr_recording* recording);
|
||||||
|
|
||||||
|
int rtr_end_recording(rtr_recording* recording, void** buffer, rtr_uint64* buffer_size);
|
||||||
|
|
||||||
|
int rtr_frame(rtr_recording* recording, double delta);
|
||||||
|
|
||||||
|
int rtr_add_key_event(rtr_recording* recording, rtr_uint32 key, rtr_uint32 state);
|
||||||
|
|
||||||
|
int rtr_add_analog_event(rtr_recording* recording, rtr_uint32 axis, double value);
|
||||||
|
|
||||||
|
/*
|
||||||
|
* Playback api
|
||||||
|
*/
|
||||||
|
|
||||||
|
int rtr_open_recording(void* buffer, rtr_uint64 buffer_size, void* user_ctx, rtr_recording* recording);
|
||||||
|
|
||||||
|
void rtr_close_recording(rtr_recording* recording);
|
||||||
|
|
||||||
|
void rtr_playback_frame(rtr_recording* recording, double delta);
|
||||||
|
|
||||||
|
int rtr_get_events(rtr_recording* recording, unsigned int* num_events, rtr_event** events);
|
||||||
|
|
||||||
|
#endif
|
||||||
|
|
||||||
|
#define RT_REPLAY_IMPLEMENTATION
|
||||||
|
#ifdef RT_REPLAY_IMPLEMENTATION
|
||||||
|
#undef RT_REPLAY_IMPLEMENTATION
|
||||||
|
|
||||||
|
#include <stddef.h>
|
||||||
|
|
||||||
|
#define RTR_UNUSED(x) (void)(sizeof(x))
|
||||||
|
#define RTR_EVENTS_PER_FRAME 256
|
||||||
|
|
||||||
|
struct rtr_record_frame
|
||||||
|
{
|
||||||
|
double timestamp;
|
||||||
|
rtr_record_frame* next;
|
||||||
|
rtr_uint32 num_events;
|
||||||
|
rtr_event events[RTR_EVENTS_PER_FRAME];
|
||||||
|
};
|
||||||
|
|
||||||
|
#define RTR_MAKE_VERSION(major, minor, patch) (\
|
||||||
|
(((major) & 0xfff) << 20) | \
|
||||||
|
(((minor) & 0xfff) << 8) | \
|
||||||
|
((patch) & 0xff))
|
||||||
|
|
||||||
|
#define RTR_VERSION RTR_MAKE_VERSION(0, 1, 0)
|
||||||
|
|
||||||
|
typedef struct
|
||||||
|
{
|
||||||
|
rtr_uint32 version;
|
||||||
|
rtr_uint32 num_frames;
|
||||||
|
} rtr_buffer_header;
|
||||||
|
|
||||||
|
typedef struct
|
||||||
|
{
|
||||||
|
double rel_timestamp;
|
||||||
|
rtr_uint32 num_events;
|
||||||
|
rtr_event events[RTR_EVENTS_PER_FRAME];
|
||||||
|
} rtr_buffer_frame;
|
||||||
|
|
||||||
|
void rtr_begin_recording(void* user_ctx, rtr_recording* recording)
|
||||||
|
{
|
||||||
|
rtr_recording rec;
|
||||||
|
rec.mode = RTR_RECORDING_MODE_RECORD;
|
||||||
|
rec.timestamp = 0.0;
|
||||||
|
rec.num_frames = 0;
|
||||||
|
rec.frames = NULL;
|
||||||
|
rec.ctx = user_ctx;
|
||||||
|
|
||||||
|
*recording = rec;
|
||||||
|
}
|
||||||
|
|
||||||
|
int rtr_end_recording(rtr_recording* recording, void** buffer, rtr_uint64* buffer_size)
|
||||||
|
{
|
||||||
|
if (recording->mode != RTR_RECORDING_MODE_RECORD)
|
||||||
|
return RTR_ERROR_INVALID_RECORDING;
|
||||||
|
|
||||||
|
/* Count the frames */
|
||||||
|
rtr_int32 unseen_frames = (rtr_int32)recording->num_frames;
|
||||||
|
|
||||||
|
for (rtr_record_frame* frame = recording->frames; frame != NULL; frame = frame->next) {
|
||||||
|
--unseen_frames;
|
||||||
|
}
|
||||||
|
if (unseen_frames != 0)
|
||||||
|
return RTR_ERROR_INVALID_RECORDING;
|
||||||
|
|
||||||
|
rtr_uint64 req_size = sizeof(rtr_buffer_header) + sizeof(rtr_buffer_frame) * recording->num_frames;
|
||||||
|
|
||||||
|
char* buf = RTR_MALLOC(recording->ctx, req_size);
|
||||||
|
if (!buf)
|
||||||
|
return RTR_ERROR_OUT_OF_MEMORY;
|
||||||
|
|
||||||
|
rtr_buffer_header* hdr = (rtr_buffer_header*)buf;
|
||||||
|
hdr->version = RTR_VERSION;
|
||||||
|
hdr->num_frames = recording->num_frames;
|
||||||
|
rtr_buffer_frame* buf_frames = (rtr_buffer_frame*)(hdr + 1);
|
||||||
|
|
||||||
|
int i = 0;
|
||||||
|
rtr_record_frame* frame = recording->frames;
|
||||||
|
while (frame) {
|
||||||
|
buf_frames[i].rel_timestamp = frame->timestamp;
|
||||||
|
buf_frames[i].num_events = frame->num_events;
|
||||||
|
RTR_MEMCPY(buf_frames[i].events, frame->events, sizeof(rtr_event) * frame->num_events);
|
||||||
|
|
||||||
|
rtr_record_frame* tmp = frame;
|
||||||
|
frame = frame->next;
|
||||||
|
|
||||||
|
RTR_FREE(recording->ctx, tmp);
|
||||||
|
}
|
||||||
|
|
||||||
|
RTR_MEMSET(recording, 0, sizeof(recording));
|
||||||
|
|
||||||
|
*buffer = (void*)buf;
|
||||||
|
*buffer_size = req_size;
|
||||||
|
|
||||||
|
return RTR_SUCCESS;
|
||||||
|
}
|
||||||
|
|
||||||
|
int rtr_frame(rtr_recording* recording, double delta)
|
||||||
|
{
|
||||||
|
rtr_record_frame* frame = (rtr_record_frame*)RTR_MALLOC(recording->ctx, sizeof(rtr_record_frame));
|
||||||
|
if (!frame) {
|
||||||
|
return RTR_ERROR_OUT_OF_MEMORY;
|
||||||
|
}
|
||||||
|
recording->timestamp += delta;
|
||||||
|
frame->num_events = 0;
|
||||||
|
frame->next = NULL;
|
||||||
|
frame->timestamp = recording->timestamp;
|
||||||
|
|
||||||
|
if (recording->last_frame)
|
||||||
|
recording->last_frame->next = frame;
|
||||||
|
recording->last_frame = frame;
|
||||||
|
++recording->num_frames;
|
||||||
|
return RTR_SUCCESS;
|
||||||
|
}
|
||||||
|
|
||||||
|
static int rtr_add_event(rtr_recording* recording, rtr_event event)
|
||||||
|
{
|
||||||
|
if (!recording->last_frame)
|
||||||
|
return RTR_ERROR_NO_FRAME;
|
||||||
|
rtr_record_frame* frame = recording->last_frame;
|
||||||
|
|
||||||
|
if (frame->num_events < RTR_EVENTS_PER_FRAME) {
|
||||||
|
frame->events[frame->num_events++] = event;
|
||||||
|
return RTR_SUCCESS;
|
||||||
|
}
|
||||||
|
else {
|
||||||
|
/* add a new "extended" frame with the same timestamp */
|
||||||
|
rtr_record_frame* ext_frame = (rtr_record_frame*)RTR_MALLOC(recording->ctx, sizeof(rtr_record_frame));
|
||||||
|
if (!ext_frame)
|
||||||
|
return RTR_ERROR_OUT_OF_MEMORY;
|
||||||
|
ext_frame->timestamp = frame->timestamp;
|
||||||
|
ext_frame->next = NULL;
|
||||||
|
ext_frame->events[0] = event;
|
||||||
|
ext_frame->num_events = 1;
|
||||||
|
|
||||||
|
frame->next = ext_frame;
|
||||||
|
recording->last_frame = ext_frame;
|
||||||
|
return RTR_SUCCESS;
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
int rtr_add_key_event(rtr_recording* recording, rtr_uint32 key, rtr_uint32 state)
|
||||||
|
{
|
||||||
|
rtr_event event;
|
||||||
|
event.type = RTR_EVENT_TYPE_KEY;
|
||||||
|
event.data.key.keycode = key;
|
||||||
|
event.data.key.state = state;
|
||||||
|
return rtr_add_event(recording, event);
|
||||||
|
}
|
||||||
|
|
||||||
|
int rtr_add_analog_event(rtr_recording* recording, rtr_uint32 axis, double value)
|
||||||
|
{
|
||||||
|
rtr_event event;
|
||||||
|
event.type = RTR_EVENT_TYPE_ANALOG;
|
||||||
|
event.data.analog.axis = axis;
|
||||||
|
event.data.analog.value = value;
|
||||||
|
return rtr_add_event(recording, event);
|
||||||
|
}
|
||||||
|
|
||||||
|
int rtr_open_recording(void* buffer, rtr_uint64 buffer_size, void* user_ctx, rtr_recording* recording)
|
||||||
|
{
|
||||||
|
rtr_buffer_header* hdr = (rtr_buffer_header*)buffer;
|
||||||
|
if (hdr->version != RTR_VERSION) {
|
||||||
|
/* because this is the only valid version, this check suffices.
|
||||||
|
* in the future, we would check if the buffer contains a version
|
||||||
|
* we recognize */
|
||||||
|
return RTR_ERROR_INVALID_RECORDING;
|
||||||
|
}
|
||||||
|
|
||||||
|
if (((hdr->num_frames * sizeof(rtr_buffer_frame)) + sizeof(hdr)) > buffer_size) {
|
||||||
|
/* something went wrong */
|
||||||
|
return RTR_ERROR_INVALID_RECORDING;
|
||||||
|
}
|
||||||
|
|
||||||
|
rtr_buffer_frame* buf_frames = (rtr_buffer_frame*)(hdr + 1);
|
||||||
|
|
||||||
|
rtr_record_frame* frames = RTR_MALLOC(user_ctx, sizeof(rtr_record_frame) * hdr->num_frames);
|
||||||
|
for (unsigned int i = 0; i < hdr->num_frames; ++i) {
|
||||||
|
frames[i].next = (i < hdr->num_frames - 1) ? &(frames[i + 1]) : NULL;
|
||||||
|
frames[i].num_events = buf_frames[i].num_events;
|
||||||
|
frames[i].timestamp = buf_frames[i].rel_timestamp;
|
||||||
|
RTR_MEMCPY(frames[i].events, buf_frames[i].events, buf_frames[i].num_events * sizeof(rtr_event));
|
||||||
|
}
|
||||||
|
|
||||||
|
recording->frames = frames;
|
||||||
|
recording->last_frame = &frames[hdr->num_frames - 1];
|
||||||
|
recording->mode = RTR_RECORDING_MODE_PLAYBACK;
|
||||||
|
recording->num_frames = hdr->num_frames;
|
||||||
|
recording->timestamp = 0.0;
|
||||||
|
recording->ctx = user_ctx;
|
||||||
|
|
||||||
|
return RTR_SUCCESS;
|
||||||
|
}
|
||||||
|
|
||||||
|
void rtr_close_recording(rtr_recording* recording)
|
||||||
|
{
|
||||||
|
if (recording->mode != RTR_RECORDING_MODE_PLAYBACK)
|
||||||
|
return;
|
||||||
|
RTR_FREE(recording->ctx, recording->frames);
|
||||||
|
RTR_MEMSET(recording, 0, sizeof(recording));
|
||||||
|
}
|
||||||
|
|
||||||
|
void rtr_playback_frame(rtr_recording* recording, double delta)
|
||||||
|
{
|
||||||
|
|
||||||
|
}
|
||||||
|
|
||||||
|
int rtr_get_events(rtr_recording* recording, unsigned int* num_events, rtr_event** events);
|
||||||
|
|
||||||
|
#endif
|
Loading…
Reference in New Issue
Block a user