Skip to content

Commit

Permalink
Change VCR api to be usable by frontend
Browse files Browse the repository at this point in the history
  • Loading branch information
Madghostek committed Oct 16, 2023
1 parent eba91d9 commit 1af01d1
Show file tree
Hide file tree
Showing 3 changed files with 102 additions and 48 deletions.
94 changes: 66 additions & 28 deletions src/VCR/VCR.cpp
Original file line number Diff line number Diff line change
Expand Up @@ -41,6 +41,12 @@ static m64p_vcr_state VCR_state = M64VCR_IDLE;
static bool VCR_readonly;
static unsigned curSample = 0; //doesnt account in for multiple controllers, used as a pointer for vector
static unsigned curVI = 0; //keeps track of VIs in a movie

struct {
BUTTONS buttons[4] {};
bool channelHasOverlay[4] {}; // no clue what the compiler will do here

} overlay;
//@TODO: add VCR_AdvanceVI();

BOOL DefaultErr(m64p_msg_level, const char*);
Expand Down Expand Up @@ -177,35 +183,66 @@ void VCR_StopMovie(BOOL restart)
}
}

//@TODO: test this
void VCR_SetKeys(BUTTONS keys, unsigned channel)
{
if (curSample >= gMovieBuffer->size())
{
gMovieBuffer->resize(gMovieBuffer->size() + BUFFER_GROWTH); //impending doom approaches...
}
(*gMovieBuffer)[curSample++].Value = keys.Value;
gMovieHeader->length_samples = curSample;
gMovieHeader->length_vis = -1;
void VCR_SetOverlay(BUTTONS keys, unsigned channel) {
if (VCR_IsPlaying() && !VCR_IsReadOnly())
(*gMovieBuffer)[curSample].Value = keys.Value;
else
overlay.buttons[channel] = keys;
}

//@TODO: add reset input detection (reserved1 and 2 true pressed, everything else not pressed)
BOOL VCR_GetKeys(BUTTONS* keys, unsigned channel)
{
//gives out inputs for channels that are present in m64 header
//less bloated than using the flags itself
if (gMovieHeader->cFlags.present & (1 << channel))
{
//check if there are frames left
//curSample is 0 indexed, so we must >= with size
if (curSample >= gMovieBuffer->size())
{
VCR_StopMovie(false);
return true;
}
keys->Value = (*gMovieBuffer)[curSample++].Value; //get the value, then advance frame
}
return false;
//@TODO: add reset input detection (reserved1 and 2 true pressed, everything
// else not pressed)
BOOL VCR_GetKeys(BUTTONS* keys, unsigned channel) {
if (overlay.channelHasOverlay[channel])
keys->Value = overlay.buttons[channel].Value;
else {
if (VCR_IsPlaying()) {
if (gMovieHeader->cFlags.present & (1 << channel))
keys->Value = (*gMovieBuffer)[curSample].Value;
}
else if (input.getKeys) {
// nothing to do, just read input plugin
input.getKeys(channel, keys);
return false;
}
else {
// input plugin doesn't exist
keys->Value = 0;
return false;
}
}
// tell outer that this button data is final.
return true;
}

void VCR_ResetOverlay() {
for (unsigned i = 0; i < 4; ++i) {
overlay.buttons[i].Value = 0;
overlay.channelHasOverlay[i] = false;
}
}

unsigned int VCR_AdvanceFrame() {
if (!VCR_IsPlaying())
return 0; // no movie

VCR_ResetOverlay();
++curSample;
// check if there are frames left
// curSample is 0 indexed, so we must >= with size
if (curSample >= gMovieBuffer->size()) {
if (VCR_IsReadOnly()) {
VCR_StopMovie(false);
return 0; // movie ended
}
else {
gMovieBuffer->resize(gMovieBuffer->size() + BUFFER_GROWTH);
gMovieHeader->length_samples = curSample;
gMovieHeader->length_vis = -1; // legacy m64 thing
return curSample;
}
}
return curSample;
}

BOOL VCR_IsPlaying()
Expand All @@ -223,7 +260,8 @@ BOOL VCR_SetReadOnly(BOOL state)
VCR_Message(M64MSG_INFO, state ? "Read only" : "Read write");
if (VCR_StateCallback)
VCR_StateCallback(M64VCRP_READONLY, state);
return VCR_readonly = state;

return VCR_readonly = state;
}

unsigned VCR_GetCurFrame()
Expand Down
27 changes: 22 additions & 5 deletions src/api/m64p_vcr.h
Original file line number Diff line number Diff line change
Expand Up @@ -80,19 +80,24 @@ typedef void (*ptr_VCR_StopMovie)(BOOL restart);
EXPORT void CALL VCR_StopMovie(BOOL restart);

/// <summary>
/// pastes given keys to current frame, then advances frame. If input buffer is too small it resizes itself
/// Sets the next input received by mupen to `keys`, overlaying the normal
/// plugin input/m64 frame.
/// This is the primary way of writing to m64 when movie
/// is being written. When movie is read-only, the inputs are still being
/// overlaid, but will not be written to file.
/// </summary>
/// <param name="keys">keys describing frame data</param>
/// <param name="channel">controller id</param>
typedef void (*ptr_VCR_SetKeys)(BUTTONS keys, unsigned channel);
EXPORT void CALL VCR_SetKeys(BUTTONS keys, unsigned channel);
typedef void (*ptr_VCR_SetOverlay)(BUTTONS keys, unsigned channel);
EXPORT void CALL VCR_SetOverlay(BUTTONS keys, unsigned channel);

/// <summary>
/// Pastes next frame to keys. If this was last frame, stops m64 playback. There always is a frame if VCR is not idle
/// Writes to `keys` whatever the emulator should receive as input on next
/// frame. If a movie is being read, it will be next frame from movie, otherwise
/// plugin input.
/// </summary>
/// <param name="keys">place where to paste inputs</param>
/// <param name="channel">controller id</param>
/// <returns>true if movie ended</returns>
typedef BOOL (*ptr_VCR_GetKeys)(BUTTONS* keys, unsigned channel);
EXPORT BOOL CALL VCR_GetKeys(BUTTONS* keys, unsigned channel);

Expand All @@ -103,6 +108,18 @@ EXPORT BOOL CALL VCR_GetKeys(BUTTONS* keys, unsigned channel);
typedef BOOL (*ptr_VCR_IsPlaying)(void);
EXPORT BOOL CALL VCR_IsPlaying();

/// <summary>
/// if VCR is active, advances frame counter.
/// If movie is being recorded, also writes current overlay (set with
/// VCR_SetOverlay) to m64 buffer otherwise does nothing.
/// </summary>
/// <returns>frame number, or 0 if vcr is idle</returns>
typedef unsigned int (*ptr_VCR_AdvanceFrame)(void);
EXPORT unsigned int CALL VCR_AdvanceFrame();

typedef void (*ptr_VCR_ResetOverlay)(void);
EXPORT void CALL VCR_ResetOverlay();

/// <summary>
/// Checks if readonly is true. Note: the value doesn't make sense if VCR is idle, it doesn't inform whether a movie is actually playing
/// </summary>
Expand Down
29 changes: 14 additions & 15 deletions src/backends/plugins_compat/input_plugin_compat.c
Original file line number Diff line number Diff line change
Expand Up @@ -79,19 +79,12 @@ static m64p_error input_plugin_get_input(void* opaque, uint32_t* input_, int is_

int pak_change_requested = 0;

/* first poll controller */
#ifdef VCR_SUPPORT
//this looks werid, but we want to recover from GetKeys fails
if (!(VCR_IsPlaying() && VCR_IsReadOnly() && !is_test && !VCR_GetKeys(&keys, cin_compat->control_id)))
{
// if gotData, don't try to overwrite keys further, it's final
BOOL gotData = VCR_GetKeys(&keys, cin_compat->control_id);
if (!gotData) {
#endif
if (!netplay_is_init())
{
if (input.getKeys)
input.getKeys(cin_compat->control_id, &keys);
}
else
{
if (netplay_is_init()) {
int netplay_controller = netplay_get_controller(cin_compat->control_id);
if (netplay_controller >= 0)
{
Expand All @@ -109,6 +102,10 @@ static m64p_error input_plugin_get_input(void* opaque, uint32_t* input_, int is_
}
}
#ifdef VCR_SUPPORT
else {
if (input.getKeys)
input.getKeys(cin_compat->control_id, &keys);
}
}
#endif

Expand Down Expand Up @@ -161,11 +158,13 @@ static m64p_error input_plugin_get_input(void* opaque, uint32_t* input_, int is_

*input_ = keys.Value;
#ifdef VCR_SUPPORT
if (VCR_IsPlaying() && !VCR_IsReadOnly() && !is_test)
{
VCR_SetKeys(keys, cin_compat->control_id);
}

VCR_SetOverlay(keys, cin_compat->control_id);
VCR_AdvanceFrame();
VCR_ResetOverlay();

#endif

return M64ERR_SUCCESS;
}

Expand Down

0 comments on commit 1af01d1

Please sign in to comment.