From 1a9b6f109a7a723abc46b52f4a28151aed07363e Mon Sep 17 00:00:00 2001 From: ansstuff Date: Thu, 20 Oct 2011 13:00:34 +0000 Subject: [PATCH] * Tasedit: refactoring, bugfixes * Tasedit: storing Markers in input history, undo/redo for Markers --- src/drivers/win/tasedit.cpp | 73 ++++--- src/drivers/win/tasedit.h | 1 + src/drivers/win/taseditlib/greenzone.cpp | 62 ++++-- src/drivers/win/taseditlib/greenzone.h | 2 + src/drivers/win/taseditlib/inputhistory.cpp | 197 ++++++++++++------- src/drivers/win/taseditlib/inputhistory.h | 10 +- src/drivers/win/taseditlib/inputsnapshot.cpp | 124 +++++++++--- src/drivers/win/taseditlib/inputsnapshot.h | 13 ++ src/drivers/win/taseditlib/markers.cpp | 33 +++- src/drivers/win/taseditlib/markers.h | 2 + src/drivers/win/taseditlib/playback.cpp | 3 +- src/drivers/win/taseditlib/taseditproj.cpp | 35 +++- src/drivers/win/window.cpp | 3 + src/fceu.cpp | 2 +- 14 files changed, 389 insertions(+), 171 deletions(-) diff --git a/src/drivers/win/tasedit.cpp b/src/drivers/win/tasedit.cpp index 42d57297..ae2cd6c1 100644 --- a/src/drivers/win/tasedit.cpp +++ b/src/drivers/win/tasedit.cpp @@ -158,7 +158,10 @@ LONG CustomDraw(NMLVCUSTOMDRAW* msg) if (cell_y == history.GetUndoHint()) { // undo hint here - msg->clrTextBk = UNDOHINT_FRAMENUM_COLOR; + if(TASEdit_show_markers && (markers.markers_array[cell_y] & MARKER_FLAG_BIT)) + msg->clrTextBk = MARKED_UNDOHINT_FRAMENUM_COLOR; + else + msg->clrTextBk = UNDOHINT_FRAMENUM_COLOR; } else if (cell_y == currFrameCounter || cell_y == playback.GetPauseFrame()) { // current frame @@ -376,14 +379,9 @@ void RightClick(LPNMITEMACTIVATE info) RightClickMenu(info); } -void MarkersChanged() -{ - project.changed = true; -} - void InputChangedRec() { - greenzone.InvalidateGreenZone(history.RegisterInputChanges(MODTYPE_RECORD, currFrameCounter, currFrameCounter)); + greenzone.InvalidateGreenZone(history.RegisterChanges(MODTYPE_RECORD, currFrameCounter, currFrameCounter)); } void ToggleJoypadBit(int column_index, int row_index, UINT KeyFlags) @@ -397,15 +395,15 @@ void ToggleJoypadBit(int column_index, int row_index, UINT KeyFlags) { currMovieData.records[*it].toggleBit(joy,bit); } - greenzone.InvalidateGreenZone(history.RegisterInputChanges(MODTYPE_CHANGE, *selectionFrames.begin(), *selectionFrames.rbegin())); + greenzone.InvalidateGreenZone(history.RegisterChanges(MODTYPE_CHANGE, *selectionFrames.begin(), *selectionFrames.rbegin())); } else { //update one row currMovieData.records[row_index].toggleBit(joy,bit); if (currMovieData.records[row_index].checkBit(joy,bit)) - greenzone.InvalidateGreenZone(history.RegisterInputChanges(MODTYPE_SET, row_index, row_index)); + greenzone.InvalidateGreenZone(history.RegisterChanges(MODTYPE_SET, row_index, row_index)); else - greenzone.InvalidateGreenZone(history.RegisterInputChanges(MODTYPE_UNSET, row_index, row_index)); + greenzone.InvalidateGreenZone(history.RegisterChanges(MODTYPE_UNSET, row_index, row_index)); } } @@ -429,9 +427,14 @@ void SingleClick(LPNMITEMACTIVATE info) { // reverse MARKER_FLAG_BIT in pointed frame markers.ToggleMarker(row_index); - MarkersChanged(); + if (markers.markers_array[row_index]) + history.RegisterChanges(MODTYPE_MARKER_SET, row_index); + else + history.RegisterChanges(MODTYPE_MARKER_UNSET, row_index); + project.changed = true; // deselect this row, so that new marker will be seen immediately ListView_SetItemState(hwndList, row_index, 0, LVIS_SELECTED); + // also no need to redraw row } } else if(column_index >= COLUMN_JOYPAD1_A && column_index <= COLUMN_JOYPAD4_R) @@ -481,7 +484,7 @@ void CloneFrames() } else frames++; } UpdateList(); - greenzone.InvalidateGreenZone(history.RegisterInputChanges(MODTYPE_CLONE, *selectionFrames.begin())); + greenzone.InvalidateGreenZone(history.RegisterChanges(MODTYPE_CLONE, *selectionFrames.begin())); } void InsertFrames() @@ -508,7 +511,7 @@ void InsertFrames() } else frames++; } UpdateList(); - greenzone.InvalidateGreenZone(history.RegisterInputChanges(MODTYPE_INSERT, *selectionFrames.begin())); + greenzone.InvalidateGreenZone(history.RegisterChanges(MODTYPE_INSERT, *selectionFrames.begin())); } void DeleteFrames() @@ -528,7 +531,7 @@ void DeleteFrames() // reduce list UpdateList(); - int result = history.RegisterInputChanges(MODTYPE_DELETE, start_index); + int result = history.RegisterChanges(MODTYPE_DELETE, start_index); if (result >= 0) { greenzone.InvalidateGreenZone(result); @@ -546,9 +549,9 @@ void ClearFrames(bool cut) currMovieData.records[*it].clear(); } if (cut) - greenzone.InvalidateGreenZone(history.RegisterInputChanges(MODTYPE_CUT, *selectionFrames.begin(), *selectionFrames.rbegin())); + greenzone.InvalidateGreenZone(history.RegisterChanges(MODTYPE_CUT, *selectionFrames.begin(), *selectionFrames.rbegin())); else - greenzone.InvalidateGreenZone(history.RegisterInputChanges(MODTYPE_CLEAR, *selectionFrames.begin(), *selectionFrames.rbegin())); + greenzone.InvalidateGreenZone(history.RegisterChanges(MODTYPE_CLEAR, *selectionFrames.begin(), *selectionFrames.rbegin())); } void Truncate() @@ -563,7 +566,7 @@ void Truncate() { currMovieData.truncateAt(frame+1); UpdateList(); - int result = history.RegisterInputChanges(MODTYPE_TRUNCATE, frame+1); + int result = history.RegisterChanges(MODTYPE_TRUNCATE, frame+1); if (result >= 0) { greenzone.InvalidateGreenZone(result); @@ -595,15 +598,17 @@ void ColumnSet(int column) // set all for(TSelectionFrames::iterator it(selectionFrames.begin()); it != selectionFrames.end(); it++) markers.markers_array[*it] |= MARKER_FLAG_BIT; + history.RegisterChanges(MODTYPE_MARKER_SET, *selectionFrames.begin(), *selectionFrames.rbegin()); } else { // unset all for(TSelectionFrames::iterator it(selectionFrames.begin()); it != selectionFrames.end(); it++) markers.markers_array[*it] &= ~MARKER_FLAG_BIT; + history.RegisterChanges(MODTYPE_MARKER_UNSET, *selectionFrames.begin(), *selectionFrames.rbegin()); } - MarkersChanged(); + project.changed = true; ClearSelection(); - RedrawList(); + // no need to RedrawList(); } else { // buttons column @@ -624,9 +629,9 @@ void ColumnSet(int column) for(TSelectionFrames::iterator it(selectionFrames.begin()); it != selectionFrames.end(); it++) currMovieData.records[*it].setBitValue(joy,button,newValue); if (newValue) - greenzone.InvalidateGreenZone(history.RegisterInputChanges(MODTYPE_SET, *selectionFrames.begin(), *selectionFrames.rbegin())); + greenzone.InvalidateGreenZone(history.RegisterChanges(MODTYPE_SET, *selectionFrames.begin(), *selectionFrames.rbegin())); else - greenzone.InvalidateGreenZone(history.RegisterInputChanges(MODTYPE_UNSET, *selectionFrames.begin(), *selectionFrames.rbegin())); + greenzone.InvalidateGreenZone(history.RegisterChanges(MODTYPE_UNSET, *selectionFrames.begin(), *selectionFrames.rbegin())); } } @@ -862,7 +867,7 @@ bool Paste() pGlobal = strchr(pGlobal, '\n'); } - greenzone.InvalidateGreenZone(history.RegisterInputChanges(MODTYPE_PASTE, *selectionFrames.begin())); + greenzone.InvalidateGreenZone(history.RegisterChanges(MODTYPE_PASTE, *selectionFrames.begin())); result = true; } @@ -1417,14 +1422,14 @@ BOOL CALLBACK WndprocTasEdit(HWND hwndDlg, UINT uMsg, WPARAM wParam, LPARAM lPar currMovieData.insertEmpty(index, frames); if (TASEdit_bind_markers) markers.insertEmpty(index, frames); - greenzone.InvalidateGreenZone(history.RegisterInputChanges(MODTYPE_INSERT, index)); + greenzone.InvalidateGreenZone(history.RegisterChanges(MODTYPE_INSERT, index)); } else { // insert at playback cursor currMovieData.insertEmpty(currFrameCounter, frames); if (TASEdit_bind_markers) markers.insertEmpty(currFrameCounter, frames); - greenzone.InvalidateGreenZone(history.RegisterInputChanges(MODTYPE_INSERT, currFrameCounter)); + greenzone.InvalidateGreenZone(history.RegisterChanges(MODTYPE_INSERT, currFrameCounter)); } } } @@ -1627,8 +1632,19 @@ void FollowUndo() { if (!CheckItemVisible(jump_frame)) { - ListView_EnsureVisible(hwndList, currMovieData.getNumRecords()-1, true); - ListView_EnsureVisible(hwndList, jump_frame, false); + // center list at jump_frame + int list_items = listItems; + if (currMovieData.fourscore) list_items--; + int lower_border = (list_items - 1) / 2; + int upper_border = (list_items - 1) - lower_border; + int index = jump_frame + lower_border; + if (index >= currMovieData.getNumRecords()) + index = currMovieData.getNumRecords()-1; + ListView_EnsureVisible(hwndList, index, false); + index = jump_frame - upper_border; + if (index < 0) + index = 0; + ListView_EnsureVisible(hwndList, index, false); } } } @@ -1710,6 +1726,7 @@ void EnterTasEdit() SetWindowPos(hwndTasEdit,HWND_TOP,0,0,0,0,SWP_NOSIZE|SWP_NOMOVE|SWP_NOOWNERZORDER); + greenzone.init(); playback.init(); // either start new movie or use current movie if (movieMode == MOVIEMODE_INACTIVE) @@ -1722,6 +1739,7 @@ void EnterTasEdit() { //use current movie to create a new project FCEUI_StopMovie(); + greenzone.TryDumpIncremental(lagFlag != 0); } // always start work from read-only mode, ready to switch to MULTITRACK_RECORDING_ALL movie_readonly = true; @@ -1780,8 +1798,6 @@ void EnterTasEdit() ListView_InsertColumn(hwndHistoryList, 0, &lvc); // init variables - greenzone.init(); - greenzone.TryDumpIncremental(lagFlag != 0); markers.init(); project.init(); history.init(TasEdit_undo_levels); @@ -1811,6 +1827,7 @@ bool ExitTasEdit() markers.free(); greenzone.clearGreenzone(); history.free(); + playback.SeekingStop(); movieMode = MOVIEMODE_INACTIVE; FCEU_DispMessage("Tasedit disengaged",0); diff --git a/src/drivers/win/tasedit.h b/src/drivers/win/tasedit.h index 53c2431c..7bb72f9b 100644 --- a/src/drivers/win/tasedit.h +++ b/src/drivers/win/tasedit.h @@ -58,6 +58,7 @@ // listview colors #define NORMAL_FRAMENUM_COLOR 0xFFFFFF #define UNDOHINT_FRAMENUM_COLOR 0xF9DDE6 +#define MARKED_UNDOHINT_FRAMENUM_COLOR 0xE1E7EC #define MARKED_FRAMENUM_COLOR 0xC0FCFF #define CUR_MARKED_FRAMENUM_COLOR 0xDEF7F4 #define CUR_FRAMENUM_COLOR 0xFCF1CE diff --git a/src/drivers/win/taseditlib/greenzone.cpp b/src/drivers/win/taseditlib/greenzone.cpp index 9ab8b172..e98eb3e6 100644 --- a/src/drivers/win/taseditlib/greenzone.cpp +++ b/src/drivers/win/taseditlib/greenzone.cpp @@ -6,6 +6,7 @@ #include "zlib.h" #include "taseditproj.h" #include "../tasedit.h" +#include "zlib.h" extern TASEDIT_PROJECT project; extern PLAYBACK playback; @@ -14,6 +15,8 @@ extern bool TASEdit_restore_position; extern void FCEU_printf(char *format, ...); +char greenzone_save_id[GREENZONE_ID_LEN] = "GREENZONE"; + GREENZONE::GREENZONE() { @@ -22,7 +25,6 @@ GREENZONE::GREENZONE() void GREENZONE::init() { clearGreenzone(); - reset(); } void GREENZONE::reset() @@ -128,8 +130,18 @@ void GREENZONE::save(EMUFILE *os) { int frame, size; int last_tick = 0; + // write "GREENZONE" string + os->fwrite(greenzone_save_id, GREENZONE_ID_LEN); // write size write32le(greenZoneCount, os); + // compress and write lag history + int len = lag_history.size(); + uLongf comprlen = (len>>9)+12 + len; + std::vector cbuf(comprlen); + compress(cbuf.data(), &comprlen, lag_history.data(), len); + write32le(comprlen, os); + os->fwrite(cbuf.data(), comprlen); + // write playback position write32le(currFrameCounter, os); // write savestates for (frame = 0; frame < greenZoneCount; ++frame) @@ -142,35 +154,47 @@ void GREENZONE::save(EMUFILE *os) } if (savestates[frame].empty()) continue; write32le(frame, os); - // write lag history - write8le(lag_history[frame], os); // write lua_colorings // write monitorings // write savestate size = savestates[frame].size(); write32le(size, os); os->fwrite(savestates[frame].data(), size); - } // write -1 as eof for greenzone write32le(-1, os); } +// returns true if couldn't load bool GREENZONE::load(EMUFILE *is) { clearGreenzone(); - lag_history.resize(currMovieData.getNumRecords()); - int frame = 0, prev_frame = 0, size = 0; + int frame = 0, prev_frame = -1, size = 0; int last_tick = 0; + // read "GREENZONE" string + char save_id[GREENZONE_ID_LEN]; + if ((int)is->fread(save_id, GREENZONE_ID_LEN) < GREENZONE_ID_LEN) goto error; + if (strcmp(greenzone_save_id, save_id)) goto error; // string is not valid // read size if (read32le((uint32 *)&size, is) && size >= 0 && size <= currMovieData.getNumRecords()) { greenZoneCount = size; savestates.resize(greenZoneCount); int greenzone_tail_frame = greenZoneCount-1 - TASEdit_greenzone_capacity; - + // read and uncompress lag history + lag_history.resize(greenZoneCount); + int comprlen; + uLongf destlen = greenZoneCount; + if (!read32le(&comprlen, is)) goto error; + if (comprlen <= 0) goto error; + std::vector cbuf(comprlen); + if (is->fread(cbuf.data(), comprlen) != comprlen) goto error; + int e = uncompress(lag_history.data(), &destlen, cbuf.data(), comprlen); + if (e != Z_OK && e != Z_BUF_ERROR) goto error; + // read playback position if (read32le((uint32 *)&frame, is)) { currFrameCounter = frame; + // read savestates while(1) { if (!read32le((uint32 *)&frame, is)) break; @@ -181,8 +205,6 @@ bool GREENZONE::load(EMUFILE *is) playback.SetProgressbar(frame, greenZoneCount); last_tick = frame / PROGRESSBAR_UPDATE_RATE; } - // read lag history - if (!read8le(&lag_history[frame], is)) break; // read lua_colorings // read monitorings // read savestate @@ -200,19 +222,27 @@ bool GREENZONE::load(EMUFILE *is) if (is->fseek(size,SEEK_CUR) != 0) break; } } - greenZoneCount = prev_frame+1; // cut greenZoneCount to last good frame - if (frame == -1) + if (prev_frame+1 == greenZoneCount) { // everything went fine - load savestate at cursor position + loadTasSavestate(currFrameCounter); + return false; + } + // uh, okay, but maybe we managed to read at least something from the file + for (; prev_frame >= 0; prev_frame--) + { if (loadTasSavestate(currFrameCounter)) - return true; - } else goto error; + { + greenZoneCount = prev_frame+1; // cut greenZoneCount to this good frame + currFrameCounter = prev_frame; + FCEU_printf("Greenzone loaded partially\n"); + return false; + } + } } } error: - // there was some error while reading greenzone - FCEU_printf("Error loading greenzone\n"); - return false; + return true; } void GREENZONE::InvalidateGreenZone(int after) diff --git a/src/drivers/win/taseditlib/greenzone.h b/src/drivers/win/taseditlib/greenzone.h index ccbf8d14..2af209f0 100644 --- a/src/drivers/win/taseditlib/greenzone.h +++ b/src/drivers/win/taseditlib/greenzone.h @@ -2,6 +2,8 @@ //#define LAG_FLAG_BIT 1 +#define GREENZONE_ID_LEN 10 + class GREENZONE { public: diff --git a/src/drivers/win/taseditlib/inputhistory.cpp b/src/drivers/win/taseditlib/inputhistory.cpp index f0103b12..55ca34cb 100644 --- a/src/drivers/win/taseditlib/inputhistory.cpp +++ b/src/drivers/win/taseditlib/inputhistory.cpp @@ -3,17 +3,19 @@ #include "movie.h" #include "../common.h" #include "../tasedit.h" -#include "inputsnapshot.h" -#include "inputhistory.h" -#include "playback.h" -#include "greenzone.h" +#include "taseditproj.h" + +extern void FCEU_printf(char *format, ...); + +extern HWND hwndHistoryList; +extern bool TASEdit_bind_markers; extern PLAYBACK playback; -extern void FCEU_printf(char *format, ...); -extern HWND hwndHistoryList; extern GREENZONE greenzone; +extern TASEDIT_PROJECT project; -char modCaptions[24][12] = {"Init", +char history_save_id[HISTORY_ID_LEN] = "HISTORY"; +char modCaptions[26][12] = {"Init", "Change", "Set", "Unset", @@ -36,7 +38,9 @@ char modCaptions[24][12] = {"Init", "Branch6", "Branch7", "Branch8", - "Branch9"}; + "Branch9", + "Mark Set", + "Mark Unset"}; char joypadCaptions[4][5] = {"(1P)", "(2P)", "(3P)", "(4P)"}; INPUT_HISTORY::INPUT_HISTORY() @@ -47,9 +51,10 @@ INPUT_HISTORY::INPUT_HISTORY() void INPUT_HISTORY::init(int new_size) { // init vars + if (new_size > 0) + history_size = new_size + 1; undo_hint_pos = old_undo_hint_pos = undo_hint_time = -1; old_show_undo_hint = show_undo_hint = false; - history_size = new_size + 1; // clear snapshots history history_total_items = 0; input_snapshots.resize(history_size); @@ -97,24 +102,36 @@ int INPUT_HISTORY::jump(int new_pos) // if nothing is done, do not invalidate greenzone if (new_pos == history_cursor_pos) return -1; + // make jump + int old_pos = history_cursor_pos; + history_cursor_pos = new_pos; + int real_pos = (history_start_pos + history_cursor_pos) % history_size; + RedrawHistoryList(); + // create undo_hint - if (new_pos < history_cursor_pos) + if (new_pos > old_pos) undo_hint_pos = GetCurrentSnapshot().jump_frame; else undo_hint_pos = GetNextToCurrentSnapshot().jump_frame; undo_hint_time = clock() + UNDO_HINT_TIME; show_undo_hint = true; - // make jump - history_cursor_pos = new_pos; - int real_pos = (history_start_pos + history_cursor_pos) % history_size; + // update markers + if (TASEdit_bind_markers) + { + if (input_snapshots[real_pos].checkMarkersDiff()) + { + input_snapshots[real_pos].toMarkers(); + project.changed = true; + } + } - int first_change = input_snapshots[real_pos].findFirstChange(currMovieData); - if (first_change < 0) return -1; // if somehow there's no changes - // update current movie - input_snapshots[real_pos].toMovie(currMovieData, first_change); - RedrawHistoryList(); + int first_change = input_snapshots[real_pos].findFirstChange(currMovieData); + if (first_change >= 0) + input_snapshots[real_pos].toMovie(currMovieData, first_change); + else + RedrawList(); return first_change; } @@ -145,7 +162,7 @@ void INPUT_HISTORY::AddInputSnapshotToHistory(INPUT_SNAPSHOT &inp) // overwrite old snapshot real_pos = (history_start_pos + history_cursor_pos) % history_size; // compare with the snapshot we're going to overwrite, if it's different then break the chain of coherent snapshots - if (input_snapshots[real_pos].checkDiff(inp)) + if (input_snapshots[real_pos].checkDiff(inp) || input_snapshots[real_pos].checkMarkersDiff(inp)) { for (int i = history_cursor_pos+1; i < history_total_items; ++i) { @@ -167,64 +184,18 @@ void INPUT_HISTORY::AddInputSnapshotToHistory(INPUT_SNAPSHOT &inp) } // returns frame of first actual change -int INPUT_HISTORY::RegisterInputChanges(int mod_type, int start, int end) +int INPUT_HISTORY::RegisterChanges(int mod_type, int start, int end) { // create new input shanshot INPUT_SNAPSHOT inp; inp.init(currMovieData); - // check if there are differences from latest snapshot - int real_pos = (history_start_pos + history_cursor_pos) % history_size; - int first_changes = inp.findFirstChange(input_snapshots[real_pos], start, end); - if (first_changes >= 0) + if (mod_type == MODTYPE_MARKER_SET || mod_type == MODTYPE_MARKER_UNSET) { - // differences found - // fade old hot_changes by 1 - - // highlight new hot changes - + // special case: changed markers, but input didn't change // fill description strcat(inp.description, modCaptions[mod_type]); - switch (mod_type) - { - case MODTYPE_CHANGE: - case MODTYPE_SET: - case MODTYPE_UNSET: - case MODTYPE_TRUNCATE: - case MODTYPE_CLEAR: - case MODTYPE_CUT: - case MODTYPE_IMPORT: - case MODTYPE_BRANCH_0: case MODTYPE_BRANCH_1: - case MODTYPE_BRANCH_2: case MODTYPE_BRANCH_3: - case MODTYPE_BRANCH_4: case MODTYPE_BRANCH_5: - case MODTYPE_BRANCH_6: case MODTYPE_BRANCH_7: - case MODTYPE_BRANCH_8: case MODTYPE_BRANCH_9: - { - inp.jump_frame = first_changes; - break; - } - case MODTYPE_INSERT: - case MODTYPE_DELETE: - case MODTYPE_PASTE: - case MODTYPE_PASTEINSERT: - case MODTYPE_CLONE: - { - // for these changes user prefers to see frame of attempted change (selection beginning), not frame of actual differences - inp.jump_frame = start; - break; - } - case MODTYPE_RECORD: - { - // add info which joypads were affected - int num = (inp.input_type + 1) * 2; // hacky, only for distingushing between normal2p and fourscore - for (int i = 0; i < num; ++i) - { - if (inp.checkJoypadDiff(input_snapshots[real_pos], first_changes, i)) - strcat(inp.description, joypadCaptions[i]); - } - inp.jump_frame = start; - } - } - // add upper and lower frame to description + inp.jump_frame = start; + // add the frame to description char framenum[11]; _itoa(start, framenum, 10); strcat(inp.description, " "); @@ -236,13 +207,83 @@ int INPUT_HISTORY::RegisterInputChanges(int mod_type, int start, int end) strcat(inp.description, framenum); } AddInputSnapshotToHistory(inp); + return -1; + } else + { + // check if there are input differences from latest snapshot + int real_pos = (history_start_pos + history_cursor_pos) % history_size; + int first_changes = inp.findFirstChange(input_snapshots[real_pos], start, end); + if (first_changes >= 0) + { + // differences found + // fade old hot_changes by 1 + + // highlight new hot changes + + // fill description + strcat(inp.description, modCaptions[mod_type]); + switch (mod_type) + { + case MODTYPE_CHANGE: + case MODTYPE_SET: + case MODTYPE_UNSET: + case MODTYPE_TRUNCATE: + case MODTYPE_CLEAR: + case MODTYPE_CUT: + case MODTYPE_IMPORT: + case MODTYPE_BRANCH_0: case MODTYPE_BRANCH_1: + case MODTYPE_BRANCH_2: case MODTYPE_BRANCH_3: + case MODTYPE_BRANCH_4: case MODTYPE_BRANCH_5: + case MODTYPE_BRANCH_6: case MODTYPE_BRANCH_7: + case MODTYPE_BRANCH_8: case MODTYPE_BRANCH_9: + { + inp.jump_frame = first_changes; + break; + } + case MODTYPE_INSERT: + case MODTYPE_DELETE: + case MODTYPE_PASTE: + case MODTYPE_PASTEINSERT: + case MODTYPE_CLONE: + { + // for these changes user prefers to see frame of attempted change (selection beginning), not frame of actual differences + inp.jump_frame = start; + break; + } + case MODTYPE_RECORD: + { + // add info which joypads were affected + int num = (inp.input_type + 1) * 2; // hacky, only for distingushing between normal2p and fourscore + for (int i = 0; i < num; ++i) + { + if (inp.checkJoypadDiff(input_snapshots[real_pos], first_changes, i)) + strcat(inp.description, joypadCaptions[i]); + } + inp.jump_frame = start; + } + } + // add upper and lower frame to description + char framenum[11]; + _itoa(start, framenum, 10); + strcat(inp.description, " "); + strcat(inp.description, framenum); + if (end > start) + { + _itoa(end, framenum, 10); + strcat(inp.description, "-"); + strcat(inp.description, framenum); + } + AddInputSnapshotToHistory(inp); + } + return first_changes; } - return first_changes; } void INPUT_HISTORY::save(EMUFILE *os) { int real_pos, last_tick = 0; + // write "HISTORY" string + os->fwrite(history_save_id, HISTORY_ID_LEN); // write vars write32le(history_cursor_pos, os); write32le(history_total_items, os); @@ -254,12 +295,17 @@ void INPUT_HISTORY::save(EMUFILE *os) playback.SetProgressbar(i, history_total_items); } } -void INPUT_HISTORY::load(EMUFILE *is) +// returns true if couldn't load +bool INPUT_HISTORY::load(EMUFILE *is) { int i = -1; INPUT_SNAPSHOT inp; // delete old snapshots input_snapshots.resize(history_size); + // read "HISTORY" string + char save_id[HISTORY_ID_LEN]; + if ((int)is->fread(save_id, HISTORY_ID_LEN) < HISTORY_ID_LEN) goto error; + if (strcmp(history_save_id, save_id)) goto error; // string is not valid // read vars if (!read32le((uint32 *)&history_cursor_pos, is)) goto error; if (!read32le((uint32 *)&history_total_items, is)) goto error; @@ -301,13 +347,12 @@ void INPUT_HISTORY::load(EMUFILE *is) undo_hint_pos = old_undo_hint_pos = undo_hint_time = -1; old_show_undo_hint = show_undo_hint = false; + // everything went well UpdateHistoryList(); RedrawHistoryList(); - return; + return false; error: - // couldn't load full history - reset it - FCEU_printf("Error loading history\n"); - init(history_size-1); + return true; } // ---------------------------- void INPUT_HISTORY::GetDispInfo(NMLVDISPINFO* nmlvDispInfo) diff --git a/src/drivers/win/taseditlib/inputhistory.h b/src/drivers/win/taseditlib/inputhistory.h index 5541a4f8..49350366 100644 --- a/src/drivers/win/taseditlib/inputhistory.h +++ b/src/drivers/win/taseditlib/inputhistory.h @@ -26,21 +26,25 @@ #define MODTYPE_BRANCH_7 21 #define MODTYPE_BRANCH_8 22 #define MODTYPE_BRANCH_9 23 +#define MODTYPE_MARKER_SET 24 +#define MODTYPE_MARKER_UNSET 25 #define HISTORY_COHERENT_COLOR 0xF9DDE6 #define HISTORY_NORMAL_COLOR 0xFFFFFF +#define HISTORY_ID_LEN 8 + class INPUT_HISTORY { public: INPUT_HISTORY(); - void init(int new_size); + void init(int new_size = 0); void free(); void update(); // called every frame void save(EMUFILE *os); - void load(EMUFILE *is); + bool load(EMUFILE *is); int undo(); int redo(); @@ -48,7 +52,7 @@ public: void AddInputSnapshotToHistory(INPUT_SNAPSHOT &inp); - int RegisterInputChanges(int mod_type, int start = 0, int end =-1); + int RegisterChanges(int mod_type, int start = 0, int end =-1); int InputChanged(int start, int end); int InputInserted(int start); diff --git a/src/drivers/win/taseditlib/inputsnapshot.cpp b/src/drivers/win/taseditlib/inputsnapshot.cpp index dce9a786..e7cab5ef 100644 --- a/src/drivers/win/taseditlib/inputsnapshot.cpp +++ b/src/drivers/win/taseditlib/inputsnapshot.cpp @@ -2,12 +2,15 @@ #include "movie.h" #include "inputsnapshot.h" +#include "markers.h" #include "zlib.h" const int bytes_per_frame[NUM_SUPPORTED_INPUT_TYPES] = {2, 4}; // 16bits for normal joypads, 32bits for fourscore extern void FCEU_printf(char *format, ...); +extern MARKERS markers; + INPUT_SNAPSHOT::INPUT_SNAPSHOT() { @@ -15,6 +18,7 @@ INPUT_SNAPSHOT::INPUT_SNAPSHOT() void INPUT_SNAPSHOT::init(MovieData& md) { + already_compressed = false; // retrieve input data from movie data size = md.getNumRecords(); input_type = (md.fourscore)?1:0; @@ -45,6 +49,9 @@ void INPUT_SNAPSHOT::init(MovieData& md) break; } } + // make a copy of markers.markers_array + markers_array = markers.markers_array; + coherent = true; // save time to description time_t raw_time; @@ -53,6 +60,11 @@ void INPUT_SNAPSHOT::init(MovieData& md) strftime(description, 10, "%H:%M:%S ", timeinfo); } +void INPUT_SNAPSHOT::toMarkers() +{ + markers.markers_array = markers_array; +} + void INPUT_SNAPSHOT::toMovie(MovieData& md, int start) { // write input data to movie data @@ -84,6 +96,30 @@ void INPUT_SNAPSHOT::toMovie(MovieData& md, int start) } } +void INPUT_SNAPSHOT::compress_data() +{ + // compress joysticks + int len = joysticks.size(); + uLongf comprlen = (len>>9)+12 + len; + joysticks_compressed.resize(comprlen); + compress(joysticks_compressed.data(), &comprlen, joysticks.data(), len); + joysticks_compressed.resize(comprlen); + // compress hot_changes + len = hot_changes.size(); + comprlen = (len>>9)+12 + len; + hot_changes_compressed.resize(comprlen); + compress(hot_changes_compressed.data(), &comprlen, hot_changes.data(), len); + hot_changes_compressed.resize(comprlen); + // compress markers + len = markers_array.size(); + comprlen = (len>>9)+12 + len; + markers_array_compressed.resize(comprlen); + compress(markers_array_compressed.data(), &comprlen, markers_array.data(), len); + markers_array_compressed.resize(comprlen); + // don't compress anymore + already_compressed = true; +} + void INPUT_SNAPSHOT::save(EMUFILE *os) { // write vars @@ -95,22 +131,18 @@ void INPUT_SNAPSHOT::save(EMUFILE *os) int len = strlen(description); write8le(len, os); os->fwrite(&description[0], len); - // compress and save joysticks data - len = joysticks.size(); - int comprlen = (len>>9)+12 + len; - std::vector cbuf(comprlen); - int e = compress(cbuf.data(), (uLongf*)&comprlen,(uint8*)joysticks.data(),len); - // write size - write32le(comprlen, os); - os->fwrite(cbuf.data(), comprlen); - // compress and save hot_changes data - len = hot_changes.size(); - comprlen = (len>>9)+12 + len; - std::vector cbuf2(comprlen); - e = compress(cbuf2.data(),(uLongf*)&comprlen,(uint8*)hot_changes.data(),len); - // write size - write32le(comprlen, os); - os->fwrite(cbuf2.data(), comprlen); + // write data + if (!already_compressed) + compress_data(); + // save joysticks data + write32le(joysticks_compressed.size(), os); + os->fwrite(joysticks_compressed.data(), joysticks_compressed.size()); + // save hot_changes data + write32le(hot_changes_compressed.size(), os); + os->fwrite(hot_changes_compressed.data(), hot_changes_compressed.size()); + // save markers data + write32le(markers_array_compressed.size(), os); + os->fwrite(markers_array_compressed.data(), markers_array_compressed.size()); } bool INPUT_SNAPSHOT::load(EMUFILE *is) { @@ -128,27 +160,41 @@ bool INPUT_SNAPSHOT::load(EMUFILE *is) if (tmp < 0 || tmp >= SNAPSHOT_DESC_MAX_LENGTH) return false; if (is->fread(&description[0], tmp) != tmp) return false; description[tmp] = 0; // add '0' because it wasn't saved - // read and uncompress joysticks data - len = size * bytes_per_frame[input_type]; - joysticks.resize(len); - // read size + // read data + already_compressed = true; int comprlen; + uLongf destlen; + // read and uncompress joysticks data + destlen = size * bytes_per_frame[input_type]; + joysticks.resize(destlen); + // read size if (!read32le(&comprlen, is)) return false; - if (comprlen <= 0 || comprlen > len) return false; - std::vector cbuf(comprlen); - if (is->fread(cbuf.data(),comprlen) != comprlen) return false; - int e = uncompress((uint8*)joysticks.data(),(uLongf*)&len,cbuf.data(),comprlen); + if (comprlen <= 0) return false; + joysticks_compressed.resize(comprlen); + if (is->fread(joysticks_compressed.data(), comprlen) != comprlen) return false; + int e = uncompress(joysticks.data(), &destlen, joysticks_compressed.data(), comprlen); if (e != Z_OK && e != Z_BUF_ERROR) return false; // read and uncompress hot_changes data - len = size * bytes_per_frame[input_type] * HOTCHANGE_BYTES_PER_JOY; - hot_changes.resize(len); + destlen = size * bytes_per_frame[input_type] * HOTCHANGE_BYTES_PER_JOY; + hot_changes.resize(destlen); // read size if (!read32le(&comprlen, is)) return false; - if (comprlen <= 0 || comprlen > len) return false; - std::vector cbuf2(comprlen); - if (is->fread(cbuf2.data(),comprlen) != comprlen) return false; - e = uncompress(hot_changes.data(),(uLongf*)&len,cbuf.data(),comprlen); + if (comprlen <= 0) return false; + hot_changes_compressed.resize(comprlen); + if (is->fread(hot_changes_compressed.data(), comprlen) != comprlen) return false; + e = uncompress(hot_changes.data(), &destlen, hot_changes_compressed.data(), comprlen); if (e != Z_OK && e != Z_BUF_ERROR) return false; + // read and uncompress markers data + destlen = size; + markers_array.resize(destlen); + // read size + if (!read32le(&comprlen, is)) return false; + if (comprlen <= 0) return false; + markers_array_compressed.resize(comprlen); + if (is->fread(markers_array_compressed.data(), comprlen) != comprlen) return false; + e = uncompress(markers_array.data(), &destlen, markers_array_compressed.data(), comprlen); + if (e != Z_OK && e != Z_BUF_ERROR) return false; + return true; } bool INPUT_SNAPSHOT::skipLoad(EMUFILE *is) @@ -204,6 +250,24 @@ bool INPUT_SNAPSHOT::checkJoypadDiff(INPUT_SNAPSHOT& inp, int frame, int joy) } return false; } + +// return true if any difference in markers_array is found +bool INPUT_SNAPSHOT::checkMarkersDiff(INPUT_SNAPSHOT& inp) +{ + if (size != inp.size) return true; + for (int i = size-1; i >= 0; i--) + if ((markers_array[i] - inp.markers_array[i]) & MARKER_FLAG_BIT) return true; + return false; +} +// return true if any difference in markers_array is found, comparing to markers.markers_array +bool INPUT_SNAPSHOT::checkMarkersDiff() +{ + if (markers_array.size() != markers.markers_array.size()) return true; + for (int i = markers_array.size()-1; i >= 0; i--) + if ((markers_array[i] - markers.markers_array[i]) & MARKER_FLAG_BIT) return true; + return false; +} + // return number of first frame of difference int INPUT_SNAPSHOT::findFirstChange(INPUT_SNAPSHOT& inp, int start, int end) { diff --git a/src/drivers/win/taseditlib/inputsnapshot.h b/src/drivers/win/taseditlib/inputsnapshot.h index e21d9b37..237a1e3b 100644 --- a/src/drivers/win/taseditlib/inputsnapshot.h +++ b/src/drivers/win/taseditlib/inputsnapshot.h @@ -14,7 +14,9 @@ class INPUT_SNAPSHOT public: INPUT_SNAPSHOT(); void init(MovieData& md); + void toMovie(MovieData& md, int start = 0); + void toMarkers(); void save(EMUFILE *os); bool load(EMUFILE *is); @@ -22,6 +24,10 @@ public: bool checkDiff(INPUT_SNAPSHOT& inp); bool checkJoypadDiff(INPUT_SNAPSHOT& inp, int frame, int joy); + + bool checkMarkersDiff(INPUT_SNAPSHOT& inp); + bool checkMarkersDiff(); + int findFirstChange(INPUT_SNAPSHOT& inp, int start = 0, int end = -1); int findFirstChange(MovieData& md); @@ -32,12 +38,19 @@ public: int input_type; // 0=normal, 1=fourscore, in future may support other input types std::vector joysticks; // Format: joy0-for-frame0, joy1-for-frame0, joy2-for-frame0, joy3-for-frame0, joy0-for-frame1, joy1-for-frame1, ... std::vector hot_changes; // Format: buttons01joy0-for-frame0, buttons23joy0-for-frame0, buttons45joy0-for-frame0, buttons67joy0-for-frame0, buttons01joy1-for-frame0, ... + std::vector markers_array; // just a copy of markers.markers_array bool coherent; // indicates whether this state was made by inputchange of previous state int jump_frame; // for jumping when making undo char description[SNAPSHOT_DESC_MAX_LENGTH]; private: + void compress_data(); + + bool already_compressed; // to compress only once + std::vector joysticks_compressed; + std::vector hot_changes_compressed; + std::vector markers_array_compressed; }; diff --git a/src/drivers/win/taseditlib/markers.cpp b/src/drivers/win/taseditlib/markers.cpp index f75f5d15..330bd67e 100644 --- a/src/drivers/win/taseditlib/markers.cpp +++ b/src/drivers/win/taseditlib/markers.cpp @@ -4,7 +4,9 @@ #include "../common.h" #include "taseditproj.h" //#include "../tasedit.h" +#include "zlib.h" +char markers_save_id[MARKERS_ID_LEN] = "MARKERS"; MARKERS::MARKERS() { @@ -29,24 +31,43 @@ void MARKERS::update() void MARKERS::save(EMUFILE *os) { + // write "MARKERS" string + os->fwrite(markers_save_id, MARKERS_ID_LEN); // write size int size = markers_array.size(); write32le(size, os); - // write array - os->fwrite(markers_array.data(), size); + // compress and write array + int len = markers_array.size(); + uLongf comprlen = (len>>9)+12 + len; + std::vector cbuf(comprlen); + compress(cbuf.data(), &comprlen, markers_array.data(), len); + write32le(comprlen, os); + os->fwrite(cbuf.data(), comprlen); } +// returns true if couldn't load bool MARKERS::load(EMUFILE *is) { markers_array.resize(currMovieData.getNumRecords()); + // read "MARKERS" string + char save_id[MARKERS_ID_LEN]; + if ((int)is->fread(save_id, MARKERS_ID_LEN) < MARKERS_ID_LEN) goto error; + if (strcmp(markers_save_id, save_id)) goto error; // string is not valid int size; if (read32le((uint32 *)&size, is) && size == currMovieData.getNumRecords()) { - // read array - if ((int)is->fread(markers_array.data(), size) == size) return true; + // read and uncompress array + int comprlen; + uLongf destlen = size; + if (!read32le(&comprlen, is)) goto error; + if (comprlen <= 0) goto error; + std::vector cbuf(comprlen); + if (is->fread(cbuf.data(), comprlen) != comprlen) goto error; + int e = uncompress(markers_array.data(), &destlen, cbuf.data(), comprlen); + if (e != Z_OK && e != Z_BUF_ERROR) goto error; + return false; } error: - FCEU_printf("Error loading markers\n"); - return false; + return true; } // ---------------------------------------------------------- void MARKERS::ToggleMarker(int frame) diff --git a/src/drivers/win/taseditlib/markers.h b/src/drivers/win/taseditlib/markers.h index 43cf6586..63a0472f 100644 --- a/src/drivers/win/taseditlib/markers.h +++ b/src/drivers/win/taseditlib/markers.h @@ -2,6 +2,8 @@ #define MARKER_FLAG_BIT 1 +#define MARKERS_ID_LEN 8 + class MARKERS { public: diff --git a/src/drivers/win/taseditlib/playback.cpp b/src/drivers/win/taseditlib/playback.cpp index 0f521732..6ba4cab6 100644 --- a/src/drivers/win/taseditlib/playback.cpp +++ b/src/drivers/win/taseditlib/playback.cpp @@ -291,7 +291,8 @@ bool PLAYBACK::JumpToFrame(int index) else currFrameCounter = i; // continue from the frame - SeekingStart(index+1); + if (index != currFrameCounter) + SeekingStart(index+1); return false; } diff --git a/src/drivers/win/taseditlib/taseditproj.cpp b/src/drivers/win/taseditlib/taseditproj.cpp index 6af44816..7137d865 100644 --- a/src/drivers/win/taseditlib/taseditproj.cpp +++ b/src/drivers/win/taseditlib/taseditproj.cpp @@ -65,23 +65,38 @@ bool TASEDIT_PROJECT::LoadProject(std::string PFN) EMUFILE_FILE ifs(filename, "rb"); - FCEU_printf("Loading TASEdit project %s\n", filename); + FCEU_printf("\nLoading TASEdit project %s\n", filename); + bool error; LoadFM2(currMovieData, &ifs, ifs.size(), false); LoadSubtitles(currMovieData); // try to load markers - if (markers.load(&ifs)) + error = markers.load(&ifs); + if (error) + { + FCEU_printf("Error loading markers\n"); + markers.init(); + } else { // try to load greenzone - if (greenzone.load(&ifs)) - { - // there was some error while loading greenzone - reset playback to frame 0 - poweron(true); - currFrameCounter = 0; - // try to load history - history.load(&ifs); - } + error = greenzone.load(&ifs); } + if (error) + { + FCEU_printf("Error loading greenzone\n"); + greenzone.init(); + playback.StartFromZero(); // reset playback to frame 0 + } else + { + // try to load history + error = history.load(&ifs); + } + if (error) + { + FCEU_printf("Error loading history\n"); + history.init(); + } + reset(); playback.updateProgressbar(); return true; diff --git a/src/drivers/win/window.cpp b/src/drivers/win/window.cpp index 0a41fad3..002dbdb5 100644 --- a/src/drivers/win/window.cpp +++ b/src/drivers/win/window.cpp @@ -1676,6 +1676,7 @@ LRESULT FAR PASCAL AppWndProc(HWND hWnd,UINT msg,WPARAM wParam,LPARAM lParam) if(!LuaConsoleHWnd) LuaConsoleHWnd = CreateDialog(fceu_hInstance, MAKEINTRESOURCE(IDD_LUA), hWnd, (DLGPROC) DlgLuaScriptDialog); else + ShowWindow(LuaConsoleHWnd, SW_SHOWNORMAL); SetForegroundWindow(LuaConsoleHWnd); break; case FCEUX_CONTEXT_CLOSELUAWINDOWS: @@ -1686,6 +1687,8 @@ LRESULT FAR PASCAL AppWndProc(HWND hWnd,UINT msg,WPARAM wParam,LPARAM lParam) //Recent Lua 1 #ifdef _S9XLUA_H case FCEUX_CONTEXT_LOADLASTLUA: + ShowWindow(LuaConsoleHWnd, SW_SHOWNORMAL); + SetForegroundWindow(LuaConsoleHWnd); if(recent_lua[0]) { if (!FCEU_LoadLuaCode(recent_lua[0])) diff --git a/src/fceu.cpp b/src/fceu.cpp index 8e18d635..0371840f 100644 --- a/src/fceu.cpp +++ b/src/fceu.cpp @@ -721,7 +721,7 @@ void FCEUI_Emulate(uint8 **pXBuf, int32 **SoundBuf, int32 *SoundBufSize, int ski #ifdef WIN32 if(FCEUMOV_Mode(MOVIEMODE_TASEDIT)) - greenzone.TryDumpIncremental((bool)lagFlag); + greenzone.TryDumpIncremental(lagFlag != 0); #endif }