diff --git a/src/program/GameEvents.cpp b/src/program/GameEvents.cpp index 407b126d..45a6a26a 100644 --- a/src/program/GameEvents.cpp +++ b/src/program/GameEvents.cpp @@ -211,6 +211,7 @@ bool GameEvents::processEvent(GameEvents::EventType type, const HotKey &hk) * Prompting a alert window must be done by the UI thread, so we are * using std::future/std::promise mechanism. */ + int frame_to_seek; std::promise answer; std::future future = answer.get_future(); emit askToShow(QString("There is a savestate in that slot from a previous game iteration. Do you want to load the associated movie?"), &answer); @@ -220,17 +221,25 @@ bool GameEvents::processEvent(GameEvents::EventType type, const HotKey &hk) return false; } - /* Loading the movie */ - movie->loadSavestateMovie(SaveStateList::get(statei).getMoviePath()); + SaveState& s = SaveStateList::get(statei); + movie->inputs = s.movie->inputs; + s.save(context, *movie); - /* Return if we already are on the correct frame */ - if (context->framecount == movie->header->savestate_framecount) - return false; + /* seek to either the current frame, or the max frame + * of the saved inputs */ + if (s.movie->inputs->nbFrames() < context->framecount) { + frame_to_seek = context->framecount; + } else { + frame_to_seek = s.movie->inputs->nbFrames(); + } + + /* Find where movies diverge, load state and seek back there */ + // todo /* Fast-forward to savestate frame */ context->config.sc.recording = SharedConfig::RECORDING_READ; - context->config.sc.movie_framecount = movie->inputs->nbFrames(); - context->seek_frame = movie->header->savestate_framecount; + context->config.sc.movie_framecount = s.movie->inputs->nbFrames(); + context->seek_frame = frame_to_seek; context->config.sc.running = true; context->config.sc_modified = true; @@ -239,6 +248,12 @@ bool GameEvents::processEvent(GameEvents::EventType type, const HotKey &hk) return false; } + if (error == SaveState::ESAVESTATEINPUTMISMATCH) { + if (!(context->config.sc.osd)) + emit alertToShow(QString("Cannot load inputs from this savestate as earlier inputs mismatch. Please seek to an earlier state for this.")); + return false; + } + if (error == SaveState::ENOSTATE) { if (!(context->config.sc.osd)) emit alertToShow(QString("There is no savestate to load in this slot")); diff --git a/src/program/SaveState.cpp b/src/program/SaveState.cpp index 019d23da..41802117 100644 --- a/src/program/SaveState.cpp +++ b/src/program/SaveState.cpp @@ -120,26 +120,24 @@ int SaveState::load(Context* context, const MovieFile& m, bool branch, bool inpu * forked savestate of previous execution). */ if ((access(pagemap_path.c_str(), F_OK) != 0) || (access(pages_path.c_str(), F_OK) != 0) || (framecount == 0)) { - /* If there is no savestate but a movie file, offer to load - * the movie and fast-forward to the savestate movie frame. + /* If there is no savestate but inputs are saved in the save + * file, offer to load the movie and fast-forward to the + * savestate movie frame. */ - if ((context->config.sc.recording != SharedConfig::NO_RECORDING) && - (access(movie_path.c_str(), F_OK) == 0)) { - - /* Load the savestate movie from disk */ - MovieFile savedmovie(context); - int ret = savedmovie.loadSavestateMovie(movie_path); - - /* Checking if our movie is a prefix of the savestate movie */ - if ((ret == 0) && savedmovie.inputs->isEqual(m.inputs, 0, context->framecount)) { - return ENOSTATEMOVIEPREFIX; - } + /* Checking if our movie is a prefix of the savestate movie + * and if savestatefile input exists */ + if (movie->inputs->nbFrames() > 0 && movie->inputs->isEqual(m.inputs, 0, context->framecount)) { + return ENOSTATEMOVIEPREFIX; + } else if (movie->inputs->nbFrames() > 0) { + sendMessage(MSGN_OSD_MSG); + sendString(std::string("Cannot load inputs from this savestate as earlier inputs mismatch")); + return ESAVESTATEINPUTMISMATCH; + } else { + sendMessage(MSGN_OSD_MSG); + sendString(no_state_msg); + return ENOSTATE; } - - sendMessage(MSGN_OSD_MSG); - sendString(no_state_msg); - return ENOSTATE; } /* Send the savestate index */ diff --git a/src/program/SaveState.h b/src/program/SaveState.h index 38d2a2d3..f4dc2dc4 100644 --- a/src/program/SaveState.h +++ b/src/program/SaveState.h @@ -41,6 +41,7 @@ class SaveState { ENOMOVIE = -3, // Could not moad movie EINPUTMISMATCH = -4, // Mistmatch inputs ENOLOAD = -5, // State loading failed + ESAVESTATEINPUTMISMATCH = -6, // Mismatch inputs with savestate file }; /* Savestate number */ diff --git a/src/program/movie/MovieFile.cpp b/src/program/movie/MovieFile.cpp index 837aa3c4..6dac9d6e 100644 --- a/src/program/movie/MovieFile.cpp +++ b/src/program/movie/MovieFile.cpp @@ -21,9 +21,14 @@ #include "../shared/inputs/AllInputs.h" #include "Context.h" +#include "SaveStateList.h" +#include "SaveState.h" #include #include +#include +#include +#include #include // O_RDONLY, O_WRONLY, O_CREAT #include #include @@ -84,6 +89,10 @@ int MovieFile::extractMovie(const std::string& moviefile) unlink(configfile.c_str()); unlink(editorfile.c_str()); unlink(inputfile.c_str()); + for (int i = 1; i <= 10; i++) { + std::filesystem::path p = std::filesystem::path(context->config.tempmoviedir) / ("inputs" + std::to_string(i)); + unlink(p.c_str()); + } unlink(annotationsfile.c_str()); /* Build the tar command */ @@ -143,6 +152,20 @@ int MovieFile::loadMovie(const std::string& moviefile) context->config.sc.movie_framecount = inputs->nbFrames(); } + /* Load savestate inputs */ + for (int i = 1; i <= 10; i++) { + std::filesystem::path p = std::filesystem::path(context->config.tempmoviedir) / ("inputs" + std::to_string(i)); + + if (std::filesystem::exists(p)) { + SaveState& s = SaveStateList::get(i); + if (!s.movie) + s.movie = std::make_unique(context); + + s.movie->inputs->load(i); + s.framecount = s.movie->inputs->nbFrames(); + } + } + return 0; } @@ -188,6 +211,13 @@ int MovieFile::saveMovie(const std::string& moviefile, uint64_t nb_frames) oss << "\" -C "; oss << context->config.tempmoviedir; oss << " inputs config.ini editor.ini annotations.txt"; + // Add only savestate input files that exist + for (int i = 1; i <= 10; i++) { + std::filesystem::path p = std::filesystem::path(context->config.tempmoviedir) / ("inputs" + std::to_string(i)); + if (std::filesystem::exists(p)) { + oss << " inputs" << i; + } + } /* Execute the tar command */ // std::cout << oss.str() << std::endl; diff --git a/src/program/movie/MovieFileInputs.cpp b/src/program/movie/MovieFileInputs.cpp index 970498f1..fb25cc8c 100644 --- a/src/program/movie/MovieFileInputs.cpp +++ b/src/program/movie/MovieFileInputs.cpp @@ -25,6 +25,8 @@ #include "MovieActionInsertFrames.h" #include "MovieActionPaint.h" #include "MovieActionRemoveFrames.h" +#include "SaveStateList.h" +#include "SaveState.h" #include "utils.h" #include "Context.h" @@ -74,7 +76,7 @@ void MovieFileInputs::clear() emit inputsReset(); } -void MovieFileInputs::load() +void MovieFileInputs::load(int savestate) { emit inputsToBeReset(); @@ -87,6 +89,9 @@ void MovieFileInputs::load() /* Open the input file and parse each line to fill our input list */ std::string input_file = context->config.tempmoviedir + "/inputs"; + if (savestate > 0) { + input_file += std::to_string(savestate); + } std::ifstream input_stream(input_file); InputSerialization::readInputs(input_stream, input_list); @@ -105,8 +110,19 @@ void MovieFileInputs::save() std::ofstream input_stream(input_file, std::ofstream::trunc); InputSerialization::writeInputs(input_stream, input_list); - input_stream.close(); + + // Save branches inputs + for (int i = 0; i <= 10; i++) { + SaveState& s = SaveStateList::get(i); + if (s.framecount != 0 && s.movie && s.movie->inputs) { + std::string branch_input_file = context->config.tempmoviedir + "/inputs" + std::to_string(i); + std::ofstream branch_stream(branch_input_file, std::ofstream::trunc); + InputSerialization::writeInputs(branch_stream, s.movie->inputs->input_list); + branch_stream.close(); + } + } + } uint64_t MovieFileInputs::nbFrames() diff --git a/src/program/movie/MovieFileInputs.h b/src/program/movie/MovieFileInputs.h index 48b94699..7f84a176 100644 --- a/src/program/movie/MovieFileInputs.h +++ b/src/program/movie/MovieFileInputs.h @@ -73,7 +73,7 @@ class MovieFileInputs : public QObject { /* Import the inputs into a list, and all the parameters. * Returns 0 if no error, or a negative value if an error occured */ - void load(); + void load(int savestate = 0); /* Write the inputs into a file and compress to the whole moviefile */ void save();