diff --git a/output/luaScripts/AVI-HeadsUpDisplay.lua b/output/luaScripts/AVI-HeadsUpDisplay.lua index 94585bdd..b178e3b4 100644 --- a/output/luaScripts/AVI-HeadsUpDisplay.lua +++ b/output/luaScripts/AVI-HeadsUpDisplay.lua @@ -17,8 +17,8 @@ pads = { buttons = { A = {x=30,y=5,w=3,h=3}, B = {x=24,y=5,w=3,h=3}, - start = {x=12,y=7,w=3,h=1}, select = {x=18,y=7,w=3,h=1}, + start = {x=12,y=7,w=3,h=1}, up = {x=4, y=1,w=2,h=2}, down = {x=4, y=7,w=2,h=2}, left = {x=1, y=4,w=2,h=2}, diff --git a/src/drivers/win/config.cpp b/src/drivers/win/config.cpp index 9188f4a9..56f31e4e 100644 --- a/src/drivers/win/config.cpp +++ b/src/drivers/win/config.cpp @@ -342,6 +342,8 @@ static CFGSTRUCT fceuconfig[] = { AC(taseditor_config.savecompact_selection), AC(taseditor_config.findnote_matchcase), AC(taseditor_config.findnote_search_up), + AC(taseditor_config.silent_autosave), + AC(taseditor_config.tooltips), ACS(taseditor_config_last_author), AC(lagCounterDisplay), AC(oldInputDisplay), diff --git a/src/drivers/win/mapinput.cpp b/src/drivers/win/mapinput.cpp index 23fed50b..57a587fa 100644 --- a/src/drivers/win/mapinput.cpp +++ b/src/drivers/win/mapinput.cpp @@ -75,6 +75,7 @@ static struct { EMUCMD_MISC_TOGGLEFULLSCREEN, SCAN_ENTER | CMD_KEY_ALT, }, { EMUCMD_TASEDITOR_REWIND, SCAN_ESCAPE, }, { EMUCMD_RERECORD_DISPLAY_TOGGLE, SCAN_M, }, + { EMUCMD_TASEDITOR_RESTORE_PLAYBACK, SCAN_ENTER, }, }; #define NUM_DEFAULT_MAPPINGS (sizeof(DefaultCommandMapping)/sizeof(DefaultCommandMapping[0])) diff --git a/src/drivers/win/res.rc b/src/drivers/win/res.rc index 6ae8adfa..ca95f641 100644 --- a/src/drivers/win/res.rc +++ b/src/drivers/win/res.rc @@ -255,8 +255,7 @@ BEGIN MENUITEM "&Insert\tShift+Ins", ID_EDIT_INSERTFRAMES MENUITEM "Insert # of Frames\tIns", ID_EDIT_INSERT MENUITEM SEPARATOR - MENUITEM "Truncate\tCtrl+T", ID_EDIT_TRUNCATE - MENUITEM SEPARATOR + MENUITEM "Truncate movie", ID_EDIT_TRUNCATE END POPUP "&View" BEGIN @@ -296,7 +295,8 @@ BEGIN END POPUP "&Help" BEGIN - MENUITEM "&TAS Editor Help", ID_HELP_TASEDITORHELP + MENUITEM "TAS Editor &Help", ID_HELP_TASEDITORHELP + MENUITEM "Enable &Tooltips", ID_HELP_TOOLTIPS MENUITEM SEPARATOR MENUITEM "&About", ID_HELP_ABOUT END @@ -376,15 +376,16 @@ TASEDITORCONTEXTMENUS MENU BEGIN POPUP "Stray" BEGIN - MENUITEM "Insert # of Frames", MENU_CONTEXT_STRAY_INSERTFRAMES + MENUITEM "Unpause emulator", ID_STRAY_UNPAUSE MENUITEM SEPARATOR - MENUITEM "Truncate movie", ID_CONTEXT_STRAY_TRUNCATE + MENUITEM "Insert # of Frames", MENU_CONTEXT_STRAY_INSERTFRAMES + MENUITEM "Truncate movie", ID_STRAY_TRUNCATE END POPUP "Selected" BEGIN MENUITEM "Set Marker", ID_SELECTED_SETMARKER MENUITEM "Remove Marker", ID_SELECTED_REMOVEMARKER - MENUITEM "Select mid &Markers", ID_SELECTED_SELECTMIDMARKERS + MENUITEM "Select between &Markers", ID_SELECTED_SELECTMIDMARKERS MENUITEM SEPARATOR MENUITEM "C&lear", ID_CONTEXT_SELECTED_CLEARFRAMES MENUITEM "&Delete", ID_CONTEXT_SELECTED_DELETEFRAMES @@ -1649,6 +1650,7 @@ END IDD_LUA DIALOGEX 0, 0, 270, 150 STYLE DS_SETFONT | DS_FIXEDSYS | WS_MINIMIZEBOX | WS_POPUP | WS_CLIPCHILDREN | WS_CAPTION | WS_SYSMENU | WS_THICKFRAME +EXSTYLE WS_EX_APPWINDOW CAPTION "Lua Script" MENU LUAWINDOW_MENU FONT 8, "MS Shell Dlg", 400, 0, 0x1 @@ -2189,7 +2191,6 @@ BEGIN VK_PRIOR, ACCEL_CTRL_PGUP, VIRTKEY, CONTROL, NOINVERT "Q", ACCEL_CTRL_Q, VIRTKEY, CONTROL, NOINVERT "S", ACCEL_CTRL_S, VIRTKEY, CONTROL, NOINVERT - "T", ACCEL_CTRL_T, VIRTKEY, CONTROL, NOINVERT "V", ACCEL_CTRL_V, VIRTKEY, CONTROL, NOINVERT "W", ACCEL_CTRL_W, VIRTKEY, CONTROL, NOINVERT "X", ACCEL_CTRL_X, VIRTKEY, CONTROL, NOINVERT @@ -2243,7 +2244,8 @@ IDB_BITMAP16 BITMAP "res\\te_16.bmp" IDB_BITMAP17 BITMAP "res\\te_17.bmp" IDB_BITMAP18 BITMAP "res\\te_18.bmp" IDB_BITMAP19 BITMAP "res\\te_19.bmp" -IDB_TE_ARROW BITMAP "res/te_arrow.bmp" +IDB_TE_ARROW BITMAP "res\\te_arrow.bmp" +IDB_TE_GREEN_ARROW BITMAP "res\\te_green_arrow.bmp" IDB_BRANCH_SPRITESHEET BITMAP "res\\branch_spritesheet.bmp" #endif // Английский (США) resources ///////////////////////////////////////////////////////////////////////////// diff --git a/src/drivers/win/resource.h b/src/drivers/win/resource.h index 4f9bb876..7488f130 100644 --- a/src/drivers/win/resource.h +++ b/src/drivers/win/resource.h @@ -234,6 +234,8 @@ #define IDC_DEBUGGER_FLAG_D 204 #define IDC_NETMOO_KEY 205 #define IDC_DEBUGGER_FLAG_I 205 +#define IDB_BITMAP20 205 +#define IDB_TE_GREEN_ARROW 205 #define IDC_NETMOO_PASS 206 #define IDC_DEBUGGER_FLAG_Z 206 #define IDC_DEBUGGER_FLAG_C 207 @@ -977,6 +979,11 @@ #define ID_HELP_TASEDITORHELP 40523 #define MENU_TASEDITOR 40524 #define ID_FILE_NEW 40525 +#define ID_HELP_SHOWTOOLTIPS 40526 +#define ID_HELP_TOOLTIPS 40527 +#define ID_STRAY_UNPAUSEEMULATOR 40528 +#define ID_STRAY_UNPAUSE 40529 +#define ID_STRAY_TRUNCATE40530 40530 #define IDC_DEBUGGER_ICONTRAY 55535 #define MW_ValueLabel2 65423 #define MW_ValueLabel1 65426 @@ -985,8 +992,8 @@ // #ifdef APSTUDIO_INVOKED #ifndef APSTUDIO_READONLY_SYMBOLS -#define _APS_NEXT_RESOURCE_VALUE 203 -#define _APS_NEXT_COMMAND_VALUE 40526 +#define _APS_NEXT_RESOURCE_VALUE 206 +#define _APS_NEXT_COMMAND_VALUE 40531 #define _APS_NEXT_CONTROL_VALUE 1280 #define _APS_NEXT_SYMED_VALUE 101 #endif diff --git a/src/drivers/win/taseditor.cpp b/src/drivers/win/taseditor.cpp index e57a4bcb..8b8ffb06 100644 --- a/src/drivers/win/taseditor.cpp +++ b/src/drivers/win/taseditor.cpp @@ -6,29 +6,22 @@ #include "main.h" // for GetRomName #include "taseditor.h" #include "version.h" -#include // for StrStrI - -#pragma comment(lib, "Shlwapi.lib") using namespace std; // TAS Editor data bool Taseditor_rewind_now = false; bool must_call_manual_lua_function = false; -// note editing/search (probably should be moved to separate class/module) -int marker_note_edit = MARKER_NOTE_EDIT_NONE; -char findnote_string[MAX_NOTE_LEN] = {0}; -int search_similar_marker = 0; // all Taseditor functional modules TASEDITOR_CONFIG taseditor_config; TASEDITOR_WINDOW taseditor_window; TASEDITOR_PROJECT project; -INPUT_HISTORY history; +HISTORY history; PLAYBACK playback; RECORDER recorder; GREENZONE greenzone; -MARKERS current_markers; +MARKERS_MANAGER markers_manager; BOOKMARKS bookmarks; POPUP_DISPLAY popup_display; TASEDITOR_LIST list; @@ -61,7 +54,7 @@ void UpdateTasEditor() // update all modules that need to be updated every frame recorder.update(); list.update(); - current_markers.update(); + markers_manager.update(); greenzone.update(); playback.update(); bookmarks.update(); @@ -81,187 +74,6 @@ void UpdateTasEditor() } } -void SingleClick(LPNMITEMACTIVATE info) -{ - int row_index = info->iItem; - if(row_index == -1) return; - int column_index = info->iSubItem; - - if(column_index == COLUMN_ICONS) - { - // click on the "icons" column - jump to the frame - selection.ClearSelection(); - playback.jump(row_index); - } else if(column_index == COLUMN_FRAMENUM || column_index == COLUMN_FRAMENUM2) - { - // click on the "frame number" column - set marker if clicked with Alt - if (info->uKeyFlags & LVKF_ALT) - { - // reverse MARKER_FLAG_BIT in pointed frame - current_markers.ToggleMarker(row_index); - selection.must_find_current_marker = playback.must_find_current_marker = true; - if (current_markers.GetMarker(row_index)) - history.RegisterMarkersChange(MODTYPE_MARKER_SET, row_index); - else - history.RegisterMarkersChange(MODTYPE_MARKER_UNSET, row_index); - list.RedrawRow(row_index); - } - } - else if(column_index >= COLUMN_JOYPAD1_A && column_index <= COLUMN_JOYPAD4_R) - { - ToggleJoypadBit(column_index, row_index, info->uKeyFlags); - } -} -void DoubleClick(LPNMITEMACTIVATE info) -{ - int row_index = info->iItem; - if(row_index == -1) return; - int column_index = info->iSubItem; - - if(column_index == COLUMN_ICONS || column_index == COLUMN_FRAMENUM || column_index == COLUMN_FRAMENUM2) - { - // double click sends playback to the frame - selection.ClearSelection(); - playback.jump(row_index); - } else if(column_index >= COLUMN_JOYPAD1_A && column_index <= COLUMN_JOYPAD4_R) - { - ToggleJoypadBit(column_index, row_index, info->uKeyFlags); - } -} - -void ToggleJoypadBit(int column_index, int row_index, UINT KeyFlags) -{ - int joy = (column_index - COLUMN_JOYPAD1_A) / NUM_JOYPAD_BUTTONS; - int bit = (column_index - COLUMN_JOYPAD1_A) % NUM_JOYPAD_BUTTONS; - if (KeyFlags & (LVKF_SHIFT|LVKF_CONTROL)) - { - // update multiple rows, using last row index as a flag to decide operation - SelectionFrames* current_selection = selection.MakeStrobe(); - SelectionFrames::iterator current_selection_end(current_selection->end()); - if (currMovieData.records[row_index].checkBit(joy, bit)) - { - // clear range - for(SelectionFrames::iterator it(current_selection->begin()); it != current_selection_end; it++) - { - currMovieData.records[*it].clearBit(joy, bit); - } - greenzone.InvalidateAndCheck(history.RegisterChanges(MODTYPE_UNSET, *current_selection->begin(), *current_selection->rbegin())); - } else - { - // set range - for(SelectionFrames::iterator it(current_selection->begin()); it != current_selection_end; it++) - { - currMovieData.records[*it].setBit(joy, bit); - } - greenzone.InvalidateAndCheck(history.RegisterChanges(MODTYPE_SET, *current_selection->begin(), *current_selection->rbegin())); - } - } else - { - // update one row - currMovieData.records[row_index].toggleBit(joy, bit); - if (currMovieData.records[row_index].checkBit(joy, bit)) - greenzone.InvalidateAndCheck(history.RegisterChanges(MODTYPE_SET, row_index, row_index)); - else - greenzone.InvalidateAndCheck(history.RegisterChanges(MODTYPE_UNSET, row_index, row_index)); - } - -} - -void ColumnSet(int column) -{ - if (column == COLUMN_FRAMENUM || column == COLUMN_FRAMENUM2) - FrameColumnSet(); - else - InputColumnSet(column); -} -void FrameColumnSet() -{ - SelectionFrames* current_selection = selection.MakeStrobe(); - if (current_selection->size() == 0) return; - SelectionFrames::iterator current_selection_begin(current_selection->begin()); - SelectionFrames::iterator current_selection_end(current_selection->end()); - - // inspect the selected frames, if they are all set, then unset all, else set all - bool unset_found = false, changes_made = false; - for(SelectionFrames::iterator it(current_selection_begin); it != current_selection_end; it++) - { - if(!current_markers.GetMarker(*it)) - { - unset_found = true; - break; - } - } - if (unset_found) - { - // set all - for(SelectionFrames::iterator it(current_selection_begin); it != current_selection_end; it++) - { - if(!current_markers.GetMarker(*it)) - { - if (current_markers.SetMarker(*it)) - { - changes_made = true; - list.RedrawRow(*it); - } - } - } - if (changes_made) - history.RegisterMarkersChange(MODTYPE_MARKER_SET, *current_selection_begin, *current_selection->rbegin()); - } else - { - // unset all - for(SelectionFrames::iterator it(current_selection_begin); it != current_selection_end; it++) - { - if(current_markers.GetMarker(*it)) - { - current_markers.ClearMarker(*it); - changes_made = true; - list.RedrawRow(*it); - } - } - if (changes_made) - history.RegisterMarkersChange(MODTYPE_MARKER_UNSET, *current_selection_begin, *current_selection->rbegin()); - } - if (changes_made) - { - selection.must_find_current_marker = playback.must_find_current_marker = true; - list.SetHeaderColumnLight(COLUMN_FRAMENUM, HEADER_LIGHT_MAX); - } -} -void InputColumnSet(int column) -{ - int joy = (column - COLUMN_JOYPAD1_A) / NUM_JOYPAD_BUTTONS; - if (joy < 0 || joy >= joysticks_per_frame[GetInputType(currMovieData)]) return; - int button = (column - COLUMN_JOYPAD1_A) % NUM_JOYPAD_BUTTONS; - - SelectionFrames* current_selection = selection.MakeStrobe(); - if (current_selection->size() == 0) return; - SelectionFrames::iterator current_selection_begin(current_selection->begin()); - SelectionFrames::iterator current_selection_end(current_selection->end()); - - //inspect the selected frames, if they are all set, then unset all, else set all - bool newValue = false; - for(SelectionFrames::iterator it(current_selection_begin); it != current_selection_end; it++) - { - if(!(currMovieData.records[*it].checkBit(joy,button))) - { - newValue = true; - break; - } - } - // apply newValue - for(SelectionFrames::iterator it(current_selection_begin); it != current_selection_end; it++) - currMovieData.records[*it].setBitValue(joy,button,newValue); - if (newValue) - { - greenzone.InvalidateAndCheck(history.RegisterChanges(MODTYPE_SET, *current_selection_begin, *current_selection->rbegin())); - } else - { - greenzone.InvalidateAndCheck(history.RegisterChanges(MODTYPE_UNSET, *current_selection_begin, *current_selection->rbegin())); - } - list.SetHeaderColumnLight(column, HEADER_LIGHT_MAX); -} - BOOL CALLBACK NewProjectProc(HWND hwndDlg, UINT message, WPARAM wParam, LPARAM lParam) { static struct NewProjectParameters* p = NULL; @@ -360,10 +172,11 @@ void NewProject() // copy input from current snapshot (from history) history.GetCurrentSnapshot().toMovie(currMovieData); if (!params.copy_current_markers) - current_markers.reset(); + markers_manager.reset(); if(params.author_name != L"") currMovieData.comments.push_back(L"author " + params.author_name); // reset Taseditor + project.init(); // new project has blank name greenzone.reset(); playback.reset(); playback.StartFromZero(); @@ -374,11 +187,8 @@ void NewProject() splicer.reset(); recorder.reset(); popup_display.reset(); - project.reset(); taseditor_window.RedrawTaseditor(); taseditor_window.UpdateCaption(); - search_similar_marker = 0; - marker_note_edit = MARKER_NOTE_EDIT_NONE; } } @@ -411,8 +221,6 @@ void OpenProject() } bool LoadProject(char* fullname) { - marker_note_edit = MARKER_NOTE_EDIT_NONE; - SetFocus(list.hwndList); // try to load project if (project.load(fullname)) { @@ -422,14 +230,12 @@ bool LoadProject(char* fullname) taseditor_window.UpdateRecentProjectsArray(fullname); taseditor_window.RedrawTaseditor(); taseditor_window.UpdateCaption(); - search_similar_marker = 0; return true; } else { // failed to load taseditor_window.RedrawTaseditor(); taseditor_window.UpdateCaption(); - search_similar_marker = 0; return false; } } @@ -731,15 +537,15 @@ void Export() char framenum[11]; std::string subtitle; int marker_id; - for (int i = 0; i < current_markers.GetMarkersSize(); ++i) + for (int i = 0; i < markers_manager.GetMarkersSize(); ++i) { - marker_id = current_markers.GetMarker(i); + marker_id = markers_manager.GetMarker(i); if (marker_id) { _itoa(i, framenum, 10); strcat(framenum, " "); subtitle = framenum; - subtitle.append(current_markers.GetNote(marker_id)); + subtitle.append(markers_manager.GetNote(marker_id)); temp_md.subtitles.push_back(subtitle); } } @@ -761,7 +567,7 @@ bool EnterTasEditor() taseditor_window.init(); if(taseditor_window.hwndTasEditor) { - SetTaseditInput(); + SetTaseditorInput(); // save "eoptions" saved_eoptions = eoptions; // set "Run in background" @@ -783,7 +589,7 @@ bool EnterTasEditor() playback.init(); greenzone.init(); recorder.init(); - current_markers.init(); + markers_manager.init(); project.init(); bookmarks.init(); popup_display.init(); @@ -809,7 +615,6 @@ bool EnterTasEditor() FCEUI_StopMovie(); movieMode = MOVIEMODE_TASEDITOR; currMovieData.emuVersion = FCEU_VERSION_NUMERIC; - greenzone.TryDumpIncremental(lagFlag != 0); } // ensure that movie has correct set of ports/fourscore SetInputType(currMovieData, GetInputType(currMovieData)); @@ -822,12 +627,11 @@ bool EnterTasEditor() history.reset(); // reset Taseditor variables must_call_manual_lua_function = false; - marker_note_edit = MARKER_NOTE_EDIT_NONE; - search_similar_marker = 0; SetFocus(history.hwndHistoryList); // set focus only once, to show selection cursor SetFocus(list.hwndList); FCEU_DispMessage("TAS Editor engaged", 0); + taseditor_window.RedrawTaseditor(); return true; } else return false; } else return true; @@ -841,7 +645,7 @@ bool ExitTasEditor() taseditor_window.exit(); // release memory list.free(); - current_markers.free(); + markers_manager.free(); greenzone.free(); bookmarks.free(); popup_display.free(); @@ -849,7 +653,7 @@ bool ExitTasEditor() playback.SeekingStop(); selection.free(); - ClearTaseditInput(); + ClearTaseditorInput(); // restore "eoptions" eoptions = saved_eoptions; // restore autosaves @@ -900,184 +704,16 @@ void SetInputType(MovieData& md, int new_input_type) } } -void SetTaseditInput() +void SetTaseditorInput() { // set "Background TAS Editor input" KeyboardSetBackgroundAccessBit(KEYBACKACCESS_TASEDITOR); JoystickSetBackgroundAccessBit(JOYBACKACCESS_TASEDITOR); } -void ClearTaseditInput() +void ClearTaseditorInput() { // clear "Background TAS Editor input" KeyboardClearBackgroundAccessBit(KEYBACKACCESS_TASEDITOR); JoystickClearBackgroundAccessBit(JOYBACKACCESS_TASEDITOR); } -void UpdateMarkerNote() -{ - if (!marker_note_edit) return; - char new_text[MAX_NOTE_LEN]; - if (marker_note_edit == MARKER_NOTE_EDIT_UPPER) - { - int len = SendMessage(playback.hwndPlaybackMarkerEdit, WM_GETTEXT, MAX_NOTE_LEN, (LPARAM)new_text); - new_text[len] = 0; - // check changes - if (strcmp(current_markers.GetNote(playback.shown_marker).c_str(), new_text)) - { - current_markers.SetNote(playback.shown_marker, new_text); - if (playback.shown_marker) - history.RegisterMarkersChange(MODTYPE_MARKER_RENAME, current_markers.GetMarkerFrame(playback.shown_marker)); - else - // zeroth marker - just assume it's set on frame 0 - history.RegisterMarkersChange(MODTYPE_MARKER_RENAME, 0); - // notify selection to change text in lower marker (in case both are showing same marker) - selection.must_find_current_marker = true; - } - } else if (marker_note_edit == MARKER_NOTE_EDIT_LOWER) - { - int len = SendMessage(selection.hwndSelectionMarkerEdit, WM_GETTEXT, MAX_NOTE_LEN, (LPARAM)new_text); - new_text[len] = 0; - // check changes - if (strcmp(current_markers.GetNote(selection.shown_marker).c_str(), new_text)) - { - current_markers.SetNote(selection.shown_marker, new_text); - if (selection.shown_marker) - history.RegisterMarkersChange(MODTYPE_MARKER_RENAME, current_markers.GetMarkerFrame(selection.shown_marker)); - else - // zeroth marker - just assume it's set on frame 0 - history.RegisterMarkersChange(MODTYPE_MARKER_RENAME, 0); - // notify playback to change text in upper marker (in case both are showing same marker) - playback.must_find_current_marker = true; - } - } -} - -BOOL CALLBACK FindNoteProc(HWND hwndDlg, UINT message, WPARAM wParam, LPARAM lParam) -{ - switch (message) - { - case WM_INITDIALOG: - { - if (taseditor_config.findnote_wndx == -32000) taseditor_config.findnote_wndx = 0; //Just in case - if (taseditor_config.findnote_wndy == -32000) taseditor_config.findnote_wndy = 0; - SetWindowPos(hwndDlg, 0, taseditor_config.findnote_wndx, taseditor_config.findnote_wndy, 0, 0, SWP_NOSIZE | SWP_NOZORDER | SWP_NOOWNERZORDER); - - CheckDlgButton(hwndDlg, IDC_MATCH_CASE, taseditor_config.findnote_matchcase?MF_CHECKED : MF_UNCHECKED); - if (taseditor_config.findnote_search_up) - Button_SetCheck(GetDlgItem(hwndDlg, IDC_RADIO_UP), BST_CHECKED); - else - Button_SetCheck(GetDlgItem(hwndDlg, IDC_RADIO_DOWN), BST_CHECKED); - HWND hwndEdit = GetDlgItem(hwndDlg, IDC_NOTE_TO_FIND); - SendMessage(hwndEdit, EM_SETLIMITTEXT, MAX_NOTE_LEN - 1, 0); - SetWindowText(hwndEdit, findnote_string); - if (GetDlgCtrlID((HWND)wParam) != IDC_NOTE_TO_FIND) - { - SetFocus(hwndEdit); - return false; - } - return true; - } - case WM_MOVE: - { - if (!IsIconic(hwndDlg)) - { - RECT wrect; - GetWindowRect(hwndDlg, &wrect); - taseditor_config.findnote_wndx = wrect.left; - taseditor_config.findnote_wndy = wrect.top; - WindowBoundsCheckNoResize(taseditor_config.findnote_wndx, taseditor_config.findnote_wndy, wrect.right); - } - break; - } - case WM_COMMAND: - { - switch (LOWORD(wParam)) - { - case IDC_NOTE_TO_FIND: - { - if(HIWORD(wParam) == EN_CHANGE) - { - if (GetWindowTextLength(GetDlgItem(hwndDlg, IDC_NOTE_TO_FIND))) - EnableWindow(GetDlgItem(hwndDlg, IDOK), true); - else - EnableWindow(GetDlgItem(hwndDlg, IDOK), false); - } - break; - } - case IDC_RADIO_UP: - taseditor_config.findnote_search_up = true; - break; - case IDC_RADIO_DOWN: - taseditor_config.findnote_search_up = false; - break; - case IDC_MATCH_CASE: - taseditor_config.findnote_matchcase ^= 1; - CheckDlgButton(hwndDlg, IDC_MATCH_CASE, taseditor_config.findnote_matchcase?MF_CHECKED : MF_UNCHECKED); - break; - case IDOK: - { - int len = SendMessage(GetDlgItem(hwndDlg, IDC_NOTE_TO_FIND), WM_GETTEXT, MAX_NOTE_LEN, (LPARAM)findnote_string); - findnote_string[len] = 0; - // scan frames from current selection to the border - int cur_marker = 0; - bool result; - int movie_size = currMovieData.getNumRecords(); - int current_frame = selection.GetCurrentSelectionBeginning(); - if (current_frame < 0 && taseditor_config.findnote_search_up) - current_frame = movie_size; - while (true) - { - // move forward - if (taseditor_config.findnote_search_up) - { - current_frame--; - if (current_frame < 0) - { - MessageBox(taseditor_window.hwndFindNote, "Nothing was found.", "Find Note", MB_OK); - break; - } - } else - { - current_frame++; - if (current_frame >= movie_size) - { - MessageBox(taseditor_window.hwndFindNote, "Nothing was found!", "Find Note", MB_OK); - break; - } - } - // scan marked frames - cur_marker = current_markers.GetMarker(current_frame); - if (cur_marker) - { - if (taseditor_config.findnote_matchcase) - result = (strstr(current_markers.GetNote(cur_marker).c_str(), findnote_string) != 0); - else - result = (StrStrI(current_markers.GetNote(cur_marker).c_str(), findnote_string) != 0); - if (result) - { - // found note containing searched string - jump there - selection.JumpToFrame(current_frame); - break; - } - } - } - return TRUE; - } - case IDCANCEL: - DestroyWindow(taseditor_window.hwndFindNote); - taseditor_window.hwndFindNote = 0; - return TRUE; - } - break; - } - case WM_CLOSE: - case WM_QUIT: - { - DestroyWindow(taseditor_window.hwndFindNote); - taseditor_window.hwndFindNote = 0; - break; - } - } - return FALSE; -} - diff --git a/src/drivers/win/taseditor.h b/src/drivers/win/taseditor.h index 9835db36..149f50ea 100644 --- a/src/drivers/win/taseditor.h +++ b/src/drivers/win/taseditor.h @@ -1,9 +1,4 @@ -enum -{ - MARKER_NOTE_EDIT_NONE, - MARKER_NOTE_EDIT_UPPER, - MARKER_NOTE_EDIT_LOWER -}; +// main TAS Editor file struct NewProjectParameters { @@ -13,14 +8,6 @@ struct NewProjectParameters std::wstring author_name; }; -void SingleClick(LPNMITEMACTIVATE info); -void DoubleClick(LPNMITEMACTIVATE info); - -void ToggleJoypadBit(int column_index, int row_index, UINT KeyFlags); -void ColumnSet(int column); -void InputColumnSet(int column); -void FrameColumnSet(); - bool EnterTasEditor(); bool ExitTasEditor(); void UpdateTasEditor(); @@ -39,8 +26,5 @@ void Export(); int GetInputType(MovieData& md); void SetInputType(MovieData& md, int new_input_type); -void SetTaseditInput(); -void ClearTaseditInput(); - -void UpdateMarkerNote(); - +void SetTaseditorInput(); +void ClearTaseditorInput(); diff --git a/src/drivers/win/taseditor/bookmark.cpp b/src/drivers/win/taseditor/bookmark.cpp index 4b7db280..5a5b0c1f 100644 --- a/src/drivers/win/taseditor/bookmark.cpp +++ b/src/drivers/win/taseditor/bookmark.cpp @@ -4,7 +4,7 @@ extern TASEDITOR_CONFIG taseditor_config; extern GREENZONE greenzone; -extern INPUT_HISTORY history; +extern HISTORY history; extern uint8 *XBuf; extern uint8 *XBackBuf; @@ -45,16 +45,16 @@ void BOOKMARK::set() flash_type = FLASH_TYPE_SET; } -void BOOKMARK::jump() +void BOOKMARK::jumped() { flash_phase = FLASH_PHASE_MAX; flash_type = FLASH_TYPE_JUMP; } -void BOOKMARK::unleashed() +void BOOKMARK::deployed() { flash_phase = FLASH_PHASE_MAX; - flash_type = FLASH_TYPE_UNLEASH; + flash_type = FLASH_TYPE_DEPLOY; } void BOOKMARK::save(EMUFILE *os) diff --git a/src/drivers/win/taseditor/bookmark.h b/src/drivers/win/taseditor/bookmark.h index a69ec037..2bd6d942 100644 --- a/src/drivers/win/taseditor/bookmark.h +++ b/src/drivers/win/taseditor/bookmark.h @@ -5,7 +5,7 @@ enum { FLASH_TYPE_SET = 0, FLASH_TYPE_JUMP = 1, - FLASH_TYPE_UNLEASH = 2, + FLASH_TYPE_DEPLOY = 2, }; #define SCREENSHOT_WIDTH 256 @@ -19,8 +19,8 @@ public: void init(); void set(); - void jump(); - void unleashed(); + void jumped(); + void deployed(); void save(EMUFILE *os); bool load(EMUFILE *is); @@ -28,7 +28,7 @@ public: bool not_empty; int flash_phase; int flash_type; - INPUT_SNAPSHOT snapshot; + SNAPSHOT snapshot; std::vector savestate; std::vector saved_screenshot; int parent_branch; diff --git a/src/drivers/win/taseditor/bookmarks.cpp b/src/drivers/win/taseditor/bookmarks.cpp index 1c88bccc..84f00021 100644 --- a/src/drivers/win/taseditor/bookmarks.cpp +++ b/src/drivers/win/taseditor/bookmarks.cpp @@ -16,11 +16,9 @@ extern PLAYBACK playback; extern TASEDITOR_SELECTION selection; extern GREENZONE greenzone; extern TASEDITOR_PROJECT project; -extern INPUT_HISTORY history; +extern HISTORY history; extern TASEDITOR_LIST list; -extern MARKERS current_markers; - -extern void UpdateMarkerNote(); +extern MARKERS_MANAGER markers_manager; // resources char bookmarks_save_id[BOOKMARKS_ID_LEN] = "BOOKMARKS"; @@ -33,7 +31,7 @@ COLORREF bookmark_flash_colors[3][FLASH_PHASE_MAX+1] = { 0x0d1241, 0x111853, 0x161e64, 0x1a2575, 0x1f2b87, 0x233197, 0x2837a8, 0x2c3db9, 0x3144cb, 0x354adc, 0x3a50ed, 0x3f57ff, // jump 0x14350f, 0x1c480f, 0x235a0f, 0x2a6c0f, 0x317f10, 0x38910f, 0x3fa30f, 0x46b50f, 0x4dc80f, 0x54da0f, 0x5bec0f, 0x63ff10, - // unleash + // deploy 0x43171d, 0x541d21, 0x652325, 0x762929, 0x872f2c, 0x983530, 0xa93b34, 0xba4137, 0xcb463b, 0xdc4c3f, 0xed5243, 0xff5947 }; // corners cursor animation int corners_cursor_shift[BRANCHES_ANIMATION_FRAMES] = {0, 1, 2, 3, 4, 5, 5, 4, 3, 2, 1, 0 }; @@ -173,6 +171,8 @@ void BOOKMARKS::free() } void BOOKMARKS::reset() { + // delete all commands if there are any + commands.resize(0); // init bookmarks bookmarks_array.resize(0); bookmarks_array.resize(TOTAL_BOOKMARKS); @@ -208,6 +208,26 @@ void BOOKMARKS::reset_vars() void BOOKMARKS::update() { + // execute all commands if needed + for (int i = 0; (i + 1) < (int)commands.size(); ) + { + int command_id = commands[i++]; + int slot = commands[i++]; + switch (command_id) + { + case COMMAND_SET: + set(slot); + break; + case COMMAND_JUMP: + jump(slot); + break; + case COMMAND_DEPLOY: + deploy(slot); + break; + } + } + commands.resize(0); + // once per 100 milliseconds fade bookmark flashes if (clock() > check_flash_shedule) { @@ -264,12 +284,38 @@ void BOOKMARKS::update() } } +// stores commands in array for update() function +void BOOKMARKS::command(int command_id, int slot) +{ + switch (command_id) + { + case COMMAND_SET: + { + if (slot < 0 || slot >= TOTAL_BOOKMARKS) + slot = DEFAULT_BOOKMARK; + commands.push_back(command_id); + commands.push_back(slot); + break; + } + case COMMAND_JUMP: + case COMMAND_DEPLOY: + { + if (slot >= 0 && slot < TOTAL_BOOKMARKS) + { + commands.push_back(command_id); + commands.push_back(slot); + } + break; + } + } +} + void BOOKMARKS::set(int slot) { if (slot < 0 || slot >= TOTAL_BOOKMARKS) return; // First save edited note (in case it's being currently edited) - UpdateMarkerNote(); + markers_manager.UpdateMarkerNote(); int previous_frame = bookmarks_array[slot].snapshot.jump_frame; // save time of this slot before rewriting it @@ -405,13 +451,13 @@ void BOOKMARKS::jump(int slot) { int frame = bookmarks_array[slot].snapshot.jump_frame; playback.jump(frame); - if (playback.GetPauseFrame()) + if (playback.pause_frame) list.FollowPauseframe(); - bookmarks_array[slot].jump(); + bookmarks_array[slot].jumped(); } } -void BOOKMARKS::unleash(int slot) +void BOOKMARKS::deploy(int slot) { if (taseditor_config.branch_only_when_rec && movie_readonly) { @@ -424,13 +470,13 @@ void BOOKMARKS::unleash(int slot) int jump_frame = bookmarks_array[slot].snapshot.jump_frame; bool markers_changed = false; - // revert current movie to the input_snapshot state + // revert current movie to the snapshot state if (taseditor_config.branch_full_movie) { // update Markers if (taseditor_config.bind_markers) { - if (bookmarks_array[slot].snapshot.my_markers.checkMarkersDiff(current_markers)) + if (bookmarks_array[slot].snapshot.MarkersDifferFromCurrent()) { bookmarks_array[slot].snapshot.copyToMarkers(); project.SetProjectChanged(); @@ -447,26 +493,26 @@ void BOOKMARKS::unleash(int slot) selection.must_find_current_marker = playback.must_find_current_marker = true; history.RegisterBranching(MODTYPE_BRANCH_0 + slot, first_change, slot); greenzone.Invalidate(first_change); - bookmarks_array[slot].unleashed(); + bookmarks_array[slot].deployed(); } else if (markers_changed) { selection.must_find_current_marker = playback.must_find_current_marker = true; history.RegisterBranching(MODTYPE_BRANCH_MARKERS_0 + slot, first_change, slot); list.RedrawList(); - bookmarks_array[slot].unleashed(); + bookmarks_array[slot].deployed(); } else { // didn't restore anything - bookmarks_array[slot].jump(); + bookmarks_array[slot].jumped(); } } else if (jump_frame > 0) { // update Markers if (taseditor_config.bind_markers) { - if (bookmarks_array[slot].snapshot.my_markers.checkMarkersDiff(current_markers, jump_frame)) + if (bookmarks_array[slot].snapshot.MarkersDifferFromCurrent(jump_frame)) { - bookmarks_array[slot].snapshot.copyToMarkers(jump_frame-1); + bookmarks_array[slot].snapshot.copyToMarkers(jump_frame); project.SetProjectChanged(); markers_changed = true; } @@ -482,17 +528,17 @@ void BOOKMARKS::unleash(int slot) selection.must_find_current_marker = playback.must_find_current_marker = true; history.RegisterBranching(MODTYPE_BRANCH_0 + slot, first_change, slot); greenzone.Invalidate(first_change); - bookmarks_array[slot].unleashed(); + bookmarks_array[slot].deployed(); } else if (markers_changed) { selection.must_find_current_marker = playback.must_find_current_marker = true; history.RegisterBranching(MODTYPE_BRANCH_MARKERS_0 + slot, first_change, slot); list.RedrawList(); - bookmarks_array[slot].unleashed(); + bookmarks_array[slot].deployed(); } else { // didn't restore anything - bookmarks_array[slot].jump(); + bookmarks_array[slot].jumped(); } } @@ -938,7 +984,7 @@ LONG BOOKMARKS::CustomDraw(NMLVCUSTOMDRAW* msg) // frame number SelectObject(msg->nmcd.hdc, list.hMainListFont); int frame = bookmarks_array[cell_y].snapshot.jump_frame; - if (frame == currFrameCounter || frame == (playback.GetPauseFrame() - 1)) + if (frame == currFrameCounter || frame == (playback.GetFlashingPauseFrame() - 1)) { // current frame msg->clrTextBk = CUR_FRAMENUM_COLOR; @@ -969,7 +1015,7 @@ LONG BOOKMARKS::CustomDraw(NMLVCUSTOMDRAW* msg) // frame number SelectObject(msg->nmcd.hdc, list.hMainListFont); int frame = bookmarks_array[cell_y].snapshot.jump_frame; - if (frame == currFrameCounter || frame == (playback.GetPauseFrame() - 1)) + if (frame == currFrameCounter || frame == (playback.GetFlashingPauseFrame() - 1)) { // current frame msg->clrTextBk = CUR_INPUT_COLOR1; @@ -1006,9 +1052,9 @@ void BOOKMARKS::LeftClick(LPNMITEMACTIVATE info) if (cell_y >= 0 && cell_x >= 0) { if (cell_x <= BOOKMARKS_COLUMN_FRAME || (taseditor_config.branch_only_when_rec && movie_readonly)) - jump((cell_y + 1) % TOTAL_BOOKMARKS); + command(COMMAND_JUMP, (cell_y + 1) % TOTAL_BOOKMARKS); else if (cell_x == BOOKMARKS_COLUMN_TIME && (!taseditor_config.branch_only_when_rec || !movie_readonly)) - unleash((cell_y + 1) % TOTAL_BOOKMARKS); + command(COMMAND_DEPLOY, (cell_y + 1) % TOTAL_BOOKMARKS); } // remove selection ListView_SetItemState(hwndBookmarksList, -1, 0, LVIS_FOCUSED|LVIS_SELECTED); @@ -1017,7 +1063,7 @@ void BOOKMARKS::RightClick(LPNMITEMACTIVATE info) { int cell_y = info->iItem; if (cell_y >= 0) - set((cell_y + 1) % TOTAL_BOOKMARKS); + command(COMMAND_SET, (cell_y + 1) % TOTAL_BOOKMARKS); // remove selection ListView_SetItemState(hwndBookmarksList, -1, 0, LVIS_FOCUSED|LVIS_SELECTED); } diff --git a/src/drivers/win/taseditor/bookmarks.h b/src/drivers/win/taseditor/bookmarks.h index 55c42ef9..46c1cdb8 100644 --- a/src/drivers/win/taseditor/bookmarks.h +++ b/src/drivers/win/taseditor/bookmarks.h @@ -10,6 +10,14 @@ enum EDIT_MODE_BRANCHES = 2, }; +enum COMMANDS +{ + COMMAND_SET = 0, + COMMAND_JUMP = 1, + COMMAND_DEPLOY = 2, + COMMAND_DELETE = 3, // not implemented, probably useless +}; + #define ITEM_UNDER_MOUSE_NONE -2 #define ITEM_UNDER_MOUSE_CLOUD -1 @@ -90,6 +98,8 @@ enum #define BOOKMARKS_ID_LEN 10 #define TIME_DESC_LENGTH 9 // "HH:MM:SS" +#define DEFAULT_BOOKMARK 1 + class BOOKMARKS { public: @@ -103,9 +113,7 @@ public: void save(EMUFILE *os, bool really_save = true); bool load(EMUFILE *is); - void set(int slot); - void jump(int slot); - void unleash(int slot); + void command(int command_id, int slot); void GetDispInfo(NMLVDISPINFO* nmlvDispInfo); LONG CustomDraw(NMLVCUSTOMDRAW* msg); @@ -132,6 +140,7 @@ public: void RecursiveAddHeight(int branch_num, int amount); void RecursiveSetYPos(int parent, int parentY); + // saved vars std::vector bookmarks_array; // not saved vars @@ -143,6 +152,10 @@ public: TRACKMOUSEEVENT tme, list_tme; private: + void set(int slot); + void jump(int slot); + void deploy(int slot); + void SetCurrentPosTime(); // also saved vars @@ -152,6 +165,7 @@ private: char current_pos_time[TIME_DESC_LENGTH]; // not saved vars + std::vector commands; int check_flash_shedule; int edit_mode; int animation_frame; diff --git a/src/drivers/win/taseditor/greenzone.cpp b/src/drivers/win/taseditor/greenzone.cpp index c3df161e..886c01f3 100644 --- a/src/drivers/win/taseditor/greenzone.cpp +++ b/src/drivers/win/taseditor/greenzone.cpp @@ -9,6 +9,8 @@ extern PLAYBACK playback; extern BOOKMARKS bookmarks; extern TASEDITOR_LIST list; +extern char lagFlag; + char greenzone_save_id[GREENZONE_ID_LEN] = "GREENZONE"; char greenzone_skipsave_id[GREENZONE_ID_LEN] = "GREENZONX"; @@ -31,7 +33,6 @@ void GREENZONE::free() savestates.resize(0); greenZoneCount = 0; lag_history.resize(0); - // reset lua_colorings // reset monitorings } @@ -42,24 +43,21 @@ void GREENZONE::reset() } void GREENZONE::update() { - if ((int)savestates.size() < greenZoneCount) - savestates.resize(greenZoneCount); - if ((int)lag_history.size() < greenZoneCount) - lag_history.resize(greenZoneCount); + // keep memorizing savestates, this function should be called at the end of every frame + if (greenZoneCount <= currFrameCounter || (int)savestates.size() <= currFrameCounter || savestates[currFrameCounter].empty()) + CollectCurrentState(); + // run cleaning from time to time if (clock() > next_cleaning_time) GreenzoneCleaning(); } -void GREENZONE::TryDumpIncremental(bool lagFlag) +void GREENZONE::CollectCurrentState() { - // if movie length is less than currFrame, pad it with empty frames - if(currMovieData.getNumRecords() <= currFrameCounter) - currMovieData.insertEmpty(-1, 1 + currFrameCounter - currMovieData.getNumRecords()); - - // update greenzone upper limit + // update greenzone upper limit if needed if (greenZoneCount <= currFrameCounter) greenZoneCount = currFrameCounter+1; + if ((int)savestates.size() < greenZoneCount) savestates.resize(greenZoneCount); if ((int)lag_history.size() < greenZoneCount) @@ -67,7 +65,7 @@ void GREENZONE::TryDumpIncremental(bool lagFlag) // if frame changed - log savestate storeTasSavestate(currFrameCounter); - // also log frame_flags + // also log lag frames if (currFrameCounter > 0) { // lagFlag indicates that lag was in previous frame @@ -91,8 +89,8 @@ bool GREENZONE::loadTasSavestate(int frame) void GREENZONE::storeTasSavestate(int frame) { - if ((int)savestates.size()<=frame) - savestates.resize(frame+1); + if ((int)savestates.size() <= frame) + savestates.resize(frame + 1); EMUFILE_MEMORY ms(&savestates[frame]); FCEUSS_SaveMS(&ms, Z_DEFAULT_COMPRESSION); @@ -103,11 +101,11 @@ void GREENZONE::GreenzoneCleaning() { int i = currFrameCounter - taseditor_config.greenzone_capacity; bool changed = false; - if (i < 0) goto finish; + if (i <= 0) goto finish; // zeroth frame should not be cleaned int limit; // 2x of 1/2 limit = i - 2 * taseditor_config.greenzone_capacity; - if (limit < -1) limit = -1; + if (limit < 0) limit = 0; for (; i > limit; i--) { if ((i & 0x1) && !savestates[i].empty()) @@ -119,7 +117,7 @@ void GREENZONE::GreenzoneCleaning() if (i < 0) goto finish; // 4x of 1/4 limit = i - 4 * taseditor_config.greenzone_capacity; - if (limit < -1) limit = -1; + if (limit < 0) limit = 0; for (; i > limit; i--) { if ((i & 0x3) && !savestates[i].empty()) @@ -131,7 +129,7 @@ void GREENZONE::GreenzoneCleaning() if (i < 0) goto finish; // 8x of 1/8 limit = i - 8 * taseditor_config.greenzone_capacity; - if (limit < -1) limit = -1; + if (limit < 0) limit = 0; for (; i > limit; i--) { if ((i & 0x7) && !savestates[i].empty()) @@ -143,7 +141,7 @@ void GREENZONE::GreenzoneCleaning() if (i < 0) goto finish; // 16x of 1/16 limit = i - 16 * taseditor_config.greenzone_capacity; - if (limit < -1) limit = -1; + if (limit < 0) limit = 0; for (; i > limit; i--) { if ((i & 0xF) && !savestates[i].empty()) @@ -153,7 +151,7 @@ void GREENZONE::GreenzoneCleaning() } } // clear all remaining - for (; i >= 0; i--) + for (; i > 0; i--) { if (!savestates[i].empty()) { @@ -208,7 +206,6 @@ void GREENZONE::save(EMUFILE *os, bool really_save) } if (savestates[frame].empty()) continue; write32le(frame, os); - // write lua_colorings // write monitorings // write savestate size = savestates[frame].size(); @@ -312,7 +309,6 @@ bool GREENZONE::load(EMUFILE *is) playback.SetProgressbar(frame, greenZoneCount); last_tick = frame / PROGRESSBAR_UPDATE_RATE; } - // read lua_colorings // read monitorings // read savestate if (!read32le(&size, is)) break; @@ -380,6 +376,10 @@ void GREENZONE::InvalidateAndCheck(int after) // either set playback cursor to the end of greenzone or run seeking to restore playback position if (currFrameCounter >= greenZoneCount) { + // remember the lost position + if (playback.lost_position_frame-1 < currFrameCounter) + playback.lost_position_frame = currFrameCounter + 1; + // auto-restore position if needed if (taseditor_config.restore_position) playback.restorePosition(); else diff --git a/src/drivers/win/taseditor/greenzone.h b/src/drivers/win/taseditor/greenzone.h index 0bec72dc..0a0adadd 100644 --- a/src/drivers/win/taseditor/greenzone.h +++ b/src/drivers/win/taseditor/greenzone.h @@ -1,10 +1,8 @@ //Specification file for Greenzone class -//#define LAG_FLAG_BIT 1 -#define TIME_BETWEEN_CLEANINGS 10000 // in milliseconds - #define GREENZONE_ID_LEN 10 +#define TIME_BETWEEN_CLEANINGS 10000 // in milliseconds // greenzone cleaning masks #define EVERY16TH 0xFFFFFFF0 #define EVERY8TH 0xFFFFFFF8 @@ -23,7 +21,7 @@ public: void save(EMUFILE *os, bool really_save = true); bool load(EMUFILE *is); - void TryDumpIncremental(bool lagFlag = true); + void CollectCurrentState(); bool loadTasSavestate(int frame); void storeTasSavestate(int frame); diff --git a/src/drivers/win/taseditor/inputhistory.cpp b/src/drivers/win/taseditor/history.cpp similarity index 67% rename from src/drivers/win/taseditor/inputhistory.cpp rename to src/drivers/win/taseditor/history.cpp index 394a9b23..c6eeaee1 100644 --- a/src/drivers/win/taseditor/inputhistory.cpp +++ b/src/drivers/win/taseditor/history.cpp @@ -1,4 +1,4 @@ -//Implementation file of Input History class (Undo feature) +//Implementation file of History class (Undo feature) #include "taseditor_project.h" LRESULT APIENTRY HistoryListWndProc(HWND hWnd, UINT msg, WPARAM wParam, LPARAM lParam); @@ -6,13 +6,14 @@ WNDPROC hwndHistoryList_oldWndProc; extern TASEDITOR_CONFIG taseditor_config; extern TASEDITOR_WINDOW taseditor_window; -extern MARKERS current_markers; +extern MARKERS_MANAGER markers_manager; extern BOOKMARKS bookmarks; extern PLAYBACK playback; extern TASEDITOR_SELECTION selection; extern GREENZONE greenzone; extern TASEDITOR_PROJECT project; extern TASEDITOR_LIST list; +extern TASEDITOR_LUA taseditor_lua; extern int joysticks_per_frame[NUM_SUPPORTED_INPUT_TYPES]; extern int GetInputType(MovieData& md); @@ -60,13 +61,14 @@ char modCaptions[41][20] = {" Init", " LUA Marker Unset", " LUA Marker Rename", " LUA Change" }; +char LuaCaptionPrefix[6] = " LUA "; char joypadCaptions[4][5] = {"(1P)", "(2P)", "(3P)", "(4P)"}; -INPUT_HISTORY::INPUT_HISTORY() +HISTORY::HISTORY() { } -void INPUT_HISTORY::init() +void HISTORY::init() { // prepare the history listview hwndHistoryList = GetDlgItem(taseditor_window.hwndTasEditor, IDC_HISTORYLIST); @@ -79,31 +81,31 @@ void INPUT_HISTORY::init() lvc.fmt = LVCFMT_LEFT; ListView_InsertColumn(hwndHistoryList, 0, &lvc); } -void INPUT_HISTORY::free() +void HISTORY::free() { - input_snapshots.resize(0); + snapshots.resize(0); history_total_items = 0; } -void INPUT_HISTORY::reset() +void HISTORY::reset() { free(); // init vars history_size = taseditor_config.undo_levels + 1; undo_hint_pos = old_undo_hint_pos = undo_hint_time = -1; old_show_undo_hint = show_undo_hint = false; - input_snapshots.resize(history_size); + snapshots.resize(history_size); history_start_pos = 0; history_cursor_pos = -1; // create initial snapshot - INPUT_SNAPSHOT inp; + SNAPSHOT inp; inp.init(currMovieData, taseditor_config.enable_hot_changes); strcat(inp.description, modCaptions[0]); inp.jump_frame = -1; - AddInputSnapshotToHistory(inp); + AddSnapshotToHistory(inp); UpdateHistoryList(); RedrawHistoryList(); } -void INPUT_HISTORY::update() +void HISTORY::update() { // update undo_hint if (old_undo_hint_pos != undo_hint_pos && old_undo_hint_pos >= 0) @@ -120,13 +122,10 @@ void INPUT_HISTORY::update() } if (old_show_undo_hint != show_undo_hint) list.RedrawRow(undo_hint_pos); // not changing bookmarks list - - - } // returns frame of first input change (for greenzone invalidation) -int INPUT_HISTORY::jump(int new_pos) +int HISTORY::jump(int new_pos) { if (new_pos < 0) new_pos = 0; else if (new_pos >= history_total_items) new_pos = history_total_items-1; // if nothing is done, do not invalidate greenzone @@ -150,25 +149,25 @@ int INPUT_HISTORY::jump(int new_pos) bool markers_changed = false; if (taseditor_config.bind_markers) { - if (input_snapshots[real_pos].my_markers.checkMarkersDiff(current_markers)) + if (snapshots[real_pos].MarkersDifferFromCurrent()) { - input_snapshots[real_pos].copyToMarkers(); + snapshots[real_pos].copyToMarkers(); project.SetProjectChanged(); markers_changed = true; } } // update current movie - int first_change = input_snapshots[real_pos].findFirstChange(currMovieData); + int first_change = snapshots[real_pos].findFirstChange(currMovieData); if (first_change >= 0) { - input_snapshots[real_pos].toMovie(currMovieData, first_change); + snapshots[real_pos].toMovie(currMovieData, first_change); selection.must_find_current_marker = playback.must_find_current_marker = true; bookmarks.ChangesMadeSinceBranch(); // list will be redrawn by greenzone invalidation } else if (markers_changed) { - current_markers.update(); + markers_manager.update(); selection.must_find_current_marker = playback.must_find_current_marker = true; bookmarks.ChangesMadeSinceBranch(); list.RedrawList(); @@ -181,19 +180,18 @@ int INPUT_HISTORY::jump(int new_pos) return first_change; } -int INPUT_HISTORY::undo() +int HISTORY::undo() { return jump(history_cursor_pos - 1); } -int INPUT_HISTORY::redo() +int HISTORY::redo() { return jump(history_cursor_pos + 1); } // ---------------------------- -void INPUT_HISTORY::AddInputSnapshotToHistory(INPUT_SNAPSHOT &inp) +void HISTORY::AddSnapshotToHistory(SNAPSHOT &inp) { - // history uses conveyor of snapshots (vector with fixed size) to aviod resizing which is awfully expensive with such large objects as INPUT_SNAPSHOT - int real_pos; + // history uses conveyor of snapshots (vector with fixed size) to aviod resizing which is awfully expensive with such large objects as SNAPSHOT if (history_cursor_pos+1 >= history_size) { // reached the end of available history_size - move history_start_pos (thus deleting oldest snapshot) @@ -203,47 +201,25 @@ void INPUT_HISTORY::AddInputSnapshotToHistory(INPUT_SNAPSHOT &inp) { // didn't reach the end of history yet history_cursor_pos++; - if (history_cursor_pos < history_total_items) - { - // 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 truncate history after this item - if (input_snapshots[real_pos].checkDiff(inp) || input_snapshots[real_pos].my_markers.checkMarkersDiff(inp.my_markers)) - { - history_total_items = history_cursor_pos+1; - UpdateHistoryList(); - } else - { - // it's not different - don't truncate history, but break the chain of coherent snapshots - for (int i = history_cursor_pos+1; i < history_total_items; ++i) - { - real_pos = (history_start_pos + i) % history_size; - input_snapshots[real_pos].coherent = false; - } - } - } else - { - // add new snapshot - history_total_items = history_cursor_pos+1; - UpdateHistoryList(); - } + history_total_items = history_cursor_pos+1; + UpdateHistoryList(); } // write snapshot - real_pos = (history_start_pos + history_cursor_pos) % history_size; - input_snapshots[real_pos] = inp; + int real_pos = (history_start_pos + history_cursor_pos) % history_size; + snapshots[real_pos] = inp; RedrawHistoryList(); } // returns frame of first actual change -int INPUT_HISTORY::RegisterChanges(int mod_type, int start, int end) +int HISTORY::RegisterChanges(int mod_type, int start, int end) { - // create new input shanshot - INPUT_SNAPSHOT inp; + // create new shanshot + SNAPSHOT inp; inp.init(currMovieData, taseditor_config.enable_hot_changes); inp.mod_type = mod_type; // 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); + int first_changes = inp.findFirstChange(snapshots[real_pos], start, end); if (first_changes >= 0) { // differences found @@ -264,7 +240,6 @@ int INPUT_HISTORY::RegisterChanges(int mod_type, int start, int end) 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 @@ -290,14 +265,11 @@ int INPUT_HISTORY::RegisterChanges(int mod_type, int start, int end) switch (mod_type) { case MODTYPE_DELETE: - inp.inheritHotChanges_DeleteSelection(&input_snapshots[real_pos]); + inp.inheritHotChanges_DeleteSelection(&snapshots[real_pos]); break; case MODTYPE_INSERT: case MODTYPE_CLONE: - inp.inheritHotChanges_InsertSelection(&input_snapshots[real_pos]); - break; - case MODTYPE_PASTEINSERT: - inp.inheritHotChanges_PasteInsert(&input_snapshots[real_pos]); + inp.inheritHotChanges_InsertSelection(&snapshots[real_pos]); break; case MODTYPE_CHANGE: case MODTYPE_SET: @@ -305,24 +277,53 @@ int INPUT_HISTORY::RegisterChanges(int mod_type, int start, int end) case MODTYPE_CLEAR: case MODTYPE_CUT: case MODTYPE_PASTE: - inp.inheritHotChanges(&input_snapshots[real_pos]); - inp.fillHotChanges(input_snapshots[real_pos], first_changes, end); + inp.inheritHotChanges(&snapshots[real_pos]); + inp.fillHotChanges(snapshots[real_pos], first_changes, end); break; case MODTYPE_TRUNCATE: - inp.copyHotChanges(&input_snapshots[real_pos]); + inp.copyHotChanges(&snapshots[real_pos]); // do not add new hotchanges and do not fade old hotchanges, because there was nothing added break; } } - AddInputSnapshotToHistory(inp); + AddSnapshotToHistory(inp); bookmarks.ChangesMadeSinceBranch(); } return first_changes; } -void INPUT_HISTORY::RegisterMarkersChange(int mod_type, int start, int end) +int HISTORY::RegisterPasteInsert(int start, SelectionFrames& inserted_set) { - // create new input shanshot - INPUT_SNAPSHOT inp; + // create new shanshot + SNAPSHOT inp; + inp.init(currMovieData, taseditor_config.enable_hot_changes); + inp.mod_type = MODTYPE_PASTEINSERT; + // 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(snapshots[real_pos], start); + if (first_changes >= 0) + { + // differences found + // fill description: + strcat(inp.description, modCaptions[inp.mod_type]); + // for PasteInsert user prefers to see frame of attempted change (selection beginning), not frame of actual differences + inp.jump_frame = start; + // add upper frame to description + char framenum[11]; + _itoa(start, framenum, 10); + strcat(inp.description, " "); + strcat(inp.description, framenum); + // set hotchanges + if (taseditor_config.enable_hot_changes) + inp.inheritHotChanges_PasteInsert(&snapshots[real_pos], inserted_set); + AddSnapshotToHistory(inp); + bookmarks.ChangesMadeSinceBranch(); + } + return first_changes; +} +void HISTORY::RegisterMarkersChange(int mod_type, int start, int end) +{ + // create new shanshot + SNAPSHOT inp; inp.init(currMovieData, taseditor_config.enable_hot_changes); inp.mod_type = mod_type; // fill description: @@ -341,14 +342,14 @@ void INPUT_HISTORY::RegisterMarkersChange(int mod_type, int start, int end) } if (taseditor_config.enable_hot_changes) inp.copyHotChanges(&GetCurrentSnapshot()); - AddInputSnapshotToHistory(inp); + AddSnapshotToHistory(inp); bookmarks.ChangesMadeSinceBranch(); project.SetProjectChanged(); } -void INPUT_HISTORY::RegisterBranching(int mod_type, int first_change, int slot) +void HISTORY::RegisterBranching(int mod_type, int first_change, int slot) { - // create new input snapshot - INPUT_SNAPSHOT inp; + // create new snapshot + SNAPSHOT inp; inp.init(currMovieData, taseditor_config.enable_hot_changes); // fill description: modification type + time of the Branch inp.mod_type = mod_type; @@ -375,25 +376,25 @@ void INPUT_HISTORY::RegisterBranching(int mod_type, int first_change, int slot) inp.copyHotChanges(&GetCurrentSnapshot()); } } - AddInputSnapshotToHistory(inp); + AddSnapshotToHistory(inp); } -void INPUT_HISTORY::RegisterRecording(int frame_of_change) +void HISTORY::RegisterRecording(int frame_of_change) { int real_pos = (history_start_pos + history_cursor_pos) % history_size; - INPUT_SNAPSHOT inp; + SNAPSHOT inp; inp.init(currMovieData, taseditor_config.enable_hot_changes); - inp.fillJoypadsDiff(input_snapshots[real_pos], frame_of_change); + inp.fillJoypadsDiff(snapshots[real_pos], frame_of_change); inp.mod_type = MODTYPE_RECORD; strcat(inp.description, modCaptions[MODTYPE_RECORD]); char framenum[11]; // check if current snapshot is also Recording and maybe it is consecutive recording if (taseditor_config.combine_consecutive_rec - && input_snapshots[real_pos].mod_type == MODTYPE_RECORD // a) also Recording - && input_snapshots[real_pos].rec_end_frame+1 == frame_of_change // b) consecutive - && input_snapshots[real_pos].rec_joypad_diff_bits == inp.rec_joypad_diff_bits) // c) recorded same set of joysticks + && snapshots[real_pos].mod_type == MODTYPE_RECORD // a) also Recording + && snapshots[real_pos].rec_end_frame+1 == frame_of_change // b) consecutive + && snapshots[real_pos].rec_joypad_diff_bits == inp.rec_joypad_diff_bits) // c) recorded same set of joysticks { // clone this snapshot and continue chain of recorded frames - inp.jump_frame = input_snapshots[real_pos].jump_frame; + inp.jump_frame = snapshots[real_pos].jump_frame; inp.rec_end_frame = frame_of_change; // add info which joypads were affected int num = joysticks_per_frame[inp.input_type]; @@ -414,11 +415,11 @@ void INPUT_HISTORY::RegisterRecording(int frame_of_change) // set hotchanges if (taseditor_config.enable_hot_changes) { - inp.copyHotChanges(&input_snapshots[real_pos]); - inp.fillHotChanges(input_snapshots[real_pos], frame_of_change, frame_of_change); + inp.copyHotChanges(&snapshots[real_pos]); + inp.fillHotChanges(snapshots[real_pos], frame_of_change, frame_of_change); } // replace current snapshot with this cloned snapshot and truncate history here - input_snapshots[real_pos] = inp; + snapshots[real_pos] = inp; history_total_items = history_cursor_pos+1; UpdateHistoryList(); RedrawHistoryList(); @@ -442,21 +443,21 @@ void INPUT_HISTORY::RegisterRecording(int frame_of_change) // set hotchanges if (taseditor_config.enable_hot_changes) { - inp.inheritHotChanges(&input_snapshots[real_pos]); - inp.fillHotChanges(input_snapshots[real_pos], frame_of_change, frame_of_change); + inp.inheritHotChanges(&snapshots[real_pos]); + inp.fillHotChanges(snapshots[real_pos], frame_of_change, frame_of_change); } - AddInputSnapshotToHistory(inp); + AddSnapshotToHistory(inp); } bookmarks.ChangesMadeSinceBranch(); } -void INPUT_HISTORY::RegisterImport(MovieData& md, char* filename) +void HISTORY::RegisterImport(MovieData& md, char* filename) { - // create new input snapshot - INPUT_SNAPSHOT inp; + // create new snapshot + SNAPSHOT inp; inp.init(md, taseditor_config.enable_hot_changes, GetInputType(currMovieData)); // 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]); + int first_changes = inp.findFirstChange(snapshots[real_pos]); if (first_changes >= 0) { // differences found @@ -470,9 +471,9 @@ void INPUT_HISTORY::RegisterImport(MovieData& md, char* filename) if (taseditor_config.enable_hot_changes) { // do not inherit old hotchanges, because imported input (most likely) doesn't have direct connection with recent edits, so old hotchanges are irrelevant and should not be copied - inp.fillHotChanges(input_snapshots[real_pos], first_changes); + inp.fillHotChanges(snapshots[real_pos], first_changes); } - AddInputSnapshotToHistory(inp); + AddSnapshotToHistory(inp); inp.toMovie(currMovieData); list.update(); bookmarks.ChangesMadeSinceBranch(); @@ -483,8 +484,72 @@ void INPUT_HISTORY::RegisterImport(MovieData& md, char* filename) MessageBox(taseditor_window.hwndTasEditor, "Imported movie has the same input.\nNo changes were made.", "TAS Editor", MB_OK); } } +int HISTORY::RegisterLuaChanges(const char* name, int start, bool InsertionDeletion_was_made) +{ + // create new shanshot + SNAPSHOT inp; + inp.init(currMovieData, taseditor_config.enable_hot_changes); + inp.mod_type = MODTYPE_LUA_CHANGE; + // 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(snapshots[real_pos], start); + if (first_changes >= 0) + { + // differences found + // fill description: + if (name[0]) + { + // custom name of operation + strcat(inp.description, LuaCaptionPrefix); + strncat(inp.description, name, LUACHANGES_NAME_MAX_LEN); + } else + { + // default name + strcat(inp.description, modCaptions[inp.mod_type]); + } + inp.jump_frame = first_changes; + // add upper frame to description + char framenum[11]; + _itoa(first_changes, framenum, 10); + strcat(inp.description, " "); + strcat(inp.description, framenum); + // set hotchanges + if (taseditor_config.enable_hot_changes) + { + if (InsertionDeletion_was_made) + { + // do it hard way: take old hot_changes and insert/delete rows to create a snapshot that is comparable to inp + if (inp.input_type == snapshots[real_pos].input_type) + { + // create temp copy of current snapshot (we need it as a container for hot_changes) + SNAPSHOT hotchanges_snapshot = snapshots[real_pos]; + if (hotchanges_snapshot.has_hot_changes) + { + hotchanges_snapshot.FadeHotChanges(); + } else + { + hotchanges_snapshot.has_hot_changes = true; + hotchanges_snapshot.hot_changes.resize(joysticks_per_frame[inp.input_type] * hotchanges_snapshot.size * HOTCHANGE_BYTES_PER_JOY); + } + // insert/delete frames in hotchanges_snapshot, so that it will be the same size as inp + taseditor_lua.InsertDelete_rows_to_Snaphot(hotchanges_snapshot); + inp.copyHotChanges(&hotchanges_snapshot); + inp.fillHotChanges(hotchanges_snapshot, first_changes); + } + } else + { + // easy way: inp.size is equal to currentsnapshot.size, so we can simply inherit hotchanges + inp.inheritHotChanges(&snapshots[real_pos]); + inp.fillHotChanges(snapshots[real_pos], first_changes); + } + } + AddSnapshotToHistory(inp); + bookmarks.ChangesMadeSinceBranch(); + } + return first_changes; +} -void INPUT_HISTORY::save(EMUFILE *os, bool really_save) +void HISTORY::save(EMUFILE *os, bool really_save) { if (really_save) { @@ -498,7 +563,7 @@ void INPUT_HISTORY::save(EMUFILE *os, bool really_save) for (int i = 0; i < history_total_items; ++i) { real_pos = (history_start_pos + i) % history_size; - input_snapshots[real_pos].save(os); + snapshots[real_pos].save(os); playback.SetProgressbar(i, history_total_items); } } else @@ -508,12 +573,12 @@ void INPUT_HISTORY::save(EMUFILE *os, bool really_save) } } // returns true if couldn't load -bool INPUT_HISTORY::load(EMUFILE *is) +bool HISTORY::load(EMUFILE *is) { int i = -1; - INPUT_SNAPSHOT inp; + SNAPSHOT inp; // delete old snapshots - input_snapshots.resize(history_size); + 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; @@ -554,7 +619,7 @@ bool INPUT_HISTORY::load(EMUFILE *is) // load snapshots for (i = 0; i < history_total_items; ++i) { - if (input_snapshots[i].load(is)) goto error; + if (snapshots[i].load(is)) goto error; playback.SetProgressbar(i, history_total_items); } // skip redo snapshots if needed @@ -575,35 +640,30 @@ error: return true; } // ---------------------------- -void INPUT_HISTORY::GetDispInfo(NMLVDISPINFO* nmlvDispInfo) +void HISTORY::GetDispInfo(NMLVDISPINFO* nmlvDispInfo) { LVITEM& item = nmlvDispInfo->item; if(item.mask & LVIF_TEXT) strcpy(item.pszText, GetItemDesc(item.iItem)); } -LONG INPUT_HISTORY::CustomDraw(NMLVCUSTOMDRAW* msg) +LONG HISTORY::CustomDraw(NMLVCUSTOMDRAW* msg) { switch(msg->nmcd.dwDrawStage) { case CDDS_PREPAINT: return CDRF_NOTIFYITEMDRAW; case CDDS_ITEMPREPAINT: - { - if (GetItemCoherence(msg->nmcd.dwItemSpec)) - msg->clrText = HISTORY_NORMAL_COLOR; - else - msg->clrText = HISTORY_INCOHERENT_COLOR; - } + msg->clrText = HISTORY_NORMAL_COLOR; default: return CDRF_DODEFAULT; } } -void INPUT_HISTORY::Click(LPNMITEMACTIVATE info) +void HISTORY::Click(LPNMITEMACTIVATE info) { - // jump to pointed input snapshot + // jump to pointed snapshot int item = info->iItem; if (item >= 0) { @@ -619,7 +679,7 @@ void INPUT_HISTORY::Click(LPNMITEMACTIVATE info) RedrawHistoryList(); } -void INPUT_HISTORY::UpdateHistoryList() +void HISTORY::UpdateHistoryList() { //update the number of items in the history list int currLVItemCount = ListView_GetItemCount(hwndHistoryList); @@ -627,33 +687,29 @@ void INPUT_HISTORY::UpdateHistoryList() ListView_SetItemCountEx(hwndHistoryList, history_total_items, LVSICF_NOSCROLL | LVSICF_NOINVALIDATEALL); } -void INPUT_HISTORY::RedrawHistoryList() +void HISTORY::RedrawHistoryList() { ListView_SetItemState(hwndHistoryList, history_cursor_pos, LVIS_FOCUSED|LVIS_SELECTED, LVIS_FOCUSED|LVIS_SELECTED); ListView_EnsureVisible(hwndHistoryList, history_cursor_pos, FALSE); InvalidateRect(hwndHistoryList, 0, FALSE); } // ---------------------------- -INPUT_SNAPSHOT& INPUT_HISTORY::GetCurrentSnapshot() +SNAPSHOT& HISTORY::GetCurrentSnapshot() { - return input_snapshots[(history_start_pos + history_cursor_pos) % history_size]; + return snapshots[(history_start_pos + history_cursor_pos) % history_size]; } -INPUT_SNAPSHOT& INPUT_HISTORY::GetNextToCurrentSnapshot() +SNAPSHOT& HISTORY::GetNextToCurrentSnapshot() { if (history_cursor_pos < history_total_items) - return input_snapshots[(history_start_pos + history_cursor_pos + 1) % history_size]; + return snapshots[(history_start_pos + history_cursor_pos + 1) % history_size]; else - return input_snapshots[(history_start_pos + history_cursor_pos) % history_size]; + return snapshots[(history_start_pos + history_cursor_pos) % history_size]; } -char* INPUT_HISTORY::GetItemDesc(int pos) +char* HISTORY::GetItemDesc(int pos) { - return input_snapshots[(history_start_pos + pos) % history_size].description; + return snapshots[(history_start_pos + pos) % history_size].description; } -bool INPUT_HISTORY::GetItemCoherence(int pos) -{ - return input_snapshots[(history_start_pos + pos) % history_size].coherent; -} -int INPUT_HISTORY::GetUndoHint() +int HISTORY::GetUndoHint() { if (show_undo_hint) return undo_hint_pos; diff --git a/src/drivers/win/taseditor/inputhistory.h b/src/drivers/win/taseditor/history.h similarity index 83% rename from src/drivers/win/taseditor/inputhistory.h rename to src/drivers/win/taseditor/history.h index e0496efa..5ebabd35 100644 --- a/src/drivers/win/taseditor/inputhistory.h +++ b/src/drivers/win/taseditor/history.h @@ -46,14 +46,13 @@ enum MODTYPE_LUA_CHANGE = 40, }; #define HISTORY_NORMAL_COLOR 0x000000 -#define HISTORY_INCOHERENT_COLOR 0x999999 #define HISTORY_ID_LEN 8 -class INPUT_HISTORY +class HISTORY { public: - INPUT_HISTORY(); + HISTORY(); void init(); void free(); void reset(); @@ -66,18 +65,17 @@ public: int redo(); int jump(int new_pos); - void AddInputSnapshotToHistory(INPUT_SNAPSHOT &inp); - int RegisterChanges(int mod_type, int start = 0, int end =-1); + int RegisterPasteInsert(int start, SelectionFrames& inserted_set); void RegisterMarkersChange(int mod_type, int start = 0, int end =-1); void RegisterBranching(int mod_type, int first_change, int slot); void RegisterRecording(int frame_of_change); void RegisterImport(MovieData& md, char* filename); + int RegisterLuaChanges(const char* name, int start, bool InsertionDeletion_was_made); - INPUT_SNAPSHOT& GetCurrentSnapshot(); - INPUT_SNAPSHOT& GetNextToCurrentSnapshot(); + SNAPSHOT& GetCurrentSnapshot(); + SNAPSHOT& GetNextToCurrentSnapshot(); char* GetItemDesc(int pos); - bool GetItemCoherence(int pos); int GetUndoHint(); void GetDispInfo(NMLVDISPINFO* nmlvDispInfo); @@ -90,7 +88,9 @@ public: HWND hwndHistoryList; private: - std::vector input_snapshots; + void AddSnapshotToHistory(SNAPSHOT &inp); + + std::vector snapshots; int history_cursor_pos; int history_start_pos; diff --git a/src/drivers/win/taseditor/markers.cpp b/src/drivers/win/taseditor/markers.cpp index 8b4236e8..bef02beb 100644 --- a/src/drivers/win/taseditor/markers.cpp +++ b/src/drivers/win/taseditor/markers.cpp @@ -1,90 +1,38 @@ //Implementation file of Markers class -#include "taseditor_project.h" +#include "../common.h" +#include "markers.h" #include "zlib.h" -#include // for StrStrI - -extern TASEDITOR_CONFIG taseditor_config; -extern TASEDITOR_WINDOW taseditor_window; -extern PLAYBACK playback; -extern TASEDITOR_SELECTION selection; - -// resources -char markers_save_id[MARKERS_ID_LEN] = "MARKERS"; -char markers_skipsave_id[MARKERS_ID_LEN] = "MARKERX"; -char keywordDelimiters[] = " !\"#$%&'()*+,-./:;<=>?@[\\]^_`{|}~"; MARKERS::MARKERS() { } -void MARKERS::init() +void MARKERS::save(EMUFILE *os) { - reset(); -} -void MARKERS::free() -{ - markers_array.resize(0); - notes.resize(0); -} -void MARKERS::reset() -{ - free(); - notes.resize(1); - notes[0] = "Power on"; - update(); -} -void MARKERS::update() -{ - if ((int)markers_array.size() < currMovieData.getNumRecords()) - markers_array.resize(currMovieData.getNumRecords()); -} - -void MARKERS::save(EMUFILE *os, bool really_save) -{ - if (really_save) + // write size + int size = markers_array.size(); + write32le(size, os); + // compress and write array + int len = markers_array.size() * sizeof(int); + uLongf comprlen = (len>>9)+12 + len; + std::vector cbuf(comprlen); + compress(&cbuf[0], &comprlen, (uint8*)&markers_array[0], len); + write32le(comprlen, os); + os->fwrite(&cbuf[0], comprlen); + // write notes + size = notes.size(); + write32le(size, os); + for (int i = 0; i < size; ++i) { - // write "MARKERS" string - os->fwrite(markers_save_id, MARKERS_ID_LEN); - // write size - int size = markers_array.size(); - write32le(size, os); - // compress and write array - int len = markers_array.size() * sizeof(int); - uLongf comprlen = (len>>9)+12 + len; - std::vector cbuf(comprlen); - compress(&cbuf[0], &comprlen, (uint8*)&markers_array[0], len); - write32le(comprlen, os); - os->fwrite(&cbuf[0], comprlen); - // write notes - size = notes.size(); - write32le(size, os); - for (int i = 0; i < size; ++i) - { - len = notes[i].length() + 1; - if (len > MAX_NOTE_LEN) len = MAX_NOTE_LEN; - write32le(len, os); - os->fwrite(notes[i].c_str(), len); - } - } else - { - // write "MARKERX" string - os->fwrite(markers_skipsave_id, MARKERS_ID_LEN); + len = notes[i].length() + 1; + if (len > MAX_NOTE_LEN) len = MAX_NOTE_LEN; + write32le(len, os); + os->fwrite(notes[i].c_str(), len); } } // returns true if couldn't load bool MARKERS::load(EMUFILE *is) { - // 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_skipsave_id, save_id)) - { - // string says to skip loading Markers - FCEU_printf("No markers in the file\n"); - reset(); - return false; - } - if (strcmp(markers_save_id, save_id)) goto error; // string is not valid int size; if (read32le(&size, is)) { @@ -92,12 +40,12 @@ bool MARKERS::load(EMUFILE *is) // read and uncompress array int comprlen, len; uLongf destlen = size * sizeof(int); - if (!read32le(&comprlen, is)) goto error; - if (comprlen <= 0) goto error; + if (!read32le(&comprlen, is)) return true; + if (comprlen <= 0) return true; std::vector cbuf(comprlen); - if (is->fread(&cbuf[0], comprlen) != comprlen) goto error; + if (is->fread(&cbuf[0], comprlen) != comprlen) return true; int e = uncompress((uint8*)&markers_array[0], &destlen, &cbuf[0], comprlen); - if (e != Z_OK && e != Z_BUF_ERROR) goto error; + if (e != Z_OK && e != Z_BUF_ERROR) return true; // read notes if (read32le(&size, is) && size >= 0) { @@ -105,484 +53,36 @@ bool MARKERS::load(EMUFILE *is) char temp_str[MAX_NOTE_LEN]; for (int i = 0; i < size; ++i) { - if (!read32le(&len, is) || len < 0) goto error; - if ((int)is->fread(temp_str, len) < len) goto error; + if (!read32le(&len, is) || len < 0) return true; + if ((int)is->fread(temp_str, len) < len) return true; notes[i] = temp_str; } // all ok return false; } } -error: - FCEU_printf("Error loading markers\n"); - reset(); return true; } bool MARKERS::skipLoad(EMUFILE *is) { - // 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_skipsave_id, save_id)) - { - // string says to skip loading Markers - reset(); - return false; - } - if (strcmp(markers_save_id, save_id)) goto error; // string is not valid int size; if (!(is->fseek(sizeof(int), SEEK_CUR))) { // read array int comprlen, len; - if (!read32le(&comprlen, is)) goto error; - if (is->fseek(comprlen, SEEK_CUR) != 0) goto error; + if (!read32le(&comprlen, is)) return true; + if (is->fseek(comprlen, SEEK_CUR) != 0) return true; // read notes if (read32le(&size, is) && size >= 0) { for (int i = 0; i < size; ++i) { - if (!read32le(&len, is) || len < 0) goto error; - if (is->fseek(len, SEEK_CUR) != 0) goto error; + if (!read32le(&len, is) || len < 0) return true; + if (is->fseek(len, SEEK_CUR) != 0) return true; } // all ok return false; } } -error: - FCEU_printf("Error skiploading markers\n"); return true; } -// ---------------------------------------------------------- -void MARKERS::MakeCopy(MARKERS& source) -{ - // provide references - source.CopyMarkersHere(markers_array, notes); -} -void MARKERS::CopyMarkersHere(std::vector& array_for_markers, std::vector& for_notes) -{ - // copy data to provided arrays - array_for_markers = markers_array; - for_notes = notes; -} -void MARKERS::RestoreFromCopy(MARKERS& source, int until_frame) -{ - if (until_frame >= 0) - { - // restore markers up to and including the frame - if ((int)markers_array.size()-1 <= until_frame) - { - // only copy head of source - source.CopyMarkersHere(markers_array, notes); - markers_array.resize(until_frame+1); - // find last marker - int last_marker = GetMarkerUp(until_frame); - // delete all notes foolowing the note of the last marker - notes.resize(last_marker+1); - } else - { - // combine head of source and tail of destination (old markers) - // 1 - head - std::vector temp_markers_array; - std::vector temp_notes; - source.CopyMarkersHere(temp_markers_array, temp_notes); - temp_markers_array.resize(until_frame+1); - // find last marker in temp_markers_array - int last_marker, frame; - for (frame = until_frame; frame >= 0; frame--) - if (temp_markers_array[frame]) break; - if (frame >= 0) - last_marker = temp_markers_array[frame]; - else - last_marker = 0; - // delete all temp_notes foolowing the note of the last marker - temp_notes.resize(last_marker+1); - // 2 - tail - // delete all markers (and their notes) up to and including until_frame - //for (int i = until_frame; i >= 0; i--) // actually no need for that - // ClearMarker(i); - // 3 - combine head and tail (if there are actually markers left in the tail) - int size = markers_array.size(); - temp_markers_array.resize(size); - for (int i = until_frame+1; i < size; ++i) - { - if (markers_array[i]) - { - last_marker++; - temp_markers_array[i] = last_marker; - temp_notes.push_back(notes[markers_array[i]]); - } - } - // 4 - save result - markers_array = temp_markers_array; - notes = temp_notes; - } - } else - { - // frame not specified, consider this as "copy all" - MakeCopy(source); - } -} -// ---------------------------------------------------------- -int MARKERS::GetMarkersSize() -{ - return markers_array.size(); -} -void MARKERS::SetMarkersSize(int new_size) -{ - // if we are truncating, clear markers that are gonna be erased (so that obsolete notes will be erased too) - for (int i = markers_array.size() - 1; i >= new_size; i--) - if (markers_array[i]) - ClearMarker(i); - markers_array.resize(new_size); -} - -int MARKERS::GetMarker(int frame) -{ - if (frame >= 0 && frame < (int)markers_array.size()) - return markers_array[frame]; - else - return 0; -} -// finds and returns # of Marker starting from start_frame and searching up -int MARKERS::GetMarkerUp(int start_frame) -{ - if (start_frame >= (int)markers_array.size()) - start_frame = markers_array.size() - 1; - for (; start_frame >= 0; start_frame--) - if (markers_array[start_frame]) return markers_array[start_frame]; - return 0; -} -// finds frame where the Marker is set -int MARKERS::GetMarkerFrame(int marker_id) -{ - for (int i = markers_array.size() - 1; i >= 0; i--) - if (markers_array[i] == marker_id) return i; - // didn't find - return -1; -} -// returns number of new marker -int MARKERS::SetMarker(int frame) -{ - if (frame < 0) - return 0; - else if (frame >= (int)markers_array.size()) - markers_array.resize(frame + 1); - else if (markers_array[frame]) - return markers_array[frame]; - - int marker_num = GetMarkerUp(frame) + 1; - markers_array[frame] = marker_num; - if (taseditor_config.empty_marker_notes) - notes.insert(notes.begin() + marker_num, 1, ""); - else - // copy previous marker note - notes.insert(notes.begin() + marker_num, 1, notes[marker_num - 1]); - // increase following markers' ids - int size = markers_array.size(); - for (frame++; frame < size; ++frame) - if (markers_array[frame]) - markers_array[frame]++; - return marker_num; -} -void MARKERS::ClearMarker(int frame) -{ - if (markers_array[frame]) - { - // erase corresponding note - notes.erase(notes.begin() + markers_array[frame]); - // erase marker - markers_array[frame] = 0; - // decrease following markers' ids - int size = markers_array.size(); - for (frame++; frame < size; ++frame) - if (markers_array[frame]) - markers_array[frame]--; - } -} -void MARKERS::ToggleMarker(int frame) -{ - if (frame >= 0 && frame < (int)markers_array.size()) - { - if (markers_array[frame]) - ClearMarker(frame); - else - SetMarker(frame); - } -} - -void MARKERS::EraseMarker(int frame) -{ - // if there's a marker, first clear it - if (markers_array[frame]) - ClearMarker(frame); - markers_array.erase(markers_array.begin() + frame); -} -void MARKERS::insertEmpty(int at, int frames) -{ - if(at == -1) - { - markers_array.resize(markers_array.size() + frames); - } else - { - markers_array.insert(markers_array.begin() + at, frames, 0); - } -} - -int MARKERS::GetNotesSize() -{ - return notes.size(); -} -std::string MARKERS::GetNote(int index) -{ - if (index >= 0 && index < (int)notes.size()) - return notes[index]; - else return notes[0]; -} -void MARKERS::SetNote(int index, const char* new_text) -{ - if (index >= 0 && index < (int)notes.size()) - notes[index] = new_text; -} - -// ---------------------------------------------------------- -// return true if any difference in markers_array is found, comparing to markers.markers_array -bool MARKERS::checkMarkersDiff(MARKERS& their_markers) -{ - if (GetMarkersSize() != their_markers.GetMarkersSize()) return true; - if (GetNotesSize() != their_markers.GetNotesSize()) return true; - for (int i = markers_array.size()-1; i >= 0; i--) - { - if (markers_array[i] != their_markers.GetMarker(i)) - return true; - else if (markers_array[i] && notes[markers_array[i]].compare(their_markers.GetNote(markers_array[i]))) - return true; - } - // also check if there's difference between 0th notes - if (notes[0].compare(their_markers.GetNote(0))) - return true; - return false; -} -// return true only when difference is found before end frame (not including end frame) -bool MARKERS::checkMarkersDiff(MARKERS& their_markers, int end) -{ - if (GetMarkersSize() != their_markers.GetMarkersSize() && (GetMarkersSize()-1 < end || their_markers.GetMarkersSize()-1 < end)) return true; - for (int i = end-1; i >= 0; i--) - { - if (markers_array[i] != their_markers.GetMarker(i)) - return true; - else if (markers_array[i] && notes[markers_array[i]].compare(their_markers.GetNote(markers_array[i]))) - return true; - } - return false; -} -// ------------------------------------------------------------------------------------ -bool ordering(const std::pair& d1, const std::pair& d2) -{ - return d1.second < d2.second; -} -void MARKERS::FindSimilar(int offset) -{ - int i, t; - int sourceMarker = playback.shown_marker; - char sourceNote[MAX_NOTE_LEN]; - strcpy(sourceNote, GetNote(sourceMarker).c_str()); - - // check if playback_marker_text is empty - if (!sourceNote[0]) - { - MessageBox(taseditor_window.hwndTasEditor, "Marker Note under Playback cursor is empty!", "Find Similar Note", MB_OK); - return; - } - // check if there's at least one note (not counting zeroth note) - if (notes.size() <= 0) - { - MessageBox(taseditor_window.hwndTasEditor, "This project doesn't have any Markers!", "Find Similar Note", MB_OK); - return; - } - - // 0 - divide source string into keywords - int totalSourceKeywords = 0; - char sourceKeywords[MAX_NUM_KEYWORDS][MAX_NOTE_LEN] = {0}; - int current_line_pos = 0; - char sourceKeywordsLine[MAX_NUM_KEYWORDS] = {0}; - char* pch; - // divide into tokens - pch = strtok(sourceNote, keywordDelimiters); - while (pch != NULL) - { - if (strlen(pch) >= KEYWORD_MIN_LEN) - { - // check if same keyword already appeared in the string - for (t = totalSourceKeywords - 1; t >= 0; t--) - if (!_stricmp(sourceKeywords[t], pch)) break; - if (t < 0) - { - // save new keyword - strcpy(sourceKeywords[totalSourceKeywords], pch); - // also set its id into the line - sourceKeywordsLine[current_line_pos++] = totalSourceKeywords + 1; - totalSourceKeywords++; - } else - { - // same keyword found - sourceKeywordsLine[current_line_pos++] = t + 1; - } - } - pch = strtok(NULL, keywordDelimiters); - } - // we found the line (sequence) of keywords - sourceKeywordsLine[current_line_pos] = 0; - - if (!totalSourceKeywords) - { - MessageBox(taseditor_window.hwndTasEditor, "Marker Note under Playback cursor doesn't have keywords!", "Find Similar Note", MB_OK); - return; - } - - // 1 - find how frequently each keyword appears in notes - std::vector keywordFound(totalSourceKeywords); - char checkedNote[MAX_NOTE_LEN]; - for (i = notes.size() - 1; i > 0; i--) - { - if (i != sourceMarker) - { - strcpy(checkedNote, notes[i].c_str()); - for (t = totalSourceKeywords - 1; t >= 0; t--) - if (StrStrI(checkedNote, sourceKeywords[t])) - keywordFound[t]++; - } - } - // findmax - int maxFound = 0; - for (t = totalSourceKeywords - 1; t >= 0; t--) - if (maxFound < keywordFound[t]) - maxFound = keywordFound[t]; - // and then calculate weight of each keyword: the more often it appears in markers, the less weight it has - std::vector keywordWeight(totalSourceKeywords); - for (t = totalSourceKeywords - 1; t >= 0; t--) - keywordWeight[t] = KEYWORD_WEIGHT_BASE + KEYWORD_WEIGHT_FACTOR * (keywordFound[t] / (double)maxFound); - - // start accumulating priorities - std::vector> notePriority(notes.size()); - - // 2 - find keywords in notes (including cases when keyword appears inside another word) - for (i = notePriority.size() - 1; i > 0; i--) - { - notePriority[i].first = i; - if (i != sourceMarker) - { - strcpy(checkedNote, notes[i].c_str()); - for (t = totalSourceKeywords - 1; t >= 0; t--) - { - if (StrStrI(checkedNote, sourceKeywords[t])) - notePriority[i].second += KEYWORD_CASEINSENTITIVE_BONUS_PER_CHAR * keywordWeight[t] * strlen(sourceKeywords[t]); - if (strstr(checkedNote, sourceKeywords[t])) - notePriority[i].second += KEYWORD_CASESENTITIVE_BONUS_PER_CHAR * keywordWeight[t] * strlen(sourceKeywords[t]); - } - } - } - - // 3 - search sequences of keywords from all other notes - current_line_pos = 0; - char checkedKeywordsLine[MAX_NUM_KEYWORDS] = {0}; - int keyword_id; - for (i = notes.size() - 1; i > 0; i--) - { - if (i != sourceMarker) - { - strcpy(checkedNote, notes[i].c_str()); - // divide into tokens - pch = strtok(checkedNote, keywordDelimiters); - while (pch != NULL) - { - if (strlen(pch) >= KEYWORD_MIN_LEN) - { - // check if the keyword is one of sourceKeywords - for (t = totalSourceKeywords - 1; t >= 0; t--) - if (!_stricmp(sourceKeywords[t], pch)) break; - if (t >= 0) - { - // the keyword is one of sourceKeywords - set its id into the line - checkedKeywordsLine[current_line_pos++] = t + 1; - } else - { - // found keyword that doesn't appear in sourceNote, give penalty - notePriority[i].second -= KEYWORD_PENALTY_FOR_STRANGERS * strlen(pch); - // since the keyword breaks our sequence of coincident keywords, check if that sequence is similar to sourceKeywordsLine - if (current_line_pos >= KEYWORDS_LINE_MIN_SEQUENCE) - { - checkedKeywordsLine[current_line_pos] = 0; - // search checkedKeywordsLine in sourceKeywordsLine - if (strstr(sourceKeywordsLine, checkedKeywordsLine)) - { - // found same sequence of keywords! add priority to this checkedNote - for (t = current_line_pos - 1; t >= 0; t--) - { - // add bonus for every keyword in the sequence - keyword_id = checkedKeywordsLine[t] - 1; - notePriority[i].second += current_line_pos * KEYWORD_SEQUENCE_BONUS_PER_CHAR * keywordWeight[keyword_id] * strlen(sourceKeywords[keyword_id]); - } - } - } - // clear checkedKeywordsLine - memset(checkedKeywordsLine, 0, MAX_NUM_KEYWORDS); - current_line_pos = 0; - } - } - pch = strtok(NULL, keywordDelimiters); - } - // finished dividing into tokens - if (current_line_pos >= KEYWORDS_LINE_MIN_SEQUENCE) - { - checkedKeywordsLine[current_line_pos] = 0; - // search checkedKeywordsLine in sourceKeywordsLine - if (strstr(sourceKeywordsLine, checkedKeywordsLine)) - { - // found same sequence of keywords! add priority to this checkedNote - for (t = current_line_pos - 1; t >= 0; t--) - { - // add bonus for every keyword in the sequence - keyword_id = checkedKeywordsLine[t] - 1; - notePriority[i].second += current_line_pos * KEYWORD_SEQUENCE_BONUS_PER_CHAR * keywordWeight[keyword_id] * strlen(sourceKeywords[keyword_id]); - } - } - } - // clear checkedKeywordsLine - memset(checkedKeywordsLine, 0, MAX_NUM_KEYWORDS); - current_line_pos = 0; - } - } - - // 4 - sort notePriority by second member of the pair - std::sort(notePriority.begin(), notePriority.end(), ordering); - - /* - // debug trace - FCEU_printf("\n\n\n\n\n\n\n\n\n\n"); - for (t = totalSourceKeywords - 1; t >= 0; t--) - FCEU_printf("Keyword: %s, %d, %f\n", sourceKeywords[t], keywordFound[t], keywordWeight[t]); - for (i = notePriority.size() - 1; i > 0; i--) - { - int marker_id = notePriority[i].first; - FCEU_printf("Result: %s, %d, %f\n", notes[marker_id].c_str(), marker_id, notePriority[i].second); - } - */ - - // Send selection to the marker found - int index = notePriority.size()-1 - offset; - if (index >= 0 && notePriority[index].second >= MIN_PRIORITY_TRESHOLD) - { - int marker_id = notePriority[index].first; - int frame = GetMarkerFrame(marker_id); - if (frame >= 0) - selection.JumpToFrame(frame); - } else - { - if (offset) - MessageBox(taseditor_window.hwndTasEditor, "Could not find more Notes similar to Marker Note under Playback cursor!", "Find Similar Note", MB_OK); - else - MessageBox(taseditor_window.hwndTasEditor, "Could not find anything similar to Marker Note under Playback cursor!", "Find Similar Note", MB_OK); - } -} - diff --git a/src/drivers/win/taseditor/markers.h b/src/drivers/win/taseditor/markers.h index 2dbe9a05..21713adc 100644 --- a/src/drivers/win/taseditor/markers.h +++ b/src/drivers/win/taseditor/markers.h @@ -1,63 +1,18 @@ //Specification file for Markers class -#define MARKERS_ID_LEN 8 #define MAX_NOTE_LEN 100 -// constants for "Find Similar Note" algorithm (may need finetuning) -#define KEYWORD_MIN_LEN 2 -#define MAX_NUM_KEYWORDS (MAX_NOTE_LEN / (KEYWORD_MIN_LEN+1)) + 1 -#define KEYWORD_WEIGHT_BASE 2.0 -#define KEYWORD_WEIGHT_FACTOR -1.0 -#define KEYWORD_CASEINSENTITIVE_BONUS_PER_CHAR 1.0 // these two should be small, because they also work when keyword is inside another keyword, giving irrelevant results -#define KEYWORD_CASESENTITIVE_BONUS_PER_CHAR 1.0 -#define KEYWORD_SEQUENCE_BONUS_PER_CHAR 5.0 -#define KEYWORD_PENALTY_FOR_STRANGERS 0.2 - -#define KEYWORDS_LINE_MIN_SEQUENCE 1 - -#define MIN_PRIORITY_TRESHOLD 5.0 - class MARKERS { public: MARKERS(); - void init(); - void free(); - void reset(); - void update(); - void save(EMUFILE *os, bool really_save = true); + void save(EMUFILE *os); bool load(EMUFILE *is); bool skipLoad(EMUFILE *is); - void MakeCopy(MARKERS& source); - void CopyMarkersHere(std::vector& array_for_markers, std::vector& for_notes); - void RestoreFromCopy(MARKERS& source, int until_frame = -1); - - int GetMarkersSize(); - void SetMarkersSize(int new_size); - - int GetMarker(int frame); - int GetMarkerUp(int start_frame); - int GetMarkerFrame(int marker_id); - - int SetMarker(int frame); - void ClearMarker(int frame); - void ToggleMarker(int frame); - - void EraseMarker(int frame); - void insertEmpty(int at, int frames); - - int GetNotesSize(); - std::string GetNote(int index); - void SetNote(int index, const char* new_text); - - bool checkMarkersDiff(MARKERS& their_markers); - bool checkMarkersDiff(MARKERS& their_markers, int end); - - void FindSimilar(int offset); - -private: std::vector markers_array; // Format: 0th = marker num (id) for frame 0, 1st = marker num for frame 1, ... std::vector notes; // Format: 0th - note for intro (Marker 0), 1st - note for Marker1, 2nd - note for Marker2, ... +private: + }; diff --git a/src/drivers/win/taseditor/markers_manager.cpp b/src/drivers/win/taseditor/markers_manager.cpp new file mode 100644 index 00000000..63c9e048 --- /dev/null +++ b/src/drivers/win/taseditor/markers_manager.cpp @@ -0,0 +1,714 @@ +//Implementation file of Markers_manager class +#include "taseditor_project.h" +#include // for StrStrI + +#pragma comment(lib, "Shlwapi.lib") + +extern TASEDITOR_CONFIG taseditor_config; +extern TASEDITOR_WINDOW taseditor_window; +extern PLAYBACK playback; +extern TASEDITOR_SELECTION selection; +extern HISTORY history; + +// resources +char markers_save_id[MARKERS_ID_LEN] = "MARKERS"; +char markers_skipsave_id[MARKERS_ID_LEN] = "MARKERX"; +char keywordDelimiters[] = " !\"#$%&'()*+,-./:;<=>?@[\\]^_`{|}~"; + +MARKERS_MANAGER::MARKERS_MANAGER() +{ + memset(findnote_string, 0, MAX_NOTE_LEN); +} + +void MARKERS_MANAGER::init() +{ + reset(); +} +void MARKERS_MANAGER::free() +{ + markers.markers_array.resize(0); + markers.notes.resize(0); +} +void MARKERS_MANAGER::reset() +{ + free(); + marker_note_edit = MARKER_NOTE_EDIT_NONE; + search_similar_marker = 0; + markers.notes.resize(1); + markers.notes[0] = "Power on"; + update(); +} +void MARKERS_MANAGER::update() +{ + if ((int)markers.markers_array.size() < currMovieData.getNumRecords()) + markers.markers_array.resize(currMovieData.getNumRecords()); +} + +void MARKERS_MANAGER::save(EMUFILE *os, bool really_save) +{ + if (really_save) + { + // write "MARKERS" string + os->fwrite(markers_save_id, MARKERS_ID_LEN); + markers.save(os); + } else + { + // write "MARKERX" string, meaning that markers are not saved + os->fwrite(markers_skipsave_id, MARKERS_ID_LEN); + } +} +// returns true if couldn't load +bool MARKERS_MANAGER::load(EMUFILE *is) +{ + // 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_skipsave_id, save_id)) + { + // string says to skip loading Markers + FCEU_printf("No markers in the file\n"); + reset(); + return false; + } + if (strcmp(markers_save_id, save_id)) goto error; // string is not valid + if (markers.load(is)) goto error; + // all ok + return false; +error: + FCEU_printf("Error loading markers\n"); + reset(); + return true; +} +// ----------------------------------------------------------------------------------------- +int MARKERS_MANAGER::GetMarkersSize() +{ + return markers.markers_array.size(); +} +void MARKERS_MANAGER::SetMarkersSize(int new_size) +{ + // if we are truncating, clear markers that are gonna be erased (so that obsolete notes will be erased too) + for (int i = markers.markers_array.size() - 1; i >= new_size; i--) + if (markers.markers_array[i]) + ClearMarker(i); + markers.markers_array.resize(new_size); +} + +int MARKERS_MANAGER::GetMarker(int frame) +{ + if (frame >= 0 && frame < (int)markers.markers_array.size()) + return markers.markers_array[frame]; + else + return 0; +} +// finds and returns # of Marker starting from start_frame and searching up +int MARKERS_MANAGER::GetMarkerUp(int start_frame) +{ + if (start_frame >= (int)markers.markers_array.size()) + start_frame = markers.markers_array.size() - 1; + for (; start_frame >= 0; start_frame--) + if (markers.markers_array[start_frame]) return markers.markers_array[start_frame]; + return 0; +} +// special version of the function +int MARKERS_MANAGER::GetMarkerUp(MARKERS& target_markers, int start_frame) +{ + if (start_frame >= (int)target_markers.markers_array.size()) + start_frame = target_markers.markers_array.size() - 1; + for (; start_frame >= 0; start_frame--) + if (target_markers.markers_array[start_frame]) return target_markers.markers_array[start_frame]; + return 0; +} +// finds frame where the Marker is set +int MARKERS_MANAGER::GetMarkerFrame(int marker_id) +{ + for (int i = markers.markers_array.size() - 1; i >= 0; i--) + if (markers.markers_array[i] == marker_id) return i; + // didn't find + return -1; +} +// returns number of new marker +int MARKERS_MANAGER::SetMarker(int frame) +{ + if (frame < 0) + return 0; + else if (frame >= (int)markers.markers_array.size()) + markers.markers_array.resize(frame + 1); + else if (markers.markers_array[frame]) + return markers.markers_array[frame]; + + int marker_num = GetMarkerUp(frame) + 1; + markers.markers_array[frame] = marker_num; + if (taseditor_config.empty_marker_notes) + markers.notes.insert(markers.notes.begin() + marker_num, 1, ""); + else + // copy previous marker note + markers.notes.insert(markers.notes.begin() + marker_num, 1, markers.notes[marker_num - 1]); + // increase following markers' ids + int size = markers.markers_array.size(); + for (frame++; frame < size; ++frame) + if (markers.markers_array[frame]) + markers.markers_array[frame]++; + return marker_num; +} +void MARKERS_MANAGER::ClearMarker(int frame) +{ + if (markers.markers_array[frame]) + { + // erase corresponding note + markers.notes.erase(markers.notes.begin() + markers.markers_array[frame]); + // erase marker + markers.markers_array[frame] = 0; + // decrease following markers' ids + int size = markers.markers_array.size(); + for (frame++; frame < size; ++frame) + if (markers.markers_array[frame]) + markers.markers_array[frame]--; + } +} +void MARKERS_MANAGER::ToggleMarker(int frame) +{ + if (frame >= 0 && frame < (int)markers.markers_array.size()) + { + if (markers.markers_array[frame]) + ClearMarker(frame); + else + SetMarker(frame); + } +} + +void MARKERS_MANAGER::EraseMarker(int frame) +{ + if (frame < (int)markers.markers_array.size()) + { + // if there's a marker, first clear it + if (markers.markers_array[frame]) + ClearMarker(frame); + markers.markers_array.erase(markers.markers_array.begin() + frame); + } +} +void MARKERS_MANAGER::insertEmpty(int at, int frames) +{ + if(at == -1) + { + markers.markers_array.resize(markers.markers_array.size() + frames); + } else + { + markers.markers_array.insert(markers.markers_array.begin() + at, frames, 0); + } +} + +int MARKERS_MANAGER::GetNotesSize() +{ + return markers.notes.size(); +} +std::string MARKERS_MANAGER::GetNote(int index) +{ + if (index >= 0 && index < (int)markers.notes.size()) + return markers.notes[index]; + else + return markers.notes[0]; +} +// special version of the function +std::string MARKERS_MANAGER::GetNote(MARKERS& target_markers, int index) +{ + if (index >= 0 && index < (int)target_markers.notes.size()) + return target_markers.notes[index]; + else + return target_markers.notes[0]; +} +void MARKERS_MANAGER::SetNote(int index, const char* new_text) +{ + if (index >= 0 && index < (int)markers.notes.size()) + markers.notes[index] = new_text; +} +// --------------------------------------------------------------------------------------- +void MARKERS_MANAGER::MakeCopyTo(MARKERS& destination) +{ + destination.markers_array = markers.markers_array; + destination.notes = markers.notes; +} +void MARKERS_MANAGER::RestoreFromCopy(MARKERS& source, int until_frame) +{ + if (until_frame >= 0) + { + // restore markers up to and not including the frame + if ((int)markers.markers_array.size() <= until_frame) + { + // only copy head of source + markers.markers_array = source.markers_array; + markers.markers_array.resize(until_frame); + markers.notes = source.notes; + // find last marker + int last_marker = GetMarkerUp(until_frame-1); + // delete all notes following the note of the last marker + markers.notes.resize(last_marker+1); + } else + { + // combine head of source and tail of destination (old markers) + // 1 - head = part of source markers + std::vector temp_markers_array; + std::vector temp_notes; + temp_markers_array = source.markers_array; + temp_markers_array.resize(until_frame); + temp_notes = source.notes; + // find last marker in temp_markers_array + int last_marker, frame; + for (frame = until_frame-1; frame >= 0; frame--) + if (temp_markers_array[frame]) break; + if (frame >= 0) + last_marker = temp_markers_array[frame]; + else + last_marker = 0; + // delete all temp_notes foolowing the note of the last marker + temp_notes.resize(last_marker+1); + // 2 - tail = part of old (current) markers + // delete all markers (and their notes) up to and not including until_frame + //for (int i = until_frame-1; i >= 0; i--) // actually no need for that + // ClearMarker(i); + // 3 - combine head and tail (if there are actually markers left in the tail) + int size = markers.markers_array.size(); + temp_markers_array.resize(size); + for (int i = until_frame; i < size; ++i) + { + if (markers.markers_array[i]) + { + last_marker++; // make new id for old marker + temp_markers_array[i] = last_marker; + temp_notes.push_back(markers.notes[markers.markers_array[i]]); // take note from old markers and add it to the end of the head + } + } + // 4 - save result + markers.markers_array = temp_markers_array; + markers.notes = temp_notes; + } + } else + { + // end frame was not specified, consider this as "copy all" + markers.markers_array = source.markers_array; + markers.notes = source.notes; + } +} + +// return true if any difference in markers_array is found, comparing to markers.markers_array +bool MARKERS_MANAGER::checkMarkersDiff(MARKERS& their_markers) +{ + if (GetMarkersSize() != their_markers.markers_array.size()) return true; + if (GetNotesSize() != their_markers.notes.size()) return true; + for (int i = markers.markers_array.size()-1; i >= 0; i--) + { + if (markers.markers_array[i] != their_markers.markers_array[i]) + return true; + else if (markers.markers_array[i] && markers.notes[markers.markers_array[i]].compare(their_markers.notes[their_markers.markers_array[i]])) + return true; + } + // also check if there's difference between 0th notes + if (markers.notes[0].compare(their_markers.notes[0])) + return true; + return false; +} +// return true only when difference is found before end frame (not including end frame) +bool MARKERS_MANAGER::checkMarkersDiff(MARKERS& their_markers, int end) +{ + if (end < 0) + return checkMarkersDiff(their_markers); + + if (markers.markers_array.size() != their_markers.markers_array.size() && ((int)markers.markers_array.size()-1 < end || (int)their_markers.markers_array.size()-1 < end)) return true; + for (int i = end-1; i >= 0; i--) + { + if (markers.markers_array[i] != their_markers.markers_array[i]) + return true; + else if (markers.markers_array[i] && markers.notes[markers.markers_array[i]].compare(their_markers.notes[their_markers.markers_array[i]])) + return true; + } + return false; +} +// ------------------------------------------------------------------------------------ +// ordering function, used by std::sort +bool ordering(const std::pair& d1, const std::pair& d2) +{ + return d1.second < d2.second; +} + +void MARKERS_MANAGER::FindSimilar() +{ + search_similar_marker = 0; + FindNextSimilar(); +} +void MARKERS_MANAGER::FindNextSimilar() +{ + int i, t; + int sourceMarker = playback.shown_marker; + char sourceNote[MAX_NOTE_LEN]; + strcpy(sourceNote, GetNote(sourceMarker).c_str()); + + // check if playback_marker_text is empty + if (!sourceNote[0]) + { + MessageBox(taseditor_window.hwndTasEditor, "Marker Note under Playback cursor is empty!", "Find Similar Note", MB_OK); + return; + } + // check if there's at least one note (not counting zeroth note) + if (markers.notes.size() <= 0) + { + MessageBox(taseditor_window.hwndTasEditor, "This project doesn't have any Markers!", "Find Similar Note", MB_OK); + return; + } + + // 0 - divide source string into keywords + int totalSourceKeywords = 0; + char sourceKeywords[MAX_NUM_KEYWORDS][MAX_NOTE_LEN] = {0}; + int current_line_pos = 0; + char sourceKeywordsLine[MAX_NUM_KEYWORDS] = {0}; + char* pch; + // divide into tokens + pch = strtok(sourceNote, keywordDelimiters); + while (pch != NULL) + { + if (strlen(pch) >= KEYWORD_MIN_LEN) + { + // check if same keyword already appeared in the string + for (t = totalSourceKeywords - 1; t >= 0; t--) + if (!_stricmp(sourceKeywords[t], pch)) break; + if (t < 0) + { + // save new keyword + strcpy(sourceKeywords[totalSourceKeywords], pch); + // also set its id into the line + sourceKeywordsLine[current_line_pos++] = totalSourceKeywords + 1; + totalSourceKeywords++; + } else + { + // same keyword found + sourceKeywordsLine[current_line_pos++] = t + 1; + } + } + pch = strtok(NULL, keywordDelimiters); + } + // we found the line (sequence) of keywords + sourceKeywordsLine[current_line_pos] = 0; + + if (!totalSourceKeywords) + { + MessageBox(taseditor_window.hwndTasEditor, "Marker Note under Playback cursor doesn't have keywords!", "Find Similar Note", MB_OK); + return; + } + + // 1 - find how frequently each keyword appears in notes + std::vector keywordFound(totalSourceKeywords); + char checkedNote[MAX_NOTE_LEN]; + for (i = markers.notes.size() - 1; i > 0; i--) + { + if (i != sourceMarker) + { + strcpy(checkedNote, markers.notes[i].c_str()); + for (t = totalSourceKeywords - 1; t >= 0; t--) + if (StrStrI(checkedNote, sourceKeywords[t])) + keywordFound[t]++; + } + } + // findmax + int maxFound = 0; + for (t = totalSourceKeywords - 1; t >= 0; t--) + if (maxFound < keywordFound[t]) + maxFound = keywordFound[t]; + // and then calculate weight of each keyword: the more often it appears in markers, the less weight it has + std::vector keywordWeight(totalSourceKeywords); + for (t = totalSourceKeywords - 1; t >= 0; t--) + keywordWeight[t] = KEYWORD_WEIGHT_BASE + KEYWORD_WEIGHT_FACTOR * (keywordFound[t] / (double)maxFound); + + // start accumulating priorities + std::vector> notePriority(markers.notes.size()); + + // 2 - find keywords in notes (including cases when keyword appears inside another word) + for (i = notePriority.size() - 1; i > 0; i--) + { + notePriority[i].first = i; + if (i != sourceMarker) + { + strcpy(checkedNote, markers.notes[i].c_str()); + for (t = totalSourceKeywords - 1; t >= 0; t--) + { + if (StrStrI(checkedNote, sourceKeywords[t])) + notePriority[i].second += KEYWORD_CASEINSENTITIVE_BONUS_PER_CHAR * keywordWeight[t] * strlen(sourceKeywords[t]); + if (strstr(checkedNote, sourceKeywords[t])) + notePriority[i].second += KEYWORD_CASESENTITIVE_BONUS_PER_CHAR * keywordWeight[t] * strlen(sourceKeywords[t]); + } + } + } + + // 3 - search sequences of keywords from all other notes + current_line_pos = 0; + char checkedKeywordsLine[MAX_NUM_KEYWORDS] = {0}; + int keyword_id; + for (i = markers.notes.size() - 1; i > 0; i--) + { + if (i != sourceMarker) + { + strcpy(checkedNote, markers.notes[i].c_str()); + // divide into tokens + pch = strtok(checkedNote, keywordDelimiters); + while (pch != NULL) + { + if (strlen(pch) >= KEYWORD_MIN_LEN) + { + // check if the keyword is one of sourceKeywords + for (t = totalSourceKeywords - 1; t >= 0; t--) + if (!_stricmp(sourceKeywords[t], pch)) break; + if (t >= 0) + { + // the keyword is one of sourceKeywords - set its id into the line + checkedKeywordsLine[current_line_pos++] = t + 1; + } else + { + // found keyword that doesn't appear in sourceNote, give penalty + notePriority[i].second -= KEYWORD_PENALTY_FOR_STRANGERS * strlen(pch); + // since the keyword breaks our sequence of coincident keywords, check if that sequence is similar to sourceKeywordsLine + if (current_line_pos >= KEYWORDS_LINE_MIN_SEQUENCE) + { + checkedKeywordsLine[current_line_pos] = 0; + // search checkedKeywordsLine in sourceKeywordsLine + if (strstr(sourceKeywordsLine, checkedKeywordsLine)) + { + // found same sequence of keywords! add priority to this checkedNote + for (t = current_line_pos - 1; t >= 0; t--) + { + // add bonus for every keyword in the sequence + keyword_id = checkedKeywordsLine[t] - 1; + notePriority[i].second += current_line_pos * KEYWORD_SEQUENCE_BONUS_PER_CHAR * keywordWeight[keyword_id] * strlen(sourceKeywords[keyword_id]); + } + } + } + // clear checkedKeywordsLine + memset(checkedKeywordsLine, 0, MAX_NUM_KEYWORDS); + current_line_pos = 0; + } + } + pch = strtok(NULL, keywordDelimiters); + } + // finished dividing into tokens + if (current_line_pos >= KEYWORDS_LINE_MIN_SEQUENCE) + { + checkedKeywordsLine[current_line_pos] = 0; + // search checkedKeywordsLine in sourceKeywordsLine + if (strstr(sourceKeywordsLine, checkedKeywordsLine)) + { + // found same sequence of keywords! add priority to this checkedNote + for (t = current_line_pos - 1; t >= 0; t--) + { + // add bonus for every keyword in the sequence + keyword_id = checkedKeywordsLine[t] - 1; + notePriority[i].second += current_line_pos * KEYWORD_SEQUENCE_BONUS_PER_CHAR * keywordWeight[keyword_id] * strlen(sourceKeywords[keyword_id]); + } + } + } + // clear checkedKeywordsLine + memset(checkedKeywordsLine, 0, MAX_NUM_KEYWORDS); + current_line_pos = 0; + } + } + + // 4 - sort notePriority by second member of the pair + std::sort(notePriority.begin(), notePriority.end(), ordering); + + /* + // debug trace + FCEU_printf("\n\n\n\n\n\n\n\n\n\n"); + for (t = totalSourceKeywords - 1; t >= 0; t--) + FCEU_printf("Keyword: %s, %d, %f\n", sourceKeywords[t], keywordFound[t], keywordWeight[t]); + for (i = notePriority.size() - 1; i > 0; i--) + { + int marker_id = notePriority[i].first; + FCEU_printf("Result: %s, %d, %f\n", notes[marker_id].c_str(), marker_id, notePriority[i].second); + } + */ + + // Send selection to the marker found + int index = notePriority.size()-1 - search_similar_marker; + if (index >= 0 && notePriority[index].second >= MIN_PRIORITY_TRESHOLD) + { + int marker_id = notePriority[index].first; + int frame = GetMarkerFrame(marker_id); + if (frame >= 0) + selection.JumpToFrame(frame); + } else + { + if (search_similar_marker) + MessageBox(taseditor_window.hwndTasEditor, "Could not find more Notes similar to Marker Note under Playback cursor!", "Find Similar Note", MB_OK); + else + MessageBox(taseditor_window.hwndTasEditor, "Could not find anything similar to Marker Note under Playback cursor!", "Find Similar Note", MB_OK); + } + + // increase search_similar_marker so that next time we'll find another note + search_similar_marker++; +} +// ------------------------------------------------------------------------------------ +void MARKERS_MANAGER::UpdateMarkerNote() +{ + if (!marker_note_edit) return; + char new_text[MAX_NOTE_LEN]; + if (marker_note_edit == MARKER_NOTE_EDIT_UPPER) + { + int len = SendMessage(playback.hwndPlaybackMarkerEdit, WM_GETTEXT, MAX_NOTE_LEN, (LPARAM)new_text); + new_text[len] = 0; + // check changes + if (strcmp(GetNote(playback.shown_marker).c_str(), new_text)) + { + SetNote(playback.shown_marker, new_text); + if (playback.shown_marker) + history.RegisterMarkersChange(MODTYPE_MARKER_RENAME, GetMarkerFrame(playback.shown_marker)); + else + // zeroth marker - just assume it's set on frame 0 + history.RegisterMarkersChange(MODTYPE_MARKER_RENAME, 0); + // notify selection to change text in lower marker (in case both are showing same marker) + selection.must_find_current_marker = true; + } + } else if (marker_note_edit == MARKER_NOTE_EDIT_LOWER) + { + int len = SendMessage(selection.hwndSelectionMarkerEdit, WM_GETTEXT, MAX_NOTE_LEN, (LPARAM)new_text); + new_text[len] = 0; + // check changes + if (strcmp(GetNote(selection.shown_marker).c_str(), new_text)) + { + SetNote(selection.shown_marker, new_text); + if (selection.shown_marker) + history.RegisterMarkersChange(MODTYPE_MARKER_RENAME, GetMarkerFrame(selection.shown_marker)); + else + // zeroth marker - just assume it's set on frame 0 + history.RegisterMarkersChange(MODTYPE_MARKER_RENAME, 0); + // notify playback to change text in upper marker (in case both are showing same marker) + playback.must_find_current_marker = true; + } + } +} +// ------------------------------------------------------------------------------------ +BOOL CALLBACK FindNoteProc(HWND hwndDlg, UINT message, WPARAM wParam, LPARAM lParam) +{ + extern MARKERS_MANAGER markers_manager; + switch (message) + { + case WM_INITDIALOG: + { + if (taseditor_config.findnote_wndx == -32000) taseditor_config.findnote_wndx = 0; //Just in case + if (taseditor_config.findnote_wndy == -32000) taseditor_config.findnote_wndy = 0; + SetWindowPos(hwndDlg, 0, taseditor_config.findnote_wndx, taseditor_config.findnote_wndy, 0, 0, SWP_NOSIZE | SWP_NOZORDER | SWP_NOOWNERZORDER); + + CheckDlgButton(hwndDlg, IDC_MATCH_CASE, taseditor_config.findnote_matchcase?MF_CHECKED : MF_UNCHECKED); + if (taseditor_config.findnote_search_up) + Button_SetCheck(GetDlgItem(hwndDlg, IDC_RADIO_UP), BST_CHECKED); + else + Button_SetCheck(GetDlgItem(hwndDlg, IDC_RADIO_DOWN), BST_CHECKED); + HWND hwndEdit = GetDlgItem(hwndDlg, IDC_NOTE_TO_FIND); + SendMessage(hwndEdit, EM_SETLIMITTEXT, MAX_NOTE_LEN - 1, 0); + SetWindowText(hwndEdit, markers_manager.findnote_string); + if (GetDlgCtrlID((HWND)wParam) != IDC_NOTE_TO_FIND) + { + SetFocus(hwndEdit); + return false; + } + return true; + } + case WM_MOVE: + { + if (!IsIconic(hwndDlg)) + { + RECT wrect; + GetWindowRect(hwndDlg, &wrect); + taseditor_config.findnote_wndx = wrect.left; + taseditor_config.findnote_wndy = wrect.top; + WindowBoundsCheckNoResize(taseditor_config.findnote_wndx, taseditor_config.findnote_wndy, wrect.right); + } + break; + } + case WM_COMMAND: + { + switch (LOWORD(wParam)) + { + case IDC_NOTE_TO_FIND: + { + if(HIWORD(wParam) == EN_CHANGE) + { + if (GetWindowTextLength(GetDlgItem(hwndDlg, IDC_NOTE_TO_FIND))) + EnableWindow(GetDlgItem(hwndDlg, IDOK), true); + else + EnableWindow(GetDlgItem(hwndDlg, IDOK), false); + } + break; + } + case IDC_RADIO_UP: + taseditor_config.findnote_search_up = true; + break; + case IDC_RADIO_DOWN: + taseditor_config.findnote_search_up = false; + break; + case IDC_MATCH_CASE: + taseditor_config.findnote_matchcase ^= 1; + CheckDlgButton(hwndDlg, IDC_MATCH_CASE, taseditor_config.findnote_matchcase?MF_CHECKED : MF_UNCHECKED); + break; + case IDOK: + { + int len = SendMessage(GetDlgItem(hwndDlg, IDC_NOTE_TO_FIND), WM_GETTEXT, MAX_NOTE_LEN, (LPARAM)markers_manager.findnote_string); + markers_manager.findnote_string[len] = 0; + // scan frames from current selection to the border + int cur_marker = 0; + bool result; + int movie_size = currMovieData.getNumRecords(); + int current_frame = selection.GetCurrentSelectionBeginning(); + if (current_frame < 0 && taseditor_config.findnote_search_up) + current_frame = movie_size; + while (true) + { + // move forward + if (taseditor_config.findnote_search_up) + { + current_frame--; + if (current_frame < 0) + { + MessageBox(taseditor_window.hwndFindNote, "Nothing was found.", "Find Note", MB_OK); + break; + } + } else + { + current_frame++; + if (current_frame >= movie_size) + { + MessageBox(taseditor_window.hwndFindNote, "Nothing was found!", "Find Note", MB_OK); + break; + } + } + // scan marked frames + cur_marker = markers_manager.GetMarker(current_frame); + if (cur_marker) + { + if (taseditor_config.findnote_matchcase) + result = (strstr(markers_manager.GetNote(cur_marker).c_str(), markers_manager.findnote_string) != 0); + else + result = (StrStrI(markers_manager.GetNote(cur_marker).c_str(), markers_manager.findnote_string) != 0); + if (result) + { + // found note containing searched string - jump there + selection.JumpToFrame(current_frame); + break; + } + } + } + return TRUE; + } + case IDCANCEL: + DestroyWindow(taseditor_window.hwndFindNote); + taseditor_window.hwndFindNote = 0; + return TRUE; + } + break; + } + case WM_CLOSE: + case WM_QUIT: + { + DestroyWindow(taseditor_window.hwndFindNote); + taseditor_window.hwndFindNote = 0; + break; + } + } + return FALSE; +} + + diff --git a/src/drivers/win/taseditor/markers_manager.h b/src/drivers/win/taseditor/markers_manager.h new file mode 100644 index 00000000..141f020c --- /dev/null +++ b/src/drivers/win/taseditor/markers_manager.h @@ -0,0 +1,76 @@ +//Specification file for Markers_manager class +#include "markers.h" + +#define MARKERS_ID_LEN 8 +// constants for "Find Similar Note" algorithm (may need finetuning) +#define KEYWORD_MIN_LEN 2 +#define MAX_NUM_KEYWORDS (MAX_NOTE_LEN / (KEYWORD_MIN_LEN+1)) + 1 +#define KEYWORD_WEIGHT_BASE 2.0 +#define KEYWORD_WEIGHT_FACTOR -1.0 +#define KEYWORD_CASEINSENTITIVE_BONUS_PER_CHAR 1.0 // these two should be small, because they also work when keyword is inside another keyword, giving irrelevant results +#define KEYWORD_CASESENTITIVE_BONUS_PER_CHAR 1.0 +#define KEYWORD_SEQUENCE_BONUS_PER_CHAR 5.0 +#define KEYWORD_PENALTY_FOR_STRANGERS 0.2 +#define KEYWORDS_LINE_MIN_SEQUENCE 1 +#define MIN_PRIORITY_TRESHOLD 5.0 + +enum +{ + MARKER_NOTE_EDIT_NONE, + MARKER_NOTE_EDIT_UPPER, + MARKER_NOTE_EDIT_LOWER +}; + +class MARKERS_MANAGER +{ +public: + MARKERS_MANAGER(); + void init(); + void free(); + void reset(); + void update(); + + void save(EMUFILE *os, bool really_save = true); + bool load(EMUFILE *is); + + int GetMarkersSize(); + void SetMarkersSize(int new_size); + + int GetMarker(int frame); + int GetMarkerUp(int start_frame); + int GetMarkerUp(MARKERS& target_markers, int start_frame); // special version of the function + int GetMarkerFrame(int marker_id); + + int SetMarker(int frame); + void ClearMarker(int frame); + void ToggleMarker(int frame); + + void EraseMarker(int frame); + void insertEmpty(int at, int frames); + + int GetNotesSize(); + std::string GetNote(int index); + std::string GetNote(MARKERS& target_markers, int index); // special version of the function + void SetNote(int index, const char* new_text); + + void MakeCopyTo(MARKERS& destination); + void RestoreFromCopy(MARKERS& source, int until_frame = -1); + + bool checkMarkersDiff(MARKERS& their_markers); + bool checkMarkersDiff(MARKERS& their_markers, int end); + + void FindSimilar(); + void FindNextSimilar(); + + void UpdateMarkerNote(); + + // not saved vars + int marker_note_edit; + char findnote_string[MAX_NOTE_LEN]; + int search_similar_marker; + +private: + // saved vars + MARKERS markers; + +}; diff --git a/src/drivers/win/taseditor/playback.cpp b/src/drivers/win/taseditor/playback.cpp index 6641f05e..50a859c8 100644 --- a/src/drivers/win/taseditor/playback.cpp +++ b/src/drivers/win/taseditor/playback.cpp @@ -1,6 +1,5 @@ //Implementation file of Playback class #include "taseditor_project.h" -#include "..\taseditor.h" // only for MARKER_NOTE_EDIT_UPPER #ifdef _S9XLUA_H extern void ForceExecuteLuaFrameFunctions(); @@ -8,18 +7,14 @@ extern void ForceExecuteLuaFrameFunctions(); extern bool Taseditor_rewind_now; extern bool turbo; -extern int marker_note_edit; -extern int search_similar_marker; extern TASEDITOR_CONFIG taseditor_config; extern TASEDITOR_WINDOW taseditor_window; -extern MARKERS current_markers; +extern MARKERS_MANAGER markers_manager; extern GREENZONE greenzone; extern TASEDITOR_LIST list; extern BOOKMARKS bookmarks; -extern void UpdateMarkerNote(); - LRESULT APIENTRY UpperMarkerEditWndProc(HWND hWnd, UINT msg, WPARAM wParam, LPARAM lParam); WNDPROC playbackMarkerEdit_oldWndproc; @@ -53,7 +48,7 @@ void PLAYBACK::reset() must_find_current_marker = true; shown_marker = 0; lastCursor = -1; - pause_frame = old_pauseframe = 0; + lost_position_frame = pause_frame = old_pauseframe = 0; old_show_pauseframe = show_pauseframe = false; old_rewind_button_state = rewind_button_state = false; old_forward_button_state = forward_button_state = false; @@ -65,6 +60,11 @@ void PLAYBACK::reset() void PLAYBACK::update() { jump_was_used_this_frame = false; + + // forget lost_position_frame when the position is restored + if (currFrameCounter + 1 == lost_position_frame) + lost_position_frame = 0; + // pause when seeking hit pause_frame if(!FCEUI_EmulationPaused()) if(pause_frame && pause_frame <= currFrameCounter + 1) @@ -124,10 +124,10 @@ void PLAYBACK::update() lastCursor = currFrameCounter; UpdateWindow(list.hwndList); // lazy update of "Playback's Marker text" - int current_marker = current_markers.GetMarkerUp(currFrameCounter); + int current_marker = markers_manager.GetMarkerUp(currFrameCounter); if (shown_marker != current_marker) { - UpdateMarkerNote(); + markers_manager.UpdateMarkerNote(); shown_marker = current_marker; RedrawMarker(); must_find_current_marker = false; @@ -137,8 +137,8 @@ void PLAYBACK::update() // [non-lazy] update "Playback's Marker text" if needed if (must_find_current_marker) { - UpdateMarkerNote(); - shown_marker = current_markers.GetMarkerUp(currFrameCounter); + markers_manager.UpdateMarkerNote(); + shown_marker = markers_manager.GetMarkerUp(currFrameCounter); RedrawMarker(); must_find_current_marker = false; } @@ -270,7 +270,7 @@ void PLAYBACK::RewindFull() // jump to previous marker int index = currFrameCounter - 1; for (; index >= 0; index--) - if (current_markers.GetMarker(index)) break; + if (markers_manager.GetMarker(index)) break; if (index >= 0) jump(index); else @@ -283,7 +283,7 @@ void PLAYBACK::ForwardFull() int last_frame = currMovieData.getNumRecords()-1; int index = currFrameCounter + 1; for (; index <= last_frame; ++index) - if (current_markers.GetMarker(index)) break; + if (markers_manager.GetMarker(index)) break; if (index <= last_frame) jump(index); else @@ -301,17 +301,18 @@ void PLAYBACK::RedrawMarker() strcat(new_text, num); SetWindowText(hwndPlaybackMarker, new_text); // change marker note - strcpy(new_text, current_markers.GetNote(shown_marker).c_str()); + strcpy(new_text, markers_manager.GetNote(shown_marker).c_str()); SetWindowText(hwndPlaybackMarkerEdit, new_text); - // reset search_similar_marker - search_similar_marker = 0; + // reset search_similar_marker, because source marker changed + markers_manager.search_similar_marker = 0; } void PLAYBACK::StartFromZero() { poweron(true); currFrameCounter = 0; - greenzone.TryDumpIncremental(); + if(currMovieData.getNumRecords() == 0) + currMovieData.insertEmpty(-1, 1); } void PLAYBACK::jump(int frame) @@ -327,7 +328,7 @@ void PLAYBACK::restorePosition() if (pause_frame) jump(pause_frame-1); else - jump(currFrameCounter); + jump(lost_position_frame-1); } bool PLAYBACK::JumpToFrame(int index) @@ -367,7 +368,7 @@ bool PLAYBACK::JumpToFrame(int index) return true; } -int PLAYBACK::GetPauseFrame() +int PLAYBACK::GetFlashingPauseFrame() { if (show_pauseframe) return pause_frame; @@ -382,7 +383,7 @@ void PLAYBACK::SetProgressbar(int a, int b) // ------------------------------------------------------------------------- LRESULT APIENTRY UpperMarkerEditWndProc(HWND hWnd, UINT msg, WPARAM wParam, LPARAM lParam) { - if (marker_note_edit == MARKER_NOTE_EDIT_UPPER) + if (markers_manager.marker_note_edit == MARKER_NOTE_EDIT_UPPER) { extern PLAYBACK playback; extern TASEDITOR_SELECTION selection; @@ -394,7 +395,7 @@ LRESULT APIENTRY UpperMarkerEditWndProc(HWND hWnd, UINT msg, WPARAM wParam, LPAR { case VK_ESCAPE: // revert text to original note text - SetWindowText(playback.hwndPlaybackMarkerEdit, current_markers.GetNote(playback.shown_marker).c_str()); + SetWindowText(playback.hwndPlaybackMarkerEdit, markers_manager.GetNote(playback.shown_marker).c_str()); SetFocus(list.hwndList); return 0; case VK_RETURN: diff --git a/src/drivers/win/taseditor/playback.h b/src/drivers/win/taseditor/playback.h index 13b9571f..d204c2b1 100644 --- a/src/drivers/win/taseditor/playback.h +++ b/src/drivers/win/taseditor/playback.h @@ -36,11 +36,12 @@ public: void StartFromZero(); - int GetPauseFrame(); + int GetFlashingPauseFrame(); void SetProgressbar(int a, int b); bool JumpToFrame(int index); + int lost_position_frame; int pause_frame; bool must_find_current_marker; int shown_marker; diff --git a/src/drivers/win/taseditor/popup_display.cpp b/src/drivers/win/taseditor/popup_display.cpp index 9e5a553f..137313a4 100644 --- a/src/drivers/win/taseditor/popup_display.cpp +++ b/src/drivers/win/taseditor/popup_display.cpp @@ -4,9 +4,9 @@ extern TASEDITOR_CONFIG taseditor_config; extern TASEDITOR_WINDOW taseditor_window; -extern MARKERS current_markers; extern BOOKMARKS bookmarks; extern TASEDITOR_LIST list; +extern MARKERS_MANAGER markers_manager; LRESULT CALLBACK ScrBmpWndProc(HWND hwnd, UINT message, WPARAM wParam, LPARAM lParam); LRESULT APIENTRY MarkerNoteTooltipWndProc(HWND hwnd, UINT message, WPARAM wParam, LPARAM lParam); @@ -217,9 +217,9 @@ void POPUP_DISPLAY::ChangeTooltipText() { // retrieve info from the pointed bookmark's markers int frame = bookmarks.bookmarks_array[bookmarks.item_under_mouse].snapshot.jump_frame; - int marker_id = bookmarks.bookmarks_array[bookmarks.item_under_mouse].snapshot.my_markers.GetMarkerUp(frame); + int marker_id = markers_manager.GetMarkerUp(bookmarks.bookmarks_array[bookmarks.item_under_mouse].snapshot.GetMarkers(), frame); char new_text[MAX_NOTE_LEN]; - strcpy(new_text, bookmarks.bookmarks_array[bookmarks.item_under_mouse].snapshot.my_markers.GetNote(marker_id).c_str()); + strcpy(new_text, markers_manager.GetNote(bookmarks.bookmarks_array[bookmarks.item_under_mouse].snapshot.GetMarkers(), marker_id).c_str()); SetWindowText(marker_note_tooltip, new_text); } diff --git a/src/drivers/win/taseditor/recorder.cpp b/src/drivers/win/taseditor/recorder.cpp index 37986346..b9a396d9 100644 --- a/src/drivers/win/taseditor/recorder.cpp +++ b/src/drivers/win/taseditor/recorder.cpp @@ -1,17 +1,15 @@ //Implementation file of RECORDER class #include "taseditor_project.h" -#include "zlib.h" extern int joysticks_per_frame[NUM_SUPPORTED_INPUT_TYPES]; extern uint32 GetGamepadPressedImmediate(); -extern void ColumnSet(int column); extern int GetInputType(MovieData& md); extern TASEDITOR_CONFIG taseditor_config; extern TASEDITOR_WINDOW taseditor_window; extern BOOKMARKS bookmarks; -extern INPUT_HISTORY history; +extern HISTORY history; extern GREENZONE greenzone; extern TASEDITOR_LIST list; @@ -131,7 +129,7 @@ void RECORDER::update() { // if the button was pressed right now if ((current_joy[joy] & (1 << button)) && !(old_joy[joy] & (1 << button))) - ColumnSet(COLUMN_JOYPAD1_A + joy * NUM_JOYPAD_BUTTONS + button); + list.ColumnSet(COLUMN_JOYPAD1_A + joy * NUM_JOYPAD_BUTTONS + button); } } } diff --git a/src/drivers/win/taseditor/inputsnapshot.cpp b/src/drivers/win/taseditor/snapshot.cpp similarity index 81% rename from src/drivers/win/taseditor/inputsnapshot.cpp rename to src/drivers/win/taseditor/snapshot.cpp index b13b84f4..f26ad9cd 100644 --- a/src/drivers/win/taseditor/inputsnapshot.cpp +++ b/src/drivers/win/taseditor/snapshot.cpp @@ -1,19 +1,19 @@ -//Implementation file of Input Snapshot class (Undo feature) +//Implementation file of Snapshot class (Undo feature) #include "taseditor_project.h" #include "zlib.h" int joysticks_per_frame[NUM_SUPPORTED_INPUT_TYPES] = {1, 2, 4}; -extern MARKERS current_markers; +extern MARKERS_MANAGER markers_manager; extern TASEDITOR_SELECTION selection; extern int GetInputType(MovieData& md); -INPUT_SNAPSHOT::INPUT_SNAPSHOT() +SNAPSHOT::SNAPSHOT() { } -void INPUT_SNAPSHOT::init(MovieData& md, bool hotchanges, int force_input_type) +void SNAPSHOT::init(MovieData& md, bool hotchanges, int force_input_type) { has_hot_changes = hotchanges; if (force_input_type < 0) @@ -64,12 +64,11 @@ void INPUT_SNAPSHOT::init(MovieData& md, bool hotchanges, int force_input_type) } } - // make a copy of current_markers - my_markers.MakeCopy(current_markers); - if ((int)my_markers.GetMarkersSize() < size) - my_markers.SetMarkersSize(size); + // make a copy of markers_manager.markers + markers_manager.MakeCopyTo(my_markers); + if ((int)my_markers.markers_array.size() < size) + my_markers.markers_array.resize(size); - coherent = true; already_compressed = false; // save current time to description time_t raw_time; @@ -78,12 +77,20 @@ void INPUT_SNAPSHOT::init(MovieData& md, bool hotchanges, int force_input_type) strftime(description, 10, "%H:%M:%S", timeinfo); } -void INPUT_SNAPSHOT::copyToMarkers(int end) +bool SNAPSHOT::MarkersDifferFromCurrent(int end) { - current_markers.RestoreFromCopy(my_markers, end); + return markers_manager.checkMarkersDiff(my_markers, end); +} +void SNAPSHOT::copyToMarkers(int end) +{ + markers_manager.RestoreFromCopy(my_markers, end); +} +MARKERS& SNAPSHOT::GetMarkers() +{ + return my_markers; } -void INPUT_SNAPSHOT::toMovie(MovieData& md, int start, int end) +void SNAPSHOT::toMovie(MovieData& md, int start, int end) { if (end < 0) end = size-1; // write input data to movie data @@ -127,7 +134,7 @@ void INPUT_SNAPSHOT::toMovie(MovieData& md, int start, int end) } } -void INPUT_SNAPSHOT::compress_data() +void SNAPSHOT::compress_data() { // compress joysticks int len = joysticks.size(); @@ -154,12 +161,11 @@ void INPUT_SNAPSHOT::compress_data() already_compressed = true; } -void INPUT_SNAPSHOT::save(EMUFILE *os) +void SNAPSHOT::save(EMUFILE *os) { // write vars write32le(size, os); write8le(input_type, os); - if (coherent) write8le(1, os); else write8le((uint8)0, os); write32le(jump_frame, os); write32le(rec_end_frame, os); write32le(rec_joypad_diff_bits, os); @@ -185,18 +191,16 @@ void INPUT_SNAPSHOT::save(EMUFILE *os) os->fwrite(&hot_changes_compressed[0], hot_changes_compressed.size()); } // save markers data - my_markers.save(os, true); + my_markers.save(os); } // returns true if couldn't load -bool INPUT_SNAPSHOT::load(EMUFILE *is) +bool SNAPSHOT::load(EMUFILE *is) { uint8 tmp; // read vars if (!read32le(&size, is)) return true; if (!read8le(&tmp, is)) return true; input_type = tmp; - if (!read8le(&tmp, is)) return true; - coherent = (tmp != 0); if (!read32le(&jump_frame, is)) return true; if (!read32le(&rec_end_frame, is)) return true; if (!read32le(&rec_joypad_diff_bits, is)) return true; @@ -249,14 +253,13 @@ bool INPUT_SNAPSHOT::load(EMUFILE *is) if (my_markers.load(is)) return true; return false; } -bool INPUT_SNAPSHOT::skipLoad(EMUFILE *is) +bool SNAPSHOT::skipLoad(EMUFILE *is) { int tmp; uint8 tmp1; // read vars if (is->fseek(sizeof(int) + // size sizeof(uint8) + // input_type - sizeof(uint8) + // coherent sizeof(int) + // jump_frame sizeof(int) + // rec_end_frame sizeof(int) + // rec_joypad_diff_bits @@ -285,7 +288,7 @@ bool INPUT_SNAPSHOT::skipLoad(EMUFILE *is) } // return true if any difference is found -bool INPUT_SNAPSHOT::checkDiff(INPUT_SNAPSHOT& inp) +bool SNAPSHOT::checkDiff(SNAPSHOT& inp) { if (size != inp.size) return true; if (findFirstChange(inp) >= 0) @@ -295,7 +298,7 @@ bool INPUT_SNAPSHOT::checkDiff(INPUT_SNAPSHOT& inp) } // fills map of bits judging on which joypads differ (this function is only used by "Record" modtype) -void INPUT_SNAPSHOT::fillJoypadsDiff(INPUT_SNAPSHOT& inp, int frame) +void SNAPSHOT::fillJoypadsDiff(SNAPSHOT& inp, int frame) { rec_joypad_diff_bits = 0; uint32 current_mask = 1; @@ -323,8 +326,12 @@ void INPUT_SNAPSHOT::fillJoypadsDiff(INPUT_SNAPSHOT& inp, int frame) } // return number of first frame of difference between two snapshots -int INPUT_SNAPSHOT::findFirstChange(INPUT_SNAPSHOT& inp, int start, int end) +int SNAPSHOT::findFirstChange(SNAPSHOT& inp, int start, int end) { + // if these two snapshots have different input_type (abnormal situation) then refuse to search and return the beginning + if (inp.input_type != input_type) + return start; + // search for differences to the specified end (or to the end of this snapshot) if (end < 0 || end >= size) end = size-1; int inp_end = inp.size; @@ -402,8 +409,8 @@ int INPUT_SNAPSHOT::findFirstChange(INPUT_SNAPSHOT& inp, int start, int end) // no changes were found return -1; } -// return number of first frame of difference between this input_snapshot and MovieData -int INPUT_SNAPSHOT::findFirstChange(MovieData& md, int start, int end) +// return number of first frame of difference between this snapshot and MovieData +int SNAPSHOT::findFirstChange(MovieData& md, int start, int end) { // search for differences to the specified end (or to the end of this snapshot / to the end of the movie) if (end < 0 || end >= size) end = size-1; @@ -449,7 +456,7 @@ int INPUT_SNAPSHOT::findFirstChange(MovieData& md, int start, int end) return -1; // no changes were found } -int INPUT_SNAPSHOT::GetJoystickInfo(int frame, int joy) +int SNAPSHOT::GetJoystickInfo(int frame, int joy) { if (frame < 0 || frame >= size) return 0; switch(input_type) @@ -463,11 +470,63 @@ int INPUT_SNAPSHOT::GetJoystickInfo(int frame, int joy) } return 0; } + +void SNAPSHOT::insertFrames(int at, int frames) +{ + size += frames; + if(at == -1) + { + // append frames to the end + commands.resize(size); + joysticks.resize(BYTES_PER_JOYSTICK * joysticks_per_frame[input_type] * size); + if (has_hot_changes) + { + hot_changes.resize(joysticks_per_frame[input_type] * size * HOTCHANGE_BYTES_PER_JOY); + // fill new hotchanges with max value + int lower_limit = joysticks_per_frame[input_type] * (size - frames) * HOTCHANGE_BYTES_PER_JOY; + for (int i = hot_changes.size() - 1; i >= lower_limit; i--) + hot_changes[i] = 0xFF; + } + } else + { + // insert frames + // insert 1 byte of commands + commands.insert(commands.begin() + at, frames, 0); + // insert X bytes of joystics + int bytes = BYTES_PER_JOYSTICK * joysticks_per_frame[input_type]; + joysticks.insert(joysticks.begin() + (at * bytes), frames * bytes, 0); + if (has_hot_changes) + { + // insert X bytes of hot_changes + bytes = joysticks_per_frame[input_type] * HOTCHANGE_BYTES_PER_JOY; + hot_changes.insert(hot_changes.begin() + (at * bytes), frames * bytes, 0xFF); + } + } + // data was changed + already_compressed = false; +} +void SNAPSHOT::eraseFrame(int frame) +{ + // erase 1 byte of commands + commands.erase(commands.begin() + frame); + // erase X bytes of joystics + int bytes = BYTES_PER_JOYSTICK * joysticks_per_frame[input_type]; + joysticks.erase(joysticks.begin() + (frame * bytes), joysticks.begin() + ((frame + 1) * bytes)); + if (has_hot_changes) + { + // erase X bytes of hot_changes + bytes = joysticks_per_frame[input_type] * HOTCHANGE_BYTES_PER_JOY; + hot_changes.erase(hot_changes.begin() + (frame * bytes), hot_changes.begin() + ((frame + 1) * bytes)); + } + size--; + // data was changed + already_compressed = false; +} // -------------------------------------------------------- -void INPUT_SNAPSHOT::copyHotChanges(INPUT_SNAPSHOT* source_of_hotchanges, int limit_frame_of_source) +void SNAPSHOT::copyHotChanges(SNAPSHOT* source_of_hotchanges, int limit_frame_of_source) { // copy hot changes from source snapshot - if (source_of_hotchanges && source_of_hotchanges->has_hot_changes) + if (source_of_hotchanges && source_of_hotchanges->has_hot_changes && source_of_hotchanges->input_type == input_type) { int min = hot_changes.size(); if (min > (int)source_of_hotchanges->hot_changes.size()) @@ -483,10 +542,10 @@ void INPUT_SNAPSHOT::copyHotChanges(INPUT_SNAPSHOT* source_of_hotchanges, int li memcpy(&hot_changes[0], &source_of_hotchanges->hot_changes[0], min); } } -void INPUT_SNAPSHOT::inheritHotChanges(INPUT_SNAPSHOT* source_of_hotchanges) +void SNAPSHOT::inheritHotChanges(SNAPSHOT* source_of_hotchanges) { // copy hot changes from source snapshot and fade them - if (source_of_hotchanges && source_of_hotchanges->has_hot_changes) + if (source_of_hotchanges && source_of_hotchanges->has_hot_changes && source_of_hotchanges->input_type == input_type) { int min = hot_changes.size(); if (min > (int)source_of_hotchanges->hot_changes.size()) @@ -496,10 +555,10 @@ void INPUT_SNAPSHOT::inheritHotChanges(INPUT_SNAPSHOT* source_of_hotchanges) FadeHotChanges(); } } -void INPUT_SNAPSHOT::inheritHotChanges_DeleteSelection(INPUT_SNAPSHOT* source_of_hotchanges) +void SNAPSHOT::inheritHotChanges_DeleteSelection(SNAPSHOT* source_of_hotchanges) { // copy hot changes from source snapshot, but omit deleted frames (which are represented by current selection) - if (source_of_hotchanges && source_of_hotchanges->has_hot_changes) + if (source_of_hotchanges && source_of_hotchanges->has_hot_changes && source_of_hotchanges->input_type == input_type) { int bytes = joysticks_per_frame[input_type] * HOTCHANGE_BYTES_PER_JOY; int frame = 0, pos = 0, source_pos = 0; @@ -525,10 +584,10 @@ void INPUT_SNAPSHOT::inheritHotChanges_DeleteSelection(INPUT_SNAPSHOT* source_of FadeHotChanges(); } } -void INPUT_SNAPSHOT::inheritHotChanges_InsertSelection(INPUT_SNAPSHOT* source_of_hotchanges) +void SNAPSHOT::inheritHotChanges_InsertSelection(SNAPSHOT* source_of_hotchanges) { // copy hot changes from source snapshot, but insert filled lines for inserted frames (which are represented by current selection) - if (source_of_hotchanges && source_of_hotchanges->has_hot_changes) + if (source_of_hotchanges && source_of_hotchanges->has_hot_changes && source_of_hotchanges->input_type == input_type) { int bytes = joysticks_per_frame[input_type] * HOTCHANGE_BYTES_PER_JOY; int frame = 0, region_len = 0, pos = 0, source_pos = 0; @@ -588,16 +647,16 @@ void INPUT_SNAPSHOT::inheritHotChanges_InsertSelection(INPUT_SNAPSHOT* source_of } } } -void INPUT_SNAPSHOT::inheritHotChanges_PasteInsert(INPUT_SNAPSHOT* source_of_hotchanges) +void SNAPSHOT::inheritHotChanges_PasteInsert(SNAPSHOT* source_of_hotchanges, SelectionFrames& inserted_set) { // copy hot changes from source snapshot and insert filled lines for inserted frames (which are represented by inserted_set) - if (source_of_hotchanges && source_of_hotchanges->has_hot_changes) + if (source_of_hotchanges && source_of_hotchanges->has_hot_changes && source_of_hotchanges->input_type == input_type) { int bytes = joysticks_per_frame[input_type] * HOTCHANGE_BYTES_PER_JOY; int frame = 0, pos = 0, source_pos = 0; int this_size = hot_changes.size(), source_size = source_of_hotchanges->hot_changes.size(); - SelectionFrames::iterator it(selection.GetInsertedSet().begin()); - SelectionFrames::iterator inserted_set_end(selection.GetInsertedSet().end()); + SelectionFrames::iterator it(inserted_set.begin()); + SelectionFrames::iterator inserted_set_end(inserted_set.end()); while (pos < this_size) { if (it != inserted_set_end && frame == *it) @@ -622,8 +681,8 @@ void INPUT_SNAPSHOT::inheritHotChanges_PasteInsert(INPUT_SNAPSHOT* source_of_hot int bytes = joysticks_per_frame[input_type] * HOTCHANGE_BYTES_PER_JOY; int frame = 0, pos = 0; int this_size = hot_changes.size(); - SelectionFrames::iterator it(selection.GetInsertedSet().begin()); - SelectionFrames::iterator inserted_set_end(selection.GetInsertedSet().end()); + SelectionFrames::iterator it(inserted_set.begin()); + SelectionFrames::iterator inserted_set_end(inserted_set.end()); while (pos < this_size) { if (it != inserted_set_end && frame == *it) @@ -644,9 +703,13 @@ void INPUT_SNAPSHOT::inheritHotChanges_PasteInsert(INPUT_SNAPSHOT* source_of_hot } } } -void INPUT_SNAPSHOT::fillHotChanges(INPUT_SNAPSHOT& inp, int start, int end) +void SNAPSHOT::fillHotChanges(SNAPSHOT& inp, int start, int end) { - // search for differences to the specified end (or to the end of this snapshot) + // if these two snapshots have different input_type (abnormal situation) then refuse to compare + if (inp.input_type != input_type) + return; + + // compare snapshots to the specified end (or to the end of this snapshot) if (end < 0 || end >= size) end = size-1; int inp_end = inp.size; switch(input_type) @@ -735,7 +798,7 @@ void INPUT_SNAPSHOT::fillHotChanges(INPUT_SNAPSHOT& inp, int start, int end) } } -void INPUT_SNAPSHOT::SetMaxHotChange_Bits(int frame, int joypad, uint8 joy_bits) +void SNAPSHOT::SetMaxHotChange_Bits(int frame, int joypad, uint8 joy_bits) { uint8 mask = 1; // check all 8 buttons and set max hot_changes for bits that are set @@ -746,7 +809,7 @@ void INPUT_SNAPSHOT::SetMaxHotChange_Bits(int frame, int joypad, uint8 joy_bits) mask <<= 1; } } -void INPUT_SNAPSHOT::SetMaxHotChange(int frame, int absolute_button) +void SNAPSHOT::SetMaxHotChange(int frame, int absolute_button) { if (frame < 0 || frame >= size || !has_hot_changes) return; // set max value (15) to the button hotness @@ -788,7 +851,7 @@ void INPUT_SNAPSHOT::SetMaxHotChange(int frame, int absolute_button) } } -void INPUT_SNAPSHOT::FadeHotChanges() +void SNAPSHOT::FadeHotChanges() { uint8 hi_half, low_half; for (int i = hot_changes.size() - 1; i >= 0; i--) @@ -804,7 +867,7 @@ void INPUT_SNAPSHOT::FadeHotChanges() } } -int INPUT_SNAPSHOT::GetHotChangeInfo(int frame, int absolute_button) +int SNAPSHOT::GetHotChangeInfo(int frame, int absolute_button) { if (!has_hot_changes || frame < 0 || frame >= size || absolute_button < 0 || absolute_button >= NUM_JOYPAD_BUTTONS * joysticks_per_frame[input_type]) return 0; diff --git a/src/drivers/win/taseditor/inputsnapshot.h b/src/drivers/win/taseditor/snapshot.h similarity index 68% rename from src/drivers/win/taseditor/inputsnapshot.h rename to src/drivers/win/taseditor/snapshot.h index 20e89c2f..cef56f90 100644 --- a/src/drivers/win/taseditor/inputsnapshot.h +++ b/src/drivers/win/taseditor/snapshot.h @@ -13,33 +13,39 @@ enum Input_types #define SNAPSHOT_DESC_MAX_LENGTH 100 -class INPUT_SNAPSHOT +class SNAPSHOT { public: - INPUT_SNAPSHOT(); + SNAPSHOT(); void init(MovieData& md, bool hotchanges, int force_input_type = -1); - void toMovie(MovieData& md, int start = 0, int end = -1); + bool MarkersDifferFromCurrent(int end = -1); void copyToMarkers(int end = -1); + MARKERS& GetMarkers(); + + void toMovie(MovieData& md, int start = 0, int end = -1); void save(EMUFILE *os); bool load(EMUFILE *is); bool skipLoad(EMUFILE *is); - bool checkDiff(INPUT_SNAPSHOT& inp); - void fillJoypadsDiff(INPUT_SNAPSHOT& inp, int frame); + bool checkDiff(SNAPSHOT& inp); + void fillJoypadsDiff(SNAPSHOT& inp, int frame); - int findFirstChange(INPUT_SNAPSHOT& inp, int start = 0, int end = -1); + int findFirstChange(SNAPSHOT& inp, int start = 0, int end = -1); int findFirstChange(MovieData& md, int start = 0, int end = -1); int GetJoystickInfo(int frame, int joy); - void copyHotChanges(INPUT_SNAPSHOT* source_of_hotchanges, int limit_frame_of_source = -1); - void inheritHotChanges(INPUT_SNAPSHOT* source_of_hotchanges); - void inheritHotChanges_DeleteSelection(INPUT_SNAPSHOT* source_of_hotchanges); - void inheritHotChanges_InsertSelection(INPUT_SNAPSHOT* source_of_hotchanges); - void inheritHotChanges_PasteInsert(INPUT_SNAPSHOT* source_of_hotchanges); - void fillHotChanges(INPUT_SNAPSHOT& inp, int start = 0, int end = -1); + void insertFrames(int at, int frames); + void eraseFrame(int frame); + + void copyHotChanges(SNAPSHOT* source_of_hotchanges, int limit_frame_of_source = -1); + void inheritHotChanges(SNAPSHOT* source_of_hotchanges); + void inheritHotChanges_DeleteSelection(SNAPSHOT* source_of_hotchanges); + void inheritHotChanges_InsertSelection(SNAPSHOT* source_of_hotchanges); + void inheritHotChanges_PasteInsert(SNAPSHOT* source_of_hotchanges, SelectionFrames& inserted_set); + void fillHotChanges(SNAPSHOT& inp, int start = 0, int end = -1); void SetMaxHotChange_Bits(int frame, int joypad, uint8 joy_bits); void SetMaxHotChange(int frame, int absolute_button); @@ -48,14 +54,12 @@ public: int GetHotChangeInfo(int frame, int absolute_button); + // saved data int size; // in frames int input_type; // theoretically TAS Editor can support any 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 commands; // Format: commands-for-frame0, commands-for-frame1, ... std::vector hot_changes; // Format: buttons01joy0-for-frame0, buttons23joy0-for-frame0, buttons45joy0-for-frame0, buttons67joy0-for-frame0, buttons01joy1-for-frame0, ... - MARKERS my_markers; - - bool coherent; // indicates whether this state was made right after previous state int jump_frame; // for jumping when making undo int rec_end_frame; // for consecutive Recordings uint32 rec_joypad_diff_bits; // for consecutive Recordings @@ -65,11 +69,13 @@ public: private: void compress_data(); - - bool already_compressed; // to compress only once + + // also saved data + MARKERS my_markers; std::vector joysticks_compressed; std::vector commands_compressed; std::vector hot_changes_compressed; + bool already_compressed; // to compress only once }; diff --git a/src/drivers/win/taseditor/splicer.cpp b/src/drivers/win/taseditor/splicer.cpp index 7cc45294..6b725176 100644 --- a/src/drivers/win/taseditor/splicer.cpp +++ b/src/drivers/win/taseditor/splicer.cpp @@ -5,8 +5,8 @@ extern TASEDITOR_WINDOW taseditor_window; extern TASEDITOR_CONFIG taseditor_config; -extern INPUT_HISTORY history; -extern MARKERS current_markers; +extern HISTORY history; +extern MARKERS_MANAGER markers_manager; extern PLAYBACK playback; extern GREENZONE greenzone; extern TASEDITOR_LIST list; @@ -105,13 +105,13 @@ void SPLICER::CloneFrames() // end of current region currMovieData.cloneRegion(*it, frames); if (taseditor_config.bind_markers) - current_markers.insertEmpty(*it, frames); + markers_manager.insertEmpty(*it, frames); frames = 1; } else frames++; } if (taseditor_config.bind_markers) { - current_markers.update(); + markers_manager.update(); selection.must_find_current_marker = playback.must_find_current_marker = true; } greenzone.InvalidateAndCheck(history.RegisterChanges(MODTYPE_CLONE, *current_selection->begin())); @@ -139,13 +139,13 @@ void SPLICER::InsertFrames() // end of current region currMovieData.insertEmpty(*it,frames); if (taseditor_config.bind_markers) - current_markers.insertEmpty(*it,frames); + markers_manager.insertEmpty(*it,frames); frames = 1; } else frames++; } if (taseditor_config.bind_markers) { - current_markers.update(); + markers_manager.update(); selection.must_find_current_marker = playback.must_find_current_marker = true; } greenzone.InvalidateAndCheck(history.RegisterChanges(MODTYPE_INSERT, *current_selection->begin())); @@ -173,7 +173,7 @@ void SPLICER::InsertNumFrames() currMovieData.insertEmpty(index, frames); if (taseditor_config.bind_markers) { - current_markers.insertEmpty(index, frames); + markers_manager.insertEmpty(index, frames); selection.must_find_current_marker = playback.must_find_current_marker = true; } // select inserted rows @@ -197,7 +197,7 @@ void SPLICER::DeleteFrames() { currMovieData.records.erase(currMovieData.records.begin() + *it); if (taseditor_config.bind_markers) - current_markers.EraseMarker(*it); + markers_manager.EraseMarker(*it); } if (taseditor_config.bind_markers) selection.must_find_current_marker = playback.must_find_current_marker = true; @@ -249,7 +249,7 @@ void SPLICER::Truncate() currMovieData.truncateAt(frame+1); if (taseditor_config.bind_markers) { - current_markers.SetMarkersSize(frame+1); + markers_manager.SetMarkersSize(frame+1); selection.must_find_current_marker = playback.must_find_current_marker = true; } list.update(); @@ -308,26 +308,24 @@ bool SPLICER::Copy(SelectionFrames* current_selection) } } clipString << std::endl; - - if (!OpenClipboard(taseditor_window.hwndTasEditor)) - return false; - EmptyClipboard(); - - HGLOBAL hGlobal = GlobalAlloc(GMEM_MOVEABLE, clipString.str().size()+1); - - if (hGlobal==INVALID_HANDLE_VALUE) - { - CloseClipboard(); - return false; - } - char *pGlobal = (char*)GlobalLock(hGlobal); - strcpy(pGlobal, clipString.str().c_str()); - GlobalUnlock(hGlobal); - SetClipboardData(CF_TEXT, hGlobal); - - CloseClipboard(); } - + // write data to clipboard + if (!OpenClipboard(taseditor_window.hwndTasEditor)) + return false; + EmptyClipboard(); + + HGLOBAL hGlobal = GlobalAlloc(GMEM_MOVEABLE, clipString.str().size()+1); + if (hGlobal==INVALID_HANDLE_VALUE) + { + CloseClipboard(); + return false; + } + char *pGlobal = (char*)GlobalLock(hGlobal); + strcpy(pGlobal, clipString.str().c_str()); + GlobalUnlock(hGlobal); + SetClipboardData(CF_TEXT, hGlobal); + + CloseClipboard(); } catch (std::bad_alloc e) { @@ -374,7 +372,7 @@ bool SPLICER::Paste() if (currMovieData.getNumRecords() < pos+range) { currMovieData.insertEmpty(currMovieData.getNumRecords(),pos+range-currMovieData.getNumRecords()); - current_markers.update(); + markers_manager.update(); } pGlobal = strchr(pGlobal, '\n'); @@ -490,15 +488,14 @@ bool SPLICER::PasteInsert() if (pGlobal[0]=='T' && pGlobal[1]=='A' && pGlobal[2]=='S') { // make sure markers have the same size as movie - current_markers.update(); - // init inserted_set (for input history hot changes) - selection.GetInsertedSet().clear(); + markers_manager.update(); + // create inserted_set (for input history hot changes) + SelectionFrames inserted_set; // Extract number of frames int range; sscanf (pGlobal+3, "%d", &range); - pGlobal = strchr(pGlobal, '\n'); char* frame; int joy=0; @@ -514,7 +511,7 @@ bool SPLICER::PasteInsert() if (currMovieData.getNumRecords() < pos) { currMovieData.insertEmpty(currMovieData.getNumRecords(), pos - currMovieData.getNumRecords()); - current_markers.update(); + markers_manager.update(); } while (*frame && *frame != '\n' && *frame != '|') ++frame; @@ -526,8 +523,8 @@ bool SPLICER::PasteInsert() // insert new frame currMovieData.insertEmpty(pos, 1); - if (taseditor_config.bind_markers) current_markers.insertEmpty(pos, 1); - selection.GetInsertedSet().insert(pos); + if (taseditor_config.bind_markers) markers_manager.insertEmpty(pos, 1); + inserted_set.insert(pos); // read this frame input int joy = 0; @@ -555,10 +552,10 @@ bool SPLICER::PasteInsert() pGlobal = strchr(pGlobal, '\n'); } - current_markers.update(); + markers_manager.update(); if (taseditor_config.bind_markers) selection.must_find_current_marker = playback.must_find_current_marker = true; - greenzone.InvalidateAndCheck(history.RegisterChanges(MODTYPE_PASTEINSERT, *current_selection_begin)); + greenzone.InvalidateAndCheck(history.RegisterPasteInsert(*current_selection_begin, inserted_set)); // flash list header columns that were changed during paste for (int joy = 0; joy < num_joypads; ++joy) { diff --git a/src/drivers/win/taseditor/taseditor_config.cpp b/src/drivers/win/taseditor/taseditor_config.cpp index 8718ce48..0f239f7b 100644 --- a/src/drivers/win/taseditor/taseditor_config.cpp +++ b/src/drivers/win/taseditor/taseditor_config.cpp @@ -49,6 +49,7 @@ TASEDITOR_CONFIG::TASEDITOR_CONFIG() findnote_search_up = false; enable_auto_function = true; silent_autosave = true; + tooltips = true; // empty name last_author[0] = 0; diff --git a/src/drivers/win/taseditor/taseditor_config.h b/src/drivers/win/taseditor/taseditor_config.h index 7d8c854c..3fd29da4 100644 --- a/src/drivers/win/taseditor/taseditor_config.h +++ b/src/drivers/win/taseditor/taseditor_config.h @@ -1,4 +1,6 @@ //Specification file for TASEDITOR_CONFIG class +// -------------------------------------------------------- + #define GREENZONE_CAPACITY_MIN 1 #define GREENZONE_CAPACITY_MAX 50000 #define GREENZONE_CAPACITY_DEFAULT 10000 @@ -63,6 +65,7 @@ public: bool findnote_search_up; bool enable_auto_function; bool silent_autosave; + bool tooltips; char last_author[AUTHOR_MAX_LEN]; private: diff --git a/src/drivers/win/taseditor/taseditor_list.cpp b/src/drivers/win/taseditor/taseditor_list.cpp index 281651c0..043d313d 100644 --- a/src/drivers/win/taseditor/taseditor_list.cpp +++ b/src/drivers/win/taseditor/taseditor_list.cpp @@ -7,7 +7,6 @@ extern int joysticks_per_frame[NUM_SUPPORTED_INPUT_TYPES]; extern char buttonNames[NUM_JOYPAD_BUTTONS][2]; -extern void ColumnSet(int column); extern TASEDITOR_CONFIG taseditor_config; extern TASEDITOR_WINDOW taseditor_window; @@ -15,8 +14,8 @@ extern BOOKMARKS bookmarks; extern PLAYBACK playback; extern RECORDER recorder; extern GREENZONE greenzone; -extern INPUT_HISTORY history; -extern MARKERS current_markers; +extern HISTORY history; +extern MARKERS_MANAGER markers_manager; extern TASEDITOR_SELECTION selection; extern int GetInputType(MovieData& md); @@ -26,13 +25,12 @@ LRESULT APIENTRY ListWndProc(HWND hWnd, UINT msg, WPARAM wParam, LPARAM lParam); WNDPROC hwndList_oldWndProc = 0, hwndHeader_oldWndproc = 0; // resources +char list_save_id[LIST_ID_LEN] = "LIST"; +char list_skipsave_id[LIST_ID_LEN] = "LISX"; COLORREF hot_changes_colors[16] = { 0x0, 0x5c4c44, 0x854604, 0xab2500, 0xc20006, 0xd6006f, 0xd40091, 0xba00a4, 0x9500ba, 0x7a00cc, 0x5800d4, 0x0045e2, 0x0063ea, 0x0079f4, 0x0092fa, 0x00aaff }; //COLORREF hot_changes_colors[16] = { 0x0, 0x661212, 0x842B4E, 0x652C73, 0x48247D, 0x383596, 0x2947AE, 0x1E53C1, 0x135DD2, 0x116EDA, 0x107EE3, 0x0F8EEB, 0x209FF4, 0x3DB1FD, 0x51C2FF, 0x4DCDFF }; COLORREF header_lights_colors[11] = { 0x0, 0x006311, 0x008500, 0x1dad00, 0x46d100, 0x6ee300, 0x97e800, 0xb8f000, 0xdaf700, 0xffff7e, 0xffffb7 }; -char list_save_id[LIST_ID_LEN] = "LIST"; -char list_skipsave_id[LIST_ID_LEN] = "LISX"; - TASEDITOR_LIST::TASEDITOR_LIST() { } @@ -68,7 +66,7 @@ void TASEDITOR_LIST::init() "Arial"); /*font name*/ bg_brush = CreateSolidBrush(GetSysColor(COLOR_BTNFACE)); - header_colors.resize(MAX_NUM_COLUMNS); + header_colors.resize(TOTAL_COLUMNS); hwndList = GetDlgItem(taseditor_window.hwndTasEditor, IDC_LIST1); // prepare the main listview @@ -145,6 +143,9 @@ void TASEDITOR_LIST::init() bmp = LoadBitmap(fceu_hInstance, MAKEINTRESOURCE(IDB_TE_ARROW)); ImageList_AddMasked(himglist, bmp, 0xFFFFFF); DeleteObject(bmp); + bmp = LoadBitmap(fceu_hInstance, MAKEINTRESOURCE(IDB_TE_GREEN_ARROW)); + ImageList_AddMasked(himglist, bmp, 0xFFFFFF); + DeleteObject(bmp); ListView_SetImageList(hwndList, himglist, LVSIL_SMALL); // setup 0th column LVCOLUMN lvc; @@ -193,8 +194,8 @@ void TASEDITOR_LIST::reset() // delete all columns except 0th while (ListView_DeleteColumn(hwndList, 1)) {} // setup columns - LVCOLUMN lvc; num_columns = 1; + LVCOLUMN lvc; // frame number column lvc.mask = LVCF_WIDTH | LVCF_TEXT | LVCF_FMT; lvc.fmt = LVCFMT_CENTER; @@ -427,7 +428,7 @@ void TASEDITOR_LIST::FollowSelection() } void TASEDITOR_LIST::FollowPauseframe() { - int jump_frame = playback.GetPauseFrame(); + int jump_frame = playback.pause_frame; if (jump_frame >= 0) { // center list at jump_frame @@ -474,6 +475,8 @@ void TASEDITOR_LIST::GetDispInfo(NMLVDISPINFO* nmlvDispInfo) { if (item.iItem == currFrameCounter) item.iImage = ARROW_IMAGE_ID; + else if (item.iItem == playback.lost_position_frame-1) + item.iImage = GREEN_ARROW_IMAGE_ID; } } break; @@ -538,7 +541,7 @@ LONG TASEDITOR_LIST::CustomDraw(NMLVCUSTOMDRAW* msg) if(cell_x == COLUMN_FRAMENUM || cell_x == COLUMN_FRAMENUM2) { // font - if(current_markers.GetMarker(cell_y)) + if(markers_manager.GetMarker(cell_y)) SelectObject(msg->nmcd.hdc, hMainListSelectFont); else SelectObject(msg->nmcd.hdc, hMainListFont); @@ -547,24 +550,24 @@ LONG TASEDITOR_LIST::CustomDraw(NMLVCUSTOMDRAW* msg) if (cell_y == history.GetUndoHint()) { // undo hint here - if (taseditor_config.show_markers && current_markers.GetMarker(cell_y)) + if (taseditor_config.show_markers && markers_manager.GetMarker(cell_y)) { msg->clrTextBk = (taseditor_config.bind_markers) ? BINDMARKED_UNDOHINT_FRAMENUM_COLOR : MARKED_UNDOHINT_FRAMENUM_COLOR; } else { msg->clrTextBk = UNDOHINT_FRAMENUM_COLOR; } - } else if (cell_y == currFrameCounter || cell_y == (playback.GetPauseFrame() - 1)) + } else if (cell_y == currFrameCounter || cell_y == (playback.GetFlashingPauseFrame() - 1)) { // current frame - if (taseditor_config.show_markers && current_markers.GetMarker(cell_y)) + if (taseditor_config.show_markers && markers_manager.GetMarker(cell_y)) { msg->clrTextBk = (taseditor_config.bind_markers) ? CUR_BINDMARKED_FRAMENUM_COLOR : CUR_MARKED_FRAMENUM_COLOR; } else { msg->clrTextBk = CUR_FRAMENUM_COLOR; } - } else if (taseditor_config.show_markers && current_markers.GetMarker(cell_y)) + } else if (taseditor_config.show_markers && markers_manager.GetMarker(cell_y)) { // marked frame msg->clrTextBk = (taseditor_config.bind_markers) ? BINDMARKED_FRAMENUM_COLOR : MARKED_FRAMENUM_COLOR; @@ -600,7 +603,7 @@ LONG TASEDITOR_LIST::CustomDraw(NMLVCUSTOMDRAW* msg) { // undo hint here msg->clrTextBk = UNDOHINT_INPUT_COLOR1; - } else if (cell_y == currFrameCounter || cell_y == (playback.GetPauseFrame() - 1)) + } else if (cell_y == currFrameCounter || cell_y == (playback.GetFlashingPauseFrame() - 1)) { // current frame msg->clrTextBk = CUR_INPUT_COLOR1; @@ -633,7 +636,7 @@ LONG TASEDITOR_LIST::CustomDraw(NMLVCUSTOMDRAW* msg) { // undo hint here msg->clrTextBk = UNDOHINT_INPUT_COLOR2; - } else if (cell_y == currFrameCounter || cell_y == (playback.GetPauseFrame() - 1)) + } else if (cell_y == currFrameCounter || cell_y == (playback.GetFlashingPauseFrame() - 1)) { // current frame msg->clrTextBk = CUR_INPUT_COLOR2; @@ -684,9 +687,191 @@ LONG TASEDITOR_LIST::HeaderCustomDraw(NMLVCUSTOMDRAW* msg) return CDRF_DODEFAULT; } } + +void TASEDITOR_LIST::SingleClick(LPNMITEMACTIVATE info) +{ + int row_index = info->iItem; + if(row_index == -1) return; + int column_index = info->iSubItem; + + if(column_index == COLUMN_ICONS) + { + // click on the "icons" column - jump to the frame + selection.ClearSelection(); + playback.jump(row_index); + } else if(column_index == COLUMN_FRAMENUM || column_index == COLUMN_FRAMENUM2) + { + // click on the "frame number" column - set marker if clicked with Alt + if (info->uKeyFlags & LVKF_ALT) + { + // reverse MARKER_FLAG_BIT in pointed frame + markers_manager.ToggleMarker(row_index); + selection.must_find_current_marker = playback.must_find_current_marker = true; + if (markers_manager.GetMarker(row_index)) + history.RegisterMarkersChange(MODTYPE_MARKER_SET, row_index); + else + history.RegisterMarkersChange(MODTYPE_MARKER_UNSET, row_index); + RedrawRow(row_index); + } + } + else if(column_index >= COLUMN_JOYPAD1_A && column_index <= COLUMN_JOYPAD4_R) + { + ToggleJoypadBit(column_index, row_index, info->uKeyFlags); + } +} +void TASEDITOR_LIST::DoubleClick(LPNMITEMACTIVATE info) +{ + int row_index = info->iItem; + if(row_index == -1) return; + int column_index = info->iSubItem; + + if(column_index == COLUMN_ICONS || column_index == COLUMN_FRAMENUM || column_index == COLUMN_FRAMENUM2) + { + // double click sends playback to the frame + selection.ClearSelection(); + playback.jump(row_index); + } else if(column_index >= COLUMN_JOYPAD1_A && column_index <= COLUMN_JOYPAD4_R) + { + ToggleJoypadBit(column_index, row_index, info->uKeyFlags); + } +} + +void TASEDITOR_LIST::ToggleJoypadBit(int column_index, int row_index, UINT KeyFlags) +{ + int joy = (column_index - COLUMN_JOYPAD1_A) / NUM_JOYPAD_BUTTONS; + int bit = (column_index - COLUMN_JOYPAD1_A) % NUM_JOYPAD_BUTTONS; + if (KeyFlags & (LVKF_SHIFT|LVKF_CONTROL)) + { + // update multiple rows, using last row index as a flag to decide operation + SelectionFrames* current_selection = selection.MakeStrobe(); + SelectionFrames::iterator current_selection_end(current_selection->end()); + if (currMovieData.records[row_index].checkBit(joy, bit)) + { + // clear range + for(SelectionFrames::iterator it(current_selection->begin()); it != current_selection_end; it++) + { + currMovieData.records[*it].clearBit(joy, bit); + } + greenzone.InvalidateAndCheck(history.RegisterChanges(MODTYPE_UNSET, *current_selection->begin(), *current_selection->rbegin())); + } else + { + // set range + for(SelectionFrames::iterator it(current_selection->begin()); it != current_selection_end; it++) + { + currMovieData.records[*it].setBit(joy, bit); + } + greenzone.InvalidateAndCheck(history.RegisterChanges(MODTYPE_SET, *current_selection->begin(), *current_selection->rbegin())); + } + } else + { + // update one row + currMovieData.records[row_index].toggleBit(joy, bit); + if (currMovieData.records[row_index].checkBit(joy, bit)) + greenzone.InvalidateAndCheck(history.RegisterChanges(MODTYPE_SET, row_index, row_index)); + else + greenzone.InvalidateAndCheck(history.RegisterChanges(MODTYPE_UNSET, row_index, row_index)); + } + +} + +void TASEDITOR_LIST::ColumnSet(int column) +{ + if (column == COLUMN_FRAMENUM || column == COLUMN_FRAMENUM2) + FrameColumnSet(); + else + InputColumnSet(column); +} +void TASEDITOR_LIST::FrameColumnSet() +{ + SelectionFrames* current_selection = selection.MakeStrobe(); + if (current_selection->size() == 0) return; + SelectionFrames::iterator current_selection_begin(current_selection->begin()); + SelectionFrames::iterator current_selection_end(current_selection->end()); + + // inspect the selected frames, if they are all set, then unset all, else set all + bool unset_found = false, changes_made = false; + for(SelectionFrames::iterator it(current_selection_begin); it != current_selection_end; it++) + { + if(!markers_manager.GetMarker(*it)) + { + unset_found = true; + break; + } + } + if (unset_found) + { + // set all + for(SelectionFrames::iterator it(current_selection_begin); it != current_selection_end; it++) + { + if(!markers_manager.GetMarker(*it)) + { + if (markers_manager.SetMarker(*it)) + { + changes_made = true; + RedrawRow(*it); + } + } + } + if (changes_made) + history.RegisterMarkersChange(MODTYPE_MARKER_SET, *current_selection_begin, *current_selection->rbegin()); + } else + { + // unset all + for(SelectionFrames::iterator it(current_selection_begin); it != current_selection_end; it++) + { + if(markers_manager.GetMarker(*it)) + { + markers_manager.ClearMarker(*it); + changes_made = true; + RedrawRow(*it); + } + } + if (changes_made) + history.RegisterMarkersChange(MODTYPE_MARKER_UNSET, *current_selection_begin, *current_selection->rbegin()); + } + if (changes_made) + { + selection.must_find_current_marker = playback.must_find_current_marker = true; + SetHeaderColumnLight(COLUMN_FRAMENUM, HEADER_LIGHT_MAX); + } +} +void TASEDITOR_LIST::InputColumnSet(int column) +{ + int joy = (column - COLUMN_JOYPAD1_A) / NUM_JOYPAD_BUTTONS; + if (joy < 0 || joy >= joysticks_per_frame[GetInputType(currMovieData)]) return; + int button = (column - COLUMN_JOYPAD1_A) % NUM_JOYPAD_BUTTONS; + + SelectionFrames* current_selection = selection.MakeStrobe(); + if (current_selection->size() == 0) return; + SelectionFrames::iterator current_selection_begin(current_selection->begin()); + SelectionFrames::iterator current_selection_end(current_selection->end()); + + //inspect the selected frames, if they are all set, then unset all, else set all + bool newValue = false; + for(SelectionFrames::iterator it(current_selection_begin); it != current_selection_end; it++) + { + if(!(currMovieData.records[*it].checkBit(joy,button))) + { + newValue = true; + break; + } + } + // apply newValue + for(SelectionFrames::iterator it(current_selection_begin); it != current_selection_end; it++) + currMovieData.records[*it].setBitValue(joy,button,newValue); + if (newValue) + { + greenzone.InvalidateAndCheck(history.RegisterChanges(MODTYPE_SET, *current_selection_begin, *current_selection->rbegin())); + } else + { + greenzone.InvalidateAndCheck(history.RegisterChanges(MODTYPE_UNSET, *current_selection_begin, *current_selection->rbegin())); + } + SetHeaderColumnLight(column, HEADER_LIGHT_MAX); +} // ------------------------------------------------------------------------- LRESULT APIENTRY HeaderWndProc(HWND hWnd, UINT msg, WPARAM wParam, LPARAM lParam) { + extern TASEDITOR_LIST list; switch(msg) { case WM_SETCURSOR: @@ -702,7 +887,7 @@ LRESULT APIENTRY HeaderWndProc(HWND hWnd, UINT msg, WPARAM wParam, LPARAM lParam info.pt.y = GET_Y_LPARAM(lParam); SendMessage(hWnd,HDM_HITTEST,0,(LPARAM)&info); if(info.iItem >= COLUMN_FRAMENUM && info.iItem <= COLUMN_FRAMENUM2) - ColumnSet(info.iItem); + list.ColumnSet(info.iItem); } } return true; diff --git a/src/drivers/win/taseditor/taseditor_list.h b/src/drivers/win/taseditor/taseditor_list.h index 7196920e..35f26c84 100644 --- a/src/drivers/win/taseditor/taseditor_list.h +++ b/src/drivers/win/taseditor/taseditor_list.h @@ -51,7 +51,7 @@ enum COLUMN_JOYPAD4_R, COLUMN_FRAMENUM2, - MAX_NUM_COLUMNS + TOTAL_COLUMNS }; // when there's too many button columns, there's need for 2nd Frame# column at the end @@ -59,6 +59,7 @@ enum #define DIGITS_IN_FRAMENUM 7 #define ARROW_IMAGE_ID 20 +#define GREEN_ARROW_IMAGE_ID 21 #define COLUMN_ICONS_WIDTH 13 #define COLUMN_FRAMENUM_WIDTH 75 @@ -135,6 +136,15 @@ public: LONG CustomDraw(NMLVCUSTOMDRAW* msg); LONG HeaderCustomDraw(NMLVCUSTOMDRAW* msg); + void SingleClick(LPNMITEMACTIVATE info); + void DoubleClick(LPNMITEMACTIVATE info); + + void ToggleJoypadBit(int column_index, int row_index, UINT KeyFlags); + void ColumnSet(int column); + void InputColumnSet(int column); + void FrameColumnSet(); + + HWND hwndList, hwndHeader; // GDI stuff diff --git a/src/drivers/win/taseditor/taseditor_lua.cpp b/src/drivers/win/taseditor/taseditor_lua.cpp index d03a6cf2..ecc7263e 100644 --- a/src/drivers/win/taseditor/taseditor_lua.cpp +++ b/src/drivers/win/taseditor/taseditor_lua.cpp @@ -1,12 +1,14 @@ //Implementation file of TASEDITOR_LUA class #include "taseditor_project.h" +extern TASEDITOR_CONFIG taseditor_config; extern TASEDITOR_WINDOW taseditor_window; -extern INPUT_HISTORY history; -extern MARKERS current_markers; +extern HISTORY history; +extern MARKERS_MANAGER markers_manager; extern BOOKMARKS bookmarks; extern RECORDER recorder; extern PLAYBACK playback; +extern GREENZONE greenzone; extern TASEDITOR_LIST list; extern TASEDITOR_SELECTION selection; @@ -18,6 +20,7 @@ TASEDITOR_LUA::TASEDITOR_LUA() void TASEDITOR_LUA::init() { + pending_changes.resize(0); hwndRunFunction = GetDlgItem(taseditor_window.hwndTasEditor, TASEDITOR_RUN_MANUAL); TaseditorUpdateManualFunctionStatus(); reset(); @@ -39,6 +42,39 @@ void TASEDITOR_LUA::DisableRunFunction() { EnableWindow(hwndRunFunction, false); } + +void TASEDITOR_LUA::InsertDelete_rows_to_Snaphot(SNAPSHOT& snapshot) +{ + int size = pending_changes.size(); + if (size) + { + // apply changes to given snapshot (only insertion/deletion) + for (int i = 0; i < size; ++i) + { + if (pending_changes[i].frame >= snapshot.size) + // expand snapshot to fit the frame + snapshot.insertFrames(-1, 1 + pending_changes[i].frame - snapshot.size); + switch (pending_changes[i].type) + { + case LUA_CHANGE_TYPE_INSERTFRAMES: + { + snapshot.insertFrames(pending_changes[i].frame, pending_changes[i].data); + break; + } + case LUA_CHANGE_TYPE_DELETEFRAMES: + { + for (int t = pending_changes[i].data; t > 0; t--) + { + if (pending_changes[i].frame < snapshot.size) + snapshot.eraseFrame(pending_changes[i].frame); + } + break; + } + } + } + } +} + // -------------------------------------------------------------------------------- // Lua functions of taseditor library @@ -52,7 +88,7 @@ bool TASEDITOR_LUA::engaged() bool TASEDITOR_LUA::markedframe(int frame) { if (FCEUMOV_Mode(MOVIEMODE_TASEDITOR)) - return current_markers.GetMarker(frame) != 0; + return markers_manager.GetMarker(frame) != 0; else return false; } @@ -61,7 +97,7 @@ bool TASEDITOR_LUA::markedframe(int frame) int TASEDITOR_LUA::getmarker(int frame) { if (FCEUMOV_Mode(MOVIEMODE_TASEDITOR)) - return current_markers.GetMarkerUp(frame); + return markers_manager.GetMarkerUp(frame); else return -1; } @@ -71,10 +107,10 @@ int TASEDITOR_LUA::setmarker(int frame) { if (FCEUMOV_Mode(MOVIEMODE_TASEDITOR)) { - int marker_id = current_markers.GetMarker(frame); + int marker_id = markers_manager.GetMarker(frame); if(!marker_id) { - marker_id = current_markers.SetMarker(frame); + marker_id = markers_manager.SetMarker(frame); if (marker_id) { // new marker was created - register changes in TAS Editor @@ -93,9 +129,9 @@ void TASEDITOR_LUA::clearmarker(int frame) { if (FCEUMOV_Mode(MOVIEMODE_TASEDITOR)) { - if (current_markers.GetMarker(frame)) + if (markers_manager.GetMarker(frame)) { - current_markers.ClearMarker(frame); + markers_manager.ClearMarker(frame); // marker was deleted - register changes in TAS Editor history.RegisterMarkersChange(MODTYPE_LUA_MARKER_UNSET, frame); selection.must_find_current_marker = playback.must_find_current_marker = true; @@ -108,8 +144,9 @@ void TASEDITOR_LUA::clearmarker(int frame) const char* TASEDITOR_LUA::getnote(int index) { if (FCEUMOV_Mode(MOVIEMODE_TASEDITOR)) - return current_markers.GetNote(index).c_str(); - else + { + return strdup(markers_manager.GetNote(index).c_str()); + } else return NULL; } @@ -121,11 +158,11 @@ void TASEDITOR_LUA::setnote(int index, const char* newtext) // rename only if newtext is different from old text char text[MAX_NOTE_LEN]; strncpy(text, newtext, MAX_NOTE_LEN - 1); - if (strcmp(current_markers.GetNote(index).c_str(), text)) + if (strcmp(markers_manager.GetNote(index).c_str(), text)) { // text differs from old note - rename - current_markers.SetNote(index, text); - history.RegisterMarkersChange(MODTYPE_LUA_MARKER_RENAME, current_markers.GetMarkerFrame(index)); + markers_manager.SetNote(index, text); + history.RegisterMarkersChange(MODTYPE_LUA_MARKER_RENAME, markers_manager.GetMarkerFrame(index)); selection.must_find_current_marker = playback.must_find_current_marker = true; } } @@ -149,6 +186,15 @@ const char* TASEDITOR_LUA::getrecordermode() return NULL; } +// int taseditor.getlostplayback() +int TASEDITOR_LUA::getlostplayback() +{ + if (FCEUMOV_Mode(MOVIEMODE_TASEDITOR)) + return playback.lost_position_frame - 1; + else + return -1; +} + // int taseditor.getplaybacktarget() int TASEDITOR_LUA::getplaybacktarget() { @@ -158,7 +204,7 @@ int TASEDITOR_LUA::getplaybacktarget() return -1; } -// taseditor.setplayback() +// taseditor.setplayback(int frame) void TASEDITOR_LUA::setplayback(int frame) { if (FCEUMOV_Mode(MOVIEMODE_TASEDITOR)) @@ -172,6 +218,220 @@ void TASEDITOR_LUA::stopseeking() playback.SeekingStop(); } +// table taseditor.getselection() +void TASEDITOR_LUA::getselection(std::vector& placeholder) +{ + if (FCEUMOV_Mode(MOVIEMODE_TASEDITOR)) + { + SelectionFrames* current_selection = selection.MakeStrobe(); + int frames = current_selection->size(); + if (!frames) return; + + placeholder.resize(frames); + SelectionFrames::iterator current_selection_end(current_selection->end()); + int i = 0; + for(SelectionFrames::iterator it(current_selection->begin()); it != current_selection_end; ++it) + placeholder[i++] = *it; + } +} + +// taseditor.setselection(table new_set) +void TASEDITOR_LUA::setselection(std::vector& new_set) +{ + if (FCEUMOV_Mode(MOVIEMODE_TASEDITOR)) + { + selection.ClearSelection(); + for (int i = new_set.size() - 1; i >= 0; i--) + selection.SetRowSelection(new_set[i]); + } +} + +// int taseditor.getinput(int frame, int joypad) +int TASEDITOR_LUA::getinput(int frame, int joypad) +{ + if (FCEUMOV_Mode(MOVIEMODE_TASEDITOR)) + { + if (frame < 0) return -1; + if (frame >= currMovieData.getNumRecords()) return 0; + switch (joypad) + { + case LUA_JOYPAD_COMMANDS: + return currMovieData.records[frame].commands; + case LUA_JOYPAD_1P: + return currMovieData.records[frame].joysticks[0]; + case LUA_JOYPAD_2P: + return currMovieData.records[frame].joysticks[1]; + case LUA_JOYPAD_3P: + return currMovieData.records[frame].joysticks[2]; + case LUA_JOYPAD_4P: + return currMovieData.records[frame].joysticks[3]; + } + return -1; + } else + { + return -1; + } +} + +// taseditor.submitinputchange(int frame, int joypad, int input) +void TASEDITOR_LUA::submitinputchange(int frame, int joypad, int input) +{ + if (FCEUMOV_Mode(MOVIEMODE_TASEDITOR)) + { + if (frame >= 0) + { + if (joypad == LUA_JOYPAD_COMMANDS || joypad == LUA_JOYPAD_1P || joypad == LUA_JOYPAD_2P || joypad == LUA_JOYPAD_3P || joypad == LUA_JOYPAD_4P) + { + PENDING_CHANGES new_change; + new_change.type = LUA_CHANGE_TYPE_INPUTCHANGE; + new_change.frame = frame; + new_change.joypad = joypad; + new_change.data = input; + pending_changes.push_back(new_change); + } + } + } +} + +void TASEDITOR_LUA::submitinsertframes(int frame, int number) +{ + if (FCEUMOV_Mode(MOVIEMODE_TASEDITOR)) + { + if (frame >= 0 && number > 0) + { + PENDING_CHANGES new_change; + new_change.type = LUA_CHANGE_TYPE_INSERTFRAMES; + new_change.frame = frame; + new_change.joypad = 0; // doesn't matter in TAS Editor v1.0, whole frame will be inserted + new_change.data = number; + pending_changes.push_back(new_change); + } + } +} + +void TASEDITOR_LUA::submitdeleteframes(int frame, int number) +{ + if (FCEUMOV_Mode(MOVIEMODE_TASEDITOR)) + { + if (frame >= 0 && number > 0) + { + PENDING_CHANGES new_change; + new_change.type = LUA_CHANGE_TYPE_DELETEFRAMES; + new_change.frame = frame; + new_change.joypad = 0; // doesn't matter in TAS Editor v1.0, whole frame will be deleted + new_change.data = number; + pending_changes.push_back(new_change); + } + } +} + +// int taseditor.applyinputchanges([string name]) +int TASEDITOR_LUA::applyinputchanges(const char* name) +{ + if (FCEUMOV_Mode(MOVIEMODE_TASEDITOR)) + { + int size = pending_changes.size(); + int start_index = currMovieData.getNumRecords() - 1; + bool InsertionDeletion_was_made = false; + if (size) + { + // apply changes to current movie data + for (int i = 0; i < size; ++i) + { + if (pending_changes[i].frame < start_index) + start_index = pending_changes[i].frame; + if (pending_changes[i].frame >= (int)currMovieData.getNumRecords()) + { + // expand movie to fit the frame + currMovieData.insertEmpty(-1, 1 + pending_changes[i].frame - currMovieData.getNumRecords()); + markers_manager.update(); + InsertionDeletion_was_made = true; + } + switch (pending_changes[i].type) + { + case LUA_CHANGE_TYPE_INPUTCHANGE: + { + switch (pending_changes[i].joypad) + { + case LUA_JOYPAD_COMMANDS: + currMovieData.records[pending_changes[i].frame].commands = pending_changes[i].data; + break; + case LUA_JOYPAD_1P: + currMovieData.records[pending_changes[i].frame].joysticks[0] = pending_changes[i].data; + break; + case LUA_JOYPAD_2P: + currMovieData.records[pending_changes[i].frame].joysticks[1] = pending_changes[i].data; + break; + case LUA_JOYPAD_3P: + currMovieData.records[pending_changes[i].frame].joysticks[2] = pending_changes[i].data; + break; + case LUA_JOYPAD_4P: + currMovieData.records[pending_changes[i].frame].joysticks[3] = pending_changes[i].data; + break; + } + break; + } + case LUA_CHANGE_TYPE_INSERTFRAMES: + { + InsertionDeletion_was_made = true; + currMovieData.insertEmpty(pending_changes[i].frame, pending_changes[i].data); + if (taseditor_config.bind_markers) + markers_manager.insertEmpty(pending_changes[i].frame, pending_changes[i].data); + break; + } + case LUA_CHANGE_TYPE_DELETEFRAMES: + { + InsertionDeletion_was_made = true; + for (int t = pending_changes[i].data; t > 0; t--) + { + if (pending_changes[i].frame < (int)currMovieData.getNumRecords()) + currMovieData.records.erase(currMovieData.records.begin() + pending_changes[i].frame); + if (taseditor_config.bind_markers) + markers_manager.EraseMarker(pending_changes[i].frame); + } + break; + } + } + } + if (taseditor_config.bind_markers) + selection.must_find_current_marker = playback.must_find_current_marker = true; + // check if user deleted all frames + if (!currMovieData.getNumRecords()) + playback.StartFromZero(); + // reduce list + list.update(); + // check actual changes + int result = history.RegisterLuaChanges(name, start_index, InsertionDeletion_was_made); + if (result >= 0) + { + greenzone.InvalidateAndCheck(result); + } else if (greenzone.greenZoneCount >= currMovieData.getNumRecords()) + { + greenzone.InvalidateAndCheck(currMovieData.getNumRecords()-1); + } else list.RedrawList(); + + pending_changes.resize(0); + return result; + } else + { + return -1; + } + } else + { + return -1; + } +} + +// taseditor.clearinputchanges() +void TASEDITOR_LUA::clearinputchanges() +{ + if (FCEUMOV_Mode(MOVIEMODE_TASEDITOR)) + { + pending_changes.resize(0); + } +} + + diff --git a/src/drivers/win/taseditor/taseditor_lua.h b/src/drivers/win/taseditor/taseditor_lua.h index 089deee7..a1214f7d 100644 --- a/src/drivers/win/taseditor/taseditor_lua.h +++ b/src/drivers/win/taseditor/taseditor_lua.h @@ -1,4 +1,29 @@ //Specification file for TASEDITOR_LUA class +#define LUACHANGES_NAME_MAX_LEN 80 // the name should not be longer than 80 letters + +struct PENDING_CHANGES +{ + uint8 type; + int frame; + uint8 joypad; + int data; +}; + +enum +{ + LUA_CHANGE_TYPE_INPUTCHANGE = 0, + LUA_CHANGE_TYPE_INSERTFRAMES = 1, + LUA_CHANGE_TYPE_DELETEFRAMES = 2, +}; + +enum +{ + LUA_JOYPAD_COMMANDS = 0, + LUA_JOYPAD_1P = 1, + LUA_JOYPAD_2P = 2, + LUA_JOYPAD_3P = 3, + LUA_JOYPAD_4P = 4, +}; class TASEDITOR_LUA { @@ -11,6 +36,8 @@ public: void EnableRunFunction(); void DisableRunFunction(); + void InsertDelete_rows_to_Snaphot(SNAPSHOT& snapshot); + // Taseditor Lua library bool engaged(); bool markedframe(int frame); @@ -21,12 +48,22 @@ public: void setnote(int index, const char* newtext); int getcurrentbranch(); const char* getrecordermode(); + int getlostplayback(); int getplaybacktarget(); void setplayback(int frame); void stopseeking(); - + void getselection(std::vector& placeholder); + void setselection(std::vector& new_set); + int getinput(int frame, int joypad); + void submitinputchange(int frame, int joypad, int input); + void submitinsertframes(int frame, int number); + void submitdeleteframes(int frame, int number); + int applyinputchanges(const char* name); + void clearinputchanges(); private: + std::vector pending_changes; + HWND hwndRunFunction; }; diff --git a/src/drivers/win/taseditor/taseditor_project.cpp b/src/drivers/win/taseditor/taseditor_project.cpp index 66d11b71..3704ccac 100644 --- a/src/drivers/win/taseditor/taseditor_project.cpp +++ b/src/drivers/win/taseditor/taseditor_project.cpp @@ -5,13 +5,13 @@ extern TASEDITOR_CONFIG taseditor_config; extern TASEDITOR_WINDOW taseditor_window; -extern MARKERS current_markers; +extern MARKERS_MANAGER markers_manager; extern BOOKMARKS bookmarks; extern POPUP_DISPLAY popup_display; extern GREENZONE greenzone; extern PLAYBACK playback; extern RECORDER recorder; -extern INPUT_HISTORY history; +extern HISTORY history; extern TASEDITOR_LIST list; extern TASEDITOR_SELECTION selection; extern SPLICER splicer; @@ -28,14 +28,14 @@ TASEDITOR_PROJECT::TASEDITOR_PROJECT() void TASEDITOR_PROJECT::init() { + projectFile = ""; + projectName = ""; + fm2FileName = ""; reset(); } void TASEDITOR_PROJECT::reset() { changed = false; - projectFile = ""; - projectName = ""; - fm2FileName = ""; } void TASEDITOR_PROJECT::update() { @@ -65,7 +65,7 @@ bool TASEDITOR_PROJECT::save() // save all modules unsigned int saved_stuff = ALL_SAVED; write32le(saved_stuff, ofs); - current_markers.save(ofs); + markers_manager.save(ofs); bookmarks.save(ofs); greenzone.save(ofs); history.save(ofs); @@ -94,7 +94,7 @@ bool TASEDITOR_PROJECT::save_compact(char* filename, bool save_binary, bool save if (save_list) saved_stuff |= LIST_SAVED; if (save_selection) saved_stuff |= SELECTION_SAVED; write32le(saved_stuff, ofs); - current_markers.save(ofs, save_markers); + markers_manager.save(ofs, save_markers); bookmarks.save(ofs, save_bookmarks); greenzone.save(ofs, save_greenzone); history.save(ofs, save_history); @@ -138,7 +138,7 @@ bool TASEDITOR_PROJECT::load(char* fullname) // load modules unsigned int saved_stuff; read32le(&saved_stuff, &ifs); - current_markers.load(&ifs); + markers_manager.load(&ifs); bookmarks.load(&ifs); greenzone.load(&ifs); history.load(&ifs); diff --git a/src/drivers/win/taseditor/taseditor_project.h b/src/drivers/win/taseditor/taseditor_project.h index 8668dae5..555d9332 100644 --- a/src/drivers/win/taseditor/taseditor_project.h +++ b/src/drivers/win/taseditor/taseditor_project.h @@ -1,22 +1,19 @@ //Specification file for the TASEDITOR_PROJECT class -#include -typedef std::set SelectionFrames; - #include #include "movie.h" #include "../common.h" #include "taseditor_config.h" #include "taseditor_window.h" -#include "markers.h" -#include "inputsnapshot.h" -#include "inputhistory.h" +#include "taseditor_sel.h" +#include "markers_manager.h" +#include "snapshot.h" +#include "history.h" #include "playback.h" #include "recorder.h" #include "greenzone.h" #include "bookmarks.h" #include "taseditor_list.h" #include "taseditor_lua.h" -#include "taseditor_sel.h" #include "splicer.h" #include "popup_display.h" diff --git a/src/drivers/win/taseditor/taseditor_sel.cpp b/src/drivers/win/taseditor/taseditor_sel.cpp index be61e9ee..e4010526 100644 --- a/src/drivers/win/taseditor/taseditor_sel.cpp +++ b/src/drivers/win/taseditor/taseditor_sel.cpp @@ -1,16 +1,13 @@ //Implementation file of TASEDITOR_SELECTION class #include "taseditor_project.h" -#include "..\taseditor.h" // only for MARKER_NOTE_EDIT_LOWER extern TASEDITOR_CONFIG taseditor_config; extern TASEDITOR_WINDOW taseditor_window; -extern MARKERS current_markers; +extern MARKERS_MANAGER markers_manager; extern TASEDITOR_LIST list; extern SPLICER splicer; -extern int marker_note_edit; extern int joysticks_per_frame[NUM_SUPPORTED_INPUT_TYPES]; -extern void UpdateMarkerNote(); LRESULT APIENTRY LowerMarkerEditWndProc(HWND hWnd, UINT msg, WPARAM wParam, LPARAM lParam); WNDPROC selectionMarkerEdit_oldWndproc; @@ -120,8 +117,8 @@ void TASEDITOR_SELECTION::update() // update "Selection's Marker text" if needed if (must_find_current_marker) { - UpdateMarkerNote(); - shown_marker = current_markers.GetMarkerUp(last_selection_beginning); + markers_manager.UpdateMarkerNote(); + shown_marker = markers_manager.GetMarkerUp(last_selection_beginning); RedrawMarker(); must_find_current_marker = false; } @@ -139,7 +136,7 @@ void TASEDITOR_SELECTION::RedrawMarker() strcat(new_text, num); SetWindowText(hwndSelectionMarker, new_text); // change marker note - strcpy(new_text, current_markers.GetNote(shown_marker).c_str()); + strcpy(new_text, markers_manager.GetNote(shown_marker).c_str()); SetWindowText(hwndSelectionMarkerEdit, new_text); } @@ -149,7 +146,7 @@ void TASEDITOR_SELECTION::JumpPrevMarker() int index = GetCurrentSelectionBeginning(); if (index < 0) index = currFrameCounter; // if nothing is selected, consider playback cursor as current selection for (index--; index >= 0; index--) - if (current_markers.GetMarker(index)) break; + if (markers_manager.GetMarker(index)) break; if (index >= 0) JumpToFrame(index); else @@ -163,7 +160,7 @@ void TASEDITOR_SELECTION::JumpNextMarker() int last_frame = currMovieData.getNumRecords()-1; for (++index; index <= last_frame; ++index) - if (current_markers.GetMarker(index)) break; + if (markers_manager.GetMarker(index)) break; if (index <= last_frame) JumpToFrame(index); else @@ -431,10 +428,10 @@ void TASEDITOR_SELECTION::SelectBetweenMarkers() // find markers // searching up starting from center-0 for (upper_marker = center; upper_marker >= 0; upper_marker--) - if (current_markers.GetMarker(upper_marker)) break; + if (markers_manager.GetMarker(upper_marker)) break; // searching down starting from center+1 for (lower_marker = center+1; lower_marker < movie_size; ++lower_marker) - if (current_markers.GetMarker(lower_marker)) break; + if (markers_manager.GetMarker(lower_marker)) break; // clear selection without clearing focused, because otherwise there's strange bug when quickly pressing Ctrl+A right after clicking on already selected row ListView_SetItemState(list.hwndList, -1, 0, LVIS_SELECTED); @@ -528,11 +525,7 @@ SelectionFrames& TASEDITOR_SELECTION::GetStrobedSelection() return temp_selection; } -SelectionFrames& TASEDITOR_SELECTION::GetInsertedSet() -{ - return inserted_set; -} -// this getter is only for inside-class use +// this getter is private SelectionFrames& TASEDITOR_SELECTION::CurrentSelection() { return selections_history[(history_start_pos + history_cursor_pos) % history_size]; @@ -540,7 +533,7 @@ SelectionFrames& TASEDITOR_SELECTION::CurrentSelection() // ------------------------------------------------------------------------- LRESULT APIENTRY LowerMarkerEditWndProc(HWND hWnd, UINT msg, WPARAM wParam, LPARAM lParam) { - if (marker_note_edit == MARKER_NOTE_EDIT_LOWER) + if (markers_manager.marker_note_edit == MARKER_NOTE_EDIT_LOWER) { extern PLAYBACK playback; extern TASEDITOR_SELECTION selection; @@ -552,7 +545,7 @@ LRESULT APIENTRY LowerMarkerEditWndProc(HWND hWnd, UINT msg, WPARAM wParam, LPAR { case VK_ESCAPE: // revert text to original note text - SetWindowText(selection.hwndSelectionMarkerEdit, current_markers.GetNote(selection.shown_marker).c_str()); + SetWindowText(selection.hwndSelectionMarkerEdit, markers_manager.GetNote(selection.shown_marker).c_str()); SetFocus(list.hwndList); return 0; case VK_RETURN: diff --git a/src/drivers/win/taseditor/taseditor_sel.h b/src/drivers/win/taseditor/taseditor_sel.h index 2f1a74d1..b51ed8d5 100644 --- a/src/drivers/win/taseditor/taseditor_sel.h +++ b/src/drivers/win/taseditor/taseditor_sel.h @@ -1,4 +1,6 @@ //Specification file for TASEDITOR_SELECTION class +#include +typedef std::set SelectionFrames; #define SELECTION_ID_LEN 10 @@ -27,7 +29,6 @@ public: void undo(); void redo(); - void jump(int new_pos); void ClearSelection(); void ClearRowSelection(int index); @@ -51,8 +52,6 @@ public: SelectionFrames* MakeStrobe(); SelectionFrames& GetStrobedSelection(); - SelectionFrames& GetInsertedSet(); - bool must_find_current_marker; int shown_marker; @@ -60,6 +59,8 @@ public: HWND hwndSelectionMarker, hwndSelectionMarkerEdit; private: + void jump(int new_pos); + SelectionFrames& CurrentSelection(); bool track_selection_changes; @@ -71,8 +72,6 @@ private: std::vector selections_history; - SelectionFrames inserted_set; - int history_cursor_pos; int history_start_pos; int history_total_items; diff --git a/src/drivers/win/taseditor/taseditor_window.cpp b/src/drivers/win/taseditor/taseditor_window.cpp index f12d6d42..5f57c774 100644 --- a/src/drivers/win/taseditor/taseditor_window.cpp +++ b/src/drivers/win/taseditor/taseditor_window.cpp @@ -4,6 +4,7 @@ #include "../Win32InputBox.h" #include "../taseditor.h" #include +#include "../../input.h" // for EMUCMD extern TASEDITOR_CONFIG taseditor_config; extern PLAYBACK playback; @@ -13,21 +14,22 @@ extern TASEDITOR_PROJECT project; extern TASEDITOR_LIST list; extern TASEDITOR_SELECTION selection; extern SPLICER splicer; -extern MARKERS current_markers; +extern MARKERS_MANAGER markers_manager; extern BOOKMARKS bookmarks; -extern INPUT_HISTORY history; +extern HISTORY history; extern POPUP_DISPLAY popup_display; extern bool turbo; extern bool muteTurbo; -extern int marker_note_edit; -extern int search_similar_marker; extern bool must_call_manual_lua_function; -BOOL CALLBACK WndprocTasEditor(HWND hwndDlg, UINT uMsg, WPARAM wParam, LPARAM lParam); +extern char* GetKeyComboName(int c); + extern BOOL CALLBACK FindNoteProc(HWND hwndDlg, UINT message, WPARAM wParam, LPARAM lParam); extern BOOL CALLBACK AboutProc(HWND hwndDlg, UINT message, WPARAM wParam, LPARAM lParam); +BOOL CALLBACK WndprocTasEditor(HWND hwndDlg, UINT uMsg, WPARAM wParam, LPARAM lParam); + // Recent Menu HMENU recent_projects_menu; char* recent_projects[] = { 0, 0, 0, 0, 0, 0, 0, 0, 0, 0 }; @@ -46,49 +48,54 @@ static struct int y; int width; int height; + char tooltip_text_base[TOOLTIP_TEXT_MAX_LEN]; + char tooltip_text[TOOLTIP_TEXT_MAX_LEN]; + bool static_rect; + int hotkey_emucmd; + HWND tooltip_hwnd; } window_items[TASEDITOR_WINDOW_TOTAL_ITEMS] = { - IDC_PROGRESS_BUTTON, -1, 0, 0, 0, - IDC_BRANCHES_BUTTON, -1, 0, 0, 0, - IDC_LIST1, 0, 0, -1, -1, - IDC_PLAYBACK_BOX, -1, 0, 0, 0, - IDC_RECORDER_BOX, -1, 0, 0, 0, - IDC_SPLICER_BOX, -1, 0, 0, 0, - IDC_LUA_BOX, -1, 0, 0, 0, - IDC_BOOKMARKS_BOX, -1, 0, 0, 0, - IDC_HISTORY_BOX, -1, 0, 0, -1, - TASEDITOR_REWIND_FULL, -1, 0, 0, 0, - TASEDITOR_REWIND, -1, 0, 0, 0, - TASEDITOR_PLAYSTOP, -1, 0, 0, 0, - TASEDITOR_FORWARD, -1, 0, 0, 0, - TASEDITOR_FORWARD_FULL, -1, 0, 0, 0, - IDC_PROGRESS1, -1, 0, 0, 0, - CHECK_FOLLOW_CURSOR, -1, 0, 0, 0, - CHECK_AUTORESTORE_PLAYBACK, -1, 0, 0, 0, - IDC_BOOKMARKSLIST, -1, 0, 0, 0, - IDC_HISTORYLIST, -1, 0, 0, -1, - IDC_RADIO_ALL, -1, 0, 0, 0, - IDC_RADIO_1P, -1, 0, 0, 0, - IDC_RADIO_2P, -1, 0, 0, 0, - IDC_RADIO_3P, -1, 0, 0, 0, - IDC_RADIO_4P, -1, 0, 0, 0, - IDC_SUPERIMPOSE, -1, 0, 0, 0, - TASEDITOR_PREV_MARKER, -1, -1, 0, -1, - TASEDITOR_FIND_BEST_SIMILAR_MARKER, -1, -1, 0, -1, - TASEDITOR_FIND_NEXT_SIMILAR_MARKER, -1, -1, 0, -1, - TASEDITOR_NEXT_MARKER, -1, -1, 0, -1, - IDC_JUMP_PLAYBACK_BUTTON, 0, 0, 0, 0, - IDC_PLAYBACK_MARKER_EDIT, 0, 0, -1, 0, - IDC_PLAYBACK_MARKER, 0, 0, 0, 0, - IDC_JUMP_SELECTION_BUTTON, 0, -1, 0, -1, - IDC_SELECTION_MARKER_EDIT, 0, -1, -1, -1, - IDC_SELECTION_MARKER, 0, -1, 0, -1, - IDC_BRANCHES_BITMAP, -1, 0, 0, 0, - CHECK_TURBO_SEEK, -1, 0, 0, 0, - IDC_TEXT_SELECTION, -1, 0, 0, 0, - IDC_TEXT_CLIPBOARD, -1, 0, 0, 0, - IDC_RECORDING, -1, 0, 0, 0, - TASEDITOR_RUN_MANUAL, -1, 0, 0, 0, - IDC_RUN_AUTO, -1, 0, 0, 0, + IDC_PROGRESS_BUTTON, -1, 0, 0, 0, "Click here whenever you want to abort seeking", "", false, 0, 0, + IDC_BRANCHES_BUTTON, -1, 0, 0, 0, "Click here to switch between Bookmarks List and Branches Tree", "", false, 0, 0, + IDC_LIST1, 0, 0, -1, -1, "", "", false, 0, 0, + IDC_PLAYBACK_BOX, -1, 0, 0, 0, "", "", false, 0, 0, + IDC_RECORDER_BOX, -1, 0, 0, 0, "", "", false, 0, 0, + IDC_SPLICER_BOX, -1, 0, 0, 0, "", "", false, 0, 0, + IDC_LUA_BOX, -1, 0, 0, 0, "", "", false, 0, 0, + IDC_BOOKMARKS_BOX, -1, 0, 0, 0, "", "", false, 0, 0, + IDC_HISTORY_BOX, -1, 0, 0, -1, "", "", false, 0, 0, + TASEDITOR_REWIND_FULL, -1, 0, 0, 0, "Send Playback to previous Marker (hotkey: Shift+PageUp)", "", false, 0, 0, + TASEDITOR_REWIND, -1, 0, 0, 0, "Rewind one frame", "", false, EMUCMD_TASEDITOR_REWIND, 0, + TASEDITOR_PLAYSTOP, -1, 0, 0, 0, "Pause/Unpause Emulation", "", false, EMUCMD_PAUSE, 0, + TASEDITOR_FORWARD, -1, 0, 0, 0, "Advance one frame", "", false, EMUCMD_FRAME_ADVANCE, 0, + TASEDITOR_FORWARD_FULL, -1, 0, 0, 0, "Send Playback to next Marker (hotkey: Shift+PageDown)", "", false, 0, 0, + IDC_PROGRESS1, -1, 0, 0, 0, "", "", false, 0, 0, + CHECK_FOLLOW_CURSOR, -1, 0, 0, 0, "The List will follow Playback cursor movements", "", false, 0, 0, + CHECK_AUTORESTORE_PLAYBACK, -1, 0, 0, 0, "If you change input above Playback, cursor will run where it was before change", "", false, 0, 0, + IDC_BOOKMARKSLIST, -1, 0, 0, 0, "Right click = set Bookmark, Left click = jump to Bookmark or load Branch", "", false, 0, 0, + IDC_HISTORYLIST, -1, 0, 0, -1, "Click to revert movie back to that time", "", false, 0, 0, + IDC_RADIO_ALL, -1, 0, 0, 0, "", "", false, 0, 0, + IDC_RADIO_1P, -1, 0, 0, 0, "", "", false, 0, 0, + IDC_RADIO_2P, -1, 0, 0, 0, "", "", false, 0, 0, + IDC_RADIO_3P, -1, 0, 0, 0, "", "", false, 0, 0, + IDC_RADIO_4P, -1, 0, 0, 0, "", "", false, 0, 0, + IDC_SUPERIMPOSE, -1, 0, 0, 0, "Allows to superimpose old input with new buttons, instead of overwriting", "", false, 0, 0, + TASEDITOR_PREV_MARKER, -1, -1, 0, -1, "Send Selection to previous Marker (hotkey: Ctrl+PageUp)", "", false, 0, 0, + TASEDITOR_FIND_BEST_SIMILAR_MARKER, -1, -1, 0, -1, "Auto-search for Marker Note", "", false, 0, 0, + TASEDITOR_FIND_NEXT_SIMILAR_MARKER, -1, -1, 0, -1, "Continue Auto-search", "", false, 0, 0, + TASEDITOR_NEXT_MARKER, -1, -1, 0, -1, "Send Selection to next Marker (hotkey: Ctrl+PageDown)", "", false, 0, 0, + IDC_JUMP_PLAYBACK_BUTTON, 0, 0, 0, 0, "Click here to scroll the List to Playback cursor", "", false, 0, 0, + IDC_PLAYBACK_MARKER_EDIT, 0, 0, -1, 0, "Click to edit text", "", false, 0, 0, + IDC_PLAYBACK_MARKER, 0, 0, 0, 0, "", "", false, 0, 0, + IDC_JUMP_SELECTION_BUTTON, 0, -1, 0, -1, "Click here to scroll the List to Selection", "", false, 0, 0, + IDC_SELECTION_MARKER_EDIT, 0, -1, -1, -1, "Click to edit text", "", false, 0, 0, + IDC_SELECTION_MARKER, 0, -1, 0, -1, "", "", false, 0, 0, + IDC_BRANCHES_BITMAP, -1, 0, 0, 0, "This window visualizes the hierarchy of your Branches", "", false, 0, 0, + CHECK_TURBO_SEEK, -1, 0, 0, 0, "Uncheck when you need to watch seeking in slow motion", "", false, 0, 0, + IDC_TEXT_SELECTION, -1, 0, 0, 0, "Current size of Selection", "", true, 0, 0, + IDC_TEXT_CLIPBOARD, -1, 0, 0, 0, "Current size of Input in the Clipboard", "", true, 0, 0, + IDC_RECORDING, -1, 0, 0, 0, "Switch Input Recording on/off", "", false, EMUCMD_MOVIE_READONLY_TOGGLE, 0, + TASEDITOR_RUN_MANUAL, -1, 0, 0, 0, "Press the button to execute Lua Manual Function", "", false, 0, 0, + IDC_RUN_AUTO, -1, 0, 0, 0, "Enable Lua Auto Function (but first it must be registered by Lua script)", "", false, 0, 0, }; TASEDITOR_WINDOW::TASEDITOR_WINDOW() @@ -108,17 +115,73 @@ void TASEDITOR_WINDOW::init() ready_for_resizing = false; hTaseditorIcon = (HICON)LoadImage(fceu_hInstance, MAKEINTRESOURCE(IDI_ICON3), IMAGE_ICON, 16, 16, LR_DEFAULTSIZE); hwndTasEditor = CreateDialog(fceu_hInstance, "TASEDITOR", hAppWnd, WndprocTasEditor); + SendMessage(hwndTasEditor, WM_SETICON, ICON_SMALL, (LPARAM)hTaseditorIcon); CalculateItems(); // restore position and size from config, also bring the window on top - //RECT desiredRect = {0, 0, taseditor_config.wndwidth, taseditor_config.wndheight}; - //AdjustWindowRectEx(&desiredRect, GetWindowLong(hwndTasEditor, GWL_STYLE), true, GetWindowLong(hwndTasEditor, GWL_EXSTYLE)); SetWindowPos(hwndTasEditor, HWND_TOP, taseditor_config.wndx, taseditor_config.wndy, taseditor_config.wndwidth, taseditor_config.wndheight, SWP_NOOWNERZORDER); - + // menus and checked items hmenu = GetMenu(hwndTasEditor); hrmenu = LoadMenu(fceu_hInstance,"TASEDITORCONTEXTMENUS"); - UpdateCheckedItems(); - + // tooltips + int x = 0; + for (int i = 0; i < TASEDITOR_WINDOW_TOTAL_ITEMS; ++i) + { + if (window_items[i].tooltip_text_base[0]) + { + // Create the tooltip. g_hInst is the global instance handle + window_items[i].tooltip_hwnd = CreateWindowEx(NULL, TOOLTIPS_CLASS, NULL, + WS_POPUP | TTS_ALWAYSTIP | TTS_BALLOON, + CW_USEDEFAULT, CW_USEDEFAULT, + CW_USEDEFAULT, CW_USEDEFAULT, + hwndTasEditor, NULL, + fceu_hInstance, NULL); + if (window_items[i].tooltip_hwnd) + { + // Associate the tooltip with the tool + TOOLINFO toolInfo = {0}; + toolInfo.cbSize = sizeof(toolInfo); + toolInfo.hwnd = hwndTasEditor; + toolInfo.uId = (UINT_PTR)GetDlgItem(hwndTasEditor, window_items[i].id); + if (window_items[i].static_rect) + { + // for static text we specify rectangle + toolInfo.uFlags = TTF_SUBCLASS; + RECT tool_rect; + GetWindowRect(GetDlgItem(hwndTasEditor, window_items[i].id), &tool_rect); + POINT pt; + pt.x = tool_rect.left; + pt.y = tool_rect.top; + ScreenToClient(hwndTasEditor, &pt); + toolInfo.rect.left = pt.x; + toolInfo.rect.right = toolInfo.rect.left + (tool_rect.right - tool_rect.left); + toolInfo.rect.top = pt.y; + toolInfo.rect.bottom = toolInfo.rect.top + (tool_rect.bottom - tool_rect.top); + } else + { + // for other controls we provide hwnd + toolInfo.uFlags = TTF_IDISHWND | TTF_SUBCLASS; + } + // add hotkey mapping if needed + if (window_items[i].hotkey_emucmd && FCEUD_CommandMapping[window_items[i].hotkey_emucmd]) + { + window_items[i].tooltip_text[0] = 0; + strcpy(window_items[i].tooltip_text, window_items[i].tooltip_text_base); + strcat(window_items[i].tooltip_text, " (hotkey: "); + strncat(window_items[i].tooltip_text, GetKeyComboName(FCEUD_CommandMapping[window_items[i].hotkey_emucmd]), TOOLTIP_TEXT_MAX_LEN - strlen(window_items[i].tooltip_text) - 1); + strncat(window_items[i].tooltip_text, ")", TOOLTIP_TEXT_MAX_LEN - strlen(window_items[i].tooltip_text) - 1); + toolInfo.lpszText = window_items[i].tooltip_text; + } else + { + toolInfo.lpszText = window_items[i].tooltip_text_base; + } + SendMessage(window_items[i].tooltip_hwnd, TTM_ADDTOOL, 0, (LPARAM)&toolInfo); + SendMessage(window_items[i].tooltip_hwnd, TTM_SETDELAYTIME, TTDT_AUTOPOP, TOOLTIPS_AUTOPOP_TIMEOUT); + } + } + } + UpdateTooltips(); + // recent projects submenu recent_projects_menu = CreateMenu(); UpdateRecentProjectsMenu(); @@ -126,6 +189,14 @@ void TASEDITOR_WINDOW::init() } void TASEDITOR_WINDOW::exit() { + for (int i = 0; i < TASEDITOR_WINDOW_TOTAL_ITEMS; ++i) + { + if (window_items[i].tooltip_hwnd) + { + DestroyWindow(window_items[i].tooltip_hwnd); + window_items[i].tooltip_hwnd = 0; + } + } if (hwndFindNote) { DestroyWindow(hwndFindNote); @@ -238,12 +309,10 @@ void TASEDITOR_WINDOW::ResizeItems() else // normal height height = window_items[i].height; - SetWindowPos(hCtrl, 0, x, y, width, height, SWP_NOZORDER | SWP_NOOWNERZORDER | SWP_NOREDRAW); + SetWindowPos(hCtrl, 0, x, y, width, height, SWP_NOZORDER | SWP_NOOWNERZORDER); } RedrawTaseditor(); - //RedrawWindow(hwndTasEditor, NULL, NULL, RDW_INVALIDATE); } - void TASEDITOR_WINDOW::WindowMovedOrResized() { RECT wrect; @@ -259,6 +328,25 @@ void TASEDITOR_WINDOW::WindowMovedOrResized() taseditor_config.wndheight = min_height; } +void TASEDITOR_WINDOW::UpdateTooltips() +{ + if (taseditor_config.tooltips) + { + for (int i = 0; i < TASEDITOR_WINDOW_TOTAL_ITEMS; ++i) + { + if (window_items[i].tooltip_hwnd) + SendMessage(window_items[i].tooltip_hwnd, TTM_ACTIVATE, true, 0); + } + } else + { + for (int i = 0; i < TASEDITOR_WINDOW_TOTAL_ITEMS; ++i) + { + if (window_items[i].tooltip_hwnd) + SendMessage(window_items[i].tooltip_hwnd, TTM_ACTIVATE, false, 0); + } + } +} + void TASEDITOR_WINDOW::UpdateCaption() { char new_caption[300]; @@ -315,7 +403,7 @@ void TASEDITOR_WINDOW::RightClickMenu(LPNMITEMACTIVATE info) bool set_found = false, unset_found = false; for(SelectionFrames::iterator it(current_selection_begin); it != current_selection_end; it++) { - if(current_markers.GetMarker(*it)) + if(markers_manager.GetMarker(*it)) set_found = true; else unset_found = true; @@ -359,7 +447,8 @@ void TASEDITOR_WINDOW::UpdateCheckedItems() CheckMenuItem(hmenu, ID_CONFIG_SUPERIMPOSE_AFFECTS_PASTE, taseditor_config.superimpose_affects_paste?MF_CHECKED : MF_UNCHECKED); CheckMenuItem(hmenu, ID_CONFIG_MUTETURBO, muteTurbo?MF_CHECKED : MF_UNCHECKED); CheckMenuItem(hmenu, ID_CONFIG_SILENTAUTOSAVE, taseditor_config.silent_autosave?MF_CHECKED : MF_UNCHECKED); - + CheckMenuItem(hmenu, ID_HELP_TOOLTIPS, taseditor_config.tooltips?MF_CHECKED : MF_UNCHECKED); + } @@ -493,7 +582,6 @@ BOOL CALLBACK WndprocTasEditor(HWND hwndDlg, UINT uMsg, WPARAM wParam, LPARAM lP { if (taseditor_config.wndx == -32000) taseditor_config.wndx = 0; //Just in case if (taseditor_config.wndy == -32000) taseditor_config.wndy = 0; - SendMessage(hwndDlg, WM_SETICON, ICON_SMALL, (LPARAM)taseditor_window.hTaseditorIcon); break; } case WM_WINDOWPOSCHANGED: @@ -542,10 +630,10 @@ BOOL CALLBACK WndprocTasEditor(HWND hwndDlg, UINT uMsg, WPARAM wParam, LPARAM lP list.GetDispInfo((NMLVDISPINFO*)lParam); break; case NM_CLICK: - SingleClick((LPNMITEMACTIVATE)lParam); + list.SingleClick((LPNMITEMACTIVATE)lParam); break; case NM_DBLCLK: - DoubleClick((LPNMITEMACTIVATE)lParam); + list.DoubleClick((LPNMITEMACTIVATE)lParam); break; case NM_RCLICK: taseditor_window.RightClick((LPNMITEMACTIVATE)lParam); @@ -610,6 +698,9 @@ BOOL CALLBACK WndprocTasEditor(HWND hwndDlg, UINT uMsg, WPARAM wParam, LPARAM lP break; } break; + case ID_STRAY_UNPAUSE: + playback.UnpauseEmulation(); + break; } break; case WM_CLOSE: @@ -620,11 +711,11 @@ BOOL CALLBACK WndprocTasEditor(HWND hwndDlg, UINT uMsg, WPARAM wParam, LPARAM lP if(LOWORD(wParam)) { taseditor_window.TASEditor_focus = true; - SetTaseditInput(); + SetTaseditorInput(); } else { taseditor_window.TASEditor_focus = false; - ClearTaseditInput(); + ClearTaseditorInput(); } break; case WM_CTLCOLORSTATIC: @@ -659,27 +750,27 @@ BOOL CALLBACK WndprocTasEditor(HWND hwndDlg, UINT uMsg, WPARAM wParam, LPARAM lP { case EN_SETFOCUS: { - marker_note_edit = MARKER_NOTE_EDIT_UPPER; + markers_manager.marker_note_edit = MARKER_NOTE_EDIT_UPPER; // enable editing SendMessage(playback.hwndPlaybackMarkerEdit, EM_SETREADONLY, false, 0); // disable FCEUX keyboard - ClearTaseditInput(); + ClearTaseditorInput(); if (taseditor_config.follow_note_context) list.FollowPlayback(); break; } case EN_KILLFOCUS: { - if (marker_note_edit == MARKER_NOTE_EDIT_UPPER) + if (markers_manager.marker_note_edit == MARKER_NOTE_EDIT_UPPER) { - UpdateMarkerNote(); - marker_note_edit = MARKER_NOTE_EDIT_NONE; + markers_manager.UpdateMarkerNote(); + markers_manager.marker_note_edit = MARKER_NOTE_EDIT_NONE; } // disable editing (make it grayed) SendMessage(playback.hwndPlaybackMarkerEdit, EM_SETREADONLY, true, 0); // enable FCEUX keyboard if (taseditor_window.TASEditor_focus) - SetTaseditInput(); + SetTaseditorInput(); break; } } @@ -691,27 +782,27 @@ BOOL CALLBACK WndprocTasEditor(HWND hwndDlg, UINT uMsg, WPARAM wParam, LPARAM lP { case EN_SETFOCUS: { - marker_note_edit = MARKER_NOTE_EDIT_LOWER; + markers_manager.marker_note_edit = MARKER_NOTE_EDIT_LOWER; // enable editing SendMessage(selection.hwndSelectionMarkerEdit, EM_SETREADONLY, false, 0); // disable FCEUX keyboard - ClearTaseditInput(); + ClearTaseditorInput(); if (taseditor_config.follow_note_context) list.FollowSelection(); break; } case EN_KILLFOCUS: { - if (marker_note_edit == MARKER_NOTE_EDIT_LOWER) + if (markers_manager.marker_note_edit == MARKER_NOTE_EDIT_LOWER) { - UpdateMarkerNote(); - marker_note_edit = MARKER_NOTE_EDIT_NONE; + markers_manager.UpdateMarkerNote(); + markers_manager.marker_note_edit = MARKER_NOTE_EDIT_NONE; } // disable editing (make it grayed) SendMessage(selection.hwndSelectionMarkerEdit, EM_SETREADONLY, true, 0); // enable FCEUX keyboard if (taseditor_window.TASEditor_focus) - SetTaseditInput(); + SetTaseditorInput(); break; } } @@ -743,36 +834,36 @@ BOOL CALLBACK WndprocTasEditor(HWND hwndDlg, UINT uMsg, WPARAM wParam, LPARAM lP ExitTasEditor(); break; case ID_EDIT_SELECTALL: - if (marker_note_edit == MARKER_NOTE_EDIT_UPPER) + if (markers_manager.marker_note_edit == MARKER_NOTE_EDIT_UPPER) SendMessage(playback.hwndPlaybackMarkerEdit, EM_SETSEL, 0, -1); - else if (marker_note_edit == MARKER_NOTE_EDIT_LOWER) + else if (markers_manager.marker_note_edit == MARKER_NOTE_EDIT_LOWER) SendMessage(selection.hwndSelectionMarkerEdit, EM_SETSEL, 0, -1); else selection.SelectAll(); break; case ACCEL_CTRL_X: case ID_EDIT_CUT: - if (marker_note_edit == MARKER_NOTE_EDIT_UPPER) + if (markers_manager.marker_note_edit == MARKER_NOTE_EDIT_UPPER) SendMessage(playback.hwndPlaybackMarkerEdit, WM_CUT, 0, 0); - else if (marker_note_edit == MARKER_NOTE_EDIT_LOWER) + else if (markers_manager.marker_note_edit == MARKER_NOTE_EDIT_LOWER) SendMessage(selection.hwndSelectionMarkerEdit, WM_CUT, 0, 0); else splicer.Cut(); break; case ACCEL_CTRL_C: case ID_EDIT_COPY: - if (marker_note_edit == MARKER_NOTE_EDIT_UPPER) + if (markers_manager.marker_note_edit == MARKER_NOTE_EDIT_UPPER) SendMessage(playback.hwndPlaybackMarkerEdit, WM_COPY, 0, 0); - else if (marker_note_edit == MARKER_NOTE_EDIT_LOWER) + else if (markers_manager.marker_note_edit == MARKER_NOTE_EDIT_LOWER) SendMessage(selection.hwndSelectionMarkerEdit, WM_COPY, 0, 0); else splicer.Copy(); break; case ACCEL_CTRL_V: case ID_EDIT_PASTE: - if (marker_note_edit == MARKER_NOTE_EDIT_UPPER) + if (markers_manager.marker_note_edit == MARKER_NOTE_EDIT_UPPER) SendMessage(playback.hwndPlaybackMarkerEdit, WM_PASTE, 0, 0); - else if (marker_note_edit == MARKER_NOTE_EDIT_LOWER) + else if (markers_manager.marker_note_edit == MARKER_NOTE_EDIT_LOWER) SendMessage(selection.hwndSelectionMarkerEdit, WM_PASTE, 0, 0); else splicer.Paste(); @@ -782,13 +873,13 @@ BOOL CALLBACK WndprocTasEditor(HWND hwndDlg, UINT uMsg, WPARAM wParam, LPARAM lP // hack to allow entering Shift-V into edit control even though accelerator steals the input message char insert_v[] = "v"; char insert_V[] = "V"; - if (marker_note_edit == MARKER_NOTE_EDIT_UPPER) + if (markers_manager.marker_note_edit == MARKER_NOTE_EDIT_UPPER) { if (GetKeyState(VK_CAPITAL) & 1) SendMessage(playback.hwndPlaybackMarkerEdit, EM_REPLACESEL, true, (LPARAM)insert_v); else SendMessage(playback.hwndPlaybackMarkerEdit, EM_REPLACESEL, true, (LPARAM)insert_V); - } else if (marker_note_edit == MARKER_NOTE_EDIT_LOWER) + } else if (markers_manager.marker_note_edit == MARKER_NOTE_EDIT_LOWER) { if (GetKeyState(VK_CAPITAL) & 1) SendMessage(selection.hwndSelectionMarkerEdit, EM_REPLACESEL, true, (LPARAM)insert_v); @@ -799,9 +890,9 @@ BOOL CALLBACK WndprocTasEditor(HWND hwndDlg, UINT uMsg, WPARAM wParam, LPARAM lP break; } case ID_EDIT_PASTEINSERT: - if (marker_note_edit == MARKER_NOTE_EDIT_UPPER) + if (markers_manager.marker_note_edit == MARKER_NOTE_EDIT_UPPER) SendMessage(playback.hwndPlaybackMarkerEdit, WM_PASTE, 0, 0); - else if (marker_note_edit == MARKER_NOTE_EDIT_LOWER) + else if (markers_manager.marker_note_edit == MARKER_NOTE_EDIT_LOWER) SendMessage(selection.hwndSelectionMarkerEdit, WM_PASTE, 0, 0); else splicer.PasteInsert(); @@ -811,20 +902,11 @@ BOOL CALLBACK WndprocTasEditor(HWND hwndDlg, UINT uMsg, WPARAM wParam, LPARAM lP case ID_CONTEXT_SELECTED_DELETEFRAMES: splicer.DeleteFrames(); break; - case ACCEL_CTRL_T: case ID_EDIT_TRUNCATE: case ID_CONTEXT_SELECTED_TRUNCATE: - case ID_CONTEXT_STRAY_TRUNCATE: + case ID_STRAY_TRUNCATE: splicer.Truncate(); break; - case ID_HELP_TASEDITHELP: - { - //OpenHelpWindow(tasedithelp); - std::string helpFileName = BaseDirectory; - helpFileName.append(taseditor_help_filename); - HtmlHelp(GetDesktopWindow(), helpFileName.c_str(), HH_DISPLAY_TOPIC, (DWORD)NULL); - break; - } case ACCEL_INS: case ID_EDIT_INSERT: case MENU_CONTEXT_STRAY_INSERTFRAMES: @@ -839,14 +921,14 @@ BOOL CALLBACK WndprocTasEditor(HWND hwndDlg, UINT uMsg, WPARAM wParam, LPARAM lP case ACCEL_DEL: case ID_EDIT_CLEAR: case ID_CONTEXT_SELECTED_CLEARFRAMES: - if (marker_note_edit == MARKER_NOTE_EDIT_UPPER) + if (markers_manager.marker_note_edit == MARKER_NOTE_EDIT_UPPER) { DWORD sel_start, sel_end; SendMessage(playback.hwndPlaybackMarkerEdit, EM_GETSEL, (WPARAM)&sel_start, (LPARAM)&sel_end); if (sel_start == sel_end) SendMessage(playback.hwndPlaybackMarkerEdit, EM_SETSEL, sel_start, sel_start + 1); SendMessage(playback.hwndPlaybackMarkerEdit, WM_CLEAR, 0, 0); - } else if (marker_note_edit == MARKER_NOTE_EDIT_LOWER) + } else if (markers_manager.marker_note_edit == MARKER_NOTE_EDIT_LOWER) { DWORD sel_start, sel_end; SendMessage(selection.hwndSelectionMarkerEdit, EM_GETSEL, (WPARAM)&sel_start, (LPARAM)&sel_end); @@ -864,7 +946,7 @@ BOOL CALLBACK WndprocTasEditor(HWND hwndDlg, UINT uMsg, WPARAM wParam, LPARAM lP taseditor_config.follow_playback ^= 1; taseditor_window.UpdateCheckedItems(); // if switched off then maybe jump to target frame - if (!taseditor_config.follow_playback && playback.GetPauseFrame()) + if (!taseditor_config.follow_playback && playback.pause_frame) list.FollowPauseframe(); break; case CHECK_TURBO_SEEK: @@ -1022,7 +1104,7 @@ BOOL CALLBACK WndprocTasEditor(HWND hwndDlg, UINT uMsg, WPARAM wParam, LPARAM lP break; case IDC_PROGRESS_BUTTON: // click on progressbar - stop seeking - if (playback.GetPauseFrame()) playback.SeekingStop(); + if (playback.pause_frame) playback.SeekingStop(); break; case IDC_BRANCHES_BUTTON: // click on "Bookmarks/Branches" - switch "View Tree of branches" @@ -1059,9 +1141,9 @@ BOOL CALLBACK WndprocTasEditor(HWND hwndDlg, UINT uMsg, WPARAM wParam, LPARAM lP taseditor_window.UpdateCheckedItems(); break; case ACCEL_CTRL_A: - if (marker_note_edit == MARKER_NOTE_EDIT_UPPER) + if (markers_manager.marker_note_edit == MARKER_NOTE_EDIT_UPPER) SendMessage(playback.hwndPlaybackMarkerEdit, EM_SETSEL, 0, -1); - else if (marker_note_edit == MARKER_NOTE_EDIT_LOWER) + else if (markers_manager.marker_note_edit == MARKER_NOTE_EDIT_LOWER) SendMessage(selection.hwndSelectionMarkerEdit, EM_SETSEL, 0, -1); else selection.SelectBetweenMarkers(); @@ -1078,10 +1160,10 @@ BOOL CALLBACK WndprocTasEditor(HWND hwndDlg, UINT uMsg, WPARAM wParam, LPARAM lP case ACCEL_CTRL_Z: case ID_EDIT_UNDO: { - if (marker_note_edit == MARKER_NOTE_EDIT_UPPER) + if (markers_manager.marker_note_edit == MARKER_NOTE_EDIT_UPPER) { SendMessage(playback.hwndPlaybackMarkerEdit, WM_UNDO, 0, 0); - } else if (marker_note_edit == MARKER_NOTE_EDIT_LOWER) + } else if (markers_manager.marker_note_edit == MARKER_NOTE_EDIT_LOWER) { SendMessage(selection.hwndSelectionMarkerEdit, WM_UNDO, 0, 0); } else @@ -1149,9 +1231,9 @@ BOOL CALLBACK WndprocTasEditor(HWND hwndDlg, UINT uMsg, WPARAM wParam, LPARAM lP bool changes_made = false; for(SelectionFrames::iterator it(current_selection_begin); it != current_selection_end; it++) { - if(!current_markers.GetMarker(*it)) + if(!markers_manager.GetMarker(*it)) { - if (current_markers.SetMarker(*it)) + if (markers_manager.SetMarker(*it)) { changes_made = true; list.RedrawRow(*it); @@ -1176,9 +1258,9 @@ BOOL CALLBACK WndprocTasEditor(HWND hwndDlg, UINT uMsg, WPARAM wParam, LPARAM lP bool changes_made = false; for(SelectionFrames::iterator it(current_selection_begin); it != current_selection_end; it++) { - if(current_markers.GetMarker(*it)) + if(markers_manager.GetMarker(*it)) { - current_markers.ClearMarker(*it); + markers_manager.ClearMarker(*it); changes_made = true; list.RedrawRow(*it); } @@ -1215,16 +1297,10 @@ BOOL CALLBACK WndprocTasEditor(HWND hwndDlg, UINT uMsg, WPARAM wParam, LPARAM lP break; } case TASEDITOR_FIND_BEST_SIMILAR_MARKER: - search_similar_marker = 0; - current_markers.FindSimilar(search_similar_marker); - search_similar_marker++; + markers_manager.FindSimilar(); break; case TASEDITOR_FIND_NEXT_SIMILAR_MARKER: - current_markers.FindSimilar(search_similar_marker); - search_similar_marker++; - break; - case ID_HELP_ABOUT: - DialogBox(fceu_hInstance, MAKEINTRESOURCE(IDD_TASEDITOR_ABOUT), taseditor_window.hwndTasEditor, AboutProc); + markers_manager.FindNextSimilar(); break; case TASEDITOR_RUN_MANUAL: // the function will be called in next window update @@ -1234,6 +1310,21 @@ BOOL CALLBACK WndprocTasEditor(HWND hwndDlg, UINT uMsg, WPARAM wParam, LPARAM lP taseditor_config.enable_auto_function ^= 1; taseditor_window.UpdateCheckedItems(); break; + case ID_HELP_TASEDITHELP: + { + std::string helpFileName = BaseDirectory; + helpFileName.append(taseditor_help_filename); + HtmlHelp(GetDesktopWindow(), helpFileName.c_str(), HH_DISPLAY_TOPIC, (DWORD)NULL); + break; + } + case ID_HELP_TOOLTIPS: + taseditor_config.tooltips ^= 1; + taseditor_window.UpdateCheckedItems(); + taseditor_window.UpdateTooltips(); + break; + case ID_HELP_ABOUT: + DialogBox(fceu_hInstance, MAKEINTRESOURCE(IDD_TASEDITOR_ABOUT), taseditor_window.hwndTasEditor, AboutProc); + break; } @@ -1252,20 +1343,3 @@ BOOL CALLBACK WndprocTasEditor(HWND hwndDlg, UINT uMsg, WPARAM wParam, LPARAM lP } - - - - - - - - - - - - - - - - - diff --git a/src/drivers/win/taseditor/taseditor_window.h b/src/drivers/win/taseditor/taseditor_window.h index d5fc8569..8cad7cdb 100644 --- a/src/drivers/win/taseditor/taseditor_window.h +++ b/src/drivers/win/taseditor/taseditor_window.h @@ -1,5 +1,7 @@ //Specification file for TASEDITOR_WINDOW class #define TASEDITOR_WINDOW_TOTAL_ITEMS 42 +#define TOOLTIP_TEXT_MAX_LEN 80 +#define TOOLTIPS_AUTOPOP_TIMEOUT 30000 enum ECONTEXTMENU { @@ -16,11 +18,11 @@ public: void reset(); void update(); - void CalculateItems(); void ResizeItems(); - void WindowMovedOrResized(); + void UpdateTooltips(); + void UpdateCaption(); void RedrawTaseditor(); @@ -36,13 +38,16 @@ public: void LoadRecentProject(int slot); HWND hwndTasEditor, hwndFindNote; - HMENU hmenu, hrmenu; - HICON hTaseditorIcon; bool TASEditor_focus; bool ready_for_resizing; int min_width; int min_height; private: + void CalculateItems(); + + HWND hToolTipWnd; + HMENU hmenu, hrmenu; + HICON hTaseditorIcon; }; diff --git a/src/fceu.cpp b/src/fceu.cpp index 713bb225..ccace72f 100644 --- a/src/fceu.cpp +++ b/src/fceu.cpp @@ -718,11 +718,6 @@ void FCEUI_Emulate(uint8 **pXBuf, int32 **SoundBuf, int32 *SoundBufSize, int ski if (movieSubtitles) ProcessSubtitles(); - -#ifdef WIN32 - if(FCEUMOV_Mode(MOVIEMODE_TASEDITOR)) - greenzone.TryDumpIncremental(lagFlag != 0); -#endif } void FCEUI_CloseGame(void) diff --git a/src/input.cpp b/src/input.cpp index 6eb291db..510bb58a 100644 --- a/src/input.cpp +++ b/src/input.cpp @@ -54,11 +54,14 @@ #include "./drivers/win/taseditor/taseditor_window.h" #include "./drivers/win/taseditor/markers.h" -#include "./drivers/win/taseditor/inputsnapshot.h" +#include "./drivers/win/taseditor/taseditor_sel.h" +#include "./drivers/win/taseditor/snapshot.h" #include "./drivers/win/taseditor/bookmarks.h" +#include "./drivers/win/taseditor/playback.h" extern bool Taseditor_rewind_now; extern BOOKMARKS bookmarks; extern TASEDITOR_WINDOW taseditor_window; +extern PLAYBACK playback; #endif // WIN32 //it is easier to declare these input drivers extern here than include a bunch of files @@ -656,6 +659,7 @@ static void FCEUI_DoExit(void); static void ToggleFullscreen(void); static void TaseditorRewindOn(void); static void TaseditorRewindOff(void); +static void TaseditorRestorePlayback(void); struct EMUCMDTABLE FCEUI_CommandTable[]= { @@ -686,7 +690,7 @@ struct EMUCMDTABLE FCEUI_CommandTable[]= { EMUCMD_SAVE_SLOT_9, EMUCMDTYPE_STATE, CommandSelectSaveSlot, 0, 0, "Savestate Slot 9", EMUCMDFLAG_TASEDITOR }, { EMUCMD_SAVE_SLOT_NEXT, EMUCMDTYPE_STATE, CommandSelectSaveSlot, 0, 0, "Next Savestate Slot", 0 }, { EMUCMD_SAVE_SLOT_PREV, EMUCMDTYPE_STATE, CommandSelectSaveSlot, 0, 0, "Previous Savestate Slot", 0 }, - { EMUCMD_SAVE_STATE, EMUCMDTYPE_STATE, CommandStateSave, 0, 0, "Save State", 0 }, + { EMUCMD_SAVE_STATE, EMUCMDTYPE_STATE, CommandStateSave, 0, 0, "Save State", EMUCMDFLAG_TASEDITOR }, { EMUCMD_SAVE_STATE_AS, EMUCMDTYPE_STATE, FCEUD_SaveStateAs, 0, 0, "Save State As...", 0 }, { EMUCMD_SAVE_STATE_SLOT_0, EMUCMDTYPE_STATE, CommandStateSave, 0, 0, "Save State to Slot 0", EMUCMDFLAG_TASEDITOR }, { EMUCMD_SAVE_STATE_SLOT_1, EMUCMDTYPE_STATE, CommandStateSave, 0, 0, "Save State to Slot 1", EMUCMDFLAG_TASEDITOR }, @@ -698,7 +702,7 @@ struct EMUCMDTABLE FCEUI_CommandTable[]= { EMUCMD_SAVE_STATE_SLOT_7, EMUCMDTYPE_STATE, CommandStateSave, 0, 0, "Save State to Slot 7", EMUCMDFLAG_TASEDITOR }, { EMUCMD_SAVE_STATE_SLOT_8, EMUCMDTYPE_STATE, CommandStateSave, 0, 0, "Save State to Slot 8", EMUCMDFLAG_TASEDITOR }, { EMUCMD_SAVE_STATE_SLOT_9, EMUCMDTYPE_STATE, CommandStateSave, 0, 0, "Save State to Slot 9", EMUCMDFLAG_TASEDITOR }, - { EMUCMD_LOAD_STATE, EMUCMDTYPE_STATE, CommandStateLoad, 0, 0, "Load State", 0 }, + { EMUCMD_LOAD_STATE, EMUCMDTYPE_STATE, CommandStateLoad, 0, 0, "Load State", EMUCMDFLAG_TASEDITOR }, { EMUCMD_LOAD_STATE_FROM, EMUCMDTYPE_STATE, FCEUD_LoadStateFrom, 0, 0, "Load State From...", 0 }, { EMUCMD_LOAD_STATE_SLOT_0, EMUCMDTYPE_STATE, CommandStateLoad, 0, 0, "Load State from Slot 0", EMUCMDFLAG_TASEDITOR }, { EMUCMD_LOAD_STATE_SLOT_1, EMUCMDTYPE_STATE, CommandStateLoad, 0, 0, "Load State from Slot 1", EMUCMDFLAG_TASEDITOR }, @@ -779,8 +783,9 @@ struct EMUCMDTABLE FCEUI_CommandTable[]= { EMUCMD_TOOL_RAMSEARCHGTE, EMUCMDTYPE_TOOL, RamSearchOpGTE, 0, 0, "Ram Search - Greater Than or Equal", 0}, { EMUCMD_TOOL_RAMSEARCHEQ, EMUCMDTYPE_TOOL, RamSearchOpEQ, 0, 0, "Ram Search - Equal", 0}, { EMUCMD_TOOL_RAMSEARCHNE, EMUCMDTYPE_TOOL, RamSearchOpNE, 0, 0, "Ram Search - Not Equal", 0}, - { EMUCMD_TASEDITOR_REWIND, EMUCMDTYPE_MISC, TaseditorRewindOn, TaseditorRewindOff, 0, "Rewind Frame (TAS Editor only)", EMUCMDFLAG_TASEDITOR }, + { EMUCMD_TASEDITOR_REWIND, EMUCMDTYPE_MISC, TaseditorRewindOn, TaseditorRewindOff, 0, "Rewind Frame (TAS Editor)", EMUCMDFLAG_TASEDITOR }, { EMUCMD_RERECORD_DISPLAY_TOGGLE, EMUCMDTYPE_MISC, FCEUI_MovieToggleRerecordDisplay, 0, 0, "Toggle Rerecord Display", EMUCMDFLAG_TASEDITOR }, + { EMUCMD_TASEDITOR_RESTORE_PLAYBACK, EMUCMDTYPE_MISC, TaseditorRestorePlayback, 0, 0, "Restore Playback (TAS Editor)", EMUCMDFLAG_TASEDITOR }, }; #define NUM_EMU_CMDS (sizeof(FCEUI_CommandTable)/sizeof(FCEUI_CommandTable[0])) @@ -840,7 +845,7 @@ static void CommandSelectSaveSlot(void) if (FCEUMOV_Mode(MOVIEMODE_TASEDITOR)) { #ifdef WIN32 - bookmarks.jump(execcmd - EMUCMD_SAVE_SLOT_0); + bookmarks.command(COMMAND_JUMP, execcmd - EMUCMD_SAVE_SLOT_0); #endif } else { @@ -858,7 +863,10 @@ static void CommandStateSave(void) if (FCEUMOV_Mode(MOVIEMODE_TASEDITOR)) { #ifdef WIN32 - bookmarks.set(execcmd - EMUCMD_SAVE_STATE_SLOT_0); + if (execcmd == EMUCMD_SAVE_STATE) + bookmarks.command(COMMAND_SET, bookmarks.GetCurrentBranch()); + else if(execcmd >= EMUCMD_SAVE_STATE_SLOT_0 && execcmd <= EMUCMD_SAVE_STATE_SLOT_9) + bookmarks.command(COMMAND_SET, execcmd - EMUCMD_SAVE_STATE_SLOT_0); #endif } else { @@ -879,7 +887,10 @@ static void CommandStateLoad(void) if (FCEUMOV_Mode(MOVIEMODE_TASEDITOR)) { #ifdef WIN32 - bookmarks.unleash(execcmd - EMUCMD_LOAD_STATE_SLOT_0); + if (execcmd == EMUCMD_LOAD_STATE) + bookmarks.command(COMMAND_DEPLOY, bookmarks.GetCurrentBranch()); + else if(execcmd >= EMUCMD_LOAD_STATE_SLOT_0 && execcmd <= EMUCMD_LOAD_STATE_SLOT_9) + bookmarks.command(COMMAND_DEPLOY, execcmd - EMUCMD_LOAD_STATE_SLOT_0); #endif } else { @@ -1177,4 +1188,10 @@ static void TaseditorRewindOff(void) #endif } +static void TaseditorRestorePlayback(void) +{ +#ifdef WIN32 + playback.restorePosition(); +#endif +} diff --git a/src/input.h b/src/input.h index 96d7b420..07dbec48 100644 --- a/src/input.h +++ b/src/input.h @@ -232,9 +232,10 @@ enum EMUCMD EMUCMD_TOOL_RAMSEARCHNE, EMUCMD_TOOL_OPENNTVIEW, EMUCMD_TASEDITOR_REWIND, + EMUCMD_RERECORD_DISPLAY_TOGGLE, //----------------------------- //keep adding these in order of newness or else the hotkey binding configs will get messed up... - EMUCMD_RERECORD_DISPLAY_TOGGLE, + EMUCMD_TASEDITOR_RESTORE_PLAYBACK, EMUCMD_MAX }; diff --git a/src/lua-engine.cpp b/src/lua-engine.cpp index 5e4089fb..709decc9 100644 --- a/src/lua-engine.cpp +++ b/src/lua-engine.cpp @@ -40,6 +40,9 @@ #ifdef WIN32 #include "drivers/win/common.h" +#include "drivers/win/taseditor/taseditor_sel.h" +#include "drivers/win/taseditor/markers.h" +#include "drivers/win/taseditor/snapshot.h" #include "drivers/win/taseditor/taseditor_lua.h" extern TASEDITOR_LUA taseditor_lua; #endif @@ -2709,7 +2712,7 @@ int movie_isplaying (lua_State *L) { // //returns the rerecord count of the current movie static int movie_rerecordcount (lua_State *L) { - if (!FCEUMOV_IsRecording() && !FCEUMOV_IsPlaying()) + if (!FCEUMOV_IsRecording() && !FCEUMOV_IsPlaying() && !FCEUMOV_Mode(MOVIEMODE_TASEDITOR)) luaL_error(L, "No movie loaded."); lua_pushinteger(L, FCEUI_GetMovieRerecordCount()); @@ -2722,7 +2725,7 @@ static int movie_rerecordcount (lua_State *L) { //returns an int value representing the total length of the current movie loaded static int movie_getlength (lua_State *L) { - if (!FCEUMOV_IsRecording() && !FCEUMOV_IsPlaying()) + if (!FCEUMOV_IsRecording() && !FCEUMOV_IsPlaying() && !FCEUMOV_Mode(MOVIEMODE_TASEDITOR)) luaL_error(L, "No movie loaded."); lua_pushinteger(L, FCEUI_GetMovieLength()); @@ -2754,7 +2757,7 @@ static int movie_setreadonly (lua_State *L) { //returns the filename of the movie loaded static int movie_getname (lua_State *L) { - if (!FCEUMOV_IsRecording() && !FCEUMOV_IsPlaying()) + if (!FCEUMOV_IsRecording() && !FCEUMOV_IsPlaying() && !FCEUMOV_Mode(MOVIEMODE_TASEDITOR)) luaL_error(L, "No movie loaded."); std::string name = FCEUI_GetMovieName(); @@ -2767,7 +2770,7 @@ static int movie_getname (lua_State *L) { //returns the filename of movie loaded with no path static int movie_getfilename (lua_State *L) { - if (!FCEUMOV_IsRecording() && !FCEUMOV_IsPlaying()) + if (!FCEUMOV_IsRecording() && !FCEUMOV_IsPlaying() && !FCEUMOV_Mode(MOVIEMODE_TASEDITOR)) luaL_error(L, "No movie loaded."); std::string name = FCEUI_GetMovieName(); @@ -4418,6 +4421,17 @@ static int taseditor_getrecordermode(lua_State *L) return 1; } +// int taseditor.getlostplayback() +static int taseditor_getlostplayback(lua_State *L) +{ +#ifdef WIN32 + lua_pushinteger(L, taseditor_lua.getlostplayback()); +#else + lua_pushinteger(L, -1); +#endif + return 1; +} + // int taseditor.getplaybacktarget() static int taseditor_getplaybacktarget(lua_State *L) { @@ -4447,8 +4461,124 @@ static int taseditor_stopseeking(lua_State *L) return 0; } +// table taseditor.getselection() +static int taseditor_getselection(lua_State *L) +{ +#ifdef WIN32 + // create temp vector and provide its reference to TAS Editor for filling the vector with data + std::vector cur_set; + taseditor_lua.getselection(cur_set); + int size = cur_set.size(); + if (size) + { + lua_createtable(L, size, 0); + for (int i = 0; i < size; ++i) + { + lua_pushinteger(L, cur_set[i]); + lua_rawseti(L, -2, i + 1); + } + } else + { + lua_pushnil(L); + } +#else + lua_pushnil(L); +#endif + return 1; +} +// taseditor.setselection(table new_set) +static int taseditor_setselection(lua_State *L) +{ +#ifdef WIN32 + std::vector cur_set; + // retrieve new_set data from table to vector + if (!lua_isnil(L, 1)) + { + luaL_checktype(L, 1, LUA_TTABLE); + int max_index = luaL_getn(L, 1); + int i = 1; + while (i <= max_index) + { + lua_rawgeti(L, 1, i); + cur_set.push_back(lua_tonumber(L, -1)); + lua_pop(L, 1); + i++; + } + } + // and provide its reference to TAS Editor for changing selection + taseditor_lua.setselection(cur_set); +#endif + return 0; +} +// int taseditor.getinput(int frame, int joypad) +static int taseditor_getinput(lua_State *L) +{ +#ifdef WIN32 + lua_pushinteger(L, taseditor_lua.getinput(luaL_checkinteger(L, 1), luaL_checkinteger(L, 2))); +#else + lua_pushinteger(L, -1); +#endif + return 1; +} + +// taseditor.submitinputchange(int frame, int joypad, int input) +static int taseditor_submitinputchange(lua_State *L) +{ +#ifdef WIN32 + taseditor_lua.submitinputchange(luaL_checkinteger(L, 1), luaL_checkinteger(L, 2), luaL_checkinteger(L, 3)); +#endif + return 0; +} + +// taseditor.submitinsertframes(int frame, int joypad, int input) +static int taseditor_submitinsertframes(lua_State *L) +{ +#ifdef WIN32 + taseditor_lua.submitinsertframes(luaL_checkinteger(L, 1), luaL_checkinteger(L, 2)); +#endif + return 0; +} + +// taseditor.submitdeleteframes(int frame, int joypad, int input) +static int taseditor_submitdeleteframes(lua_State *L) +{ +#ifdef WIN32 + taseditor_lua.submitdeleteframes(luaL_checkinteger(L, 1), luaL_checkinteger(L, 2)); +#endif + return 0; +} + +// int taseditor.applyinputchanges([string name]) +static int taseditor_applyinputchanges(lua_State *L) +{ +#ifdef WIN32 + if (lua_isnil(L, 1)) + { + lua_pushinteger(L, taseditor_lua.applyinputchanges("")); + } else + { + const char* name = lua_tostring(L, 1); + if (name) + lua_pushinteger(L, taseditor_lua.applyinputchanges(name)); + else + lua_pushinteger(L, taseditor_lua.applyinputchanges("")); + } +#else + lua_pushinteger(L, -1); +#endif + return 1; +} + +// taseditor.clearinputchanges() +static int taseditor_clearinputchanges(lua_State *L) +{ +#ifdef WIN32 + taseditor_lua.clearinputchanges(); +#endif + return 0; +} static int doPopup(lua_State *L, const char* deftype, const char* deficon) { @@ -5221,9 +5351,18 @@ static const struct luaL_reg taseditorlib[] = { {"setnote", taseditor_setnote}, {"getcurrentbranch", taseditor_getcurrentbranch}, {"getrecordermode", taseditor_getrecordermode}, + {"getlostplayback", taseditor_getlostplayback}, {"getplaybacktarget", taseditor_getplaybacktarget}, {"setplayback", taseditor_setplayback}, {"stopseeking", taseditor_stopseeking}, + {"getselection", taseditor_getselection}, + {"setselection", taseditor_setselection}, + {"getinput", taseditor_getinput}, + {"submitinputchange", taseditor_submitinputchange}, + {"submitinsertframes", taseditor_submitinsertframes}, + {"submitdeleteframes", taseditor_submitdeleteframes}, + {"applyinputchanges", taseditor_applyinputchanges}, + {"clearinputchanges", taseditor_clearinputchanges}, {NULL,NULL} }; diff --git a/src/movie.cpp b/src/movie.cpp index f039e498..570fe008 100644 --- a/src/movie.cpp +++ b/src/movie.cpp @@ -979,9 +979,9 @@ void FCEUMOV_AddInputState() #ifdef _WIN32 if(movieMode == MOVIEMODE_TASEDITOR) { - // if movie length is less than currFrame, pad it with empty frames - if((int)currMovieData.records.size() <= currFrameCounter) - currMovieData.insertEmpty(-1, 1 + currFrameCounter - (int)currMovieData.records.size()); + // if movie length is less or equal to currFrame, pad it with empty frames + if((int)currMovieData.records.size()-1 <= currFrameCounter) + currMovieData.insertEmpty(-1, 2 + currFrameCounter - (int)currMovieData.records.size()); MovieRecord* mr = &currMovieData.records[currFrameCounter]; if(movie_readonly || turbo || playback.pause_frame > currFrameCounter) @@ -1001,6 +1001,9 @@ void FCEUMOV_AddInputState() joyports[0].log(mr); joyports[1].log(mr); recorder.InputChanged(); + // return data from movie to joyports in case Recorder changed it (for example, by applying Superimpose) + joyports[0].load(mr); + joyports[1].load(mr); } _currCommand = 0; } else diff --git a/vc/vc10_fceux.vcxproj b/vc/vc10_fceux.vcxproj index 901868f2..0c3b6a36 100644 --- a/vc/vc10_fceux.vcxproj +++ b/vc/vc10_fceux.vcxproj @@ -424,12 +424,13 @@ - - + + + @@ -750,12 +751,13 @@ - - + + + @@ -866,6 +868,8 @@ + + @@ -908,6 +912,7 @@ $(OutDir)auxlib.lua;%(Outputs) + diff --git a/vc/vc10_fceux.vcxproj.filters b/vc/vc10_fceux.vcxproj.filters index 3d308fcb..1961a95c 100644 --- a/vc/vc10_fceux.vcxproj.filters +++ b/vc/vc10_fceux.vcxproj.filters @@ -940,12 +940,6 @@ drivers\win\taseditor - - drivers\win\taseditor - - - drivers\win\taseditor - drivers\win\taseditor @@ -955,6 +949,15 @@ drivers\win\taseditor + + drivers\win\taseditor + + + drivers\win\taseditor + + + drivers\win\taseditor + @@ -1432,18 +1435,21 @@ drivers\win\taseditor - - drivers\win\taseditor - - - drivers\win\taseditor - drivers\win\taseditor drivers\win\taseditor + + drivers\win\taseditor + + + drivers\win\taseditor + + + drivers\win\taseditor + @@ -1529,6 +1535,9 @@ + + +