* Taseditor: fixed AdjustLag feature

* Taseditor: changed fm3 version to v2
* Taseditor: Ctrl+Z/Ctrl+Y trigger twice when crossing AdjustLag operations
This commit is contained in:
ansstuff 2012-08-25 19:59:07 +00:00
parent acfb5bf912
commit fd35046e62
31 changed files with 1493 additions and 1219 deletions

View File

@ -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();

View File

@ -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)
}
// ----------------------------------------------------------

View File

@ -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

View File

@ -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<int> 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<int> 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);

View File

@ -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;

View File

@ -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<uint8> 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<uint8> 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<uint8>& GREENZONE::GetSavestate(int frame)

View File

@ -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<uint8>& GetSavestate(int frame);
void WriteSavestate(int frame, std::vector<uint8>& savestate);
bool SavestateIsEmpty(int frame);
// saved data
LAGLOG laglog;
private:
void CollectCurrentState();
// saved data
int greenZoneCount;
std::vector<std::vector<uint8>> savestates;
std::vector<uint8> lag_history;
// not saved data
int next_cleaning_time;
};

View File

@ -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)

View File

@ -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
};

View File

@ -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;
}

View File

@ -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<uint8> joysticks; // Format: joy0-for-frame0, joy1-for-frame0, joy2-for-frame0, joy3-for-frame0, joy0-for-frame1, joy1-for-frame1, ...
std::vector<uint8> commands; // Format: commands-for-frame0, commands-for-frame1, ...
std::vector<uint8> hot_changes; // Format: buttons01joy0-for-frame0, buttons23joy0-for-frame0, buttons45joy0-for-frame0, buttons67joy0-for-frame0, buttons01joy1-for-frame0, ...
private:
// also saved data
std::vector<uint8> joysticks_compressed;
std::vector<uint8> commands_compressed;
std::vector<uint8> hot_changes_compressed;
// not saved data
bool already_compressed; // to compress only once
};

View File

@ -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;
}

View File

@ -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<uint8> lag_log_compressed;
// not saved data
std::vector<uint8> lag_log;
bool already_compressed; // to compress only once
};

View File

@ -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;
}

View File

@ -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<std::string> notes; // Format: 0th - note for intro (Marker 0), 1st - note for Marker1, 2nd - note for Marker2, ...

View File

@ -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)
{

View File

@ -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;

View File

@ -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

View File

@ -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)
{

View File

@ -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;

View File

@ -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);
}

View File

@ -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;
}

View File

@ -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])
{

File diff suppressed because it is too large Load Diff

View File

@ -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<uint8> joysticks; // Format: joy0-for-frame0, joy1-for-frame0, joy2-for-frame0, joy3-for-frame0, joy0-for-frame1, joy1-for-frame1, ...
std::vector<uint8> commands; // Format: commands-for-frame0, commands-for-frame1, ...
std::vector<uint8> 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<uint8> joysticks_compressed;
std::vector<uint8> commands_compressed;
std::vector<uint8> hot_changes_compressed;
bool already_compressed; // to compress only once
};

View File

@ -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;

View File

@ -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;
}

View File

@ -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
{

View File

@ -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"

View File

@ -427,6 +427,8 @@
<ClCompile Include="..\src\drivers\win\taseditor\editor.cpp" />
<ClCompile Include="..\src\drivers\win\taseditor\greenzone.cpp" />
<ClCompile Include="..\src\drivers\win\taseditor\history.cpp" />
<ClCompile Include="..\src\drivers\win\taseditor\inputlog.cpp" />
<ClCompile Include="..\src\drivers\win\taseditor\laglog.cpp" />
<ClCompile Include="..\src\drivers\win\taseditor\markers.cpp" />
<ClCompile Include="..\src\drivers\win\taseditor\markers_manager.cpp" />
<ClCompile Include="..\src\drivers\win\taseditor\piano_roll.cpp" />
@ -755,6 +757,8 @@
<ClInclude Include="..\src\drivers\win\taseditor\editor.h" />
<ClInclude Include="..\src\drivers\win\taseditor\greenzone.h" />
<ClInclude Include="..\src\drivers\win\taseditor\history.h" />
<ClInclude Include="..\src\drivers\win\taseditor\inputlog.h" />
<ClInclude Include="..\src\drivers\win\taseditor\laglog.h" />
<ClInclude Include="..\src\drivers\win\taseditor\markers.h" />
<ClInclude Include="..\src\drivers\win\taseditor\markers_manager.h" />
<ClInclude Include="..\src\drivers\win\taseditor\piano_roll.h" />

View File

@ -964,6 +964,12 @@
<ClCompile Include="..\src\boards\116.cpp">
<Filter>boards</Filter>
</ClCompile>
<ClCompile Include="..\src\drivers\win\taseditor\inputlog.cpp">
<Filter>drivers\win\taseditor</Filter>
</ClCompile>
<ClCompile Include="..\src\drivers\win\taseditor\laglog.cpp">
<Filter>drivers\win\taseditor</Filter>
</ClCompile>
</ItemGroup>
<ItemGroup>
<ClInclude Include="..\src\cart.h">
@ -1459,6 +1465,12 @@
<ClInclude Include="..\src\drivers\win\taseditor\popup_display.h">
<Filter>drivers\win\taseditor</Filter>
</ClInclude>
<ClInclude Include="..\src\drivers\win\taseditor\inputlog.h">
<Filter>drivers\win\taseditor</Filter>
</ClInclude>
<ClInclude Include="..\src\drivers\win\taseditor\laglog.h">
<Filter>drivers\win\taseditor</Filter>
</ClInclude>
</ItemGroup>
<ItemGroup>
<ResourceCompile Include="..\src\drivers\win\res.rc">