Skip to content

Commit

Permalink
Implement greenzone using existing savestates (#134)
Browse files Browse the repository at this point in the history
  • Loading branch information
clementgallet committed Dec 8, 2020
1 parent 83fc275 commit eb31114
Show file tree
Hide file tree
Showing 8 changed files with 163 additions and 18 deletions.
1 change: 1 addition & 0 deletions CHANGELOG.md
Original file line number Diff line number Diff line change
Expand Up @@ -29,6 +29,7 @@
* Check for shared config size between program and library
* Closing the game window takes effect even when paused (#65)
* Can remove multiple ram watches
* Implement greenzone using existing savestates (#134)

### Changed

Expand Down
5 changes: 5 additions & 0 deletions src/program/SaveState.cpp
Original file line number Diff line number Diff line change
Expand Up @@ -128,6 +128,11 @@ int SaveState::save(Context* context, MovieFile& movie)

/* Checking that saving succeeded */
int message = receiveMessage();

/* Set framecount */
if (message == MSGB_SAVING_SUCCEEDED)
framecount = context->framecount;

return message;
}

Expand Down
3 changes: 3 additions & 0 deletions src/program/SaveState.h
Original file line number Diff line number Diff line change
Expand Up @@ -47,6 +47,9 @@ class SaveState {
/* Id of parent savestate, or -1 if no parent */
int parent;

/* Frame count of the savestate */
uint64_t framecount;

/* Return the savestate movie path */
const std::string& getMoviePath();

Expand Down
47 changes: 46 additions & 1 deletion src/program/SaveStateList.cpp
Original file line number Diff line number Diff line change
Expand Up @@ -31,6 +31,9 @@ static SaveState states[NB_STATES];
/* Id of last loaded or saved savestate */
static int last_state_id = -1;

/* Old id of root savestate */
static int old_root_state_id = -1;

SaveState& SaveStateList::get(int id)
{
if (id < 0 || id > NB_STATES) {
Expand All @@ -50,6 +53,8 @@ int SaveStateList::save(int id, Context* context, MovieFile& movie)
int message = ss.save(context, movie);

if (message == MSGB_SAVING_SUCCEEDED) {
/* Update root savestate */
old_root_state_id = rootStateFramecount();

/* Update parent of every child to its grandparent */
for (int cid = 0; cid < NB_STATES; cid++) {
Expand All @@ -59,7 +64,7 @@ int SaveStateList::save(int id, Context* context, MovieFile& movie)
states[cid].parent = ss.parent;
}

/* Update parent of savestate */
/* Update parent of savestate */
if (id != last_state_id)
ss.parent = last_state_id;

Expand All @@ -81,8 +86,48 @@ int SaveStateList::postLoad(int id, Context* context, MovieFile& movie, bool bra
int message = ss.postLoad(context, movie, branch);

if (message == MSGB_LOADING_SUCCEEDED) {
/* Update root savestate */
old_root_state_id = rootStateFramecount();

last_state_id = id;
}

return message;
}

int64_t SaveStateList::rootStateFramecount()
{
if (last_state_id == -1)
return -1;

int parent_id = last_state_id;
uint64_t framecount;

while (parent_id != -1) {
framecount = states[parent_id].framecount;
parent_id = states[parent_id].parent;
}

return framecount;
}

int64_t SaveStateList::oldRootStateFramecount()
{
return old_root_state_id;
}

int SaveStateList::nearestState(uint64_t framecount)
{
if (last_state_id == -1)
return -1;

int parent_id = last_state_id;

while (parent_id != -1) {
if (states[parent_id].framecount <= framecount)
return parent_id;
parent_id = states[parent_id].parent;
}

return -1;
}
10 changes: 10 additions & 0 deletions src/program/SaveStateList.h
Original file line number Diff line number Diff line change
Expand Up @@ -37,6 +37,16 @@ namespace SaveStateList {

/* Process after loading state from its id and handle parent */
int postLoad(int id, Context* context, MovieFile& movie, bool branch);

/* Returns the framecount of the root state, or -1 if already root */
int64_t rootStateFramecount();

/* Returns the previous framecount of the root state */
int64_t oldRootStateFramecount();

/* Returns the nearest state id in current branch before framecount */
int nearestState(uint64_t framecount);

}

#endif
104 changes: 88 additions & 16 deletions src/program/ui/InputEditorModel.cpp
Original file line number Diff line number Diff line change
Expand Up @@ -22,11 +22,13 @@
#include <QGuiApplication>
#include <QPalette>
#include <QFont>
#include <QThread>
#include <sstream>

#include <iostream>
#include <set>

#include "InputEditorModel.h"
#include "../SaveStateList.h"

InputEditorModel::InputEditorModel(Context* c, MovieFile* m, QObject *parent) : QAbstractTableModel(parent), context(c), movie(m)
{
Expand All @@ -51,8 +53,16 @@ Qt::ItemFlags InputEditorModel::flags(const QModelIndex &index) const
if (index.column() < 2)
return QAbstractItemModel::flags(index);

if (index.row() < static_cast<int>(context->framecount))
return QAbstractItemModel::flags(index);
/* Don't toggle past inputs before root savestate */
uint64_t root_frame = SaveStateList::rootStateFramecount();
if (root_frame == -1) {
if (index.row() < static_cast<int>(context->framecount))
return QAbstractItemModel::flags(index);
}
else {
if (index.row() < root_frame)
return QAbstractItemModel::flags(index);
}

const SingleInput si = movie->input_set[index.column()-2];

Expand Down Expand Up @@ -95,14 +105,6 @@ QVariant InputEditorModel::data(const QModelIndex &index, int role) const
}

if (role == Qt::BackgroundRole) {
/* Return white-ish for future inputs */
// if (index.row() > static_cast<int>(context->framecount))
// return QBrush(QColor(0xff, 0xfe, 0xee));

/* Return white-ish for savestate column */
// if (index.column() == 0)
// return QBrush(QColor(0xff, 0xfe, 0xee));

/* Main color */
QColor color = QGuiApplication::palette().window().color();
int r, g, b;
Expand Down Expand Up @@ -140,6 +142,13 @@ QVariant InputEditorModel::data(const QModelIndex &index, int role) const
}
}
}

/* Greenzone */
if (index.row() < static_cast<int>(context->framecount)) {
int root_frame = SaveStateList::rootStateFramecount();
if (root_frame != -1 && index.row() >= root_frame)
color = color.darker(105);
}

/* Frame containing a savestate */
for (unsigned int i=0; i<savestate_frames.size(); i++) {
Expand Down Expand Up @@ -220,8 +229,12 @@ bool InputEditorModel::setData(const QModelIndex &index, const QVariant &value,
if (index.column() < 2)
return false;

if (index.row() < static_cast<int>(context->framecount))
return false;
/* Rewind to past frame is needed */
if (index.row() < static_cast<int>(context->framecount)) {
bool ret = rewind(index.row());
if (!ret)
return false;
}

const SingleInput si = movie->input_set[index.column()-2];

Expand Down Expand Up @@ -283,9 +296,16 @@ bool InputEditorModel::toggleInput(const QModelIndex &index)
if (index.column() < 2)
return false;

/* Don't toggle past inputs */
if (index.row() < static_cast<int>(context->framecount))
return false;
/* Don't toggle past inputs before root savestate */
uint64_t root_frame = SaveStateList::rootStateFramecount();
if (root_frame == -1) {
if (index.row() < static_cast<int>(context->framecount))
return false;
}
else {
if (index.row() < root_frame)
return false;
}

SingleInput si = movie->input_set[index.column()-2];

Expand All @@ -297,6 +317,13 @@ bool InputEditorModel::toggleInput(const QModelIndex &index)
if (index.row() == movie->input_list.size()) {
insertRows(movie->input_list.size(), 1, QModelIndex());
}

/* Rewind to past frame is needed */
if (index.row() < static_cast<int>(context->framecount)) {
bool ret = rewind(index.row());
if (!ret)
return false;
}

AllInputs &ai = movie->input_list[index.row()];

Expand Down Expand Up @@ -697,6 +724,18 @@ void InputEditorModel::registerSavestate(int slot, unsigned long long frame)
last_savestate = savestate_frames[slot];
emit dataChanged(createIndex(old_savestate,0), createIndex(old_savestate,0));
emit dataChanged(createIndex(last_savestate,0), createIndex(last_savestate,0));

/* Update greenzone between old and new root savestate */
int64_t oldRoot = SaveStateList::oldRootStateFramecount();
int64_t newRoot = SaveStateList::rootStateFramecount();

if (oldRoot == -1 || newRoot == -1)
return;

if (oldRoot < newRoot)
emit dataChanged(createIndex(oldRoot,0), createIndex(newRoot,columnCount()));
else
emit dataChanged(createIndex(newRoot,0), createIndex(oldRoot,columnCount()));
}

void InputEditorModel::moveInputs(int oldIndex, int newIndex)
Expand All @@ -705,3 +744,36 @@ void InputEditorModel::moveInputs(int oldIndex, int newIndex)
movie->input_set.erase(movie->input_set.begin() + oldIndex);
movie->input_set.insert(movie->input_set.begin() + newIndex, si);
}

bool InputEditorModel::rewind(uint64_t framecount)
{
if (framecount >= context->framecount)
return false;

int state = SaveStateList::nearestState(framecount);
if (state == -1)
return false;

/* Switch to playback if needed */
int recording = context->config.sc.recording;
if (recording == SharedConfig::RECORDING_WRITE) {
context->hotkey_pressed_queue.push(HOTKEY_READWRITE);
}

/* Load state */
context->hotkey_pressed_queue.push(HOTKEY_LOADSTATE1 + (state-1));

/* Fast-forward to frame */
context->pause_frame = framecount;

context->hotkey_pressed_queue.push(HOTKEY_FASTFORWARD);
if (!context->config.sc.running)
context->hotkey_pressed_queue.push(HOTKEY_PLAYPAUSE);

/* Switch back */
if (recording == SharedConfig::RECORDING_WRITE) {
context->hotkey_pressed_queue.push(HOTKEY_READWRITE);
}

return true;
}
3 changes: 3 additions & 0 deletions src/program/ui/InputEditorModel.h
Original file line number Diff line number Diff line change
Expand Up @@ -103,6 +103,9 @@ class InputEditorModel : public QAbstractTableModel {
/* User moved a column */
void moveInputs(int oldIndex, int newIndex);

/* Rewind to frame, return if succeeded */
bool rewind(uint64_t framecount);

public slots:
/* Toggle a single input and return the new value */
bool toggleInput(const QModelIndex &index);
Expand Down
8 changes: 7 additions & 1 deletion src/program/ui/InputEditorView.cpp
Original file line number Diff line number Diff line change
Expand Up @@ -243,14 +243,20 @@ void InputEditorView::mousePressEvent(QMouseEvent *event)
return QTableView::mousePressEvent(event);
}

if (index.column() < 2) {
if (index.column() == 1) {
return QTableView::mousePressEvent(event);
}

selectionModel()->clear();
mouseSection = index.column();
mouseRow = index.row();

/* Rewind when clicking for column */
if (mouseSection == 0) {
inputEditorModel->rewind(mouseRow);
return;
}

/* For editable items, copy the value. Else, copy the opposite value */
if (inputEditorModel->flags(index) & Qt::ItemIsEditable) {
mouseValue = inputEditorModel->data(index, Qt::EditRole).toInt(nullptr);
Expand Down

0 comments on commit eb31114

Please sign in to comment.