diff --git a/src/drivers/win/taseditor.cpp b/src/drivers/win/taseditor.cpp index 05d25851..e4ae3d28 100644 --- a/src/drivers/win/taseditor.cpp +++ b/src/drivers/win/taseditor.cpp @@ -322,7 +322,7 @@ void NewProject() ApplyMovieInputConfig(); if (params.copy_current_input) // copy Input from current snapshot (from history) - history.GetCurrentSnapshot().toMovie(currMovieData); + history.GetCurrentSnapshot().inputlog.toMovie(currMovieData); if (!params.copy_current_markers) markers_manager.reset(); if (params.author_name != L"") currMovieData.comments.push_back(L"author " + params.author_name); @@ -330,6 +330,9 @@ void NewProject() // reset Taseditor project.init(); // new project has blank name greenzone.reset(); + if (params.copy_current_input) + // copy LagLog from current snapshot (from history) + greenzone.laglog = history.GetCurrentSnapshot().laglog; playback.reset(); playback.StartFromZero(); bookmarks.reset(); diff --git a/src/drivers/win/taseditor/bookmark.cpp b/src/drivers/win/taseditor/bookmark.cpp index af6b2529..01fb5ffe 100644 --- a/src/drivers/win/taseditor/bookmark.cpp +++ b/src/drivers/win/taseditor/bookmark.cpp @@ -47,13 +47,13 @@ void BOOKMARK::free() bool BOOKMARK::checkDiffFromCurrent() { // check if the Bookmark data differs from current project/MovieData/Markers/settings - if (not_empty && snapshot.jump_frame == currFrameCounter) + if (not_empty && snapshot.keyframe == currFrameCounter) { - if (snapshot.size == currMovieData.getNumRecords() && snapshot.findFirstChange(currMovieData) < 0) + if (snapshot.inputlog.size == currMovieData.getNumRecords() && snapshot.inputlog.findFirstChange(currMovieData) < 0) { if (!snapshot.MarkersDifferFromCurrent()) { - if (snapshot.has_hot_changes == taseditor_config.enable_hot_changes) + if (snapshot.inputlog.has_hot_changes == taseditor_config.enable_hot_changes) { return false; } @@ -67,9 +67,9 @@ void BOOKMARK::set() { // copy Input and Hotchanges snapshot.init(currMovieData, taseditor_config.enable_hot_changes); - snapshot.jump_frame = currFrameCounter; + snapshot.keyframe = currFrameCounter; if (taseditor_config.enable_hot_changes) - snapshot.copyHotChanges(&history.GetCurrentSnapshot()); + snapshot.inputlog.copyHotChanges(&history.GetCurrentSnapshot().inputlog); // copy savestate savestate = greenzone.GetSavestate(currFrameCounter); // save screenshot @@ -164,5 +164,3 @@ bool BOOKMARK::skipLoad(EMUFILE *is) } // ---------------------------------------------------------- - - diff --git a/src/drivers/win/taseditor/bookmarks.cpp b/src/drivers/win/taseditor/bookmarks.cpp index 075262cc..c5d751f7 100644 --- a/src/drivers/win/taseditor/bookmarks.cpp +++ b/src/drivers/win/taseditor/bookmarks.cpp @@ -205,7 +205,7 @@ void BOOKMARKS::init() lvc.mask = LVCF_WIDTH; lvc.cx = BOOKMARKSLIST_COLUMN_ICONS_WIDTH; ListView_InsertColumn(hwndBookmarksList, 0, &lvc); - // jump_frame column + // keyframe column lvc.mask = LVCF_WIDTH | LVCF_FMT; lvc.fmt = LVCFMT_CENTER; lvc.cx = BOOKMARKSLIST_COLUMN_FRAMENUM_WIDTH; @@ -352,7 +352,7 @@ void BOOKMARKS::set(int slot) // First save changes in edited note (in case it's being currently edited) markers_manager.UpdateMarkerNote(); - int previous_frame = bookmarks_array[slot].snapshot.jump_frame; + int previous_frame = bookmarks_array[slot].snapshot.keyframe; if (bookmarks_array[slot].checkDiffFromCurrent()) { BOOKMARK backup_copy(bookmarks_array[slot]); @@ -363,8 +363,8 @@ void BOOKMARKS::set(int slot) if (slot != old_current_branch && old_current_branch != ITEM_UNDER_MOUSE_CLOUD) { // current_branch was switched to slot, redraw Bookmarks List to change the color of digits - piano_roll.RedrawRow(bookmarks_array[old_current_branch].snapshot.jump_frame); - RedrawChangedBookmarks(bookmarks_array[old_current_branch].snapshot.jump_frame); + piano_roll.RedrawRow(bookmarks_array[old_current_branch].snapshot.keyframe); + RedrawChangedBookmarks(bookmarks_array[old_current_branch].snapshot.keyframe); } // also redraw List rows if (previous_frame >= 0 && previous_frame != currFrameCounter) @@ -389,7 +389,7 @@ void BOOKMARKS::jump(int slot) if (slot < 0 || slot >= TOTAL_BOOKMARKS) return; if (bookmarks_array[slot].not_empty) { - int frame = bookmarks_array[slot].snapshot.jump_frame; + int frame = bookmarks_array[slot].snapshot.keyframe; playback.jump(frame); piano_roll.FollowPauseframe(); bookmarks_array[slot].jumped(); @@ -406,7 +406,7 @@ void BOOKMARKS::deploy(int slot) if (slot < 0 || slot >= TOTAL_BOOKMARKS) return; if (!bookmarks_array[slot].not_empty) return; - int jump_frame = bookmarks_array[slot].snapshot.jump_frame; + int keyframe = bookmarks_array[slot].snapshot.keyframe; bool markers_changed = false; // revert Markers to the Bookmarked state //if (taseditor_config.bind_markers) @@ -420,17 +420,20 @@ void BOOKMARKS::deploy(int slot) // revert current movie data to the Bookmarked state if (taseditor_config.branch_full_movie) { - bookmarks_array[slot].snapshot.toMovie(currMovieData); + bookmarks_array[slot].snapshot.inputlog.toMovie(currMovieData); } else { // restore movie up to and not including bookmarked frame (simulating old TASing method) - if (jump_frame) - bookmarks_array[slot].snapshot.toMovie(currMovieData, 0, jump_frame - 1); + if (keyframe) + bookmarks_array[slot].snapshot.inputlog.toMovie(currMovieData, 0, keyframe - 1); else currMovieData.truncateAt(0); - // add empty frame at the end (at jump_frame) + // add empty frame at the end (at keyframe) currMovieData.insertEmpty(-1, 1); } + // revert Greenzone's LagLog to the Bookmarked state + greenzone.laglog = bookmarks_array[slot].snapshot.laglog; + int first_change = history.RegisterBranching(slot, markers_changed); if (first_change >= 0) { @@ -449,18 +452,18 @@ void BOOKMARKS::deploy(int slot) bookmarks_array[slot].jumped(); } // jump to the target (bookmarked frame) - if (greenzone.SavestateIsEmpty(jump_frame)) - greenzone.WriteSavestate(jump_frame, bookmarks_array[slot].savestate); - playback.jump(jump_frame); + if (greenzone.SavestateIsEmpty(keyframe)) + greenzone.WriteSavestate(keyframe, bookmarks_array[slot].savestate); + playback.jump(keyframe); // switch current branch to this branch int old_current_branch = branches.GetCurrentBranch(); branches.HandleBookmarkDeploy(slot); if (slot != old_current_branch && old_current_branch != ITEM_UNDER_MOUSE_CLOUD) { - piano_roll.RedrawRow(bookmarks_array[old_current_branch].snapshot.jump_frame); - RedrawChangedBookmarks(bookmarks_array[old_current_branch].snapshot.jump_frame); - piano_roll.RedrawRow(jump_frame); - RedrawChangedBookmarks(jump_frame); + piano_roll.RedrawRow(bookmarks_array[old_current_branch].snapshot.keyframe); + RedrawChangedBookmarks(bookmarks_array[old_current_branch].snapshot.keyframe); + piano_roll.RedrawRow(keyframe); + RedrawChangedBookmarks(keyframe); } FCEU_DispMessage("Branch %d loaded.", 0, slot); } @@ -553,7 +556,7 @@ void BOOKMARKS::RedrawChangedBookmarks(int frame) { for (int i = 0; i < TOTAL_BOOKMARKS; ++i) { - if (bookmarks_array[i].snapshot.jump_frame == frame) + if (bookmarks_array[i].snapshot.keyframe == frame) RedrawBookmark(i); } } @@ -614,7 +617,7 @@ void BOOKMARKS::GetDispInfo(NMLVDISPINFO* nmlvDispInfo) case BOOKMARKS_COLUMN_FRAME: { if (bookmarks_array[(item.iItem + 1) % TOTAL_BOOKMARKS].not_empty) - U32ToDecStr(item.pszText, bookmarks_array[(item.iItem + 1) % TOTAL_BOOKMARKS].snapshot.jump_frame, DIGITS_IN_FRAMENUM); + U32ToDecStr(item.pszText, bookmarks_array[(item.iItem + 1) % TOTAL_BOOKMARKS].snapshot.keyframe, DIGITS_IN_FRAMENUM); break; } case BOOKMARKS_COLUMN_TIME: @@ -650,7 +653,7 @@ LONG BOOKMARKS::CustomDraw(NMLVCUSTOMDRAW* msg) { // frame number SelectObject(msg->nmcd.hdc, piano_roll.hMainListFont); - int frame = bookmarks_array[cell_y].snapshot.jump_frame; + int frame = bookmarks_array[cell_y].snapshot.keyframe; if (frame == currFrameCounter || frame == (playback.GetFlashingPauseFrame() - 1)) { // current frame @@ -659,7 +662,7 @@ LONG BOOKMARKS::CustomDraw(NMLVCUSTOMDRAW* msg) { if (!greenzone.SavestateIsEmpty(frame)) { - if (greenzone.GetLagHistoryAtFrame(frame)) + if (greenzone.laglog.GetLagInfoAtFrame(frame)) msg->clrTextBk = LAG_FRAMENUM_COLOR; else msg->clrTextBk = GREENZONE_FRAMENUM_COLOR; @@ -668,7 +671,7 @@ LONG BOOKMARKS::CustomDraw(NMLVCUSTOMDRAW* msg) || (!greenzone.SavestateIsEmpty(frame & EVERY4TH) && !greenzone.SavestateIsEmpty((frame & EVERY4TH) + 4)) || (!greenzone.SavestateIsEmpty(frame & EVERY2ND) && !greenzone.SavestateIsEmpty((frame & EVERY2ND) + 2))) { - if (greenzone.GetLagHistoryAtFrame(frame)) + if (greenzone.laglog.GetLagInfoAtFrame(frame)) msg->clrTextBk = PALE_LAG_FRAMENUM_COLOR; else msg->clrTextBk = PALE_GREENZONE_FRAMENUM_COLOR; @@ -681,7 +684,7 @@ LONG BOOKMARKS::CustomDraw(NMLVCUSTOMDRAW* msg) { // frame number SelectObject(msg->nmcd.hdc, piano_roll.hMainListFont); - int frame = bookmarks_array[cell_y].snapshot.jump_frame; + int frame = bookmarks_array[cell_y].snapshot.keyframe; if (frame == currFrameCounter || frame == (playback.GetFlashingPauseFrame() - 1)) { // current frame @@ -690,7 +693,7 @@ LONG BOOKMARKS::CustomDraw(NMLVCUSTOMDRAW* msg) { if (!greenzone.SavestateIsEmpty(frame)) { - if (greenzone.GetLagHistoryAtFrame(frame)) + if (greenzone.laglog.GetLagInfoAtFrame(frame)) msg->clrTextBk = LAG_INPUT_COLOR1; else msg->clrTextBk = GREENZONE_INPUT_COLOR1; @@ -699,7 +702,7 @@ LONG BOOKMARKS::CustomDraw(NMLVCUSTOMDRAW* msg) || (!greenzone.SavestateIsEmpty(frame & EVERY4TH) && !greenzone.SavestateIsEmpty((frame & EVERY4TH) + 4)) || (!greenzone.SavestateIsEmpty(frame & EVERY2ND) && !greenzone.SavestateIsEmpty((frame & EVERY2ND) + 2))) { - if (greenzone.GetLagHistoryAtFrame(frame)) + if (greenzone.laglog.GetLagInfoAtFrame(frame)) msg->clrTextBk = PALE_LAG_INPUT_COLOR1; else msg->clrTextBk = PALE_GREENZONE_INPUT_COLOR1; @@ -728,12 +731,12 @@ void BOOKMARKS::RightClick() int BOOKMARKS::FindBookmarkAtFrame(int frame) { int cur_bookmark = branches.GetCurrentBranch(); - if (cur_bookmark != ITEM_UNDER_MOUSE_CLOUD && bookmarks_array[cur_bookmark].snapshot.jump_frame == frame) + if (cur_bookmark != ITEM_UNDER_MOUSE_CLOUD && bookmarks_array[cur_bookmark].snapshot.keyframe == frame) return cur_bookmark + TOTAL_BOOKMARKS; // blue digit has highest priority when drawing for (int i = 0; i < TOTAL_BOOKMARKS; ++i) { cur_bookmark = (i + 1) % TOTAL_BOOKMARKS; - if (bookmarks_array[cur_bookmark].not_empty && bookmarks_array[cur_bookmark].snapshot.jump_frame == frame) + if (bookmarks_array[cur_bookmark].not_empty && bookmarks_array[cur_bookmark].snapshot.keyframe == frame) return cur_bookmark; // green digit } return -1; // no Bookmarks at the frame diff --git a/src/drivers/win/taseditor/branches.cpp b/src/drivers/win/taseditor/branches.cpp index 085f058c..d715c16f 100644 --- a/src/drivers/win/taseditor/branches.cpp +++ b/src/drivers/win/taseditor/branches.cpp @@ -312,7 +312,7 @@ void BRANCHES::update() } else { parent = FindFullTimelineForBranch(current_branch); - if (parent != current_branch && bookmarks.bookmarks_array[parent].snapshot.jump_frame == bookmarks.bookmarks_array[current_branch].snapshot.jump_frame) + if (parent != current_branch && bookmarks.bookmarks_array[parent].snapshot.keyframe == bookmarks.bookmarks_array[current_branch].snapshot.keyframe) parent = current_branch; } do @@ -325,12 +325,12 @@ void BRANCHES::update() if (parent == ITEM_UNDER_MOUSE_CLOUD) lower_frame = -1; else - lower_frame = bookmarks.bookmarks_array[parent].snapshot.jump_frame; + lower_frame = bookmarks.bookmarks_array[parent].snapshot.keyframe; } while (parent != ITEM_UNDER_MOUSE_CLOUD && currFrameCounter < lower_frame); if (branch == TOTAL_BOOKMARKS) upper_frame = currMovieData.getNumRecords() - 1; else - upper_frame = bookmarks.bookmarks_array[branch].snapshot.jump_frame; + upper_frame = bookmarks.bookmarks_array[branch].snapshot.keyframe; branch_x = BranchCurrX[branch]; branch_y = BranchCurrY[branch]; if (parent == ITEM_UNDER_MOUSE_CLOUD) @@ -611,12 +611,12 @@ void BRANCHES::RedrawBranchesTree() if (IsSafeToShowBranchesData()) { SetBkMode(hBitmapDC, TRANSPARENT); - // jump_frame of item under cursor (except cloud - it doesn't have particular frame) + // keyframe of item under cursor (except cloud - it doesn't have particular frame) if (bookmarks.item_under_mouse == TOTAL_BOOKMARKS || (bookmarks.item_under_mouse >= 0 && bookmarks.item_under_mouse < TOTAL_BOOKMARKS && bookmarks.bookmarks_array[bookmarks.item_under_mouse].not_empty)) { char framenum_string[DIGITS_IN_FRAMENUM+1] = {0}; if (bookmarks.item_under_mouse < TOTAL_BOOKMARKS) - U32ToDecStr(framenum_string, bookmarks.bookmarks_array[bookmarks.item_under_mouse].snapshot.jump_frame, DIGITS_IN_FRAMENUM); + U32ToDecStr(framenum_string, bookmarks.bookmarks_array[bookmarks.item_under_mouse].snapshot.keyframe, DIGITS_IN_FRAMENUM); else U32ToDecStr(framenum_string, currFrameCounter, DIGITS_IN_FRAMENUM); SetTextColor(hBitmapDC, BRANCHES_TEXT_SHADOW_COLOR); @@ -769,19 +769,19 @@ void BRANCHES::InvalidateBranchSlot(int slot) parents[i] = ITEM_UNDER_MOUSE_CLOUD; } } -// returns the frame of first difference between snapshots of two Branches +// returns the frame of first difference between InputLogs of snapshots of two Branches int BRANCHES::GetFirstDifference(int first_branch, int second_branch) { if (first_branch == second_branch) - return bookmarks.bookmarks_array[first_branch].snapshot.size; + return bookmarks.bookmarks_array[first_branch].snapshot.inputlog.size; if (cached_first_difference[first_branch][second_branch] == FIRST_DIFFERENCE_UNKNOWN) { if (bookmarks.bookmarks_array[first_branch].not_empty && bookmarks.bookmarks_array[second_branch].not_empty) { - int frame = bookmarks.bookmarks_array[first_branch].snapshot.findFirstChange(bookmarks.bookmarks_array[second_branch].snapshot); + int frame = bookmarks.bookmarks_array[first_branch].snapshot.inputlog.findFirstChange(bookmarks.bookmarks_array[second_branch].snapshot.inputlog); if (frame < 0) - frame = bookmarks.bookmarks_array[first_branch].snapshot.size; + frame = bookmarks.bookmarks_array[first_branch].snapshot.inputlog.size; cached_first_difference[first_branch][second_branch] = frame; cached_first_difference[second_branch][first_branch] = frame; return frame; @@ -796,7 +796,7 @@ int BRANCHES::FindFullTimelineForBranch(int branch_num) { cached_timelines[branch_num] = branch_num; // by default std::vector candidates; - int temp_jump_frame, temp_parent, max_jump_frame, max_first_difference; + int temp_keyframe, temp_parent, max_keyframe, max_first_difference; // 1 - find max_first_difference among Branches that are in the same timeline max_first_difference = -1; int first_diff; @@ -805,18 +805,18 @@ int BRANCHES::FindFullTimelineForBranch(int branch_num) if (i != branch_num && bookmarks.bookmarks_array[i].not_empty) { first_diff = GetFirstDifference(branch_num, i); - if (first_diff >= bookmarks.bookmarks_array[i].snapshot.jump_frame) + if (first_diff >= bookmarks.bookmarks_array[i].snapshot.keyframe) if (max_first_difference < first_diff) max_first_difference = first_diff; } } - // 2 - find max_jump_frame among those Branches whose first_diff >= max_first_difference - max_jump_frame = -1; + // 2 - find max_keyframe among those Branches whose first_diff >= max_first_difference + max_keyframe = -1; for (int i = TOTAL_BOOKMARKS-1; i >= 0; i--) { if (bookmarks.bookmarks_array[i].not_empty) { - if (i != branch_num && GetFirstDifference(branch_num, i) >= max_first_difference && GetFirstDifference(branch_num, i) >= bookmarks.bookmarks_array[i].snapshot.jump_frame) + if (i != branch_num && GetFirstDifference(branch_num, i) >= max_first_difference && GetFirstDifference(branch_num, i) >= bookmarks.bookmarks_array[i].snapshot.keyframe) { // ensure that this candidate belongs to children/grandchildren of current_branch temp_parent = parents[i]; @@ -825,17 +825,17 @@ int BRANCHES::FindFullTimelineForBranch(int branch_num) if (temp_parent == branch_num) { candidates.push_back(i); - temp_jump_frame = bookmarks.bookmarks_array[i].snapshot.jump_frame; - if (max_jump_frame < temp_jump_frame) - max_jump_frame = temp_jump_frame; + temp_keyframe = bookmarks.bookmarks_array[i].snapshot.keyframe; + if (max_keyframe < temp_keyframe) + max_keyframe = temp_keyframe; } } } } - // 3 - remove those candidates who have jump_frame < max_jump_frame + // 3 - remove those candidates who have keyframe < max_keyframe for (int i = candidates.size()-1; i >= 0; i--) { - if (bookmarks.bookmarks_array[candidates[i]].snapshot.jump_frame < max_jump_frame) + if (bookmarks.bookmarks_array[candidates[i]].snapshot.keyframe < max_keyframe) candidates.erase(candidates.begin() + i); } // 4 - get first of candidates (if there are many then it will be the Branch with highest id number) @@ -882,21 +882,21 @@ void BRANCHES::RecalculateParents() { // find best parent for every Branch std::vector candidates; - int temp_jump_frame, temp_parent, max_jump_frame, max_first_difference; + int temp_keyframe, temp_parent, max_keyframe, max_first_difference; for (int i1 = TOTAL_BOOKMARKS-1; i1 >= 0; i1--) { int i = (i1 + 1) % TOTAL_BOOKMARKS; if (bookmarks.bookmarks_array[i].not_empty) { - int jump_frame = bookmarks.bookmarks_array[i].snapshot.jump_frame; - // 1 - find all candidates and max_jump_frame among them + int keyframe = bookmarks.bookmarks_array[i].snapshot.keyframe; + // 1 - find all candidates and max_keyframe among them candidates.resize(0); - max_jump_frame = -1; + max_keyframe = -1; for (int t1 = TOTAL_BOOKMARKS-1; t1 >= 0; t1--) { int t = (t1 + 1) % TOTAL_BOOKMARKS; - temp_jump_frame = bookmarks.bookmarks_array[t].snapshot.jump_frame; - if (t != i && bookmarks.bookmarks_array[t].not_empty && temp_jump_frame <= jump_frame && GetFirstDifference(t, i) >= temp_jump_frame) + temp_keyframe = bookmarks.bookmarks_array[t].snapshot.keyframe; + if (t != i && bookmarks.bookmarks_array[t].not_empty && temp_keyframe <= keyframe && GetFirstDifference(t, i) >= temp_keyframe) { // ensure that this candidate doesn't belong to children/grandchildren of this Branch temp_parent = parents[t]; @@ -906,19 +906,19 @@ void BRANCHES::RecalculateParents() { // all ok, this is good candidate for being the parent of the Branch candidates.push_back(t); - if (max_jump_frame < temp_jump_frame) - max_jump_frame = temp_jump_frame; + if (max_keyframe < temp_keyframe) + max_keyframe = temp_keyframe; } } } if (candidates.size()) { - // 2 - remove those candidates who have jump_frame < max_jump_frame - // and for those who have jump_frame == max_jump_frame, find max_first_difference + // 2 - remove those candidates who have keyframe < max_keyframe + // and for those who have keyframe == max_keyframe, find max_first_difference max_first_difference = -1; for (int t = candidates.size()-1; t >= 0; t--) { - if (bookmarks.bookmarks_array[candidates[t]].snapshot.jump_frame < max_jump_frame) + if (bookmarks.bookmarks_array[candidates[t]].snapshot.keyframe < max_keyframe) candidates.erase(candidates.begin() + t); else if (max_first_difference < GetFirstDifference(candidates[t], i)) max_first_difference = GetFirstDifference(candidates[t], i); diff --git a/src/drivers/win/taseditor/editor.cpp b/src/drivers/win/taseditor/editor.cpp index 8ff1a7cd..4a4f27f6 100644 --- a/src/drivers/win/taseditor/editor.cpp +++ b/src/drivers/win/taseditor/editor.cpp @@ -204,7 +204,7 @@ void EDITOR::InputSetPattern(int start, int end, int joy, int button, int consec for (int i = start; i <= end; ++i) { // skip lag frames - if (taseditor_config.pattern_skips_lag && greenzone.GetLagHistoryAtFrame(i)) + if (taseditor_config.pattern_skips_lag && greenzone.laglog.GetLagInfoAtFrame(i)) continue; value = (autofire_patterns[current_pattern][pattern_offset] != 0); if (currMovieData.records[i].checkBit(joy, button) != value) @@ -285,7 +285,7 @@ bool EDITOR::FrameColumnSetPattern() for(SelectionFrames::iterator it(current_selection_begin); it != current_selection_end; it++) { // skip lag frames - if (taseditor_config.pattern_skips_lag && greenzone.GetLagHistoryAtFrame(*it)) + if (taseditor_config.pattern_skips_lag && greenzone.laglog.GetLagInfoAtFrame(*it)) continue; if (autofire_patterns[current_pattern][pattern_offset]) { @@ -370,7 +370,7 @@ bool EDITOR::InputColumnSetPattern(int joy, int button) for(SelectionFrames::iterator it(current_selection_begin); it != current_selection_end; it++) { // skip lag frames - if (taseditor_config.pattern_skips_lag && greenzone.GetLagHistoryAtFrame(*it)) + if (taseditor_config.pattern_skips_lag && greenzone.laglog.GetLagInfoAtFrame(*it)) continue; currMovieData.records[*it].setBitValue(joy, button, autofire_patterns[current_pattern][pattern_offset] != 0); pattern_offset++; @@ -455,7 +455,7 @@ void EDITOR::AdjustUp(int at) // reduce Piano Roll piano_roll.UpdateItemCount(); // check and register changes - history.RegisterChanges(MODTYPE_ADJUST_UP, at); + history.RegisterChanges(MODTYPE_ADJUST_LAG, at, -1, NULL, -1); greenzone.Invalidate(at); if (markers_changed) selection.must_find_current_marker = playback.must_find_current_marker = true; @@ -473,7 +473,7 @@ void EDITOR::AdjustDown(int at) markers_changed = true; } // check and register changes - history.RegisterChanges(MODTYPE_ADJUST_DOWN, at); + history.RegisterChanges(MODTYPE_ADJUST_LAG, at, -1, NULL, 1); greenzone.Invalidate(at); if (markers_changed) selection.must_find_current_marker = playback.must_find_current_marker = true; diff --git a/src/drivers/win/taseditor/greenzone.cpp b/src/drivers/win/taseditor/greenzone.cpp index b5345a13..ead48159 100644 --- a/src/drivers/win/taseditor/greenzone.cpp +++ b/src/drivers/win/taseditor/greenzone.cpp @@ -11,10 +11,10 @@ Greenzone - Access zone [Singleton] * stores array of savestates, used for faster movie navigation by Playback cursor -* also stores the frame-by-frame log of lag appearance +* also stores LagLog of current movie Input * saves and loads the data from a project file. On error: truncates Greenzone to last successfully read savestate * regularly checks if there's a savestate of current emulation state, if there's no such savestate in array then creates one and updates lag info for previous frame -* implements the working of "Auto-adjust Input due to lag" +* implements the working of "Auto-adjust Input due to lag" feature * regularly runs gradual cleaning of the savestates array (for memory saving), deleting oldest savestates * on demand: (when movie Input was changed) truncates the size of Greenzone, deleting savestates that became irrelevant because of new Input. After truncating it may also move Playback cursor (which must always reside within Greenzone) and may launch Playback seeking * stores resources: save id, properties of gradual cleaning, timing of cleaning @@ -49,7 +49,7 @@ void GREENZONE::free() { savestates.resize(0); greenZoneCount = 0; - lag_history.resize(0); + laglog.reset(); } void GREENZONE::reset() { @@ -57,8 +57,8 @@ void GREENZONE::reset() } void GREENZONE::update() { - // keep memorizing savestates, this function should be called at the end of every frame - if (greenZoneCount <= currFrameCounter || (int)savestates.size() <= currFrameCounter || !savestates[currFrameCounter].size() || (int)lag_history.size() <= currFrameCounter) + // keep collecting savestates, this function should be called at the end of every frame + if (greenZoneCount <= currFrameCounter || (int)savestates.size() <= currFrameCounter || !savestates[currFrameCounter].size() || laglog.GetSize() <= currFrameCounter) CollectCurrentState(); // run cleaning from time to time @@ -74,8 +74,6 @@ void GREENZONE::CollectCurrentState() if ((int)savestates.size() < greenZoneCount) savestates.resize(greenZoneCount); - if ((int)lag_history.size() < greenZoneCount) - lag_history.resize(greenZoneCount, 0); // if frame changed - log savestate EMUFILE_MEMORY ms(&savestates[currFrameCounter]); @@ -86,14 +84,14 @@ void GREENZONE::CollectCurrentState() if (currFrameCounter > 0) { // lagFlag indicates that lag was in previous frame - int old_lagFlag = lag_history[currFrameCounter - 1]; + int old_lagFlag = laglog.GetLagInfoAtFrame(currFrameCounter - 1); // Auto-adjust Input due to lag if (taseditor_config.adjust_input_due_to_lag) { if (old_lagFlag && !lagFlag) { // there's no more lag on previous frame - shift Input up - lag_history.erase(lag_history.begin() + (currFrameCounter - 1)); + laglog.EraseLagFrame(currFrameCounter - 1); editor.AdjustUp(currFrameCounter - 1); // since AdjustUp didn't restore Playback cursor, we must rewind here bool emu_was_paused = (FCEUI_EmulationPaused() != 0); @@ -106,7 +104,7 @@ void GREENZONE::CollectCurrentState() } else if (!old_lagFlag && lagFlag) { // there's new lag on previous frame - shift Input down - lag_history.insert(lag_history.begin() + (currFrameCounter - 1), 1); + laglog.InsertLagFrame(currFrameCounter - 1); editor.AdjustDown(currFrameCounter - 1); // since AdjustDown didn't restore Playback cursor, we must rewind here bool emu_was_paused = (FCEUI_EmulationPaused() != 0); @@ -116,13 +114,17 @@ void GREENZONE::CollectCurrentState() playback.SeekingStart(saved_pause_frame); if (emu_was_paused) playback.PauseEmulation(); + } else + { + // old_lagFlag == lagFlag + laglog.SetLagInfo(currFrameCounter - 1, (lagFlag != 0)); } } else { if (lagFlag) - lag_history[currFrameCounter - 1] = 1; + laglog.SetLagInfo(currFrameCounter - 1, true); else - lag_history[currFrameCounter - 1] = 0; + laglog.SetLagInfo(currFrameCounter - 1, false); } } } @@ -221,17 +223,10 @@ void GREENZONE::save(EMUFILE *os, bool really_save) { // write "GREENZONE" string os->fwrite(greenzone_save_id, GREENZONE_ID_LEN); + // write LagLog + laglog.save(os); // write size write32le(greenZoneCount, os); - // compress and write lag history - int len = lag_history.size(); - if (len > currMovieData.getNumRecords()) - len = currMovieData.getNumRecords(); - uLongf comprlen = (len>>9)+12 + len; - std::vector cbuf(comprlen); - compress(&cbuf[0], &comprlen, &lag_history[0], len); - write32le(comprlen, os); - os->fwrite(&cbuf[0], comprlen); // write Playback cursor position write32le(currFrameCounter, os); // write savestates @@ -259,6 +254,8 @@ void GREENZONE::save(EMUFILE *os, bool really_save) { // write "GREENZONX" string os->fwrite(greenzone_skipsave_id, GREENZONE_ID_LEN); + // write LagLog + laglog.save(os); // write Playback cursor position write32le(currFrameCounter, os); if (currFrameCounter > 0) @@ -288,6 +285,8 @@ bool GREENZONE::load(EMUFILE *is, bool really_load) if (!strcmp(greenzone_skipsave_id, save_id)) { // string says to skip loading Greenzone + // read LagLog + laglog.load(is); // read Playback cursor position if (read32le(&frame, is)) { @@ -321,21 +320,13 @@ bool GREENZONE::load(EMUFILE *is, bool really_load) goto error; } if (strcmp(greenzone_save_id, save_id)) goto error; // string is not valid + // read LagLog + laglog.load(is); // read size if (read32le(&size, is) && size >= 0 && size <= currMovieData.getNumRecords()) { greenZoneCount = size; savestates.resize(greenZoneCount); - // read and uncompress lag history - uLongf destlen = currMovieData.getNumRecords(); - lag_history.resize(destlen, 0); - int comprlen; - if (!read32le(&comprlen, is)) goto error; - if (comprlen <= 0) goto error; - std::vector cbuf(comprlen); - if (is->fread(&cbuf[0], comprlen) != comprlen) goto error; - int e = uncompress(&lag_history[0], &destlen, &cbuf[0], comprlen); - if (e != Z_OK && e != Z_BUF_ERROR) goto error; // read Playback cursor position if (read32le(&frame, is)) { @@ -485,13 +476,6 @@ int GREENZONE::GetSize() { return greenZoneCount; } -bool GREENZONE::GetLagHistoryAtFrame(int frame) -{ - if (frame < (int)lag_history.size()) - return lag_history[frame] != 0; - else - return false; -} // this should only be used by Bookmark Set procedure std::vector& GREENZONE::GetSavestate(int frame) diff --git a/src/drivers/win/taseditor/greenzone.h b/src/drivers/win/taseditor/greenzone.h index 1eccc55a..6be60e1b 100644 --- a/src/drivers/win/taseditor/greenzone.h +++ b/src/drivers/win/taseditor/greenzone.h @@ -1,5 +1,7 @@ // Specification file for Greenzone class +#include "laglog.h" + #define GREENZONE_ID_LEN 10 #define TIME_BETWEEN_CLEANINGS 10000 // in milliseconds @@ -34,20 +36,21 @@ public: int FindBeginningOfGreenZone(int starting_index = 0); int GetSize(); - bool GetLagHistoryAtFrame(int frame); std::vector& GetSavestate(int frame); void WriteSavestate(int frame, std::vector& savestate); bool SavestateIsEmpty(int frame); + // saved data + LAGLOG laglog; + private: void CollectCurrentState(); // saved data int greenZoneCount; std::vector> savestates; - std::vector lag_history; // not saved data int next_cleaning_time; - + }; diff --git a/src/drivers/win/taseditor/history.cpp b/src/drivers/win/taseditor/history.cpp index 0ca0336b..f6ce35a5 100644 --- a/src/drivers/win/taseditor/history.cpp +++ b/src/drivers/win/taseditor/history.cpp @@ -12,7 +12,7 @@ History - History of movie modifications * stores array of History items (snapshots, backup_bookmarks, backup_current_branch) and pointer to current snapshot * saves and loads the data from a project file. On error: clears the array and starts new history by making snapshot of current movie data -* on demand: checks the difference between the last snapshot and current movie, and makes a decision to create new point of rollback. In special cases it can create a point of rollback without checking the difference, assuming that caller already checked it +* on demand: checks the difference between the last snapshot's Inputlog and current movie Input, and makes a decision to create new point of rollback. In special cases it can create a point of rollback without checking the difference, assuming that caller already checked it * implements all restoring operations: undo, redo, revert to any snapshot from the array * also stores the state of "undo pointer" * regularly updates the state of "undo pointer" @@ -103,7 +103,6 @@ char modCaptions[MODTYPES_TOTAL][20] = {" Initialization", " LUA Marker Remove", " LUA Marker Rename", " LUA Change", - " AdjustLag", " AdjustLag" }; char LuaCaptionPrefix[6] = " LUA "; char joypadCaptions[4][5] = {"(1P)", "(2P)", "(3P)", "(4P)"}; @@ -151,9 +150,9 @@ void HISTORY::reset() snap.init(currMovieData, taseditor_config.enable_hot_changes); snap.mod_type = MODTYPE_INIT; strcat(snap.description, modCaptions[snap.mod_type]); - snap.jump_frame = -1; + snap.keyframe = -1; snap.start_frame = 0; - snap.end_frame = snap.size - 1; + snap.end_frame = snap.inputlog.size - 1; AddItemToHistory(snap); UpdateHistoryList(); RedrawHistoryList(); @@ -249,9 +248,10 @@ int HISTORY::JumpInTime(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 - if (new_pos == history_cursor_pos) return -1; + new_pos = history_total_items - 1; + // if nothing is done, do not invalidate Greenzone + if (new_pos == history_cursor_pos) + return -1; // make jump int old_pos = history_cursor_pos; @@ -284,9 +284,9 @@ int HISTORY::JumpInTime(int new_pos) // swap Bookmark and its backup version slot = (mod_type - MODTYPE_BOOKMARK_0) % TOTAL_BOOKMARKS; BOOKMARK temp_bookmark(bookmarks.bookmarks_array[slot]); - frames_to_redraw.push_back(bookmarks.bookmarks_array[slot].snapshot.jump_frame); + frames_to_redraw.push_back(bookmarks.bookmarks_array[slot].snapshot.keyframe); bookmarks.bookmarks_array[slot] = backup_bookmarks[real_pos]; - frames_to_redraw.push_back(bookmarks.bookmarks_array[slot].snapshot.jump_frame); + frames_to_redraw.push_back(bookmarks.bookmarks_array[slot].snapshot.keyframe); bookmarks_to_redraw.push_back(slot); backup_bookmarks[real_pos] = temp_bookmark; branches.InvalidateBranchSlot(slot); @@ -307,9 +307,9 @@ int HISTORY::JumpInTime(int new_pos) // swap Bookmark and its backup version slot = (mod_type - MODTYPE_BOOKMARK_0) % TOTAL_BOOKMARKS; BOOKMARK temp_bookmark(bookmarks.bookmarks_array[slot]); - frames_to_redraw.push_back(bookmarks.bookmarks_array[slot].snapshot.jump_frame); + frames_to_redraw.push_back(bookmarks.bookmarks_array[slot].snapshot.keyframe); bookmarks.bookmarks_array[slot] = backup_bookmarks[real_pos]; - frames_to_redraw.push_back(bookmarks.bookmarks_array[slot].snapshot.jump_frame); + frames_to_redraw.push_back(bookmarks.bookmarks_array[slot].snapshot.keyframe); bookmarks_to_redraw.push_back(slot); backup_bookmarks[real_pos] = temp_bookmark; branches.InvalidateBranchSlot(slot); @@ -336,12 +336,12 @@ int HISTORY::JumpInTime(int new_pos) // current_branch was switched, redraw Bookmarks List to change the color of digits if (old_current_branch != ITEM_UNDER_MOUSE_CLOUD) { - frames_to_redraw.push_back(bookmarks.bookmarks_array[old_current_branch].snapshot.jump_frame); + frames_to_redraw.push_back(bookmarks.bookmarks_array[old_current_branch].snapshot.keyframe); bookmarks_to_redraw.push_back(old_current_branch); } if (current_branch != ITEM_UNDER_MOUSE_CLOUD) { - frames_to_redraw.push_back(bookmarks.bookmarks_array[current_branch].snapshot.jump_frame); + frames_to_redraw.push_back(bookmarks.bookmarks_array[current_branch].snapshot.keyframe); bookmarks_to_redraw.push_back(current_branch); } } @@ -361,9 +361,9 @@ int HISTORY::JumpInTime(int new_pos) // create undo_hint if (new_pos > old_pos) - undo_hint_pos = GetCurrentSnapshot().jump_frame; // redo + undo_hint_pos = GetCurrentSnapshot().keyframe; // redo else - undo_hint_pos = GetNextToCurrentSnapshot().jump_frame; // undo + undo_hint_pos = GetNextToCurrentSnapshot().keyframe; // undo undo_hint_time = clock() + UNDO_HINT_TIME; show_undo_hint = true; @@ -377,11 +377,12 @@ int HISTORY::JumpInTime(int new_pos) markers_changed = true; } - // update current movie data - int first_change = snapshots[real_pos].findFirstChange(currMovieData); + // update current movie data and Greenzone's LagLog + int first_change = snapshots[real_pos].inputlog.findFirstChange(currMovieData); if (first_change >= 0) { - snapshots[real_pos].toMovie(currMovieData, first_change); + snapshots[real_pos].inputlog.toMovie(currMovieData, first_change); + greenzone.laglog = snapshots[real_pos].laglog; selection.must_find_current_marker = playback.must_find_current_marker = true; project.SetProjectChanged(); // Piano Roll Redraw will be called by Greenzone invalidation @@ -403,14 +404,29 @@ int HISTORY::JumpInTime(int new_pos) void HISTORY::undo() { - int result = JumpInTime(history_cursor_pos - 1); + int step = -1; + if (taseditor_config.adjust_input_due_to_lag) + { + // if we're undoing an AdjustLag operation then undo two operations + if (GetCurrentSnapshot().mod_type == MODTYPE_ADJUST_LAG) + step = -2; + } + int result = JumpInTime(history_cursor_pos + step); if (result >= 0) greenzone.InvalidateAndCheck(result); return; } void HISTORY::redo() { - int result = JumpInTime(history_cursor_pos + 1); + int step = 1; + if (taseditor_config.adjust_input_due_to_lag) + { + // if we're redoing an operation followed by AdjustLag then redo two operations + if (history_cursor_pos + 2 <= history_total_items) + if (snapshots[(history_start_pos + history_cursor_pos + 2) % history_size].mod_type == MODTYPE_ADJUST_LAG) + step = 2; + } + int result = JumpInTime(history_cursor_pos + step); if (result >= 0) greenzone.InvalidateAndCheck(result); return; @@ -418,7 +434,7 @@ void HISTORY::redo() // ---------------------------- void HISTORY::AddItemToHistory(SNAPSHOT &snap, int cur_branch) { - // history uses conveyor of items (vector with fixed size) to aviod frequent resizing, which would be awfully expensive with such large objects as SNAPSHOT and BOOKMARK + // history uses conveyor of items (vector with fixed size) to aviod frequent reallocations caused by vector resizing, which would be awfully expensive with such large objects as SNAPSHOT and BOOKMARK if (history_total_items >= history_size) { // reached the end of available history_size - move history_start_pos (thus deleting oldest snapshot) @@ -440,7 +456,7 @@ void HISTORY::AddItemToHistory(SNAPSHOT &snap, int cur_branch) } void HISTORY::AddItemToHistory(SNAPSHOT &snap, int cur_branch, BOOKMARK &bookm) { - // history uses conveyor of items (vector with fixed size) to aviod frequent resizing, which would be awfully expensive with such large objects as SNAPSHOT and BOOKMARK + // history uses conveyor of items (vector with fixed size) to aviod frequent reallocations caused by vector resizing, which would be awfully expensive with such large objects as SNAPSHOT and BOOKMARK if (history_total_items >= history_size) { // reached the end of available history_size - move history_start_pos (thus deleting oldest snapshot) @@ -471,14 +487,14 @@ int HISTORY::RegisterChanges(int mod_type, int start, int end, const char* comme snap.init(currMovieData, taseditor_config.enable_hot_changes); // check if there are Input differences from latest snapshot int real_pos = (history_start_pos + history_cursor_pos) % history_size; - int first_changes = snap.findFirstChange(snapshots[real_pos], start, end); + int first_changes = snap.inputlog.findFirstChange(snapshots[real_pos].inputlog, start, end); if (first_changes >= 0) { // differences found // fill description: snap.mod_type = mod_type; strcat(snap.description, modCaptions[snap.mod_type]); - // set jump_frame + // set keyframe switch (mod_type) { case MODTYPE_SET: @@ -487,7 +503,7 @@ int HISTORY::RegisterChanges(int mod_type, int start, int end, const char* comme case MODTYPE_CLEAR: case MODTYPE_CUT: { - snap.jump_frame = first_changes; + snap.keyframe = first_changes; break; } case MODTYPE_INSERT: @@ -495,31 +511,27 @@ int HISTORY::RegisterChanges(int mod_type, int start, int end, const char* comme case MODTYPE_PASTE: case MODTYPE_CLONE: case MODTYPE_PATTERN: - case MODTYPE_ADJUST_UP: - case MODTYPE_ADJUST_DOWN: + case MODTYPE_ADJUST_LAG: { // for these changes user prefers to see frame of attempted change (Selection cursor position), not frame of actual differences - snap.jump_frame = start; + snap.keyframe = start; break; } } // set start_frame, end_frame, consecutive_tag - if (snap.mod_type == MODTYPE_ADJUST_UP || snap.mod_type == MODTYPE_ADJUST_DOWN) + if (snap.mod_type == MODTYPE_ADJUST_LAG) { // special operation: AdjustLag snap.start_frame = snap.end_frame = start; - if (snap.mod_type == MODTYPE_ADJUST_UP) - snap.consecutive_tag = -1; - else - snap.consecutive_tag = 1; + snap.consecutive_tag = consecutive_tag; // -1 for Adjust Up, +1 for Adjust Down // combine Adjustment with previous snapshot if needed bool combine = false; - if (snapshots[real_pos].mod_type == MODTYPE_ADJUST_UP || snapshots[real_pos].mod_type == MODTYPE_ADJUST_DOWN) + if (snapshots[real_pos].mod_type == MODTYPE_ADJUST_LAG) combine = true; if (combine) { - if (snap.jump_frame > snapshots[real_pos].jump_frame) - snap.jump_frame = snapshots[real_pos].jump_frame; + if (snap.keyframe > snapshots[real_pos].keyframe) + snap.keyframe = snapshots[real_pos].keyframe; if (snap.start_frame > snapshots[real_pos].start_frame) snap.start_frame = snapshots[real_pos].start_frame; if (snap.end_frame < snapshots[real_pos].end_frame) @@ -529,10 +541,12 @@ int HISTORY::RegisterChanges(int mod_type, int start, int end, const char* comme // set hotchanges if (taseditor_config.enable_hot_changes) { - if (snap.mod_type == MODTYPE_ADJUST_UP) - snap.inheritHotChanges_DeleteNum(&snapshots[real_pos], start, 1, !combine); + if (consecutive_tag < 0) + // it was Adjust Up + snap.inputlog.inheritHotChanges_DeleteNum(&snapshots[real_pos].inputlog, start, 1, !combine); else - snap.inheritHotChanges_InsertNum(&snapshots[real_pos], start, 1, !combine); + // it was Adjust Down + snap.inputlog.inheritHotChanges_InsertNum(&snapshots[real_pos].inputlog, start, 1, !combine); } // add "consecutive_tag" to description char framenum[11]; @@ -577,8 +591,8 @@ int HISTORY::RegisterChanges(int mod_type, int start, int end, const char* comme if (consecutive_tag && taseditor_config.combine_consecutive && snapshots[real_pos].mod_type == snap.mod_type && snapshots[real_pos].consecutive_tag == snap.consecutive_tag) { // combine Drawing with previous snapshot - if (snap.jump_frame > snapshots[real_pos].jump_frame) - snap.jump_frame = snapshots[real_pos].jump_frame; + if (snap.keyframe > snapshots[real_pos].keyframe) + snap.keyframe = snapshots[real_pos].keyframe; if (snap.start_frame > snapshots[real_pos].start_frame) snap.start_frame = snapshots[real_pos].start_frame; if (snap.end_frame < snapshots[real_pos].end_frame) @@ -603,8 +617,8 @@ int HISTORY::RegisterChanges(int mod_type, int start, int end, const char* comme // set hotchanges if (taseditor_config.enable_hot_changes) { - snap.copyHotChanges(&snapshots[real_pos]); - snap.fillHotChanges(snapshots[real_pos], first_changes, end); + snap.inputlog.copyHotChanges(&snapshots[real_pos].inputlog); + snap.inputlog.fillHotChanges(snapshots[real_pos].inputlog, first_changes, end); } // replace current snapshot with this cloned snapshot and truncate history here snapshots[real_pos] = snap; @@ -638,11 +652,11 @@ int HISTORY::RegisterChanges(int mod_type, int start, int end, const char* comme switch (mod_type) { case MODTYPE_DELETE: - snap.inheritHotChanges_DeleteSelection(&snapshots[real_pos]); + snap.inputlog.inheritHotChanges_DeleteSelection(&snapshots[real_pos].inputlog); break; case MODTYPE_INSERT: case MODTYPE_CLONE: - snap.inheritHotChanges_InsertSelection(&snapshots[real_pos]); + snap.inputlog.inheritHotChanges_InsertSelection(&snapshots[real_pos].inputlog); break; case MODTYPE_SET: case MODTYPE_UNSET: @@ -650,11 +664,11 @@ int HISTORY::RegisterChanges(int mod_type, int start, int end, const char* comme case MODTYPE_CUT: case MODTYPE_PASTE: case MODTYPE_PATTERN: - snap.inheritHotChanges(&snapshots[real_pos]); - snap.fillHotChanges(snapshots[real_pos], first_changes, end); + snap.inputlog.inheritHotChanges(&snapshots[real_pos].inputlog); + snap.inputlog.fillHotChanges(snapshots[real_pos].inputlog, first_changes, end); break; case MODTYPE_TRUNCATE: - snap.copyHotChanges(&snapshots[real_pos]); + snap.inputlog.copyHotChanges(&snapshots[real_pos].inputlog); // do not add new hotchanges and do not fade old hotchanges, because there was nothing added break; } @@ -674,14 +688,14 @@ int HISTORY::RegisterInsertNum(int start, int frames) snap.init(currMovieData, taseditor_config.enable_hot_changes); // check if there are Input differences from latest snapshot int real_pos = (history_start_pos + history_cursor_pos) % history_size; - int first_changes = snap.findFirstChange(snapshots[real_pos], start); + int first_changes = snap.inputlog.findFirstChange(snapshots[real_pos].inputlog, start); if (first_changes >= 0) { // differences found // fill description: snap.mod_type = MODTYPE_INSERTNUM; strcat(snap.description, modCaptions[snap.mod_type]); - snap.jump_frame = start; + snap.keyframe = start; snap.start_frame = start; snap.end_frame = start + frames - 1; char framenum[11]; @@ -694,7 +708,7 @@ int HISTORY::RegisterInsertNum(int start, int frames) strcat(snap.description, framenum); // set hotchanges if (taseditor_config.enable_hot_changes) - snap.inheritHotChanges_InsertNum(&snapshots[real_pos], start, frames, true); + snap.inputlog.inheritHotChanges_InsertNum(&snapshots[real_pos].inputlog, start, frames, true); AddItemToHistory(snap); branches.ChangesMadeSinceBranch(); project.SetProjectChanged(); @@ -708,7 +722,7 @@ int HISTORY::RegisterPasteInsert(int start, SelectionFrames& inserted_set) snap.init(currMovieData, taseditor_config.enable_hot_changes); // check if there are Input differences from latest snapshot int real_pos = (history_start_pos + history_cursor_pos) % history_size; - int first_changes = snap.findFirstChange(snapshots[real_pos], start); + int first_changes = snap.inputlog.findFirstChange(snapshots[real_pos].inputlog, start); if (first_changes >= 0) { // differences found @@ -716,7 +730,7 @@ int HISTORY::RegisterPasteInsert(int start, SelectionFrames& inserted_set) snap.mod_type = MODTYPE_PASTEINSERT; strcat(snap.description, modCaptions[snap.mod_type]); // for PasteInsert user prefers to see frame of attempted change (Selection cursor position), not frame of actual differences - snap.jump_frame = start; + snap.keyframe = start; snap.start_frame = start; snap.end_frame = -1; // add upper frame to description @@ -726,7 +740,7 @@ int HISTORY::RegisterPasteInsert(int start, SelectionFrames& inserted_set) strcat(snap.description, framenum); // set hotchanges if (taseditor_config.enable_hot_changes) - snap.inheritHotChanges_PasteInsert(&snapshots[real_pos], inserted_set); + snap.inputlog.inheritHotChanges_PasteInsert(&snapshots[real_pos].inputlog, inserted_set); AddItemToHistory(snap); branches.ChangesMadeSinceBranch(); project.SetProjectChanged(); @@ -741,7 +755,7 @@ void HISTORY::RegisterMarkersChange(int mod_type, int start, int end, const char // fill description: snap.mod_type = mod_type; strcat(snap.description, modCaptions[mod_type]); - snap.jump_frame = start; + snap.keyframe = start; snap.start_frame = start; snap.end_frame = end; // add the frame to description @@ -768,7 +782,7 @@ void HISTORY::RegisterMarkersChange(int mod_type, int start, int end, const char } // Hotchanges aren't changed if (taseditor_config.enable_hot_changes) - snap.copyHotChanges(&GetCurrentSnapshot()); + snap.inputlog.copyHotChanges(&GetCurrentSnapshot().inputlog); AddItemToHistory(snap); branches.ChangesMadeSinceBranch(); project.SetProjectChanged(); @@ -778,16 +792,16 @@ void HISTORY::RegisterBookmarkSet(int slot, BOOKMARK& backup_copy, int old_curre // create new snapshot SNAPSHOT snap; snap.init(currMovieData, taseditor_config.enable_hot_changes); - // fill description: modification type + jump_frame of the Bookmark + // fill description: modification type + keyframe of the Bookmark snap.mod_type = MODTYPE_BOOKMARK_0 + slot; strcat(snap.description, modCaptions[snap.mod_type]); - snap.start_frame = snap.end_frame = snap.jump_frame = bookmarks.bookmarks_array[slot].snapshot.jump_frame; + snap.start_frame = snap.end_frame = snap.keyframe = bookmarks.bookmarks_array[slot].snapshot.keyframe; char framenum[11]; strcat(snap.description, " "); - _itoa(snap.jump_frame, framenum, 10); + _itoa(snap.keyframe, framenum, 10); strcat(snap.description, framenum); if (taseditor_config.enable_hot_changes) - snap.copyHotChanges(&GetCurrentSnapshot()); + snap.inputlog.copyHotChanges(&GetCurrentSnapshot().inputlog); AddItemToHistory(snap, old_current_branch, backup_copy); project.SetProjectChanged(); } @@ -798,7 +812,7 @@ int HISTORY::RegisterBranching(int slot, bool markers_changed) snap.init(currMovieData, taseditor_config.enable_hot_changes); // check if there are Input differences from latest snapshot int real_pos = (history_start_pos + history_cursor_pos) % history_size; - int first_changes = snap.findFirstChange(snapshots[real_pos]); + int first_changes = snap.inputlog.findFirstChange(snapshots[real_pos].inputlog); if (first_changes >= 0) { // differences found @@ -806,12 +820,12 @@ int HISTORY::RegisterBranching(int slot, bool markers_changed) snap.mod_type = MODTYPE_BRANCH_0 + slot; strcat(snap.description, modCaptions[snap.mod_type]); strcat(snap.description, bookmarks.bookmarks_array[slot].snapshot.description); - snap.jump_frame = first_changes; + snap.keyframe = first_changes; snap.start_frame = first_changes; snap.end_frame = -1; if (taseditor_config.enable_hot_changes) // copy hotchanges of the Branch - snap.copyHotChanges(&bookmarks.bookmarks_array[slot].snapshot); + snap.inputlog.copyHotChanges(&bookmarks.bookmarks_array[slot].snapshot.inputlog); AddItemToHistory(snap, branches.GetCurrentBranch()); project.SetProjectChanged(); } else if (markers_changed) @@ -820,12 +834,12 @@ int HISTORY::RegisterBranching(int slot, bool markers_changed) snap.mod_type = MODTYPE_BRANCH_MARKERS_0 + slot; strcat(snap.description, modCaptions[snap.mod_type]); strcat(snap.description, bookmarks.bookmarks_array[slot].snapshot.description); - snap.jump_frame = bookmarks.bookmarks_array[slot].snapshot.jump_frame; + snap.keyframe = bookmarks.bookmarks_array[slot].snapshot.keyframe; snap.start_frame = 0; snap.end_frame = -1; // Input was not changed, only Markers were changed if (taseditor_config.enable_hot_changes) - snap.copyHotChanges(&GetCurrentSnapshot()); + snap.inputlog.copyHotChanges(&GetCurrentSnapshot().inputlog); AddItemToHistory(snap, branches.GetCurrentBranch()); project.SetProjectChanged(); } @@ -836,7 +850,7 @@ void HISTORY::RegisterRecording(int frame_of_change) int real_pos = (history_start_pos + history_cursor_pos) % history_size; SNAPSHOT snap; snap.init(currMovieData, taseditor_config.enable_hot_changes); - snap.fillJoypadsDiff(snapshots[real_pos], frame_of_change); + snap.rec_joypad_diff_bits = snap.inputlog.fillJoypadsDiff(snapshots[real_pos].inputlog, frame_of_change); // fill description: snap.mod_type = MODTYPE_RECORD; strcat(snap.description, modCaptions[MODTYPE_RECORD]); @@ -848,12 +862,12 @@ void HISTORY::RegisterRecording(int frame_of_change) && snapshots[real_pos].rec_joypad_diff_bits == snap.rec_joypad_diff_bits) // c) recorded same set of joysticks { // clone this snapshot and continue chain of recorded frames - snap.jump_frame = snapshots[real_pos].jump_frame; - snap.start_frame = snapshots[real_pos].jump_frame; + snap.keyframe = snapshots[real_pos].keyframe; + snap.start_frame = snapshots[real_pos].keyframe; snap.end_frame = frame_of_change; snap.consecutive_tag = frame_of_change; // add info which joypads were affected - int num = joysticks_per_frame[snap.input_type]; + int num = joysticks_per_frame[snap.inputlog.input_type]; uint32 current_mask = 1; for (int i = 0; i < num; ++i) { @@ -871,8 +885,8 @@ void HISTORY::RegisterRecording(int frame_of_change) // set hotchanges if (taseditor_config.enable_hot_changes) { - snap.copyHotChanges(&snapshots[real_pos]); - snap.fillHotChanges(snapshots[real_pos], frame_of_change, frame_of_change); + snap.inputlog.copyHotChanges(&snapshots[real_pos].inputlog); + snap.inputlog.fillHotChanges(snapshots[real_pos].inputlog, frame_of_change, frame_of_change); } // replace current snapshot with this cloned snapshot and truncate history here snapshots[real_pos] = snap; @@ -882,9 +896,9 @@ void HISTORY::RegisterRecording(int frame_of_change) } else { // not consecutive - add new snapshot to history - snap.jump_frame = snap.start_frame = snap.end_frame = snap.consecutive_tag = frame_of_change; + snap.keyframe = snap.start_frame = snap.end_frame = snap.consecutive_tag = frame_of_change; // add info which joypads were affected - int num = joysticks_per_frame[snap.input_type]; + int num = joysticks_per_frame[snap.inputlog.input_type]; uint32 current_mask = 1; for (int i = 0; i < num; ++i) { @@ -899,8 +913,8 @@ void HISTORY::RegisterRecording(int frame_of_change) // set hotchanges if (taseditor_config.enable_hot_changes) { - snap.inheritHotChanges(&snapshots[real_pos]); - snap.fillHotChanges(snapshots[real_pos], frame_of_change, frame_of_change); + snap.inputlog.inheritHotChanges(&snapshots[real_pos].inputlog); + snap.inputlog.fillHotChanges(snapshots[real_pos].inputlog, frame_of_change, frame_of_change); } AddItemToHistory(snap); } @@ -914,13 +928,13 @@ int HISTORY::RegisterImport(MovieData& md, char* filename) snap.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 = snap.findFirstChange(snapshots[real_pos]); + int first_changes = snap.inputlog.findFirstChange(snapshots[real_pos].inputlog); if (first_changes >= 0) { // differences found - snap.jump_frame = first_changes; + snap.keyframe = first_changes; snap.start_frame = 0; - snap.end_frame = snap.size - 1; + snap.end_frame = snap.inputlog.size - 1; // fill description: snap.mod_type = MODTYPE_IMPORT; strcat(snap.description, modCaptions[snap.mod_type]); @@ -930,10 +944,11 @@ int 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 - snap.fillHotChanges(snapshots[real_pos], first_changes); + snap.inputlog.fillHotChanges(snapshots[real_pos].inputlog, first_changes); } AddItemToHistory(snap); - snap.toMovie(currMovieData); + // Replace current movie data with this snapshot's InputLog, not changing Greenzone's LagLog + snap.inputlog.toMovie(currMovieData); piano_roll.UpdateItemCount(); branches.ChangesMadeSinceBranch(); project.SetProjectChanged(); @@ -947,7 +962,7 @@ int HISTORY::RegisterLuaChanges(const char* name, int start, bool InsertionDelet snap.init(currMovieData, taseditor_config.enable_hot_changes); // check if there are Input differences from latest snapshot int real_pos = (history_start_pos + history_cursor_pos) % history_size; - int first_changes = snap.findFirstChange(snapshots[real_pos], start); + int first_changes = snap.inputlog.findFirstChange(snapshots[real_pos].inputlog, start); if (first_changes >= 0) { // differences found @@ -963,7 +978,7 @@ int HISTORY::RegisterLuaChanges(const char* name, int start, bool InsertionDelet // set default name strcat(snap.description, modCaptions[snap.mod_type]); } - snap.jump_frame = first_changes; + snap.keyframe = first_changes; snap.start_frame = start; snap.end_frame = -1; // add upper frame to description @@ -977,28 +992,28 @@ int HISTORY::RegisterLuaChanges(const char* name, int start, bool InsertionDelet if (InsertionDeletion_was_made) { // do it hard way: take old hot_changes and insert/delete rows to create a snapshot that is comparable to the snap - if (snap.input_type == snapshots[real_pos].input_type) + if (snap.inputlog.input_type == snapshots[real_pos].inputlog.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) + if (hotchanges_snapshot.inputlog.has_hot_changes) { - hotchanges_snapshot.FadeHotChanges(); + hotchanges_snapshot.inputlog.FadeHotChanges(); } else { - hotchanges_snapshot.has_hot_changes = true; - hotchanges_snapshot.hot_changes.resize(joysticks_per_frame[snap.input_type] * hotchanges_snapshot.size * HOTCHANGE_BYTES_PER_JOY); + hotchanges_snapshot.inputlog.has_hot_changes = true; + hotchanges_snapshot.inputlog.hot_changes.resize(joysticks_per_frame[snap.inputlog.input_type] * hotchanges_snapshot.inputlog.size * HOTCHANGE_BYTES_PER_JOY); } // insert/delete frames in hotchanges_snapshot, so that it will be the same size as the snap taseditor_lua.InsertDelete_rows_to_Snaphot(hotchanges_snapshot); - snap.copyHotChanges(&hotchanges_snapshot); - snap.fillHotChanges(hotchanges_snapshot, first_changes); + snap.inputlog.copyHotChanges(&hotchanges_snapshot.inputlog); + snap.inputlog.fillHotChanges(hotchanges_snapshot.inputlog, first_changes); } } else { // easy way: snap.size is equal to currentsnapshot.size, so we can simply inherit hotchanges - snap.inheritHotChanges(&snapshots[real_pos]); - snap.fillHotChanges(snapshots[real_pos], first_changes); + snap.inputlog.inheritHotChanges(&snapshots[real_pos].inputlog); + snap.inputlog.fillHotChanges(snapshots[real_pos].inputlog, first_changes); } } AddItemToHistory(snap); @@ -1240,8 +1255,7 @@ int HISTORY::GetCategoryOfOperation(int mod_type) return CATEGORY_MARKERS_CHANGE; case MODTYPE_LUA_CHANGE: return CATEGORY_INPUT_MARKERS_CHANGE; - case MODTYPE_ADJUST_UP: - case MODTYPE_ADJUST_DOWN: + case MODTYPE_ADJUST_LAG: return CATEGORY_INPUT_MARKERS_CHANGE; } @@ -1258,6 +1272,7 @@ SNAPSHOT& HISTORY::GetNextToCurrentSnapshot() if (history_cursor_pos < history_total_items) return snapshots[(history_start_pos + history_cursor_pos + 1) % history_size]; else + // return current snapshot return snapshots[(history_start_pos + history_cursor_pos) % history_size]; } char* HISTORY::GetItemDesc(int pos) diff --git a/src/drivers/win/taseditor/history.h b/src/drivers/win/taseditor/history.h index ddf925b8..bc34c452 100644 --- a/src/drivers/win/taseditor/history.h +++ b/src/drivers/win/taseditor/history.h @@ -66,8 +66,7 @@ enum MOD_TYPES MODTYPE_LUA_MARKER_REMOVE, MODTYPE_LUA_MARKER_RENAME, MODTYPE_LUA_CHANGE, - MODTYPE_ADJUST_UP, - MODTYPE_ADJUST_DOWN, + MODTYPE_ADJUST_LAG, MODTYPES_TOTAL }; diff --git a/src/drivers/win/taseditor/inputlog.cpp b/src/drivers/win/taseditor/inputlog.cpp new file mode 100644 index 00000000..56f1b0d2 --- /dev/null +++ b/src/drivers/win/taseditor/inputlog.cpp @@ -0,0 +1,924 @@ +/* --------------------------------------------------------------------------------- +Implementation file of InputLog class +Copyright (c) 2011-2012 AnS + +(The MIT License) +Permission is hereby granted, free of charge, to any person obtaining a copy of this software and associated documentation files (the "Software"), to deal in the Software without restriction, including without limitation the rights to use, copy, modify, merge, publish, distribute, sublicense, and/or sell copies of the Software, and to permit persons to whom the Software is furnished to do so, subject to the following conditions: +The above copyright notice and this permission notice shall be included in all copies or substantial portions of the Software. +THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY, FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM, OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN THE SOFTWARE. +------------------------------------------------------------------------------------ +InputLog - Log of Input + +* stores the data about Input state: size, type of Input, Input Log data (commands and joysticks) +* optionally can store map of Hot Changes +* implements InputLog creation: copying Input, copying Hot Changes +* implements full/partial restoring of data from InputLog: Input, Hot Changes +* implements compression and decompression of stored data +* saves and loads the data from a project file. On error: sends warning to caller +* implements searching of first mismatch comparing two InputLogs or comparing this InputLog to a movie +* provides interface for reading specific data: reading Input of any given frame, reading value at any point of Hot Changes map +* implements all operations with Hot Changes maps: copying (full/partial), updating/fading, setting new hot places by comparing two InputLogs +------------------------------------------------------------------------------------ */ + +#include "taseditor_project.h" +#include "zlib.h" + +extern SELECTION selection; +extern int GetInputType(MovieData& md); + +int joysticks_per_frame[NUM_SUPPORTED_INPUT_TYPES] = {1, 2, 4}; + +INPUTLOG::INPUTLOG() +{ +} + +void INPUTLOG::init(MovieData& md, bool hotchanges, int force_input_type) +{ + has_hot_changes = hotchanges; + if (force_input_type < 0) + input_type = GetInputType(md); + else + input_type = force_input_type; + // retrieve Input data from movie data + size = md.getNumRecords(); + joysticks.resize(BYTES_PER_JOYSTICK * joysticks_per_frame[input_type] * size); // it's much faster to have this format than have [frame][joy] or other structures + commands.resize(size); // commands take 1 byte per frame + if (has_hot_changes) + hot_changes.resize(joysticks_per_frame[input_type] * size * HOTCHANGE_BYTES_PER_JOY); + + // fill Input vector + int pos = 0; + switch(input_type) + { + case INPUT_TYPE_FOURSCORE: + { + for (int frame = 0; frame < size; ++frame) + { + joysticks[pos++] = md.records[frame].joysticks[0]; + joysticks[pos++] = md.records[frame].joysticks[1]; + joysticks[pos++] = md.records[frame].joysticks[2]; + joysticks[pos++] = md.records[frame].joysticks[3]; + commands[frame] = md.records[frame].commands; + } + break; + } + case INPUT_TYPE_2P: + { + for (int frame = 0; frame < size; ++frame) + { + joysticks[pos++] = md.records[frame].joysticks[0]; + joysticks[pos++] = md.records[frame].joysticks[1]; + commands[frame] = md.records[frame].commands; + } + break; + } + case INPUT_TYPE_1P: + { + for (int frame = 0; frame < size; ++frame) + { + joysticks[pos++] = md.records[frame].joysticks[0]; + commands[frame] = md.records[frame].commands; + } + break; + } + } + + already_compressed = false; +} + +void INPUTLOG::toMovie(MovieData& md, int start, int end) +{ + if (end < 0 || end >= size) end = size - 1; + // write Input data to movie data + md.records.resize(end + 1); + switch(input_type) + { + case INPUT_TYPE_FOURSCORE: + { + int pos = start * BYTES_PER_JOYSTICK * joysticks_per_frame[input_type]; + for (int frame = start; frame <= end; ++frame) + { + md.records[frame].joysticks[0] = joysticks[pos++]; + md.records[frame].joysticks[1] = joysticks[pos++]; + md.records[frame].joysticks[2] = joysticks[pos++]; + md.records[frame].joysticks[3] = joysticks[pos++]; + md.records[frame].commands = commands[frame]; + } + break; + } + case INPUT_TYPE_2P: + { + int pos = start * BYTES_PER_JOYSTICK * joysticks_per_frame[input_type]; + for (int frame = start; frame <= end; ++frame) + { + md.records[frame].joysticks[0] = joysticks[pos++]; + md.records[frame].joysticks[1] = joysticks[pos++]; + md.records[frame].commands = commands[frame]; + } + break; + } + case INPUT_TYPE_1P: + { + int pos = start * BYTES_PER_JOYSTICK * joysticks_per_frame[input_type]; + for (int frame = start; frame <= end; ++frame) + { + md.records[frame].joysticks[0] = joysticks[pos++]; + md.records[frame].commands = commands[frame]; + } + break; + } + } +} + +void INPUTLOG::compress_data() +{ + // compress joysticks + int len = joysticks.size(); + uLongf comprlen = (len>>9)+12 + len; + joysticks_compressed.resize(comprlen); + compress(&joysticks_compressed[0], &comprlen, &joysticks[0], len); + joysticks_compressed.resize(comprlen); + // compress commands + len = commands.size(); + comprlen = (len>>9)+12 + len; + commands_compressed.resize(comprlen); + compress(&commands_compressed[0], &comprlen, &commands[0], len); + commands_compressed.resize(comprlen); + if (has_hot_changes) + { + // compress hot_changes + len = hot_changes.size(); + comprlen = (len>>9)+12 + len; + hot_changes_compressed.resize(comprlen); + compress(&hot_changes_compressed[0], &comprlen, &hot_changes[0], len); + hot_changes_compressed.resize(comprlen); + } + // don't recompress anymore + already_compressed = true; +} +bool INPUTLOG::Get_already_compressed() +{ + return already_compressed; +} + +void INPUTLOG::save(EMUFILE *os) +{ + // write vars + write32le(size, os); + write8le(input_type, os); + // write data + if (!already_compressed) + compress_data(); + // save joysticks data + write32le(joysticks_compressed.size(), os); + os->fwrite(&joysticks_compressed[0], joysticks_compressed.size()); + // save commands data + write32le(commands_compressed.size(), os); + os->fwrite(&commands_compressed[0], commands_compressed.size()); + if (has_hot_changes) + { + write8le((uint8)1, os); + // save hot_changes data + write32le(hot_changes_compressed.size(), os); + os->fwrite(&hot_changes_compressed[0], hot_changes_compressed.size()); + } else + { + write8le((uint8)0, os); + } +} +// returns true if couldn't load +bool INPUTLOG::load(EMUFILE *is) +{ + uint8 tmp; + // read vars + if (!read32le(&size, is)) return true; + if (!read8le(&tmp, is)) return true; + input_type = tmp; + // read data + already_compressed = true; + int comprlen; + uLongf destlen; + // read and uncompress joysticks data + destlen = size * BYTES_PER_JOYSTICK * joysticks_per_frame[input_type]; + joysticks.resize(destlen); + // read size + if (!read32le(&comprlen, is)) return true; + if (comprlen <= 0) return true; + joysticks_compressed.resize(comprlen); + if (is->fread(&joysticks_compressed[0], comprlen) != comprlen) return true; + int e = uncompress(&joysticks[0], &destlen, &joysticks_compressed[0], comprlen); + if (e != Z_OK && e != Z_BUF_ERROR) return true; + // read and uncompress commands data + destlen = size; + commands.resize(destlen); + // read size + if (!read32le(&comprlen, is)) return true; + if (comprlen <= 0) return true; + commands_compressed.resize(comprlen); + if (is->fread(&commands_compressed[0], comprlen) != comprlen) return true; + e = uncompress(&commands[0], &destlen, &commands_compressed[0], comprlen); + if (e != Z_OK && e != Z_BUF_ERROR) return true; + // read hotchanges + if (!read8le(&tmp, is)) return true; + has_hot_changes = (tmp != 0); + if (has_hot_changes) + { + // read and uncompress hot_changes data + destlen = size * joysticks_per_frame[input_type] * HOTCHANGE_BYTES_PER_JOY; + hot_changes.resize(destlen); + // read size + if (!read32le(&comprlen, is)) return true; + if (comprlen <= 0) return true; + hot_changes_compressed.resize(comprlen); + if (is->fread(&hot_changes_compressed[0], comprlen) != comprlen) return true; + e = uncompress(&hot_changes[0], &destlen, &hot_changes_compressed[0], comprlen); + if (e != Z_OK && e != Z_BUF_ERROR) return true; + } + return false; +} +bool INPUTLOG::skipLoad(EMUFILE *is) +{ + int tmp; + uint8 tmp1; + // skip vars + if (is->fseek(sizeof(int) + // size + sizeof(uint8) // input_type + , SEEK_CUR)) return true; + // skip joysticks data + if (!read32le(&tmp, is)) return true; + if (is->fseek(tmp, SEEK_CUR) != 0) return true; + // skip commands data + if (!read32le(&tmp, is)) return true; + if (is->fseek(tmp, SEEK_CUR) != 0) return true; + // skip hot_changes data + if (!read8le(&tmp1, is)) return true; + if (tmp1) + { + if (!read32le(&tmp, is)) return true; + if (is->fseek(tmp, SEEK_CUR) != 0) return true; + } + return false; +} +// -------------------------------------------------------------------------------------------- +// fills map of bits judging on which joypads differ (this function is only used by "Record" modtype) +uint32 INPUTLOG::fillJoypadsDiff(INPUTLOG& their_log, int frame) +{ + uint32 joypad_diff_bits = 0; + uint32 current_mask = 1; + switch(input_type) + { + case INPUT_TYPE_FOURSCORE: + case INPUT_TYPE_2P: + case INPUT_TYPE_1P: + { + int pos = frame * BYTES_PER_JOYSTICK * joysticks_per_frame[input_type]; + for (int i = 0; i < BYTES_PER_JOYSTICK * joysticks_per_frame[input_type]; ++i) + { + if (pos < (their_log.size * BYTES_PER_JOYSTICK * joysticks_per_frame[input_type])) + { + if (joysticks[pos+i] != their_log.joysticks[pos+i]) joypad_diff_bits |= current_mask; + } else + { + if (joysticks[pos+i]) joypad_diff_bits |= current_mask; + } + current_mask <<= 1; + } + break; + } + } + return joypad_diff_bits; +} +// return number of first frame of difference between two InputLogs +int INPUTLOG::findFirstChange(INPUTLOG& their_log, int start, int end) +{ + // if these two InputLogs have different input_type (abnormal situation) then refuse to search and return the beginning + if (their_log.input_type != input_type) + return start; + + // search for differences to the specified end (or to the end of this InputLog) + if (end < 0 || end >= size) end = size-1; + int their_log_end = their_log.size; + switch(input_type) + { + case INPUT_TYPE_FOURSCORE: + { + for (int frame = start, pos = start * BYTES_PER_JOYSTICK * joysticks_per_frame[input_type]; frame <= end; ++frame) + { + // return the frame if found different byte, or found emptiness in their_log when there's non-zero value here + if (frame < their_log_end) + { + if (joysticks[pos] != their_log.joysticks[pos]) return frame; + pos++; + if (joysticks[pos] != their_log.joysticks[pos]) return frame; + pos++; + if (joysticks[pos] != their_log.joysticks[pos]) return frame; + pos++; + if (joysticks[pos] != their_log.joysticks[pos]) return frame; + pos++; + if (commands[frame] != their_log.commands[frame]) return frame; + } else + { + if (joysticks[pos++]) return frame; + if (joysticks[pos++]) return frame; + if (joysticks[pos++]) return frame; + if (joysticks[pos++]) return frame; + if (commands[frame]) return frame; + } + } + break; + } + case INPUT_TYPE_2P: + { + for (int frame = start, pos = start * BYTES_PER_JOYSTICK * joysticks_per_frame[input_type]; frame <= end; ++frame) + { + // return the frame if found different byte, or found emptiness in their_log when there's non-zero value here + if (frame < their_log_end) + { + if (joysticks[pos] != their_log.joysticks[pos]) return frame; + pos++; + if (joysticks[pos] != their_log.joysticks[pos]) return frame; + pos++; + if (commands[frame] != their_log.commands[frame]) return frame; + } else + { + if (joysticks[pos++]) return frame; + if (joysticks[pos++]) return frame; + if (commands[frame]) return frame; + } + } + break; + } + case INPUT_TYPE_1P: + { + for (int frame = start, pos = start * BYTES_PER_JOYSTICK * joysticks_per_frame[input_type]; frame <= end; ++frame) + { + // return the frame if found different byte, or found emptiness in their_log when there's non-zero value here + if (frame < their_log_end) + { + if (joysticks[pos] != their_log.joysticks[pos]) return frame; + pos++; + if (commands[frame] != their_log.commands[frame]) return frame; + } else + { + if (joysticks[pos++]) return frame; + if (commands[frame]) return frame; + } + } + break; + } + } + // if my_size is less then their_size, return last frame + 1 (= size) as the frame of difference + if (size < their_log_end) return size; + // no changes were found + return -1; +} +// return number of first frame of difference between this InputLog and MovieData +int INPUTLOG::findFirstChange(MovieData& md, int start, int end) +{ + // search for differences to the specified end (or to the end of this InputLog / to the end of the movie) + if (end < 0 || end >= size) end = size-1; + if (end >= md.getNumRecords()) end = md.getNumRecords()-1; + + switch(input_type) + { + case INPUT_TYPE_FOURSCORE: + { + for (int frame = start, pos = start * BYTES_PER_JOYSTICK * joysticks_per_frame[input_type]; frame <= end; ++frame) + { + if (joysticks[pos++] != md.records[frame].joysticks[0]) return frame; + if (joysticks[pos++] != md.records[frame].joysticks[1]) return frame; + if (joysticks[pos++] != md.records[frame].joysticks[2]) return frame; + if (joysticks[pos++] != md.records[frame].joysticks[3]) return frame; + if (commands[frame] != md.records[frame].commands) return frame; + } + break; + } + case INPUT_TYPE_2P: + { + for (int frame = start, pos = start * BYTES_PER_JOYSTICK * joysticks_per_frame[input_type]; frame <= end; ++frame) + { + if (joysticks[pos++] != md.records[frame].joysticks[0]) return frame; + if (joysticks[pos++] != md.records[frame].joysticks[1]) return frame; + if (commands[frame] != md.records[frame].commands) return frame; + } + break; + } + case INPUT_TYPE_1P: + { + for (int frame = start, pos = start * BYTES_PER_JOYSTICK * joysticks_per_frame[input_type]; frame <= end; ++frame) + { + if (joysticks[pos++] != md.records[frame].joysticks[0]) return frame; + if (commands[frame] != md.records[frame].commands) return frame; + } + break; + } + } + // if sizes differ, return last frame + 1 from the lesser of them + if (size < md.getNumRecords() && end >= size-1) + return size; + else if (size > md.getNumRecords() && end >= md.getNumRecords()-1) + return md.getNumRecords(); + + return -1; // no changes were found +} + +int INPUTLOG::GetJoystickInfo(int frame, int joy) +{ + if (frame < 0 || frame >= size) return 0; + switch(input_type) + { + case INPUT_TYPE_FOURSCORE: + case INPUT_TYPE_2P: + case INPUT_TYPE_1P: + { + return joysticks[frame * BYTES_PER_JOYSTICK * joysticks_per_frame[input_type] + joy]; + } + } + return 0; +} +int INPUTLOG::GetCommandsInfo(int frame) +{ + if (frame < 0 || frame >= size) return 0; + return commands[frame]; +} + +void INPUTLOG::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 INPUTLOG::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 INPUTLOG::copyHotChanges(INPUTLOG* source_of_hotchanges, int limit_frame_of_source) +{ + // copy hot changes from source InputLog + 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()) + min = source_of_hotchanges->hot_changes.size(); + + // special case for Branches: if limit_frame if specified, then copy only hotchanges from 0 to limit_frame + if (limit_frame_of_source >= 0) + { + if (min > limit_frame_of_source * joysticks_per_frame[input_type] * HOTCHANGE_BYTES_PER_JOY) + min = limit_frame_of_source * joysticks_per_frame[input_type] * HOTCHANGE_BYTES_PER_JOY; + } + + memcpy(&hot_changes[0], &source_of_hotchanges->hot_changes[0], min); + } +} +void INPUTLOG::inheritHotChanges(INPUTLOG* source_of_hotchanges) +{ + // copy hot changes from source InputLog and fade them + 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()) + min = source_of_hotchanges->hot_changes.size(); + + memcpy(&hot_changes[0], &source_of_hotchanges->hot_changes[0], min); + FadeHotChanges(); + } +} +void INPUTLOG::inheritHotChanges_DeleteSelection(INPUTLOG* source_of_hotchanges) +{ + // copy hot changes from source InputLog, but omit deleted frames (which are represented by current selection) + 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.GetStrobedSelection().begin()); + while (pos < this_size && source_pos < source_size) + { + if (it != selection.GetStrobedSelection().end() && frame == *it) + { + // this frame is selected + it++; + // omit the frame + source_pos += bytes; + } else + { + // copy hotchanges of this frame + memcpy(&hot_changes[pos], &source_of_hotchanges->hot_changes[source_pos], bytes); + pos += bytes; + source_pos += bytes; + } + frame++; + } + FadeHotChanges(); + } +} +void INPUTLOG::inheritHotChanges_InsertSelection(INPUTLOG* source_of_hotchanges) +{ + // copy hot changes from source InputLog, but insert filled lines for inserted frames (which are represented by current selection) + 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; + int this_size = hot_changes.size(), source_size = source_of_hotchanges->hot_changes.size(); + SelectionFrames::iterator it(selection.GetStrobedSelection().begin()); + SelectionFrames::iterator current_selection_end(selection.GetStrobedSelection().end()); + while (pos < this_size) + { + if (it != current_selection_end && frame == *it) + { + // this frame is selected + it++; + region_len++; + // set filled line to the frame + memset(&hot_changes[pos], 0xFF, bytes); + } else if (source_pos < source_size) + { + // this frame is not selected + frame -= region_len; + region_len = 0; + // copy hotchanges of this frame + memcpy(&hot_changes[pos], &source_of_hotchanges->hot_changes[source_pos], bytes); + FadeHotChanges(pos, pos + bytes); + source_pos += bytes; + } + pos += bytes; + frame++; + } + } else + { + // no old data, just fill selected lines + int bytes = joysticks_per_frame[input_type] * HOTCHANGE_BYTES_PER_JOY; + int frame = 0, region_len = 0, pos = 0; + int this_size = hot_changes.size(); + SelectionFrames::iterator it(selection.GetStrobedSelection().begin()); + SelectionFrames::iterator current_selection_end(selection.GetStrobedSelection().end()); + while (pos < this_size) + { + if (it != current_selection_end && frame == *it) + { + // this frame is selected + it++; + region_len++; + // set filled line to the frame + memset(&hot_changes[pos], 0xFF, bytes); + // exit loop when all frames in the Selection are handled + if (it == current_selection_end) break; + } else + { + // this frame is not selected + frame -= region_len; + region_len = 0; + // leave zeros in this frame + } + pos += bytes; + frame++; + } + } +} +void INPUTLOG::inheritHotChanges_DeleteNum(INPUTLOG* source_of_hotchanges, int start, int frames, bool fade_old) +{ + int bytes = joysticks_per_frame[input_type] * HOTCHANGE_BYTES_PER_JOY; + // copy hot changes from source InputLog up to "start" and from "start+frames" to end + if (source_of_hotchanges && source_of_hotchanges->has_hot_changes && source_of_hotchanges->input_type == input_type) + { + int this_size = hot_changes.size(), source_size = source_of_hotchanges->hot_changes.size(); + int bytes_to_copy = bytes * start; + int dest_pos = 0, source_pos = 0; + if (bytes_to_copy > source_size) + bytes_to_copy = source_size; + memcpy(&hot_changes[dest_pos], &source_of_hotchanges->hot_changes[source_pos], bytes_to_copy); + dest_pos += bytes_to_copy; + source_pos += bytes_to_copy + bytes * frames; + bytes_to_copy = this_size - dest_pos; + if (bytes_to_copy > source_size - source_pos) + bytes_to_copy = source_size - source_pos; + memcpy(&hot_changes[dest_pos], &source_of_hotchanges->hot_changes[source_pos], bytes_to_copy); + if (fade_old) + FadeHotChanges(); + } +} +void INPUTLOG::inheritHotChanges_InsertNum(INPUTLOG* source_of_hotchanges, int start, int frames, bool fade_old) +{ + int bytes = joysticks_per_frame[input_type] * HOTCHANGE_BYTES_PER_JOY; + // copy hot changes from source InputLog up to "start", then make a gap, then copy from "start+frames" to end + if (source_of_hotchanges && source_of_hotchanges->has_hot_changes && source_of_hotchanges->input_type == input_type) + { + int this_size = hot_changes.size(), source_size = source_of_hotchanges->hot_changes.size(); + int bytes_to_copy = bytes * start; + int dest_pos = 0, source_pos = 0; + if (bytes_to_copy > source_size) + bytes_to_copy = source_size; + memcpy(&hot_changes[dest_pos], &source_of_hotchanges->hot_changes[source_pos], bytes_to_copy); + dest_pos += bytes_to_copy + bytes * frames; + source_pos += bytes_to_copy; + bytes_to_copy = this_size - dest_pos; + if (bytes_to_copy > source_size - source_pos) + bytes_to_copy = source_size - source_pos; + memcpy(&hot_changes[dest_pos], &source_of_hotchanges->hot_changes[source_pos], bytes_to_copy); + if (fade_old) + FadeHotChanges(); + } + // fill the gap with max_hot lines on frames from "start" to "start+frames" + memset(&hot_changes[bytes * start], 0xFF, bytes * frames); +} +void INPUTLOG::inheritHotChanges_PasteInsert(INPUTLOG* source_of_hotchanges, SelectionFrames& inserted_set) +{ + // copy hot changes from source InputLog and insert filled lines for inserted frames (which are represented by inserted_set) + 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(inserted_set.begin()); + SelectionFrames::iterator inserted_set_end(inserted_set.end()); + while (pos < this_size) + { + if (it != inserted_set_end && frame == *it) + { + // this frame was inserted + it++; + // set filled line to the frame + memset(&hot_changes[pos], 0xFF, bytes); + } else if (source_pos < source_size) + { + // copy hotchanges of this frame + memcpy(&hot_changes[pos], &source_of_hotchanges->hot_changes[source_pos], bytes); + FadeHotChanges(pos, pos + bytes); + source_pos += bytes; + } + pos += bytes; + frame++; + } + } else + { + // no old data, just fill selected lines + 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(inserted_set.begin()); + SelectionFrames::iterator inserted_set_end(inserted_set.end()); + while (pos < this_size) + { + if (it != inserted_set_end && frame == *it) + { + // this frame was inserted + it++; + // set filled line to the frame + memset(&hot_changes[pos], 0xFF, bytes); + pos += bytes; + // exit loop when all inserted_set frames are handled + if (it == inserted_set_end) break; + } else + { + // leave zeros in this frame + pos += bytes; + } + frame++; + } + } +} +void INPUTLOG::fillHotChanges(INPUTLOG& their_log, int start, int end) +{ + // if these two InputLogs have different input_type (abnormal situation) then refuse to compare + if (their_log.input_type != input_type) + return; + + // compare InputLogs to the specified end (or to the end of this InputLog) + if (end < 0 || end >= size) end = size-1; + int their_log_end = their_log.size; + switch(input_type) + { + case INPUT_TYPE_FOURSCORE: + { + for (int frame = start, pos = start * BYTES_PER_JOYSTICK * joysticks_per_frame[input_type]; frame <= end; ++frame) + { + // consider changed if found different byte, or found emptiness in their_log when there's non-zero value here + if (frame < their_log_end) + { + if (joysticks[pos] != their_log.joysticks[pos]) + SetMaxHotChange_Bits(frame, 0, joysticks[pos] ^ their_log.joysticks[pos]); + pos++; + if (joysticks[pos] != their_log.joysticks[pos]) + SetMaxHotChange_Bits(frame, 1, joysticks[pos] ^ their_log.joysticks[pos]); + pos++; + if (joysticks[pos] != their_log.joysticks[pos]) + SetMaxHotChange_Bits(frame, 2, joysticks[pos] ^ their_log.joysticks[pos]); + pos++; + if (joysticks[pos] != their_log.joysticks[pos]) + SetMaxHotChange_Bits(frame, 3, joysticks[pos] ^ their_log.joysticks[pos]); + pos++; + } else + { + if (joysticks[pos]) + SetMaxHotChange_Bits(frame, 0, joysticks[pos]); + pos++; + if (joysticks[pos]) + SetMaxHotChange_Bits(frame, 1, joysticks[pos]); + pos++; + if (joysticks[pos]) + SetMaxHotChange_Bits(frame, 2, joysticks[pos]); + pos++; + if (joysticks[pos]) + SetMaxHotChange_Bits(frame, 3, joysticks[pos]); + pos++; + } + } + break; + } + case INPUT_TYPE_2P: + { + for (int frame = start, pos = start * BYTES_PER_JOYSTICK * joysticks_per_frame[input_type]; frame <= end; ++frame) + { + // consider changed if found different byte, or found emptiness in their_log when there's non-zero value here + if (frame < their_log_end) + { + if (joysticks[pos] != their_log.joysticks[pos]) + SetMaxHotChange_Bits(frame, 0, joysticks[pos] ^ their_log.joysticks[pos]); + pos++; + if (joysticks[pos] != their_log.joysticks[pos]) + SetMaxHotChange_Bits(frame, 1, joysticks[pos] ^ their_log.joysticks[pos]); + pos++; + } else + { + if (joysticks[pos]) + SetMaxHotChange_Bits(frame, 0, joysticks[pos]); + pos++; + if (joysticks[pos]) + SetMaxHotChange_Bits(frame, 1, joysticks[pos]); + pos++; + } + } + break; + } + case INPUT_TYPE_1P: + { + for (int frame = start, pos = start * BYTES_PER_JOYSTICK * joysticks_per_frame[input_type]; frame <= end; ++frame) + { + // consider changed if found different byte, or found emptiness in their_log when there's non-zero value here + if (frame < their_log_end) + { + if (joysticks[pos] != their_log.joysticks[pos]) + SetMaxHotChange_Bits(frame, 0, joysticks[pos] ^ their_log.joysticks[pos]); + pos++; + } else + { + if (joysticks[pos]) + SetMaxHotChange_Bits(frame, 0, joysticks[pos]); + pos++; + } + } + break; + } + } +} + +void INPUTLOG::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 + for (int i = 0; i < 8; ++i) + { + if (joy_bits & mask) + SetMaxHotChange(frame, joypad * 8 + i); + mask <<= 1; + } +} +void INPUTLOG::SetMaxHotChange(int frame, int absolute_button) +{ + if (frame < 0 || frame >= size || !has_hot_changes) return; + // set max value (15) to the button hotness + switch(input_type) + { + case INPUT_TYPE_FOURSCORE: + { + // 32 buttons = 16bytes + if (absolute_button & 1) + // odd buttons (B, T, D, R) - set upper 4 bits of the byte + hot_changes[(frame << 4) | (absolute_button >> 1)] |= 0xF0; + else + // even buttons (A, S, U, L) - set lower 4 bits of the byte + hot_changes[(frame << 4) | (absolute_button >> 1)] |= 0x0F; + break; + } + case INPUT_TYPE_2P: + { + // 16 buttons = 8bytes + if (absolute_button & 1) + // odd buttons (B, T, D, R) - set upper 4 bits of the byte + hot_changes[(frame << 3) | (absolute_button >> 1)] |= 0xF0; + else + // even buttons (A, S, U, L) - set lower 4 bits of the byte + hot_changes[(frame << 3) | (absolute_button >> 1)] |= 0x0F; + break; + } + case INPUT_TYPE_1P: + { + // 8 buttons = 4bytes + if (absolute_button & 1) + // odd buttons (B, T, D, R) - set upper 4 bits of the byte + hot_changes[(frame << 2) | (absolute_button >> 1)] |= 0xF0; + else + // even buttons (A, S, U, L) - set lower 4 bits of the byte + hot_changes[(frame << 2) | (absolute_button >> 1)] |= 0x0F; + break; + } + } +} + +void INPUTLOG::FadeHotChanges(int start_byte, int end_byte) +{ + uint8 hi_half, low_half; + if (end_byte < 0) + end_byte = hot_changes.size(); + for (int i = end_byte - 1; i >= start_byte; i--) + { + if (hot_changes[i]) + { + hi_half = hot_changes[i] >> 4; + low_half = hot_changes[i] & 15; + if (hi_half) hi_half--; + if (low_half) low_half--; + hot_changes[i] = (hi_half << 4) | low_half; + } + } +} + +int INPUTLOG::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; + + uint8 val; + switch(input_type) + { + case INPUT_TYPE_FOURSCORE: + { + // 32 buttons, 16bytes + val = hot_changes[(frame << 4) + (absolute_button >> 1)]; + break; + } + case INPUT_TYPE_2P: + { + // 16 buttons, 8bytes + val = hot_changes[(frame << 3) + (absolute_button >> 1)]; + break; + } + case INPUT_TYPE_1P: + { + // 8 buttons, 4bytes + val = hot_changes[(frame << 2) + (absolute_button >> 1)]; + break; + } + } + + if (absolute_button & 1) + // odd buttons (B, T, D, R) - upper 4 bits of the byte + return val >> 4; + else + // even buttons (A, S, U, L) - lower 4 bits of the byte + return val & 15; +} + diff --git a/src/drivers/win/taseditor/inputlog.h b/src/drivers/win/taseditor/inputlog.h new file mode 100644 index 00000000..b7ac12c1 --- /dev/null +++ b/src/drivers/win/taseditor/inputlog.h @@ -0,0 +1,75 @@ +// Specification file for InputLog class + +enum Input_types +{ + INPUT_TYPE_1P, + INPUT_TYPE_2P, + INPUT_TYPE_FOURSCORE, + + NUM_SUPPORTED_INPUT_TYPES +}; + +#define BYTES_PER_JOYSTICK 1 // 1 byte per 1 joystick (8 buttons) +#define HOTCHANGE_BYTES_PER_JOY 4 // 4 bytes per 8 buttons + +class INPUTLOG +{ +public: + INPUTLOG(); + void init(MovieData& md, bool hotchanges, int force_input_type = -1); + void toMovie(MovieData& md, int start = 0, int end = -1); + + void save(EMUFILE *os); + bool load(EMUFILE *is); + bool skipLoad(EMUFILE *is); + + void compress_data(); + bool Get_already_compressed(); + + uint32 INPUTLOG::fillJoypadsDiff(INPUTLOG& their_log, int frame); + int findFirstChange(INPUTLOG& their_log, int start = 0, int end = -1); + int findFirstChange(MovieData& md, int start = 0, int end = -1); + + int GetJoystickInfo(int frame, int joy); + int GetCommandsInfo(int frame); + + void insertFrames(int at, int frames); + void eraseFrame(int frame); + + void copyHotChanges(INPUTLOG* source_of_hotchanges, int limit_frame_of_source = -1); + void inheritHotChanges(INPUTLOG* source_of_hotchanges); + void inheritHotChanges_DeleteSelection(INPUTLOG* source_of_hotchanges); + void inheritHotChanges_InsertSelection(INPUTLOG* source_of_hotchanges); + void inheritHotChanges_DeleteNum(INPUTLOG* source_of_hotchanges, int start, int frames, bool fade_old); + void inheritHotChanges_InsertNum(INPUTLOG* source_of_hotchanges, int start, int frames, bool fade_old); + void inheritHotChanges_PasteInsert(INPUTLOG* source_of_hotchanges, SelectionFrames& inserted_set); + void fillHotChanges(INPUTLOG& their_log, int start = 0, int end = -1); + + void SetMaxHotChange_Bits(int frame, int joypad, uint8 joy_bits); + void SetMaxHotChange(int frame, int absolute_button); + + void FadeHotChanges(int start_byte = 0, int end_byte = -1); + + 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 + bool has_hot_changes; + + // not saved data + 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, ... + +private: + + // also saved data + std::vector joysticks_compressed; + std::vector commands_compressed; + std::vector hot_changes_compressed; + + // not saved data + bool already_compressed; // to compress only once +}; + diff --git a/src/drivers/win/taseditor/laglog.cpp b/src/drivers/win/taseditor/laglog.cpp new file mode 100644 index 00000000..bc63ab03 --- /dev/null +++ b/src/drivers/win/taseditor/laglog.cpp @@ -0,0 +1,130 @@ +/* --------------------------------------------------------------------------------- +Implementation file of LagLog class +Copyright (c) 2011-2012 AnS + +(The MIT License) +Permission is hereby granted, free of charge, to any person obtaining a copy of this software and associated documentation files (the "Software"), to deal in the Software without restriction, including without limitation the rights to use, copy, modify, merge, publish, distribute, sublicense, and/or sell copies of the Software, and to permit persons to whom the Software is furnished to do so, subject to the following conditions: +The above copyright notice and this permission notice shall be included in all copies or substantial portions of the Software. +THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY, FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM, OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN THE SOFTWARE. +------------------------------------------------------------------------------------ +LagLog - Log of Lag appearance + +* stores the frame-by-frame log of lag appearance +* implements compression and decompression of stored data +* saves and loads the data from a project file. On error: sends warning to caller +* provides interface for reading and writing log data +------------------------------------------------------------------------------------ */ + +#include "taseditor_project.h" +#include "zlib.h" + +LAGLOG::LAGLOG() +{ + already_compressed = false; +} + +void LAGLOG::reset() +{ + lag_log.resize(0); + already_compressed = false; +} + +void LAGLOG::compress_data() +{ + int len = lag_log.size() * sizeof(uint8); + uLongf comprlen = (len>>9)+12 + len; + lag_log_compressed.resize(comprlen); + compress(&lag_log_compressed[0], &comprlen, (uint8*)&lag_log[0], len); + lag_log_compressed.resize(comprlen); + already_compressed = true; +} +bool LAGLOG::Get_already_compressed() +{ + return already_compressed; +} +void LAGLOG::Reset_already_compressed() +{ + already_compressed = false; +} + +void LAGLOG::save(EMUFILE *os) +{ + // write size + int size = lag_log.size(); + write32le(size, os); + // write array + if (!already_compressed) + compress_data(); + write32le(lag_log_compressed.size(), os); + os->fwrite(&lag_log_compressed[0], lag_log_compressed.size()); +} +// returns true if couldn't load +bool LAGLOG::load(EMUFILE *is) +{ + int size; + if (read32le(&size, is)) + { + lag_log.resize(size); + // read and uncompress array + already_compressed = true; + int comprlen; + uLongf destlen = size * sizeof(int); + if (!read32le(&comprlen, is)) return true; + if (comprlen <= 0) return true; + lag_log_compressed.resize(comprlen); + if (is->fread(&lag_log_compressed[0], comprlen) != comprlen) return true; + int e = uncompress((uint8*)&lag_log[0], &destlen, &lag_log_compressed[0], comprlen); + if (e != Z_OK && e != Z_BUF_ERROR) return true; + // all ok + return false; + } + return true; +} +bool LAGLOG::skipLoad(EMUFILE *is) +{ + if (!(is->fseek(sizeof(int), SEEK_CUR))) + { + // read array + int comprlen; + if (!read32le(&comprlen, is)) return true; + if (is->fseek(comprlen, SEEK_CUR) != 0) return true; + // all ok + return false; + } + return true; +} +// ------------------------------------------------------------------------------------------------- +void LAGLOG::SetLagInfo(int frame, bool lagFlag) +{ + if ((int)lag_log.size() <= frame) + lag_log.resize(frame + 1); + + if (lagFlag) + lag_log[frame] = 1; + else + lag_log[frame] = 0; + + already_compressed = false; +} +void LAGLOG::EraseLagFrame(int frame) +{ + lag_log.erase(lag_log.begin() + (currFrameCounter - 1)); +} +void LAGLOG::InsertLagFrame(int frame) +{ + lag_log.insert(lag_log.begin() + frame, 1); +} + +// getters +int LAGLOG::GetSize() +{ + return lag_log.size(); +} +bool LAGLOG::GetLagInfoAtFrame(int frame) +{ + if (frame < (int)lag_log.size()) + return (lag_log[frame] != 0); + else + return false; +} + diff --git a/src/drivers/win/taseditor/laglog.h b/src/drivers/win/taseditor/laglog.h new file mode 100644 index 00000000..5671e645 --- /dev/null +++ b/src/drivers/win/taseditor/laglog.h @@ -0,0 +1,31 @@ +// Specification file for LagLog class + +class LAGLOG +{ +public: + LAGLOG(); + void reset(); + + void compress_data(); + bool Get_already_compressed(); + void Reset_already_compressed(); + + void save(EMUFILE *os); + bool load(EMUFILE *is); + bool skipLoad(EMUFILE *is); + + void SetLagInfo(int frame, bool lagFlag); + void EraseLagFrame(int frame); + void InsertLagFrame(int frame); + + int GetSize(); + bool GetLagInfoAtFrame(int frame); + +private: + // saved data + std::vector lag_log_compressed; + + // not saved data + std::vector lag_log; + bool already_compressed; // to compress only once +}; diff --git a/src/drivers/win/taseditor/markers.cpp b/src/drivers/win/taseditor/markers.cpp index f63eb29b..92f6ff62 100644 --- a/src/drivers/win/taseditor/markers.cpp +++ b/src/drivers/win/taseditor/markers.cpp @@ -82,7 +82,6 @@ bool MARKERS::load(EMUFILE *is) } bool MARKERS::skipLoad(EMUFILE *is) { - int size; if (!(is->fseek(sizeof(int), SEEK_CUR))) { // read array @@ -90,9 +89,9 @@ bool MARKERS::skipLoad(EMUFILE *is) if (!read32le(&comprlen, is)) return true; if (is->fseek(comprlen, SEEK_CUR) != 0) return true; // read notes - if (read32le(&size, is) && size >= 0) + if (read32le(&comprlen, is) && comprlen >= 0) { - for (int i = 0; i < size; ++i) + for (int i = 0; i < comprlen; ++i) { if (!read32le(&len, is) || len < 0) return true; if (is->fseek(len, SEEK_CUR) != 0) return true; @@ -117,8 +116,8 @@ bool MARKERS::Get_already_compressed() { return already_compressed; } -void MARKERS::Set_already_compressed(bool value) +void MARKERS::Reset_already_compressed() { - already_compressed = value; + already_compressed = false; } diff --git a/src/drivers/win/taseditor/markers.h b/src/drivers/win/taseditor/markers.h index daeaeefd..d330aef4 100644 --- a/src/drivers/win/taseditor/markers.h +++ b/src/drivers/win/taseditor/markers.h @@ -13,7 +13,7 @@ public: void compress_data(); bool Get_already_compressed(); - void Set_already_compressed(bool value); + void Reset_already_compressed(); // saved data std::vector notes; // Format: 0th - note for intro (Marker 0), 1st - note for Marker1, 2nd - note for Marker2, ... diff --git a/src/drivers/win/taseditor/markers_manager.cpp b/src/drivers/win/taseditor/markers_manager.cpp index 188c4e81..264a486b 100644 --- a/src/drivers/win/taseditor/markers_manager.cpp +++ b/src/drivers/win/taseditor/markers_manager.cpp @@ -73,7 +73,7 @@ void MARKERS_MANAGER::save(EMUFILE *os, bool really_save) { // write "MARKERS" string os->fwrite(markers_save_id, MARKERS_ID_LEN); - markers.Set_already_compressed(false); // must recompress data, because most likely it has changed since last compression + markers.Reset_already_compressed(); // must recompress data, because most likely it has changed since last compression markers.save(os); } else { @@ -293,7 +293,7 @@ void MARKERS_MANAGER::MakeCopyTo(MARKERS& destination) { destination.markers_array = markers.markers_array; destination.notes = markers.notes; - destination.Set_already_compressed(false); + destination.Reset_already_compressed(); } void MARKERS_MANAGER::RestoreFromCopy(MARKERS& source) { diff --git a/src/drivers/win/taseditor/piano_roll.cpp b/src/drivers/win/taseditor/piano_roll.cpp index 41cf0378..dc7accbc 100644 --- a/src/drivers/win/taseditor/piano_roll.cpp +++ b/src/drivers/win/taseditor/piano_roll.cpp @@ -924,11 +924,11 @@ void PIANO_ROLL::FollowPlaybackIfNeeded() } void PIANO_ROLL::FollowUndo() { - int jump_frame = history.GetUndoHint(); - if (taseditor_config.jump_to_undo && jump_frame >= 0) + int keyframe = history.GetUndoHint(); + if (taseditor_config.jump_to_undo && keyframe >= 0) { - if (!CheckItemVisible(jump_frame)) - CenterListAt(jump_frame); + if (!CheckItemVisible(keyframe)) + CenterListAt(keyframe); } } void PIANO_ROLL::FollowSelection() @@ -1253,7 +1253,7 @@ void PIANO_ROLL::GetDispInfo(NMLVDISPINFO* nmlvDispInfo) item.pszText[2] = 0; } else { - if (taseditor_config.enable_hot_changes && history.GetCurrentSnapshot().GetHotChangeInfo(item.iItem, item.iSubItem - COLUMN_JOYPAD1_A)) + if (taseditor_config.enable_hot_changes && history.GetCurrentSnapshot().inputlog.GetHotChangeInfo(item.iItem, item.iSubItem - COLUMN_JOYPAD1_A)) { item.pszText[0] = 45; // "-" item.pszText[1] = 0; @@ -1281,7 +1281,7 @@ LONG PIANO_ROLL::CustomDraw(NMLVCUSTOMDRAW* msg) { // text color if (taseditor_config.enable_hot_changes && cell_x >= COLUMN_JOYPAD1_A && cell_x <= COLUMN_JOYPAD4_R) - msg->clrText = hot_changes_colors[history.GetCurrentSnapshot().GetHotChangeInfo(cell_y, cell_x - COLUMN_JOYPAD1_A)]; + msg->clrText = hot_changes_colors[history.GetCurrentSnapshot().inputlog.GetHotChangeInfo(cell_y, cell_x - COLUMN_JOYPAD1_A)]; else msg->clrText = NORMAL_TEXT_COLOR; // bg color and text font @@ -1323,7 +1323,7 @@ LONG PIANO_ROLL::CustomDraw(NMLVCUSTOMDRAW* msg) if (!greenzone.SavestateIsEmpty(cell_y)) { // the frame is normal Greenzone frame - if (greenzone.GetLagHistoryAtFrame(cell_y)) + if (greenzone.laglog.GetLagInfoAtFrame(cell_y)) msg->clrTextBk = LAG_FRAMENUM_COLOR; else msg->clrTextBk = GREENZONE_FRAMENUM_COLOR; @@ -1333,14 +1333,14 @@ LONG PIANO_ROLL::CustomDraw(NMLVCUSTOMDRAW* msg) || (!greenzone.SavestateIsEmpty(cell_y & EVERY2ND) && !greenzone.SavestateIsEmpty((cell_y & EVERY2ND) + 2))) { // the frame is in a gap (in Greenzone tail) - if (greenzone.GetLagHistoryAtFrame(cell_y)) + if (greenzone.laglog.GetLagInfoAtFrame(cell_y)) msg->clrTextBk = PALE_LAG_FRAMENUM_COLOR; else msg->clrTextBk = PALE_GREENZONE_FRAMENUM_COLOR; } else { // the frame is above Greenzone tail - if (greenzone.GetLagHistoryAtFrame(cell_y)) + if (greenzone.laglog.GetLagInfoAtFrame(cell_y)) msg->clrTextBk = VERY_PALE_LAG_FRAMENUM_COLOR; else msg->clrTextBk = VERY_PALE_GREENZONE_FRAMENUM_COLOR; @@ -1348,7 +1348,7 @@ LONG PIANO_ROLL::CustomDraw(NMLVCUSTOMDRAW* msg) } else { // the frame is below Greenzone head - if (greenzone.GetLagHistoryAtFrame(cell_y)) + if (greenzone.laglog.GetLagInfoAtFrame(cell_y)) msg->clrTextBk = VERY_PALE_LAG_FRAMENUM_COLOR; else msg->clrTextBk = VERY_PALE_GREENZONE_FRAMENUM_COLOR; @@ -1378,7 +1378,7 @@ LONG PIANO_ROLL::CustomDraw(NMLVCUSTOMDRAW* msg) if (!greenzone.SavestateIsEmpty(cell_y)) { // the frame is normal Greenzone frame - if (greenzone.GetLagHistoryAtFrame(cell_y)) + if (greenzone.laglog.GetLagInfoAtFrame(cell_y)) msg->clrTextBk = LAG_INPUT_COLOR1; else msg->clrTextBk = GREENZONE_INPUT_COLOR1; @@ -1388,14 +1388,14 @@ LONG PIANO_ROLL::CustomDraw(NMLVCUSTOMDRAW* msg) || (!greenzone.SavestateIsEmpty(cell_y & EVERY2ND) && !greenzone.SavestateIsEmpty((cell_y & EVERY2ND) + 2))) { // the frame is in a gap (in Greenzone tail) - if (greenzone.GetLagHistoryAtFrame(cell_y)) + if (greenzone.laglog.GetLagInfoAtFrame(cell_y)) msg->clrTextBk = PALE_LAG_INPUT_COLOR1; else msg->clrTextBk = PALE_GREENZONE_INPUT_COLOR1; } else { // the frame is above Greenzone tail - if (greenzone.GetLagHistoryAtFrame(cell_y)) + if (greenzone.laglog.GetLagInfoAtFrame(cell_y)) msg->clrTextBk = VERY_PALE_LAG_INPUT_COLOR1; else msg->clrTextBk = VERY_PALE_GREENZONE_INPUT_COLOR1; @@ -1403,7 +1403,7 @@ LONG PIANO_ROLL::CustomDraw(NMLVCUSTOMDRAW* msg) } else { // the frame is below Greenzone head - if (greenzone.GetLagHistoryAtFrame(cell_y)) + if (greenzone.laglog.GetLagInfoAtFrame(cell_y)) msg->clrTextBk = VERY_PALE_LAG_INPUT_COLOR1; else msg->clrTextBk = VERY_PALE_GREENZONE_INPUT_COLOR1; @@ -1433,7 +1433,7 @@ LONG PIANO_ROLL::CustomDraw(NMLVCUSTOMDRAW* msg) if (!greenzone.SavestateIsEmpty(cell_y)) { // the frame is normal Greenzone frame - if (greenzone.GetLagHistoryAtFrame(cell_y)) + if (greenzone.laglog.GetLagInfoAtFrame(cell_y)) msg->clrTextBk = LAG_INPUT_COLOR2; else msg->clrTextBk = GREENZONE_INPUT_COLOR2; @@ -1443,14 +1443,14 @@ LONG PIANO_ROLL::CustomDraw(NMLVCUSTOMDRAW* msg) || (!greenzone.SavestateIsEmpty(cell_y & EVERY2ND) && !greenzone.SavestateIsEmpty((cell_y & EVERY2ND) + 2))) { // the frame is in a gap (in Greenzone tail) - if (greenzone.GetLagHistoryAtFrame(cell_y)) + if (greenzone.laglog.GetLagInfoAtFrame(cell_y)) msg->clrTextBk = PALE_LAG_INPUT_COLOR2; else msg->clrTextBk = PALE_GREENZONE_INPUT_COLOR2; } else { // the frame is above Greenzone tail - if (greenzone.GetLagHistoryAtFrame(cell_y)) + if (greenzone.laglog.GetLagInfoAtFrame(cell_y)) msg->clrTextBk = VERY_PALE_LAG_INPUT_COLOR2; else msg->clrTextBk = VERY_PALE_GREENZONE_INPUT_COLOR2; @@ -1458,7 +1458,7 @@ LONG PIANO_ROLL::CustomDraw(NMLVCUSTOMDRAW* msg) } else { // the frame is below Greenzone head - if (greenzone.GetLagHistoryAtFrame(cell_y)) + if (greenzone.laglog.GetLagInfoAtFrame(cell_y)) msg->clrTextBk = VERY_PALE_LAG_INPUT_COLOR2; else msg->clrTextBk = VERY_PALE_GREENZONE_INPUT_COLOR2; diff --git a/src/drivers/win/taseditor/piano_roll.h b/src/drivers/win/taseditor/piano_roll.h index 882167ca..110ee3c1 100644 --- a/src/drivers/win/taseditor/piano_roll.h +++ b/src/drivers/win/taseditor/piano_roll.h @@ -112,9 +112,9 @@ enum DRAG_MODES #define PALE_GREENZONE_INPUT_COLOR1 0xD3F9D2 #define PALE_GREENZONE_INPUT_COLOR2 0xBAEBBA -#define VERY_PALE_GREENZONE_FRAMENUM_COLOR 0xF1FFF1 -#define VERY_PALE_GREENZONE_INPUT_COLOR1 0xE7FCE7 -#define VERY_PALE_GREENZONE_INPUT_COLOR2 0xD9F4D9 +#define VERY_PALE_GREENZONE_FRAMENUM_COLOR 0xF9FFF9 +#define VERY_PALE_GREENZONE_INPUT_COLOR1 0xE0FBE0 +#define VERY_PALE_GREENZONE_INPUT_COLOR2 0xD2F2D2 #define LAG_FRAMENUM_COLOR 0xDDDCFF #define LAG_INPUT_COLOR1 0xD2D0F0 @@ -124,9 +124,9 @@ enum DRAG_MODES #define PALE_LAG_INPUT_COLOR1 0xDADAF4 #define PALE_LAG_INPUT_COLOR2 0xCFCEEA -#define VERY_PALE_LAG_FRAMENUM_COLOR 0xF0F0FF -#define VERY_PALE_LAG_INPUT_COLOR1 0xEBEBF9 -#define VERY_PALE_LAG_INPUT_COLOR2 0xE5E5F3 +#define VERY_PALE_LAG_FRAMENUM_COLOR 0xE9E9FF +#define VERY_PALE_LAG_INPUT_COLOR1 0xE5E5F7 +#define VERY_PALE_LAG_INPUT_COLOR2 0xE0E0F1 #define CUR_FRAMENUM_COLOR 0xFCF1CE #define CUR_INPUT_COLOR1 0xF8EBB6 diff --git a/src/drivers/win/taseditor/playback.cpp b/src/drivers/win/taseditor/playback.cpp index 798d085e..fdbbdc90 100644 --- a/src/drivers/win/taseditor/playback.cpp +++ b/src/drivers/win/taseditor/playback.cpp @@ -68,7 +68,7 @@ void PLAYBACK::init() } void PLAYBACK::reset() { - autopause_at_the_end = false; + must_autopause_at_the_end = true; must_find_current_marker = true; shown_marker = 0; lastCursor = currFrameCounter; @@ -141,7 +141,7 @@ void PLAYBACK::update() // pause when seeking hits pause_frame if (pause_frame && currFrameCounter + 1 >= pause_frame) SeekingStop(); - else if (currFrameCounter >= GetLostPosition() && currFrameCounter >= currMovieData.getNumRecords() - 1 && autopause_at_the_end && taseditor_config.autopause_at_finish) + else if (currFrameCounter >= GetLostPosition() && currFrameCounter >= currMovieData.getNumRecords() - 1 && must_autopause_at_the_end && taseditor_config.autopause_at_finish) // pause at the end of the movie PauseEmulation(); @@ -183,18 +183,22 @@ void PLAYBACK::update() { // externally unpaused - show empty progressbar SetProgressbar(0, 1); - if (currFrameCounter < currMovieData.getNumRecords()-1) - autopause_at_the_end = true; - else - autopause_at_the_end = false; } else { // externally paused - progressbar should be full SetProgressbar(1, 1); - autopause_at_the_end = false; } } + // prepare to stop at the end of the movie if user unpauses emulator + if (emu_paused) + { + if (currFrameCounter < currMovieData.getNumRecords() - 1) + must_autopause_at_the_end = true; + else + must_autopause_at_the_end = false; + } + // update the Playback cursor if (currFrameCounter != lastCursor) { diff --git a/src/drivers/win/taseditor/playback.h b/src/drivers/win/taseditor/playback.h index 325306b3..aefefeeb 100644 --- a/src/drivers/win/taseditor/playback.h +++ b/src/drivers/win/taseditor/playback.h @@ -61,7 +61,7 @@ private: int lost_position_frame; bool lost_position_is_stable; // for when Greenzone invalidates several times, but the end of current segment must remain the same - bool autopause_at_the_end; + bool must_autopause_at_the_end; bool old_emu_paused, emu_paused; int old_pauseframe; bool old_show_pauseframe, show_pauseframe; diff --git a/src/drivers/win/taseditor/popup_display.cpp b/src/drivers/win/taseditor/popup_display.cpp index 389dc217..f27d26a2 100644 --- a/src/drivers/win/taseditor/popup_display.cpp +++ b/src/drivers/win/taseditor/popup_display.cpp @@ -245,10 +245,10 @@ void POPUP_DISPLAY::RedrawScreenshotBitmap() void POPUP_DISPLAY::ChangeDescrText() { // retrieve info from the pointed bookmark's Markers - int frame = bookmarks.bookmarks_array[bookmarks.item_under_mouse].snapshot.jump_frame; - int marker_id = markers_manager.GetMarkerUp(bookmarks.bookmarks_array[bookmarks.item_under_mouse].snapshot.GetMarkers(), frame); + int frame = bookmarks.bookmarks_array[bookmarks.item_under_mouse].snapshot.keyframe; + int marker_id = markers_manager.GetMarkerUp(bookmarks.bookmarks_array[bookmarks.item_under_mouse].snapshot.markers, frame); char new_text[MAX_NOTE_LEN]; - strcpy(new_text, markers_manager.GetNote(bookmarks.bookmarks_array[bookmarks.item_under_mouse].snapshot.GetMarkers(), marker_id).c_str()); + strcpy(new_text, markers_manager.GetNote(bookmarks.bookmarks_array[bookmarks.item_under_mouse].snapshot.markers, marker_id).c_str()); SetWindowText(marker_note_descr, new_text); } diff --git a/src/drivers/win/taseditor/recorder.cpp b/src/drivers/win/taseditor/recorder.cpp index ac310281..d7ca196a 100644 --- a/src/drivers/win/taseditor/recorder.cpp +++ b/src/drivers/win/taseditor/recorder.cpp @@ -241,7 +241,7 @@ void RECORDER::InputChanged() // take previous values from current snapshot, new Input from current movie for (int i = 0; i < num_joys; ++i) { - old_joy[i] = history.GetCurrentSnapshot().GetJoystickInfo(currFrameCounter, i); + old_joy[i] = history.GetCurrentSnapshot().inputlog.GetJoystickInfo(currFrameCounter, i); if (!taseditor_config.pattern_recording || editor.autofire_patterns[old_current_pattern][pattern_offset]) new_joy[i] = currMovieData.records[currFrameCounter].joysticks[i]; else @@ -296,7 +296,7 @@ void RECORDER::InputChanged() if (!changes_made) { // check if new commands were recorded - if (currMovieData.records[currFrameCounter].commands != history.GetCurrentSnapshot().GetCommandsInfo(currFrameCounter)) + if (currMovieData.records[currFrameCounter].commands != history.GetCurrentSnapshot().inputlog.GetCommandsInfo(currFrameCounter)) changes_made = true; } diff --git a/src/drivers/win/taseditor/selection.cpp b/src/drivers/win/taseditor/selection.cpp index e9890f49..a6a6e03a 100644 --- a/src/drivers/win/taseditor/selection.cpp +++ b/src/drivers/win/taseditor/selection.cpp @@ -491,7 +491,7 @@ void SELECTION::SetRegionSelectionPattern(int start, int end) for (int i = start; i <= end; ++i) { // skip lag frames - if (taseditor_config.pattern_skips_lag && greenzone.GetLagHistoryAtFrame(i)) + if (taseditor_config.pattern_skips_lag && greenzone.laglog.GetLagInfoAtFrame(i)) continue; if (editor.autofire_patterns[current_pattern][pattern_offset]) { diff --git a/src/drivers/win/taseditor/snapshot.cpp b/src/drivers/win/taseditor/snapshot.cpp index eecfaeda..472a12fe 100644 --- a/src/drivers/win/taseditor/snapshot.cpp +++ b/src/drivers/win/taseditor/snapshot.cpp @@ -9,25 +9,19 @@ THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR IMPLI ------------------------------------------------------------------------------------ Snapshot - Snapshot of all edited data -* stores the data of specific snapshot of the movie: size, Input data (commands and joysticks), Markers at the moment of creating the snapshot, keyframe, start and end frame of operation, type of operation and description of the snapshot (including the time of creation) +* stores the data of specific snapshot of the movie: InputLog, LagLog, Markers at the moment of creating the snapshot, keyframe, start and end frame of operation, type of operation and description of the snapshot (including the time of creation) * also stores info about sequential recording/drawing of Input -* optionally can store map of Hot Changes -* implements snapshot creation: copying Input, copying Hot Changes, copying Markers, setting time of creation -* implements full/partial restoring of data from snapshot: Input, Hot Changes, Markers -* implements compression and decompression of stored data -* saves and loads the data from a project file. On error: sends warning to caller -* implements searching of first mismatch comparing two snapshots ot comparing snapshot to a movie -* provides interface for reading certain data: reading Input of any certain frame, reading value at any point of Hot Changes map -* implements all operations with Hot Changes maps: copying (full/partial), updating/fading, setting new hot places by comparing Input of two snapshots +* streamlines snapshot creation: copying Input from movie data, copying LagLog from Greenzone, copying Markers from Markers Manager, setting time of creation +* streamlines restoring Markers data from snapshot +* saves and loads stored data from a project file. On error: sends warning to caller ------------------------------------------------------------------------------------ */ #include "taseditor_project.h" #include "zlib.h" -int joysticks_per_frame[NUM_SUPPORTED_INPUT_TYPES] = {1, 2, 4}; - extern MARKERS_MANAGER markers_manager; extern SELECTION selection; +extern GREENZONE greenzone; extern int GetInputType(MovieData& md); @@ -37,61 +31,17 @@ SNAPSHOT::SNAPSHOT() void SNAPSHOT::init(MovieData& md, bool hotchanges, int force_input_type) { - has_hot_changes = hotchanges; - if (force_input_type < 0) - input_type = GetInputType(md); - else - input_type = force_input_type; - // retrieve Input data from movie data - size = md.getNumRecords(); - joysticks.resize(BYTES_PER_JOYSTICK * joysticks_per_frame[input_type] * size); // it's much faster to have this format than have [frame][joy] or other structures - commands.resize(size); // commands take 1 byte per frame - if (has_hot_changes) - hot_changes.resize(joysticks_per_frame[input_type] * size * HOTCHANGE_BYTES_PER_JOY); + inputlog.init(md, hotchanges, force_input_type); - // fill Input vector - int pos = 0; - switch(input_type) - { - case INPUT_TYPE_FOURSCORE: - { - for (int frame = 0; frame < size; ++frame) - { - joysticks[pos++] = md.records[frame].joysticks[0]; - joysticks[pos++] = md.records[frame].joysticks[1]; - joysticks[pos++] = md.records[frame].joysticks[2]; - joysticks[pos++] = md.records[frame].joysticks[3]; - commands[frame] = md.records[frame].commands; - } - break; - } - case INPUT_TYPE_2P: - { - for (int frame = 0; frame < size; ++frame) - { - joysticks[pos++] = md.records[frame].joysticks[0]; - joysticks[pos++] = md.records[frame].joysticks[1]; - commands[frame] = md.records[frame].commands; - } - break; - } - case INPUT_TYPE_1P: - { - for (int frame = 0; frame < size; ++frame) - { - joysticks[pos++] = md.records[frame].joysticks[0]; - commands[frame] = md.records[frame].commands; - } - break; - } - } + // take a copy from greenzone.laglog + laglog = greenzone.laglog; + laglog.Reset_already_compressed(); - // 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); + // take a copy of markers_manager.markers + markers_manager.MakeCopyTo(markers); + if ((int)markers.markers_array.size() < inputlog.size) + markers.markers_array.resize(inputlog.size); - already_compressed = false; // save current time to description time_t raw_time; time(&raw_time); @@ -101,896 +51,88 @@ void SNAPSHOT::init(MovieData& md, bool hotchanges, int force_input_type) bool SNAPSHOT::MarkersDifferFromCurrent() { - return markers_manager.checkMarkersDiff(my_markers); + return markers_manager.checkMarkersDiff(markers); } void SNAPSHOT::copyToMarkers() { - markers_manager.RestoreFromCopy(my_markers); + markers_manager.RestoreFromCopy(markers); } -MARKERS& SNAPSHOT::GetMarkers() -{ - return my_markers; -} - -void SNAPSHOT::toMovie(MovieData& md, int start, int end) -{ - if (end < 0 || end >= size) end = size - 1; - // write Input data to movie data - md.records.resize(end + 1); - switch(input_type) - { - case INPUT_TYPE_FOURSCORE: - { - int pos = start * BYTES_PER_JOYSTICK * joysticks_per_frame[input_type]; - for (int frame = start; frame <= end; ++frame) - { - md.records[frame].joysticks[0] = joysticks[pos++]; - md.records[frame].joysticks[1] = joysticks[pos++]; - md.records[frame].joysticks[2] = joysticks[pos++]; - md.records[frame].joysticks[3] = joysticks[pos++]; - md.records[frame].commands = commands[frame]; - } - break; - } - case INPUT_TYPE_2P: - { - int pos = start * BYTES_PER_JOYSTICK * joysticks_per_frame[input_type]; - for (int frame = start; frame <= end; ++frame) - { - md.records[frame].joysticks[0] = joysticks[pos++]; - md.records[frame].joysticks[1] = joysticks[pos++]; - md.records[frame].commands = commands[frame]; - } - break; - } - case INPUT_TYPE_1P: - { - int pos = start * BYTES_PER_JOYSTICK * joysticks_per_frame[input_type]; - for (int frame = start; frame <= end; ++frame) - { - md.records[frame].joysticks[0] = joysticks[pos++]; - md.records[frame].commands = commands[frame]; - } - break; - } - } -} - +// ----------------------------------------------------------------------------------------- void SNAPSHOT::compress_data() { - // compress joysticks - int len = joysticks.size(); - uLongf comprlen = (len>>9)+12 + len; - joysticks_compressed.resize(comprlen); - compress(&joysticks_compressed[0], &comprlen, &joysticks[0], len); - joysticks_compressed.resize(comprlen); - // compress commands - len = commands.size(); - comprlen = (len>>9)+12 + len; - commands_compressed.resize(comprlen); - compress(&commands_compressed[0], &comprlen, &commands[0], len); - commands_compressed.resize(comprlen); - if (has_hot_changes) - { - // compress hot_changes - len = hot_changes.size(); - comprlen = (len>>9)+12 + len; - hot_changes_compressed.resize(comprlen); - compress(&hot_changes_compressed[0], &comprlen, &hot_changes[0], len); - hot_changes_compressed.resize(comprlen); - } - // don't recompress anymore - already_compressed = true; - // also see if we can compress my_markers - if (!my_markers.Get_already_compressed()) - my_markers.compress_data(); + if (!inputlog.Get_already_compressed()) + inputlog.compress_data(); + if (!markers.Get_already_compressed()) + markers.compress_data(); } bool SNAPSHOT::Get_already_compressed() { - return already_compressed; + // only consider this snapshot fully compressed when all of InputLog, LagLog and Markers are compressed + return (inputlog.Get_already_compressed() && markers.Get_already_compressed()); } void SNAPSHOT::save(EMUFILE *os) { // write vars - write32le(size, os); - write8le(input_type, os); - write32le(jump_frame, os); + write32le(keyframe, os); write32le(start_frame, os); write32le(end_frame, os); write32le(consecutive_tag, os); write32le(rec_joypad_diff_bits, os); write32le(mod_type, os); - if (has_hot_changes) write8le((uint8)1, os); else write8le((uint8)0, os); // write description int len = strlen(description); write8le(len, os); os->fwrite(&description[0], len); - // write data - if (!already_compressed) - compress_data(); - // save joysticks data - write32le(joysticks_compressed.size(), os); - os->fwrite(&joysticks_compressed[0], joysticks_compressed.size()); - // save commands data - write32le(commands_compressed.size(), os); - os->fwrite(&commands_compressed[0], commands_compressed.size()); - if (has_hot_changes) - { - // save hot_changes data - write32le(hot_changes_compressed.size(), os); - os->fwrite(&hot_changes_compressed[0], hot_changes_compressed.size()); - } + // save InputLog data + inputlog.save(os); + // save Markers data - my_markers.save(os); + markers.save(os); } // returns true if couldn't load 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 (!read32le(&jump_frame, is)) return true; + if (!read32le(&keyframe, is)) return true; if (!read32le(&start_frame, is)) return true; if (!read32le(&end_frame, is)) return true; if (!read32le(&consecutive_tag, is)) return true; if (!read32le(&rec_joypad_diff_bits, is)) return true; if (!read32le(&mod_type, is)) return true; - if (!read8le(&tmp, is)) return true; - has_hot_changes = (tmp != 0); // read description if (!read8le(&tmp, is)) return true; if (tmp >= SNAPSHOT_DESC_MAX_LENGTH) return true; if (is->fread(&description[0], tmp) != tmp) return true; description[tmp] = 0; // add '0' because it wasn't saved in the file - // read data - already_compressed = true; - int comprlen; - uLongf destlen; - // read and uncompress joysticks data - destlen = size * BYTES_PER_JOYSTICK * joysticks_per_frame[input_type]; - joysticks.resize(destlen); - // read size - if (!read32le(&comprlen, is)) return true; - if (comprlen <= 0) return true; - joysticks_compressed.resize(comprlen); - if (is->fread(&joysticks_compressed[0], comprlen) != comprlen) return true; - int e = uncompress(&joysticks[0], &destlen, &joysticks_compressed[0], comprlen); - if (e != Z_OK && e != Z_BUF_ERROR) return true; - // read and uncompress commands data - destlen = size; - commands.resize(destlen); - // read size - if (!read32le(&comprlen, is)) return true; - if (comprlen <= 0) return true; - commands_compressed.resize(comprlen); - if (is->fread(&commands_compressed[0], comprlen) != comprlen) return true; - e = uncompress(&commands[0], &destlen, &commands_compressed[0], comprlen); - if (e != Z_OK && e != Z_BUF_ERROR) return true; - if (has_hot_changes) - { - // read and uncompress hot_changes data - destlen = size * joysticks_per_frame[input_type] * HOTCHANGE_BYTES_PER_JOY; - hot_changes.resize(destlen); - // read size - if (!read32le(&comprlen, is)) return true; - if (comprlen <= 0) return true; - hot_changes_compressed.resize(comprlen); - if (is->fread(&hot_changes_compressed[0], comprlen) != comprlen) return true; - e = uncompress(&hot_changes[0], &destlen, &hot_changes_compressed[0], comprlen); - if (e != Z_OK && e != Z_BUF_ERROR) return true; - } + // load InputLog data + if (inputlog.load(is)) return true; + // load Markers data - if (my_markers.load(is)) return true; + if (markers.load(is)) return true; return false; } bool SNAPSHOT::skipLoad(EMUFILE *is) { - int tmp; uint8 tmp1; - // read vars - if (is->fseek(sizeof(int) + // size - sizeof(uint8) + // input_type - sizeof(int) + // jump_frame + // skip vars + if (is->fseek(sizeof(int) + // keyframe sizeof(int) + // start_frame sizeof(int) + // end_frame sizeof(int) + // consecutive_tag sizeof(int) + // rec_joypad_diff_bits - sizeof(int) + // mod_type - sizeof(uint8) // has_hot_changes + sizeof(int) // mod_type , SEEK_CUR)) return true; - // read description + // skip description if (!read8le(&tmp1, is)) return true; if (tmp1 >= SNAPSHOT_DESC_MAX_LENGTH) return true; if (is->fseek(tmp1, SEEK_CUR) != 0) return true; - // read joysticks data - if (!read32le(&tmp, is)) return true; - if (is->fseek(tmp, SEEK_CUR) != 0) return true; - // read commands data - if (!read32le(&tmp, is)) return true; - if (is->fseek(tmp, SEEK_CUR) != 0) return true; - if (has_hot_changes) - { - // read hot_changes data - if (!read32le(&tmp, is)) return true; - if (is->fseek(tmp, SEEK_CUR) != 0) return true; - } - // read Markers data - if (my_markers.skipLoad(is)) return true; + // skip InputLog data + if (inputlog.skipLoad(is)) return true; + + // skip Markers data + if (markers.skipLoad(is)) return true; return false; } -// return true if any difference is found -bool SNAPSHOT::checkDiff(SNAPSHOT& snap) -{ - if (size != snap.size) return true; - if (findFirstChange(snap) >= 0) - return true; - else - return false; -} - -// fills map of bits judging on which joypads differ (this function is only used by "Record" modtype) -void SNAPSHOT::fillJoypadsDiff(SNAPSHOT& snap, int frame) -{ - rec_joypad_diff_bits = 0; - uint32 current_mask = 1; - switch(input_type) - { - case INPUT_TYPE_FOURSCORE: - case INPUT_TYPE_2P: - case INPUT_TYPE_1P: - { - int pos = frame * BYTES_PER_JOYSTICK * joysticks_per_frame[input_type]; - for (int i = 0; i < BYTES_PER_JOYSTICK * joysticks_per_frame[input_type]; ++i) - { - if (pos < (snap.size * BYTES_PER_JOYSTICK * joysticks_per_frame[input_type])) - { - if (joysticks[pos+i] != snap.joysticks[pos+i]) rec_joypad_diff_bits |= current_mask; - } else - { - if (joysticks[pos+i]) rec_joypad_diff_bits |= current_mask; - } - current_mask <<= 1; - } - break; - } - } -} - -// return number of first frame of difference between two snapshots -int SNAPSHOT::findFirstChange(SNAPSHOT& snap, int start, int end) -{ - // if these two snapshots have different input_type (abnormal situation) then refuse to search and return the beginning - if (snap.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 snap_end = snap.size; - switch(input_type) - { - case INPUT_TYPE_FOURSCORE: - { - for (int frame = start, pos = start * BYTES_PER_JOYSTICK * joysticks_per_frame[input_type]; frame <= end; ++frame) - { - // return the frame if found different byte, or found emptiness in the snap when there's non-zero value here - if (frame < snap_end) - { - if (joysticks[pos] != snap.joysticks[pos]) return frame; - pos++; - if (joysticks[pos] != snap.joysticks[pos]) return frame; - pos++; - if (joysticks[pos] != snap.joysticks[pos]) return frame; - pos++; - if (joysticks[pos] != snap.joysticks[pos]) return frame; - pos++; - if (commands[frame] != snap.commands[frame]) return frame; - } else - { - if (joysticks[pos++]) return frame; - if (joysticks[pos++]) return frame; - if (joysticks[pos++]) return frame; - if (joysticks[pos++]) return frame; - if (commands[frame]) return frame; - } - } - break; - } - case INPUT_TYPE_2P: - { - for (int frame = start, pos = start * BYTES_PER_JOYSTICK * joysticks_per_frame[input_type]; frame <= end; ++frame) - { - // return the frame if found different byte, or found emptiness in the snap when there's non-zero value here - if (frame < snap_end) - { - if (joysticks[pos] != snap.joysticks[pos]) return frame; - pos++; - if (joysticks[pos] != snap.joysticks[pos]) return frame; - pos++; - if (commands[frame] != snap.commands[frame]) return frame; - } else - { - if (joysticks[pos++]) return frame; - if (joysticks[pos++]) return frame; - if (commands[frame]) return frame; - } - } - break; - } - case INPUT_TYPE_1P: - { - for (int frame = start, pos = start * BYTES_PER_JOYSTICK * joysticks_per_frame[input_type]; frame <= end; ++frame) - { - // return the frame if found different byte, or found emptiness in the snap when there's non-zero value here - if (frame < snap_end) - { - if (joysticks[pos] != snap.joysticks[pos]) return frame; - pos++; - if (commands[frame] != snap.commands[frame]) return frame; - } else - { - if (joysticks[pos++]) return frame; - if (commands[frame]) return frame; - } - } - break; - } - } - // if my_size is less then their_size, return last frame + 1 (= size) as the frame of difference - if (size < snap_end) return size; - // no changes were found - return -1; -} -// 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; - if (end >= md.getNumRecords()) end = md.getNumRecords()-1; - - switch(input_type) - { - case INPUT_TYPE_FOURSCORE: - { - for (int frame = start, pos = start * BYTES_PER_JOYSTICK * joysticks_per_frame[input_type]; frame <= end; ++frame) - { - if (joysticks[pos++] != md.records[frame].joysticks[0]) return frame; - if (joysticks[pos++] != md.records[frame].joysticks[1]) return frame; - if (joysticks[pos++] != md.records[frame].joysticks[2]) return frame; - if (joysticks[pos++] != md.records[frame].joysticks[3]) return frame; - if (commands[frame] != md.records[frame].commands) return frame; - } - break; - } - case INPUT_TYPE_2P: - { - for (int frame = start, pos = start * BYTES_PER_JOYSTICK * joysticks_per_frame[input_type]; frame <= end; ++frame) - { - if (joysticks[pos++] != md.records[frame].joysticks[0]) return frame; - if (joysticks[pos++] != md.records[frame].joysticks[1]) return frame; - if (commands[frame] != md.records[frame].commands) return frame; - } - break; - } - case INPUT_TYPE_1P: - { - for (int frame = start, pos = start * BYTES_PER_JOYSTICK * joysticks_per_frame[input_type]; frame <= end; ++frame) - { - if (joysticks[pos++] != md.records[frame].joysticks[0]) return frame; - if (commands[frame] != md.records[frame].commands) return frame; - } - break; - } - } - // if sizes differ, return last frame + 1 from the lesser of them - if (size < md.getNumRecords() && end >= size-1) - return size; - else if (size > md.getNumRecords() && end >= md.getNumRecords()-1) - return md.getNumRecords(); - - return -1; // no changes were found -} - -int SNAPSHOT::GetJoystickInfo(int frame, int joy) -{ - if (frame < 0 || frame >= size) return 0; - switch(input_type) - { - case INPUT_TYPE_FOURSCORE: - case INPUT_TYPE_2P: - case INPUT_TYPE_1P: - { - return joysticks[frame * BYTES_PER_JOYSTICK * joysticks_per_frame[input_type] + joy]; - } - } - return 0; -} -int SNAPSHOT::GetCommandsInfo(int frame) -{ - if (frame < 0 || frame >= size) return 0; - return commands[frame]; -} - -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 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 && source_of_hotchanges->input_type == input_type) - { - int min = hot_changes.size(); - if (min > (int)source_of_hotchanges->hot_changes.size()) - min = source_of_hotchanges->hot_changes.size(); - - // special case for Branches: if limit_frame if specified, then copy only hotchanges from 0 to limit_frame - if (limit_frame_of_source >= 0) - { - if (min > limit_frame_of_source * joysticks_per_frame[input_type] * HOTCHANGE_BYTES_PER_JOY) - min = limit_frame_of_source * joysticks_per_frame[input_type] * HOTCHANGE_BYTES_PER_JOY; - } - - memcpy(&hot_changes[0], &source_of_hotchanges->hot_changes[0], min); - } -} -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 && source_of_hotchanges->input_type == input_type) - { - int min = hot_changes.size(); - if (min > (int)source_of_hotchanges->hot_changes.size()) - min = source_of_hotchanges->hot_changes.size(); - - memcpy(&hot_changes[0], &source_of_hotchanges->hot_changes[0], min); - FadeHotChanges(); - } -} -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 && 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.GetStrobedSelection().begin()); - while (pos < this_size && source_pos < source_size) - { - if (it != selection.GetStrobedSelection().end() && frame == *it) - { - // this frame is selected - it++; - // omit the frame - source_pos += bytes; - } else - { - // copy hotchanges of this frame - memcpy(&hot_changes[pos], &source_of_hotchanges->hot_changes[source_pos], bytes); - pos += bytes; - source_pos += bytes; - } - frame++; - } - FadeHotChanges(); - } -} -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 && 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; - int this_size = hot_changes.size(), source_size = source_of_hotchanges->hot_changes.size(); - SelectionFrames::iterator it(selection.GetStrobedSelection().begin()); - SelectionFrames::iterator current_selection_end(selection.GetStrobedSelection().end()); - while (pos < this_size) - { - if (it != current_selection_end && frame == *it) - { - // this frame is selected - it++; - region_len++; - // set filled line to the frame - memset(&hot_changes[pos], 0xFF, bytes); - } else if (source_pos < source_size) - { - // this frame is not selected - frame -= region_len; - region_len = 0; - // copy hotchanges of this frame - memcpy(&hot_changes[pos], &source_of_hotchanges->hot_changes[source_pos], bytes); - FadeHotChanges(pos, pos + bytes); - source_pos += bytes; - } - pos += bytes; - frame++; - } - } else - { - // no old data, just fill selected lines - int bytes = joysticks_per_frame[input_type] * HOTCHANGE_BYTES_PER_JOY; - int frame = 0, region_len = 0, pos = 0; - int this_size = hot_changes.size(); - SelectionFrames::iterator it(selection.GetStrobedSelection().begin()); - SelectionFrames::iterator current_selection_end(selection.GetStrobedSelection().end()); - while (pos < this_size) - { - if (it != current_selection_end && frame == *it) - { - // this frame is selected - it++; - region_len++; - // set filled line to the frame - memset(&hot_changes[pos], 0xFF, bytes); - // exit loop when all frames in the Selection are handled - if (it == current_selection_end) break; - } else - { - // this frame is not selected - frame -= region_len; - region_len = 0; - // leave zeros in this frame - } - pos += bytes; - frame++; - } - } -} -void SNAPSHOT::inheritHotChanges_DeleteNum(SNAPSHOT* source_of_hotchanges, int start, int frames, bool fade_old) -{ - int bytes = joysticks_per_frame[input_type] * HOTCHANGE_BYTES_PER_JOY; - // copy hot changes from source snapshot up to "start" and from "start+frames" to end - if (source_of_hotchanges && source_of_hotchanges->has_hot_changes && source_of_hotchanges->input_type == input_type) - { - int this_size = hot_changes.size(), source_size = source_of_hotchanges->hot_changes.size(); - int bytes_to_copy = bytes * start; - int dest_pos = 0, source_pos = 0; - if (bytes_to_copy > source_size) - bytes_to_copy = source_size; - memcpy(&hot_changes[dest_pos], &source_of_hotchanges->hot_changes[source_pos], bytes_to_copy); - dest_pos += bytes_to_copy; - source_pos += bytes_to_copy + bytes * frames; - bytes_to_copy = this_size - dest_pos; - if (bytes_to_copy > source_size - source_pos) - bytes_to_copy = source_size - source_pos; - memcpy(&hot_changes[dest_pos], &source_of_hotchanges->hot_changes[source_pos], bytes_to_copy); - if (fade_old) - FadeHotChanges(); - } -} -void SNAPSHOT::inheritHotChanges_InsertNum(SNAPSHOT* source_of_hotchanges, int start, int frames, bool fade_old) -{ - int bytes = joysticks_per_frame[input_type] * HOTCHANGE_BYTES_PER_JOY; - // copy hot changes from source snapshot up to "start", then make a gap, then copy from "start+frames" to end - if (source_of_hotchanges && source_of_hotchanges->has_hot_changes && source_of_hotchanges->input_type == input_type) - { - int this_size = hot_changes.size(), source_size = source_of_hotchanges->hot_changes.size(); - int bytes_to_copy = bytes * start; - int dest_pos = 0, source_pos = 0; - if (bytes_to_copy > source_size) - bytes_to_copy = source_size; - memcpy(&hot_changes[dest_pos], &source_of_hotchanges->hot_changes[source_pos], bytes_to_copy); - dest_pos += bytes_to_copy + bytes * frames; - source_pos += bytes_to_copy; - bytes_to_copy = this_size - dest_pos; - if (bytes_to_copy > source_size - source_pos) - bytes_to_copy = source_size - source_pos; - memcpy(&hot_changes[dest_pos], &source_of_hotchanges->hot_changes[source_pos], bytes_to_copy); - if (fade_old) - FadeHotChanges(); - } - // fill the gap with max_hot lines on frames from "start" to "start+frames" - memset(&hot_changes[bytes * start], 0xFF, bytes * frames); -} -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 && 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(inserted_set.begin()); - SelectionFrames::iterator inserted_set_end(inserted_set.end()); - while (pos < this_size) - { - if (it != inserted_set_end && frame == *it) - { - // this frame was inserted - it++; - // set filled line to the frame - memset(&hot_changes[pos], 0xFF, bytes); - } else if (source_pos < source_size) - { - // copy hotchanges of this frame - memcpy(&hot_changes[pos], &source_of_hotchanges->hot_changes[source_pos], bytes); - FadeHotChanges(pos, pos + bytes); - source_pos += bytes; - } - pos += bytes; - frame++; - } - } else - { - // no old data, just fill selected lines - 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(inserted_set.begin()); - SelectionFrames::iterator inserted_set_end(inserted_set.end()); - while (pos < this_size) - { - if (it != inserted_set_end && frame == *it) - { - // this frame was inserted - it++; - // set filled line to the frame - memset(&hot_changes[pos], 0xFF, bytes); - pos += bytes; - // exit loop when all inserted_set frames are handled - if (it == inserted_set_end) break; - } else - { - // leave zeros in this frame - pos += bytes; - } - frame++; - } - } -} -void SNAPSHOT::fillHotChanges(SNAPSHOT& snap, int start, int end) -{ - // if these two snapshots have different input_type (abnormal situation) then refuse to compare - if (snap.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 snap_end = snap.size; - switch(input_type) - { - case INPUT_TYPE_FOURSCORE: - { - for (int frame = start, pos = start * BYTES_PER_JOYSTICK * joysticks_per_frame[input_type]; frame <= end; ++frame) - { - // consider changed if found different byte, or found emptiness in the snap when there's non-zero value here - if (frame < snap_end) - { - if (joysticks[pos] != snap.joysticks[pos]) - SetMaxHotChange_Bits(frame, 0, joysticks[pos] ^ snap.joysticks[pos]); - pos++; - if (joysticks[pos] != snap.joysticks[pos]) - SetMaxHotChange_Bits(frame, 1, joysticks[pos] ^ snap.joysticks[pos]); - pos++; - if (joysticks[pos] != snap.joysticks[pos]) - SetMaxHotChange_Bits(frame, 2, joysticks[pos] ^ snap.joysticks[pos]); - pos++; - if (joysticks[pos] != snap.joysticks[pos]) - SetMaxHotChange_Bits(frame, 3, joysticks[pos] ^ snap.joysticks[pos]); - pos++; - } else - { - if (joysticks[pos]) - SetMaxHotChange_Bits(frame, 0, joysticks[pos]); - pos++; - if (joysticks[pos]) - SetMaxHotChange_Bits(frame, 1, joysticks[pos]); - pos++; - if (joysticks[pos]) - SetMaxHotChange_Bits(frame, 2, joysticks[pos]); - pos++; - if (joysticks[pos]) - SetMaxHotChange_Bits(frame, 3, joysticks[pos]); - pos++; - } - } - break; - } - case INPUT_TYPE_2P: - { - for (int frame = start, pos = start * BYTES_PER_JOYSTICK * joysticks_per_frame[input_type]; frame <= end; ++frame) - { - // consider changed if found different byte, or found emptiness in the snap when there's non-zero value here - if (frame < snap_end) - { - if (joysticks[pos] != snap.joysticks[pos]) - SetMaxHotChange_Bits(frame, 0, joysticks[pos] ^ snap.joysticks[pos]); - pos++; - if (joysticks[pos] != snap.joysticks[pos]) - SetMaxHotChange_Bits(frame, 1, joysticks[pos] ^ snap.joysticks[pos]); - pos++; - } else - { - if (joysticks[pos]) - SetMaxHotChange_Bits(frame, 0, joysticks[pos]); - pos++; - if (joysticks[pos]) - SetMaxHotChange_Bits(frame, 1, joysticks[pos]); - pos++; - } - } - break; - } - case INPUT_TYPE_1P: - { - for (int frame = start, pos = start * BYTES_PER_JOYSTICK * joysticks_per_frame[input_type]; frame <= end; ++frame) - { - // consider changed if found different byte, or found emptiness in the snap when there's non-zero value here - if (frame < snap_end) - { - if (joysticks[pos] != snap.joysticks[pos]) - SetMaxHotChange_Bits(frame, 0, joysticks[pos] ^ snap.joysticks[pos]); - pos++; - } else - { - if (joysticks[pos]) - SetMaxHotChange_Bits(frame, 0, joysticks[pos]); - pos++; - } - } - break; - } - } -} - -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 - for (int i = 0; i < 8; ++i) - { - if (joy_bits & mask) - SetMaxHotChange(frame, joypad * 8 + i); - mask <<= 1; - } -} -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 - switch(input_type) - { - case INPUT_TYPE_FOURSCORE: - { - // 32 buttons = 16bytes - if (absolute_button & 1) - // odd buttons (B, T, D, R) - set upper 4 bits of the byte - hot_changes[(frame << 4) | (absolute_button >> 1)] |= 0xF0; - else - // even buttons (A, S, U, L) - set lower 4 bits of the byte - hot_changes[(frame << 4) | (absolute_button >> 1)] |= 0x0F; - break; - } - case INPUT_TYPE_2P: - { - // 16 buttons = 8bytes - if (absolute_button & 1) - // odd buttons (B, T, D, R) - set upper 4 bits of the byte - hot_changes[(frame << 3) | (absolute_button >> 1)] |= 0xF0; - else - // even buttons (A, S, U, L) - set lower 4 bits of the byte - hot_changes[(frame << 3) | (absolute_button >> 1)] |= 0x0F; - break; - } - case INPUT_TYPE_1P: - { - // 8 buttons = 4bytes - if (absolute_button & 1) - // odd buttons (B, T, D, R) - set upper 4 bits of the byte - hot_changes[(frame << 2) | (absolute_button >> 1)] |= 0xF0; - else - // even buttons (A, S, U, L) - set lower 4 bits of the byte - hot_changes[(frame << 2) | (absolute_button >> 1)] |= 0x0F; - break; - } - } -} - -void SNAPSHOT::FadeHotChanges(int start_byte, int end_byte) -{ - uint8 hi_half, low_half; - if (end_byte < 0) - end_byte = hot_changes.size(); - for (int i = end_byte - 1; i >= start_byte; i--) - { - if (hot_changes[i]) - { - hi_half = hot_changes[i] >> 4; - low_half = hot_changes[i] & 15; - if (hi_half) hi_half--; - if (low_half) low_half--; - hot_changes[i] = (hi_half << 4) | low_half; - } - } -} - -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; - - uint8 val; - switch(input_type) - { - case INPUT_TYPE_FOURSCORE: - { - // 32 buttons, 16bytes - val = hot_changes[(frame << 4) + (absolute_button >> 1)]; - break; - } - case INPUT_TYPE_2P: - { - // 16 buttons, 8bytes - val = hot_changes[(frame << 3) + (absolute_button >> 1)]; - break; - } - case INPUT_TYPE_1P: - { - // 8 buttons, 4bytes - val = hot_changes[(frame << 2) + (absolute_button >> 1)]; - break; - } - } - - if (absolute_button & 1) - // odd buttons (B, T, D, R) - upper 4 bits of the byte - return val >> 4; - else - // even buttons (A, S, U, L) - lower 4 bits of the byte - return val & 15; -} - diff --git a/src/drivers/win/taseditor/snapshot.h b/src/drivers/win/taseditor/snapshot.h index 9a9d89d1..4ec495bc 100644 --- a/src/drivers/win/taseditor/snapshot.h +++ b/src/drivers/win/taseditor/snapshot.h @@ -1,16 +1,6 @@ // Specification file for Snapshot class -enum Input_types -{ - INPUT_TYPE_1P, - INPUT_TYPE_2P, - INPUT_TYPE_FOURSCORE, - - NUM_SUPPORTED_INPUT_TYPES -}; - -#define BYTES_PER_JOYSTICK 1 // 1 byte per 1 joystick (8 buttons) -#define HOTCHANGE_BYTES_PER_JOY 4 // 4 bytes per 8 buttons +#include "inputlog.h" #define SNAPSHOT_DESC_MAX_LENGTH 100 @@ -22,70 +12,27 @@ public: bool MarkersDifferFromCurrent(); void copyToMarkers(); - MARKERS& GetMarkers(); - void toMovie(MovieData& md, int start = 0, int end = -1); + void compress_data(); + bool Get_already_compressed(); void save(EMUFILE *os); bool load(EMUFILE *is); bool skipLoad(EMUFILE *is); - void compress_data(); - bool Get_already_compressed(); - - bool checkDiff(SNAPSHOT& snap); - void fillJoypadsDiff(SNAPSHOT& snap, int frame); - - int findFirstChange(SNAPSHOT& snap, int start = 0, int end = -1); - int findFirstChange(MovieData& md, int start = 0, int end = -1); - - int GetJoystickInfo(int frame, int joy); - int GetCommandsInfo(int frame); - - 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_DeleteNum(SNAPSHOT* source_of_hotchanges, int start, int frames, bool fade_old); - void inheritHotChanges_InsertNum(SNAPSHOT* source_of_hotchanges, int start, int frames, bool fade_old); - void inheritHotChanges_PasteInsert(SNAPSHOT* source_of_hotchanges, SelectionFrames& inserted_set); - void fillHotChanges(SNAPSHOT& snap, int start = 0, int end = -1); - - void SetMaxHotChange_Bits(int frame, int joypad, uint8 joy_bits); - void SetMaxHotChange(int frame, int absolute_button); - - void FadeHotChanges(int start_byte = 0, int end_byte = -1); - - 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 - int jump_frame; // for jumping when making undo + INPUTLOG inputlog; + LAGLOG laglog; + MARKERS markers; + int keyframe; // for jumping when making undo int start_frame; // for consecutive Draws int end_frame; // for consecutive Draws int consecutive_tag; // for consecutive Recordings and Draws uint32 rec_joypad_diff_bits; // for consecutive Recordings int mod_type; char description[SNAPSHOT_DESC_MAX_LENGTH]; - bool has_hot_changes; - - // not saved data - 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, ... private: - - // 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/taseditor_config.cpp b/src/drivers/win/taseditor/taseditor_config.cpp index 0f698641..81dc7d76 100644 --- a/src/drivers/win/taseditor/taseditor_config.cpp +++ b/src/drivers/win/taseditor/taseditor_config.cpp @@ -51,7 +51,7 @@ TASEDITOR_CONFIG::TASEDITOR_CONFIG() view_branches_tree = false; branch_scr_hud = true; restore_position = false; - adjust_input_due_to_lag = false; + adjust_input_due_to_lag = true; greenzone_capacity = GREENZONE_CAPACITY_DEFAULT; undo_levels = UNDO_LEVELS_DEFAULT; autosave_period = AUTOSAVE_PERIOD_DEFAULT; diff --git a/src/drivers/win/taseditor/taseditor_lua.cpp b/src/drivers/win/taseditor/taseditor_lua.cpp index 37480f6e..5879160a 100644 --- a/src/drivers/win/taseditor/taseditor_lua.cpp +++ b/src/drivers/win/taseditor/taseditor_lua.cpp @@ -75,22 +75,22 @@ void TASEDITOR_LUA::InsertDelete_rows_to_Snaphot(SNAPSHOT& snapshot) // apply changes to given snapshot (only insertion/deletion) for (int i = 0; i < size; ++i) { - if (pending_changes[i].frame >= snapshot.size) + if (pending_changes[i].frame >= snapshot.inputlog.size) // expand snapshot to fit the frame - snapshot.insertFrames(-1, 1 + pending_changes[i].frame - snapshot.size); + snapshot.inputlog.insertFrames(-1, 1 + pending_changes[i].frame - snapshot.inputlog.size); switch (pending_changes[i].type) { case LUA_CHANGE_TYPE_INSERTFRAMES: { - snapshot.insertFrames(pending_changes[i].frame, pending_changes[i].data); + snapshot.inputlog.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); + if (pending_changes[i].frame < snapshot.inputlog.size) + snapshot.inputlog.eraseFrame(pending_changes[i].frame); } break; } diff --git a/src/drivers/win/taseditor/taseditor_project.h b/src/drivers/win/taseditor/taseditor_project.h index a75504c4..4f4e32a2 100644 --- a/src/drivers/win/taseditor/taseditor_project.h +++ b/src/drivers/win/taseditor/taseditor_project.h @@ -5,6 +5,7 @@ #include "../common.h" #include "taseditor_config.h" #include "taseditor_window.h" +#include "greenzone.h" #include "selection.h" #include "markers_manager.h" #include "snapshot.h" @@ -13,7 +14,6 @@ #include "history.h" #include "playback.h" #include "recorder.h" -#include "greenzone.h" #include "piano_roll.h" #include "taseditor_lua.h" #include "splicer.h" @@ -29,7 +29,7 @@ #define PIANO_ROLL_SAVED 16 #define SELECTION_SAVED 32 -#define PROJECT_FILE_CURRENT_VERSION 1 +#define PROJECT_FILE_CURRENT_VERSION 2 class TASEDITOR_PROJECT { diff --git a/src/lua-engine.cpp b/src/lua-engine.cpp index 22b617f8..9cb3d502 100644 --- a/src/lua-engine.cpp +++ b/src/lua-engine.cpp @@ -41,6 +41,7 @@ #ifdef WIN32 #include "drivers/win/common.h" #include "drivers/win/taseditor/selection.h" +#include "drivers/win/taseditor/laglog.h" #include "drivers/win/taseditor/markers.h" #include "drivers/win/taseditor/snapshot.h" #include "drivers/win/taseditor/taseditor_lua.h" diff --git a/vc/vc10_fceux.vcxproj b/vc/vc10_fceux.vcxproj index f550f2e3..6c8bc39d 100644 --- a/vc/vc10_fceux.vcxproj +++ b/vc/vc10_fceux.vcxproj @@ -427,6 +427,8 @@ + + @@ -755,6 +757,8 @@ + + diff --git a/vc/vc10_fceux.vcxproj.filters b/vc/vc10_fceux.vcxproj.filters index d3139089..b602923c 100644 --- a/vc/vc10_fceux.vcxproj.filters +++ b/vc/vc10_fceux.vcxproj.filters @@ -964,6 +964,12 @@ boards + + drivers\win\taseditor + + + drivers\win\taseditor + @@ -1459,6 +1465,12 @@ drivers\win\taseditor + + drivers\win\taseditor + + + drivers\win\taseditor +