* Taseditor: changed the order of "Select between Markers"

* Taseditor: applying PAL and PPU flags when loading projects
* Taseditor: much better way of calculating Branches Tree; hinting full timelines
* Taseditor: miniarrow showing current Playback cursor position in Branches Tree
* Taseditor: "Bookmark#" modtype, undo/redo works for Bookmarks
* Taseditor: version data in fm3
* fixed HUD/messages dumping in AVI

[[Split portion of a mixed commit.]]
This commit is contained in:
ansstuff 2012-04-07 17:10:29 +00:00
parent 91d5c1f076
commit f79d4f9a3f
29 changed files with 1087 additions and 432 deletions

View File

@ -434,28 +434,20 @@ bool FCEUI_AviIsRecording()
{
if(avi_file)
return true;
return false;
}
bool FCEUI_AviEnableHUDrecording()
{
if (enableHUDrecording)
return true;
return false;
return enableHUDrecording;
}
void FCEUI_SetAviEnableHUDrecording(bool enable)
{
enableHUDrecording = enable;
}
bool FCEUI_AviDisableMovieMessages()
{
if (disableMovieMessages)
return true;
return false;
return disableMovieMessages;
}
void FCEUI_SetAviDisableMovieMessages(bool disable)
{

View File

@ -62,8 +62,8 @@ extern uint8 gNoBGFillColor;
extern bool rightClickEnabled;
extern int CurrentState;
extern bool pauseWhileActive; //adelikat: Cheats dialog
extern bool AVIenableHUDrecording;
extern bool AVIdisableMovieMessages;
extern bool enableHUDrecording;
extern bool disableMovieMessages;
extern bool replaceP2StartWithMicrophone;
extern bool SingleInstanceOnly;
extern bool oldInputDisplay;
@ -389,8 +389,8 @@ static CFGSTRUCT fceuconfig[] = {
AC(backupSavestates),
AC(compressSavestates),
AC(pauseWhileActive),
AC(AVIenableHUDrecording),
AC(AVIdisableMovieMessages),
AC(enableHUDrecording),
AC(disableMovieMessages),
AC(replaceP2StartWithMicrophone),
AC(SingleInstanceOnly),

Binary file not shown.

Before

Width:  |  Height:  |  Size: 23 KiB

After

Width:  |  Height:  |  Size: 23 KiB

View File

@ -49,6 +49,10 @@ EDITOR editor;
extern int joysticks_per_frame[NUM_SUPPORTED_INPUT_TYPES];
extern bool turbo;
extern int pal_emulation;
extern int newppu;
extern void PushCurrentVideoSettings();
extern void RefreshThrottleFPS();
// temporarily saved FCEUX config
int saved_eoptions;
int saved_EnableAutosave;
@ -122,8 +126,8 @@ bool EnterTasEditor()
// ensure that movie has correct set of ports/fourscore
SetInputType(currMovieData, GetInputType(currMovieData));
// force the input configuration stored in the movie to apply to FCEUX config
FCEUD_SetInput(currMovieData.fourscore, currMovieData.microphone, (ESI)currMovieData.ports[0], (ESI)currMovieData.ports[1], (ESIFC)currMovieData.ports[2]);
// reset some modules that need MovidData info
ApplyMovieInputConfig();
// reset some modules that need MovieData info
piano_roll.reset();
recorder.reset();
// create initial snapshot in history
@ -366,8 +370,8 @@ bool LoadProject(char* fullname)
// try to load project
if (project.load(fullname))
{
// update FCEUX input config
FCEUD_SetInput(currMovieData.fourscore, currMovieData.microphone, (ESI)currMovieData.ports[0], (ESI)currMovieData.ports[1], (ESIFC)currMovieData.ports[2]);
// loaded successfully
ApplyMovieInputConfig();
// add new file to Recent menu
taseditor_window.UpdateRecentProjectsArray(fullname);
taseditor_window.RedrawTaseditor();
@ -749,6 +753,19 @@ void SetInputType(MovieData& md, int new_input_type)
}
}
void ApplyMovieInputConfig()
{
// update FCEUX input config
FCEUD_SetInput(currMovieData.fourscore, currMovieData.microphone, (ESI)currMovieData.ports[0], (ESI)currMovieData.ports[1], (ESIFC)currMovieData.ports[2]);
// update PAL flag
pal_emulation = currMovieData.palFlag;
FCEUI_SetVidSystem(pal_emulation);
RefreshThrottleFPS();
PushCurrentVideoSettings();
// update PPU type
newppu = currMovieData.PPUflag;
}
// this getter contains formula to decide whether to record or replay movie
bool TaseditorIsRecording()
{

View File

@ -26,5 +26,7 @@ void Export();
int GetInputType(MovieData& md);
void SetInputType(MovieData& md, int new_input_type);
void ApplyMovieInputConfig();
bool TaseditorIsRecording();

View File

@ -27,13 +27,40 @@ extern uint8 *XBackBuf;
BOOKMARK::BOOKMARK()
{
not_empty = false;
}
void BOOKMARK::init()
{
free();
}
void BOOKMARK::free()
{
not_empty = false;
flash_type = flash_phase = 0;
snapshot.jump_frame = -1;
SNAPSHOT tmp;
snapshot = tmp;
savestate.resize(0);
saved_screenshot.resize(0);
}
bool BOOKMARK::checkDiffFromCurrent()
{
// check if the Bookmark data differs from current project/MovieData/Markers/settings
if (not_empty && snapshot.jump_frame == currFrameCounter)
{
if (snapshot.size == currMovieData.getNumRecords() && snapshot.findFirstChange(currMovieData) < 0)
{
if (!snapshot.MarkersDifferFromCurrent())
{
if (snapshot.has_hot_changes == taseditor_config.enable_hot_changes)
{
return false;
}
}
}
}
return true;
}
void BOOKMARK::set()
@ -94,7 +121,7 @@ bool BOOKMARK::load(EMUFILE *is)
{
uint8 tmp;
if (!read8le(&tmp, is)) return true;
not_empty = tmp != 0;
not_empty = (tmp != 0);
if (not_empty)
{
// read snapshot
@ -108,11 +135,33 @@ bool BOOKMARK::load(EMUFILE *is)
if (!read32le(&size, is)) return true;
saved_screenshot.resize(size);
if ((int)is->fread(&saved_screenshot[0], size) < size) return true;
} else
{
free();
}
// all ok
flash_type = flash_phase = 0;
return false;
}
bool BOOKMARK::skipLoad(EMUFILE *is)
{
uint8 tmp;
if (!read8le(&tmp, is)) return true;
if (tmp != 0)
{
// read snapshot
if (snapshot.skipLoad(is)) return true;
// read savestate
int size;
if (!read32le(&size, is)) return true;
if (is->fseek(size, SEEK_CUR)) return true;
// read saved_screenshot
if (!read32le(&size, is)) return true;
if (is->fseek(size, SEEK_CUR)) return true;
}
// all ok
return false;
}
// ----------------------------------------------------------

View File

@ -19,13 +19,16 @@ class BOOKMARK
public:
BOOKMARK();
void init();
void free();
bool checkDiffFromCurrent();
void set();
void jumped();
void deployed();
void save(EMUFILE *os);
bool load(EMUFILE *is);
bool skipLoad(EMUFILE *is);
// saved vars
bool not_empty;

View File

@ -169,7 +169,7 @@ void BOOKMARKS::update()
if (bookmarks_array[i].flash_phase != FLASH_PHASE_BUTTONHELD)
{
bookmarks_array[i].flash_phase = FLASH_PHASE_BUTTONHELD;
RedrawBookmarksRow((i + TOTAL_BOOKMARKS - 1) % TOTAL_BOOKMARKS);
RedrawBookmark(i);
branches.must_redraw_branches_tree = true; // because border of branch digit has changed
}
} else
@ -177,7 +177,7 @@ void BOOKMARKS::update()
if (bookmarks_array[i].flash_phase > 0)
{
bookmarks_array[i].flash_phase--;
RedrawBookmarksRow((i + TOTAL_BOOKMARKS - 1) % TOTAL_BOOKMARKS);
RedrawBookmark(i);
branches.must_redraw_branches_tree = true; // because border of branch digit has changed
}
}
@ -233,41 +233,36 @@ void BOOKMARKS::set(int slot)
markers_manager.UpdateMarkerNote();
int previous_frame = bookmarks_array[slot].snapshot.jump_frame;
// save time of this slot before rewriting it
char saved_time[TIME_DESC_LENGTH];
if (bookmarks_array[slot].not_empty)
if (bookmarks_array[slot].checkDiffFromCurrent())
{
strncpy(saved_time, bookmarks_array[slot].snapshot.description, TIME_DESC_LENGTH - 1);
saved_time[TIME_DESC_LENGTH - 1] = 0;
} else
{
saved_time[0] = 0;
}
// write current MovieData to the slot
bookmarks_array[slot].set();
// find its new place in Branches Tree
int old_current_branch = branches.GetCurrentBranch();
branches.HandleBookmarkSet(slot, saved_time);
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);
}
// also redraw List rows
if (previous_frame >= 0 && previous_frame != currFrameCounter)
{
piano_roll.RedrawRow(previous_frame);
RedrawChangedBookmarks(previous_frame);
}
piano_roll.RedrawRow(currFrameCounter);
RedrawChangedBookmarks(currFrameCounter);
// if screenshot of the slot is currently shown - reinit and redraw the picture
if (popup_display.screenshot_currently_shown == slot)
popup_display.screenshot_currently_shown = ITEM_UNDER_MOUSE_NONE;
BOOKMARK backup_copy(bookmarks_array[slot]);
bookmarks_array[slot].set();
// rebuild Branches Tree
int old_current_branch = branches.GetCurrentBranch();
branches.HandleBookmarkSet(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);
}
// also redraw List rows
if (previous_frame >= 0 && previous_frame != currFrameCounter)
{
piano_roll.RedrawRow(previous_frame);
RedrawChangedBookmarks(previous_frame);
}
piano_roll.RedrawRow(currFrameCounter);
RedrawChangedBookmarks(currFrameCounter);
// if screenshot of the slot is currently shown - reinit and redraw the picture
if (popup_display.screenshot_currently_shown == slot)
popup_display.screenshot_currently_shown = ITEM_UNDER_MOUSE_NONE;
project.SetProjectChanged();
FCEU_DispMessage("Branch %d saved.", 0, slot);
history.RegisterBookmarkSet(slot, backup_copy, old_current_branch);
project.SetProjectChanged();
must_check_item_under_mouse = true;
FCEU_DispMessage("Branch %d saved.", 0, slot);
}
}
void BOOKMARKS::jump(int slot)
@ -294,7 +289,7 @@ void BOOKMARKS::deploy(int slot)
if (!bookmarks_array[slot].not_empty) return;
int jump_frame = bookmarks_array[slot].snapshot.jump_frame;
int old_current_branch = branches.GetCurrentBranch();
bool markers_changed = false;
// revert current movie to the snapshot state
if (taseditor_config.branch_full_movie)
@ -317,13 +312,13 @@ void BOOKMARKS::deploy(int slot)
bookmarks_array[slot].snapshot.toMovie(currMovieData, first_change);
piano_roll.UpdateItemCount();
selection.must_find_current_marker = playback.must_find_current_marker = true;
history.RegisterBranching(MODTYPE_BRANCH_0 + slot, first_change, slot);
history.RegisterBranching(MODTYPE_BRANCH_0 + slot, first_change, slot, old_current_branch);
greenzone.Invalidate(first_change);
bookmarks_array[slot].deployed();
} else if (markers_changed)
{
selection.must_find_current_marker = playback.must_find_current_marker = true;
history.RegisterBranching(MODTYPE_BRANCH_MARKERS_0 + slot, first_change, slot);
history.RegisterBranching(MODTYPE_BRANCH_MARKERS_0 + slot, first_change, slot, old_current_branch);
piano_roll.RedrawList();
bookmarks_array[slot].deployed();
} else
@ -352,13 +347,13 @@ void BOOKMARKS::deploy(int slot)
bookmarks_array[slot].snapshot.toMovie(currMovieData, first_change, jump_frame-1);
piano_roll.UpdateItemCount();
selection.must_find_current_marker = playback.must_find_current_marker = true;
history.RegisterBranching(MODTYPE_BRANCH_0 + slot, first_change, slot);
history.RegisterBranching(MODTYPE_BRANCH_0 + slot, first_change, slot, old_current_branch);
greenzone.Invalidate(first_change);
bookmarks_array[slot].deployed();
} else if (markers_changed)
{
selection.must_find_current_marker = playback.must_find_current_marker = true;
history.RegisterBranching(MODTYPE_BRANCH_MARKERS_0 + slot, first_change, slot);
history.RegisterBranching(MODTYPE_BRANCH_MARKERS_0 + slot, first_change, slot, old_current_branch);
piano_roll.RedrawList();
bookmarks_array[slot].deployed();
} else
@ -369,24 +364,20 @@ void BOOKMARKS::deploy(int slot)
}
// if greenzone reduced so much that we can't jump immediately - substitute target frame greenzone with our savestate
if (greenzone.greenZoneCount <= jump_frame || greenzone.savestates[jump_frame].empty())
if (greenzone.greenZoneCount <= jump_frame)
{
if ((int)greenzone.savestates.size() <= jump_frame)
greenzone.savestates.resize(jump_frame+1);
// clear old savestates: from current end of greenzone to new end of greenzone
if (greenzone.greenZoneCount <= jump_frame)
{
for (int i = greenzone.greenZoneCount; i < jump_frame; ++i)
greenzone.ClearSavestate(i);
greenzone.greenZoneCount = jump_frame+1;
}
for (int i = greenzone.greenZoneCount; i <= jump_frame; ++i)
greenzone.ClearSavestate(i);
greenzone.greenZoneCount = jump_frame+1;
}
if (greenzone.savestates[jump_frame].empty())
// restore savestate for immediate jump
greenzone.savestates[jump_frame] = bookmarks_array[slot].savestate;
}
greenzone.update();
// 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)
{
@ -419,8 +410,14 @@ void BOOKMARKS::save(EMUFILE *os, bool really_save)
}
}
// returns true if couldn't load
bool BOOKMARKS::load(EMUFILE *is)
bool BOOKMARKS::load(EMUFILE *is, bool really_load)
{
if (!really_load)
{
reset();
branches.reset();
return false;
}
// read "BOOKMARKS" string
char save_id[BOOKMARKS_ID_LEN];
if ((int)is->fread(save_id, BOOKMARKS_ID_LEN) < BOOKMARKS_ID_LEN) goto error;
@ -474,22 +471,26 @@ void BOOKMARKS::RedrawBookmarksCaption()
must_check_item_under_mouse = true;
SetWindowText(hwndBookmarks, bookmarksCaption[edit_mode]);
}
void BOOKMARKS::RedrawBookmarksList()
void BOOKMARKS::RedrawBookmarksList(bool erase_bg)
{
if (edit_mode != EDIT_MODE_BRANCHES)
InvalidateRect(hwndBookmarksList, 0, FALSE);
InvalidateRect(hwndBookmarksList, 0, erase_bg);
}
void BOOKMARKS::RedrawChangedBookmarks(int frame)
{
for (int i = 0; i < TOTAL_BOOKMARKS; ++i)
{
if (bookmarks_array[i].snapshot.jump_frame == frame)
RedrawBookmarksRow((i + TOTAL_BOOKMARKS - 1) % TOTAL_BOOKMARKS);
RedrawBookmark(i);
}
}
void BOOKMARKS::RedrawBookmarksRow(int index)
void BOOKMARKS::RedrawBookmark(int bookmark_number)
{
ListView_RedrawItems(hwndBookmarksList, index, index);
RedrawListRow((bookmark_number + TOTAL_BOOKMARKS - 1) % TOTAL_BOOKMARKS);
}
void BOOKMARKS::RedrawListRow(int row_index)
{
ListView_RedrawItems(hwndBookmarksList, row_index, row_index);
}
void BOOKMARKS::MouseMove(int new_x, int new_y)
@ -578,7 +579,7 @@ LONG BOOKMARKS::CustomDraw(NMLVCUSTOMDRAW* msg)
{
if (!greenzone.savestates[frame].empty())
{
if (taseditor_config.show_lag_frames && greenzone.lag_history[frame])
if (taseditor_config.show_lag_frames && greenzone.GetLagHistoryAtFrame(frame))
msg->clrTextBk = LAG_FRAMENUM_COLOR;
else
msg->clrTextBk = GREENZONE_FRAMENUM_COLOR;
@ -587,7 +588,7 @@ LONG BOOKMARKS::CustomDraw(NMLVCUSTOMDRAW* msg)
|| (!greenzone.savestates[frame & EVERY4TH].empty() && (int)greenzone.savestates.size() > (frame | 0x3) + 1 && !greenzone.savestates[(frame | 0x3) + 1].empty())
|| (!greenzone.savestates[frame & EVERY2ND].empty() && !greenzone.savestates[(frame | 0x1) + 1].empty()))
{
if (taseditor_config.show_lag_frames && greenzone.lag_history[frame])
if (taseditor_config.show_lag_frames && greenzone.GetLagHistoryAtFrame(frame))
msg->clrTextBk = PALE_LAG_FRAMENUM_COLOR;
else
msg->clrTextBk = PALE_GREENZONE_FRAMENUM_COLOR;
@ -609,7 +610,7 @@ LONG BOOKMARKS::CustomDraw(NMLVCUSTOMDRAW* msg)
{
if (!greenzone.savestates[frame].empty())
{
if (taseditor_config.show_lag_frames && greenzone.lag_history[frame])
if (taseditor_config.show_lag_frames && greenzone.GetLagHistoryAtFrame(frame))
msg->clrTextBk = LAG_INPUT_COLOR1;
else
msg->clrTextBk = GREENZONE_INPUT_COLOR1;
@ -618,7 +619,7 @@ LONG BOOKMARKS::CustomDraw(NMLVCUSTOMDRAW* msg)
|| (!greenzone.savestates[frame & EVERY4TH].empty() && (int)greenzone.savestates.size() > (frame | 0x3) + 1 && !greenzone.savestates[(frame | 0x3) + 1].empty())
|| (!greenzone.savestates[frame & EVERY2ND].empty() && !greenzone.savestates[(frame | 0x1) + 1].empty()))
{
if (taseditor_config.show_lag_frames && greenzone.lag_history[frame])
if (taseditor_config.show_lag_frames && greenzone.GetLagHistoryAtFrame(frame))
msg->clrTextBk = PALE_LAG_INPUT_COLOR1;
else
msg->clrTextBk = PALE_GREENZONE_INPUT_COLOR1;
@ -651,7 +652,8 @@ int BOOKMARKS::FindBookmarkAtFrame(int frame)
return current_branch + TOTAL_BOOKMARKS; // blue digit has highest priority when drawing
for (int i = 0; i < TOTAL_BOOKMARKS; ++i)
{
if (bookmarks_array[i].snapshot.jump_frame == frame) return i; // green digit
if (bookmarks_array[i].not_empty && bookmarks_array[i].snapshot.jump_frame == frame)
return i; // green digit
}
return -1; // no Bookmarks at the frame
}

View File

@ -54,7 +54,7 @@ public:
void update();
void save(EMUFILE *os, bool really_save = true);
bool load(EMUFILE *is);
bool load(EMUFILE *is, bool really_load = true);
void command(int command_id, int slot = -1);
@ -66,9 +66,10 @@ public:
int FindBookmarkAtFrame(int frame);
void RedrawBookmarksCaption();
void RedrawBookmarksList();
void RedrawBookmarksList(bool erase_bg = false);
void RedrawChangedBookmarks(int frame);
void RedrawBookmarksRow(int index);
void RedrawBookmark(int bookmark_number);
void RedrawListRow(int row_index);
void MouseMove(int new_x, int new_y);
void FindItemUnderMouse();

View File

@ -11,18 +11,19 @@ Branches - Manager of Branches
[Singleton]
* stores info about Branches (relations of Bookmarks) and the id of current Branch
* also stores the time of the last modification (see fireball) and the time of the root of Branches Tree (see cloudlet)
* finds best place for every new Bookmark in the Branches Tree
* also stores the time of the last modification (see fireball) and the time of project beginning (see cloudlet)
* also caches data used in calculations (cached_first_difference, cached_timelines)
* saves and loads the data from a project file. On error: sends warning to caller
* implements the working of Branches Tree: creating, recalculating relations, animating, redrawing, mouseover
* on demand: reacts on project changes and recalculates Branches Tree
* regularly updates animations in Branches Tree
* on demand: reacts on Bookmarks/current Movie changes and recalculates the Branches Tree
* regularly updates animations in Branches Tree and calculates Playback cursor position on the Tree
* stores resources: coordinates for building Branches Tree, animation timings
------------------------------------------------------------------------------------ */
#include "taseditor_project.h"
#include "utils/xstring.h"
#include "zlib.h"
#include <math.h>
#pragma comment(lib, "msimg32.lib")
@ -81,6 +82,7 @@ void BRANCHES::init()
hOldBitmap2 = (HBITMAP)SelectObject(hSpritesheetDC, branchesSpritesheet);
// create pens
normal_pen = CreatePen(PS_SOLID, 1, 0x0);
timeline_pen = CreatePen(PS_SOLID, 1, 0x0020E0);
select_pen = CreatePen(PS_SOLID, 2, 0xFF9080);
reset();
@ -91,6 +93,8 @@ void BRANCHES::init()
void BRANCHES::free()
{
parents.resize(0);
cached_first_difference.resize(0);
cached_timelines.resize(0);
BranchX.resize(0);
BranchY.resize(0);
BranchPrevX.resize(0);
@ -131,6 +135,21 @@ void BRANCHES::free()
DeleteObject(branchesSpritesheet);
branchesSpritesheet = NULL;
}
if (normal_pen)
{
DeleteObject(normal_pen);
normal_pen = 0;
}
if (timeline_pen)
{
DeleteObject(normal_pen);
timeline_pen = 0;
}
if (select_pen)
{
DeleteObject(normal_pen);
select_pen = 0;
}
}
void BRANCHES::reset()
{
@ -138,14 +157,24 @@ void BRANCHES::reset()
for (int i = TOTAL_BOOKMARKS-1; i >= 0; i--)
parents[i] = ITEM_UNDER_MOUSE_CLOUD;
cached_timelines.resize(TOTAL_BOOKMARKS);
cached_first_difference.resize(TOTAL_BOOKMARKS);
for (int i = TOTAL_BOOKMARKS-1; i >= 0; i--)
{
cached_timelines[i] = ITEM_UNDER_MOUSE_CLOUD;
cached_first_difference[i].resize(TOTAL_BOOKMARKS);
for (int t = TOTAL_BOOKMARKS-1; t >= 0; t--)
cached_first_difference[i][t] = FIRST_DIFFERENCE_UNKNOWN;
}
// set positions of slots to default coordinates
for (int i = TOTAL_BOOKMARKS; i >= 0; i--)
{
BranchX[i] = BranchPrevX[i] = BranchCurrX[i] = EMPTY_BRANCHES_X;
BranchY[i] = BranchPrevY[i] = BranchCurrY[i] = EMPTY_BRANCHES_Y_BASE + EMPTY_BRANCHES_Y_FACTOR * ((i + TOTAL_BOOKMARKS - 1) % TOTAL_BOOKMARKS);
}
CursorX = CursorPrevX = CloudX = CloudPrevX = BRANCHES_CLOUD_X;
CursorY = CursorPrevY = BRANCHES_CLOUD_Y;
CloudX = CloudPrevX = BRANCHES_CLOUD_X;
cursor_x = cursor_y = 0;
reset_vars();
@ -160,6 +189,7 @@ void BRANCHES::reset()
void BRANCHES::reset_vars()
{
transition_phase = animation_frame = 0;
playback_x = playback_y = -50;
must_recalculate_branches_tree = must_redraw_branches_tree = true;
next_animation_time = clock() + BRANCHES_ANIMATION_TICK;
}
@ -205,6 +235,92 @@ void BRANCHES::update()
// just update sprites
InvalidateRect(bookmarks.hwndBranchesBitmap, 0, FALSE);
}
// calculate playback position
int branch, branch_x, branch_y, parent, parent_x, parent_y, upper_frame, lower_frame;
double distance;
if (current_branch != ITEM_UNDER_MOUSE_CLOUD)
{
if (changes_since_current_branch)
parent = TOTAL_BOOKMARKS;
else
parent = FindFullTimelineForBranch(current_branch);
do
{
branch = parent;
if (branch == TOTAL_BOOKMARKS)
parent = current_branch;
else
parent = parents[branch];
if (parent == ITEM_UNDER_MOUSE_CLOUD)
lower_frame = -1;
else
lower_frame = bookmarks.bookmarks_array[parent].snapshot.jump_frame;
} 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;
branch_x = BranchCurrX[branch];
branch_y = BranchCurrY[branch];
if (parent == ITEM_UNDER_MOUSE_CLOUD)
{
parent_x = cloud_x;
parent_y = BRANCHES_CLOUD_Y;
} else
{
parent_x = BranchCurrX[parent];
parent_y = BranchCurrY[parent];
}
if (upper_frame != lower_frame)
distance = (double)(currFrameCounter - lower_frame) / (double)(upper_frame - lower_frame);
else
distance = 0;
if (distance > 1.0) distance = 1.0;
playback_x = parent_x + distance * (branch_x - parent_x);
playback_y = parent_y + distance * (branch_y - parent_y);
} else
{
if (changes_since_current_branch)
{
// special case: there's only cloud + fireball
upper_frame = currMovieData.getNumRecords() - 1;
lower_frame = 0;
parent_x = cloud_x;
parent_y = BRANCHES_CLOUD_Y;
branch_x = BranchCurrX[TOTAL_BOOKMARKS];
branch_y = BranchCurrY[TOTAL_BOOKMARKS];
if (upper_frame != lower_frame)
distance = (double)(currFrameCounter - lower_frame) / (double)(upper_frame - lower_frame);
else
distance = 0;
if (distance > 1.0) distance = 1.0;
playback_x = parent_x + distance * (branch_x - parent_x);
playback_y = parent_y + distance * (branch_y - parent_y);
} else
{
// special case: there's only cloud
playback_x = cloud_x;
playback_y = BRANCHES_CLOUD_Y;
}
}
// move cursor to playback position
double dx = playback_x - cursor_x;
double dy = playback_y - cursor_y;
distance = sqrt(dx*dx + dy*dy);
if (distance < CURSOR_MIN_DISTANCE || distance > CURSOR_MAX_DISTANCE)
{
// teleport
cursor_x = playback_x;
cursor_y = playback_y;
} else
{
// advance
double speed = sqrt(distance);
if (speed < CURSOR_MIN_SPEED)
speed = CURSOR_MIN_SPEED;
cursor_x += dx * speed / distance;
cursor_y += dy * speed / distance;
}
if (must_redraw_branches_tree)
RedrawBranchesTree();
}
@ -226,6 +342,12 @@ void BRANCHES::save(EMUFILE *os)
// write all 10 parents
for (int i = 0; i < TOTAL_BOOKMARKS; ++i)
write32le(parents[i], os);
// write cached_timelines
os->fwrite(&cached_timelines[0], TOTAL_BOOKMARKS);
// write cached_first_difference
for (int i = 0; i < TOTAL_BOOKMARKS; ++i)
for (int t = 0; t < TOTAL_BOOKMARKS; ++t)
write32le(cached_first_difference[i][t], os);
}
// returns true if couldn't load
bool BRANCHES::load(EMUFILE *is)
@ -242,6 +364,12 @@ bool BRANCHES::load(EMUFILE *is)
// read all 10 parents
for (int i = 0; i < TOTAL_BOOKMARKS; ++i)
if (!read32le(&parents[i], is)) goto error;
// read cached_timelines
if ((int)is->fread(&cached_timelines[0], TOTAL_BOOKMARKS) < TOTAL_BOOKMARKS) goto error;
// read cached_first_difference
for (int i = 0; i < TOTAL_BOOKMARKS; ++i)
for (int t = 0; t < TOTAL_BOOKMARKS; ++t)
if (!read32le(&cached_first_difference[i][t], is)) goto error;
// all ok
reset_vars();
return false;
@ -272,7 +400,7 @@ void BRANCHES::RedrawBranchesTree()
GradientFill(hBitmapDC, vertex, 2, &gRect, 1, GRADIENT_FILL_RECT_H);
// lines
int branch_x, branch_y, parent_x, parent_y, child_id;
int branch, branch_x, branch_y, parent_x, parent_y, child_id;
SelectObject(hBitmapDC, normal_pen);
for (int t = Children.size() - 1; t >= 0; t--)
{
@ -295,40 +423,72 @@ void BRANCHES::RedrawBranchesTree()
}
}
}
// lines for item under mouse
SelectObject(hBitmapDC, select_pen);
int branch = bookmarks.item_under_mouse;
if (branch == TOTAL_BOOKMARKS)
branch = current_branch;
while (branch >= 0)
// lines for current timeline
if (current_branch != ITEM_UNDER_MOUSE_CLOUD)
{
branch_x = BranchCurrX[branch];
branch_y = BranchCurrY[branch];
MoveToEx(hBitmapDC, branch_x, branch_y, 0);
branch = parents[branch];
if (branch >= 0)
SelectObject(hBitmapDC, timeline_pen);
if (changes_since_current_branch)
branch = current_branch;
else
branch = FindFullTimelineForBranch(current_branch);
while (branch >= 0)
{
branch_x = BranchCurrX[branch];
branch_x = BranchCurrX[branch];
branch_y = BranchCurrY[branch];
} else
{
branch_x = cloud_x;
branch_y = BRANCHES_CLOUD_Y;
MoveToEx(hBitmapDC, branch_x, branch_y, 0);
branch = parents[branch];
if (branch == ITEM_UNDER_MOUSE_CLOUD)
{
branch_x = cloud_x;
branch_y = BRANCHES_CLOUD_Y;
} else
{
branch_x = BranchCurrX[branch];
branch_y = BranchCurrY[branch];
}
LineTo(hBitmapDC, branch_x, branch_y);
}
}
// lines for item under mouse
if (bookmarks.item_under_mouse >= 0 && bookmarks.item_under_mouse <= TOTAL_BOOKMARKS)
{
SelectObject(hBitmapDC, select_pen);
if (bookmarks.item_under_mouse == TOTAL_BOOKMARKS)
branch = current_branch;
else
branch = FindFullTimelineForBranch(bookmarks.item_under_mouse);
while (branch >= 0)
{
branch_x = BranchCurrX[branch];
branch_y = BranchCurrY[branch];
MoveToEx(hBitmapDC, branch_x, branch_y, 0);
branch = parents[branch];
if (branch == ITEM_UNDER_MOUSE_CLOUD)
{
branch_x = cloud_x;
branch_y = BRANCHES_CLOUD_Y;
} else
{
branch_x = BranchCurrX[branch];
branch_y = BranchCurrY[branch];
}
LineTo(hBitmapDC, branch_x, branch_y);
}
LineTo(hBitmapDC, branch_x, branch_y);
}
if (changes_since_current_branch)
{
if (bookmarks.item_under_mouse != TOTAL_BOOKMARKS)
SelectObject(hBitmapDC, normal_pen);
if (current_branch >= 0)
{
parent_x = BranchCurrX[current_branch];
parent_y = BranchCurrY[current_branch];
} else
if (bookmarks.item_under_mouse == TOTAL_BOOKMARKS)
SelectObject(hBitmapDC, select_pen);
else
SelectObject(hBitmapDC, timeline_pen);
if (current_branch == ITEM_UNDER_MOUSE_CLOUD)
{
parent_x = cloud_x;
parent_y = BRANCHES_CLOUD_Y;
} else
{
parent_x = BranchCurrX[current_branch];
parent_y = BranchCurrY[current_branch];
}
MoveToEx(hBitmapDC, parent_x, parent_y, 0);
branch_x = BranchCurrX[TOTAL_BOOKMARKS];
@ -440,26 +600,27 @@ void BRANCHES::PaintBranchesBitmap(HDC hdc)
TransparentBlt(hBufferDC, branch_x, branch_y, BRANCHES_FIREBALL_WIDTH, BRANCHES_FIREBALL_HEIGHT, hSpritesheetDC, BRANCHES_FIREBALL_SPRITESHEET_END_X - fireball_size * BRANCHES_FIREBALL_WIDTH, BRANCHES_FIREBALL_SPRITESHEET_Y, BRANCHES_FIREBALL_WIDTH, BRANCHES_FIREBALL_HEIGHT, 0x00FF00);
}
}
// blinking Playback cursor point
if (animation_frame & 1)
TransparentBlt(hBufferDC, playback_x - BRANCHES_MINIARROW_HALFWIDTH, playback_y - BRANCHES_MINIARROW_HALFHEIGHT, BRANCHES_MINIARROW_WIDTH, BRANCHES_MINIARROW_HEIGHT, hSpritesheetDC, BRANCHES_MINIARROW_SPRITESHEET_X, BRANCHES_MINIARROW_SPRITESHEET_Y, BRANCHES_MINIARROW_WIDTH, BRANCHES_MINIARROW_HEIGHT, 0x00FF00);
// corners cursor
branch_x = (CursorX * (BRANCHES_TRANSITION_MAX - transition_phase) + CursorPrevX * transition_phase) / BRANCHES_TRANSITION_MAX;
branch_y = (CursorY * (BRANCHES_TRANSITION_MAX - transition_phase) + CursorPrevY * transition_phase) / BRANCHES_TRANSITION_MAX;
int current_corners_cursor_shift = BRANCHES_CORNER_BASE_SHIFT + corners_cursor_shift[animation_frame];
int corner_x, corner_y;
// upper left
corner_x = branch_x - current_corners_cursor_shift - BRANCHES_CORNER_HALFWIDTH;
corner_y = branch_y - current_corners_cursor_shift - BRANCHES_CORNER_HALFHEIGHT;
corner_x = cursor_x - current_corners_cursor_shift - BRANCHES_CORNER_HALFWIDTH;
corner_y = cursor_y - current_corners_cursor_shift - BRANCHES_CORNER_HALFHEIGHT;
TransparentBlt(hBufferDC, corner_x, corner_y, BRANCHES_CORNER_WIDTH, BRANCHES_CORNER_HEIGHT, hSpritesheetDC, BRANCHES_CORNER1_SPRITESHEET_X, BRANCHES_CORNER1_SPRITESHEET_Y, BRANCHES_CORNER_WIDTH, BRANCHES_CORNER_HEIGHT, 0x00FF00);
// upper right
corner_x = branch_x + current_corners_cursor_shift - BRANCHES_CORNER_HALFWIDTH;
corner_y = branch_y - current_corners_cursor_shift - BRANCHES_CORNER_HALFHEIGHT;
corner_x = cursor_x + current_corners_cursor_shift - BRANCHES_CORNER_HALFWIDTH;
corner_y = cursor_y - current_corners_cursor_shift - BRANCHES_CORNER_HALFHEIGHT;
TransparentBlt(hBufferDC, corner_x, corner_y, BRANCHES_CORNER_WIDTH, BRANCHES_CORNER_HEIGHT, hSpritesheetDC, BRANCHES_CORNER2_SPRITESHEET_X, BRANCHES_CORNER2_SPRITESHEET_Y, BRANCHES_CORNER_WIDTH, BRANCHES_CORNER_HEIGHT, 0x00FF00);
// lower left
corner_x = branch_x - current_corners_cursor_shift - BRANCHES_CORNER_HALFWIDTH;
corner_y = branch_y + current_corners_cursor_shift - BRANCHES_CORNER_HALFHEIGHT;
corner_x = cursor_x - current_corners_cursor_shift - BRANCHES_CORNER_HALFWIDTH;
corner_y = cursor_y + current_corners_cursor_shift - BRANCHES_CORNER_HALFHEIGHT;
TransparentBlt(hBufferDC, corner_x, corner_y, BRANCHES_CORNER_WIDTH, BRANCHES_CORNER_HEIGHT, hSpritesheetDC, BRANCHES_CORNER3_SPRITESHEET_X, BRANCHES_CORNER3_SPRITESHEET_Y, BRANCHES_CORNER_WIDTH, BRANCHES_CORNER_HEIGHT, 0x00FF00);
// lower right
corner_x = branch_x + current_corners_cursor_shift - BRANCHES_CORNER_HALFWIDTH;
corner_y = branch_y + current_corners_cursor_shift - BRANCHES_CORNER_HALFHEIGHT;
corner_x = cursor_x + current_corners_cursor_shift - BRANCHES_CORNER_HALFWIDTH;
corner_y = cursor_y + current_corners_cursor_shift - BRANCHES_CORNER_HALFHEIGHT;
TransparentBlt(hBufferDC, corner_x, corner_y, BRANCHES_CORNER_WIDTH, BRANCHES_CORNER_HEIGHT, hSpritesheetDC, BRANCHES_CORNER4_SPRITESHEET_X, BRANCHES_CORNER4_SPRITESHEET_Y, BRANCHES_CORNER_WIDTH, BRANCHES_CORNER_HEIGHT, 0x00FF00);
// finish - paste buffer bitmap to screen
BitBlt(hdc, 0, 0, BRANCHES_BITMAP_WIDTH, BRANCHES_BITMAP_HEIGHT, hBufferDC, 0, 0, SRCCOPY);
@ -469,104 +630,19 @@ int BRANCHES::GetCurrentBranch()
{
return current_branch;
}
// this function finds best place for a new Bookmark in the Branches Tree
void BRANCHES::HandleBookmarkSet(int slot, char* slot_time)
bool BRANCHES::GetChangesSinceCurrentBranch()
{
if (slot != current_branch)
{
// inherit current branch, forget previous parent
int parent = parents[slot];
if (parent == ITEM_UNDER_MOUSE_CLOUD && slot_time[0])
{
// check if this was the only child of cloud parent, if so then set cloud time to the saved_time
int i = 0;
for (; i < TOTAL_BOOKMARKS; ++i)
{
if (bookmarks.bookmarks_array[i].not_empty && parents[i] == ITEM_UNDER_MOUSE_CLOUD && i != slot)
break;
}
if (i >= TOTAL_BOOKMARKS)
// didn't find another child of cloud, so after this slot disconnects, cloud will have 0 children
// it will mean that old cloud disappears and new cloud is formed where the slot was
strcpy(cloud_time, slot_time);
}
// before disconnecting from old parent, connect all childs to the old parent
for (int i = 0; i < TOTAL_BOOKMARKS; ++i)
{
if (bookmarks.bookmarks_array[i].not_empty && parents[i] == slot)
parents[i] = parent;
}
parents[slot] = current_branch;
}
return changes_since_current_branch;
}
// if parent is invalid (first_change < parent.jump_frame) then find better parent
int factor = 0;
// also if parent == cloud, then try to find better parent
int parent = parents[slot];
if (parent != ITEM_UNDER_MOUSE_CLOUD)
factor = bookmarks.bookmarks_array[slot].snapshot.findFirstChange(bookmarks.bookmarks_array[parent].snapshot);
if (parent == ITEM_UNDER_MOUSE_CLOUD || (factor >= 0 && factor < bookmarks.bookmarks_array[parent].snapshot.jump_frame))
{
// find highest frame of change
std::vector<int> DecisiveFactor(TOTAL_BOOKMARKS);
int best_branch = ITEM_UNDER_MOUSE_CLOUD;
for (int i = TOTAL_BOOKMARKS-1; i >= 0; i--)
{
if (i != slot && i != parent && bookmarks.bookmarks_array[i].not_empty && bookmarks.bookmarks_array[slot].snapshot.size >= bookmarks.bookmarks_array[i].snapshot.jump_frame)
{
factor = bookmarks.bookmarks_array[slot].snapshot.findFirstChange(bookmarks.bookmarks_array[i].snapshot);
if (factor < 0)
{
// this branch is identical to this slot
DecisiveFactor[i] = 2 * bookmarks.bookmarks_array[i].snapshot.size;
} else if (factor >= bookmarks.bookmarks_array[i].snapshot.jump_frame)
{
// hey, this branch could be our new parent...
DecisiveFactor[i] = 2 * factor;
} else
DecisiveFactor[i] = 0;
} else
{
DecisiveFactor[i] = 0;
}
}
// add +1 as a bonus to current parents and grandparents (a bit of nepotism here)
while (parent >= 0)
{
if (DecisiveFactor[parent])
DecisiveFactor[parent]++;
parent = parents[parent];
}
// find max
factor = 0;
for (int i = TOTAL_BOOKMARKS-1; i >= 0; i--)
{
if (DecisiveFactor[i] && DecisiveFactor[i] > factor)
{
factor = DecisiveFactor[i];
best_branch = i;
}
}
parent = parents[slot];
if (parent != best_branch)
{
// before disconnecting from old parent, connect all childs to the old parent
for (int i = 0; i < TOTAL_BOOKMARKS; ++i)
{
if (bookmarks.bookmarks_array[i].not_empty && parents[i] == slot)
parents[i] = parent;
}
// found new parent
parents[slot] = best_branch;
must_recalculate_branches_tree = true;
}
}
// switch current branch to this branch
if (slot != current_branch || changes_since_current_branch)
must_recalculate_branches_tree = true;
void BRANCHES::HandleBookmarkSet(int slot)
{
// new Branch is written into the slot
InvalidateBranchSlot(slot);
RecalculateParents();
current_branch = slot;
changes_since_current_branch = false;
must_recalculate_branches_tree = true;
}
void BRANCHES::HandleBookmarkDeploy(int slot)
{
@ -574,6 +650,101 @@ void BRANCHES::HandleBookmarkDeploy(int slot)
changes_since_current_branch = false;
must_recalculate_branches_tree = true;
}
void BRANCHES::HandleHistoryJump(int new_current_branch, bool new_changes_since_current_branch)
{
RecalculateParents();
current_branch = new_current_branch;
changes_since_current_branch = new_changes_since_current_branch;
if (new_changes_since_current_branch)
SetCurrentPosTime();
must_recalculate_branches_tree = true;
}
void BRANCHES::InvalidateBranchSlot(int slot)
{
for (int i = TOTAL_BOOKMARKS-1; i >= 0; i--)
{
cached_timelines[i] = ITEM_UNDER_MOUSE_CLOUD;
cached_first_difference[i][slot] = FIRST_DIFFERENCE_UNKNOWN;
cached_first_difference[slot][i] = FIRST_DIFFERENCE_UNKNOWN;
parents[i] = ITEM_UNDER_MOUSE_CLOUD;
}
}
// returns the frame of first difference between 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;
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);
if (frame < 0)
frame = bookmarks.bookmarks_array[first_branch].snapshot.size;
cached_first_difference[first_branch][second_branch] = frame;
cached_first_difference[second_branch][first_branch] = frame;
return frame;
} else return 0;
} else
return cached_first_difference[first_branch][second_branch];
}
int BRANCHES::FindFullTimelineForBranch(int branch_num)
{
if (cached_timelines[branch_num] == ITEM_UNDER_MOUSE_CLOUD)
{
cached_timelines[branch_num] = branch_num; // by default
std::vector<int> candidates;
int temp_jump_frame, temp_parent, max_jump_frame, max_first_difference;
// 1 - find max_first_difference among Branches that are in the same timeline
max_first_difference = -1;
int first_diff;
for (int i = TOTAL_BOOKMARKS-1; i >= 0; i--)
{
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 (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;
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)
{
// ensure that this candidate belongs to children/grandchildren of current_branch
temp_parent = parents[i];
while (temp_parent != ITEM_UNDER_MOUSE_CLOUD && temp_parent != branch_num)
temp_parent = parents[temp_parent];
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;
}
}
}
}
// 3 - remove those candidates who have jump_frame < max_jump_frame
for (int i = candidates.size()-1; i >= 0; i--)
{
if (bookmarks.bookmarks_array[candidates[i]].snapshot.jump_frame < max_jump_frame)
candidates.erase(candidates.begin() + i);
}
// 4 - get first of candidates (if there are many then it will be the Branch with highest id number)
if (candidates.size())
cached_timelines[branch_num] = candidates[0];
}
return cached_timelines[branch_num];
}
void BRANCHES::ChangesMadeSinceBranch()
{
@ -592,7 +763,7 @@ void BRANCHES::FindItemUnderMouse(int mouse_x, int mouse_y)
int prev_item_under_mouse = bookmarks.item_under_mouse;
bookmarks.item_under_mouse = ITEM_UNDER_MOUSE_NONE;
for (int i = 0; i < TOTAL_BOOKMARKS; ++i)
if (bookmarks.item_under_mouse == ITEM_UNDER_MOUSE_NONE && mouse_x >= BranchCurrX[i] - DIGIT_RECT_HALFWIDTH_COLLISION && mouse_x < BranchCurrX[i] - DIGIT_RECT_HALFWIDTH_COLLISION + DIGIT_RECT_WIDTH_COLLISION && mouse_y >= BranchCurrY[i] - DIGIT_RECT_HALFHEIGHT_COLLISION && mouse_y < BranchCurrY[i] - DIGIT_RECT_HALFHEIGHT_COLLISION + DIGIT_RECT_HEIGHT_COLLISION)
if (bookmarks.item_under_mouse == ITEM_UNDER_MOUSE_NONE && bookmarks.bookmarks_array[i].not_empty && mouse_x >= BranchCurrX[i] - DIGIT_RECT_HALFWIDTH_COLLISION && mouse_x < BranchCurrX[i] - DIGIT_RECT_HALFWIDTH_COLLISION + DIGIT_RECT_WIDTH_COLLISION && mouse_y >= BranchCurrY[i] - DIGIT_RECT_HALFHEIGHT_COLLISION && mouse_y < BranchCurrY[i] - DIGIT_RECT_HALFHEIGHT_COLLISION + DIGIT_RECT_HEIGHT_COLLISION)
bookmarks.item_under_mouse = i;
if (bookmarks.item_under_mouse == ITEM_UNDER_MOUSE_NONE && mouse_x >= cloud_x - BRANCHES_CLOUD_HALFWIDTH && mouse_x < cloud_x - BRANCHES_CLOUD_HALFWIDTH + BRANCHES_CLOUD_WIDTH && mouse_y >= BRANCHES_CLOUD_Y - BRANCHES_CLOUD_HALFHEIGHT && mouse_y < BRANCHES_CLOUD_Y - BRANCHES_CLOUD_HALFHEIGHT + BRANCHES_CLOUD_HEIGHT)
bookmarks.item_under_mouse = ITEM_UNDER_MOUSE_CLOUD;
@ -610,6 +781,62 @@ void BRANCHES::SetCurrentPosTime()
strftime(current_pos_time, TIME_DESC_LENGTH, "%H:%M:%S", timeinfo);
}
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;
for (int i = TOTAL_BOOKMARKS-1; i >= 0; i--)
{
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
candidates.resize(0);
max_jump_frame = -1;
for (int t = TOTAL_BOOKMARKS-1; t >= 0; t--)
{
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)
{
// ensure that this candidate doesn't belong to children/grandchildren of this Branch
temp_parent = parents[t];
while (temp_parent != ITEM_UNDER_MOUSE_CLOUD && temp_parent != i)
temp_parent = parents[temp_parent];
if (temp_parent == ITEM_UNDER_MOUSE_CLOUD)
{
// all ok, this is a 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 (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
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)
candidates.erase(candidates.begin() + t);
else if (max_first_difference < GetFirstDifference(candidates[t], i))
max_first_difference = GetFirstDifference(candidates[t], i);
}
// 3 - remove those candidates who have FirstDifference < max_first_difference
for (int t = candidates.size()-1; t >= 0; t--)
{
if (GetFirstDifference(candidates[t], i) < max_first_difference)
candidates.erase(candidates.begin() + t);
}
// 4 - get first of candidates (if there are many then it will be the Branch with highest id number)
if (candidates.size())
parents[i] = candidates[0];
}
}
}
}
void BRANCHES::RecalculateBranchesTree()
{
// save previous values
@ -619,8 +846,6 @@ void BRANCHES::RecalculateBranchesTree()
BranchPrevY[i] = (BranchY[i] * (BRANCHES_TRANSITION_MAX - transition_phase) + BranchPrevY[i] * transition_phase) / BRANCHES_TRANSITION_MAX;
}
CloudPrevX = (CloudX * (BRANCHES_TRANSITION_MAX - transition_phase) + CloudPrevX * transition_phase) / BRANCHES_TRANSITION_MAX;
CursorPrevX = (CursorX * (BRANCHES_TRANSITION_MAX - transition_phase) + CursorPrevX * transition_phase) / BRANCHES_TRANSITION_MAX;
CursorPrevY = (CursorY * (BRANCHES_TRANSITION_MAX - transition_phase) + CursorPrevY * transition_phase) / BRANCHES_TRANSITION_MAX;
transition_phase = BRANCHES_TRANSITION_MAX;
// 0. Prepare arrays
@ -853,9 +1078,6 @@ void BRANCHES::RecalculateBranchesTree()
if (bookmarks.bookmarks_array[i].not_empty)
BranchX[i] += CloudX;
BranchX[TOTAL_BOOKMARKS] += CloudX;
// target cursor
CursorX = BranchX[TOTAL_BOOKMARKS];
CursorY = BranchY[TOTAL_BOOKMARKS];
// finished recalculating
must_recalculate_branches_tree = false;
must_redraw_branches_tree = true;
@ -887,6 +1109,7 @@ void BRANCHES::RecursiveSetYPos(int parent, int parentY)
}
}
}
// ----------------------------------------------------------------------------------------
LRESULT APIENTRY BranchesBitmapWndProc(HWND hWnd, UINT msg, WPARAM wParam, LPARAM lParam)
{

View File

@ -2,6 +2,9 @@
#define BRANCHES_ANIMATION_TICK 50 // animate at 20FPS
#define BRANCHES_TRANSITION_MAX 8
#define CURSOR_MIN_DISTANCE 1.0
#define CURSOR_MAX_DISTANCE 256.0
#define CURSOR_MIN_SPEED 1.0
// branches bitmap
#define BRANCHES_BITMAP_WIDTH 170
@ -70,7 +73,15 @@
#define BRANCHES_CORNER3_SPRITESHEET_Y 7
#define BRANCHES_CORNER4_SPRITESHEET_X 213
#define BRANCHES_CORNER4_SPRITESHEET_Y 7
#define BRANCHES_CORNER_BASE_SHIFT 6
#define BRANCHES_CORNER_BASE_SHIFT 5
#define BRANCHES_MINIARROW_SPRITESHEET_X 180
#define BRANCHES_MINIARROW_SPRITESHEET_Y 15
#define BRANCHES_MINIARROW_WIDTH 3
#define BRANCHES_MINIARROW_HALFWIDTH BRANCHES_MINIARROW_WIDTH/2
#define BRANCHES_MINIARROW_HEIGHT 5
#define BRANCHES_MINIARROW_HALFHEIGHT BRANCHES_MINIARROW_HEIGHT/2
#define FIRST_DIFFERENCE_UNKNOWN -2
class BRANCHES
{
@ -86,20 +97,28 @@ public:
bool load(EMUFILE *is);
int GetCurrentBranch();
bool GetChangesSinceCurrentBranch();
void RedrawBranchesTree();
void PaintBranchesBitmap(HDC hdc);
void HandleBookmarkSet(int slot, char* slot_time);
void HandleBookmarkSet(int slot);
void HandleBookmarkDeploy(int slot);
void HandleHistoryJump(int new_current_branch, bool new_changes_since_current_branch);
void InvalidateBranchSlot(int slot);
int GetFirstDifference(int first_branch, int second_branch);
int FindFullTimelineForBranch(int branch_num);
void ChangesMadeSinceBranch();
void FindItemUnderMouse(int mouse_x, int mouse_y);
void RecalculateParents();
void RecalculateBranchesTree();
void RecursiveAddHeight(int branch_num, int amount);
void RecursiveSetYPos(int parent, int parentY);
// saved vars
std::vector<int> parents;
@ -115,10 +134,14 @@ private:
bool changes_since_current_branch;
char cloud_time[TIME_DESC_LENGTH];
char current_pos_time[TIME_DESC_LENGTH];
std::vector<std::vector<int>> cached_first_difference;
std::vector<int8> cached_timelines; // stores id of the last branch on the timeline of every Branch. Sometimes it's the id of the Branch itself, but sometimes it's an id of its child/frandchild that shares the same input
// not saved vars
int animation_frame;
int next_animation_time;
int playback_x, playback_y;
double cursor_x, cursor_y;
std::vector<int> BranchX; // in pixels
std::vector<int> BranchY;
std::vector<int> BranchPrevX;
@ -126,14 +149,13 @@ private:
std::vector<int> BranchCurrX;
std::vector<int> BranchCurrY;
int CloudX, CloudPrevX, cloud_x;
int CursorX, CursorPrevX, CursorY, CursorPrevY;
int transition_phase;
int fireball_size;
// GDI stuff
HBRUSH normal_brush;
RECT temp_rect;
HPEN normal_pen, select_pen;
HPEN normal_pen, timeline_pen, select_pen;
HBITMAP branches_hbitmap, hOldBitmap, buffer_hbitmap, hOldBitmap1, branchesSpritesheet, hOldBitmap2;
HDC hBitmapDC, hBufferDC, hSpritesheetDC;

View File

@ -64,7 +64,7 @@ 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].empty())
if (greenZoneCount <= currFrameCounter || (int)savestates.size() <= currFrameCounter || savestates[currFrameCounter].empty() || (int)lag_history.size() <= currFrameCounter)
CollectCurrentState();
// run cleaning from time to time
@ -250,9 +250,15 @@ void GREENZONE::save(EMUFILE *os, bool really_save)
}
}
// returns true if couldn't load
bool GREENZONE::load(EMUFILE *is)
bool GREENZONE::load(EMUFILE *is, bool really_load)
{
free();
if (!really_load)
{
reset();
playback.StartFromZero(); // reset playback to frame 0
return false;
}
int frame = 0, prev_frame = -1, size = 0;
int last_tick = 0;
// read "GREENZONE" string

View File

@ -2,7 +2,7 @@
#define GREENZONE_ID_LEN 10
#define TIME_BETWEEN_CLEANINGS 15000 // in milliseconds
#define TIME_BETWEEN_CLEANINGS 10000 // in milliseconds
// greenzone cleaning masks
#define EVERY16TH 0xFFFFFFF0
#define EVERY8TH 0xFFFFFFF8
@ -21,7 +21,7 @@ public:
void update();
void save(EMUFILE *os, bool really_save = true);
bool load(EMUFILE *is);
bool load(EMUFILE *is, bool really_load = true);
void CollectCurrentState();
@ -38,12 +38,15 @@ public:
bool GetLagHistoryAtFrame(int frame);
// data
// saved data
int greenZoneCount;
std::vector<std::vector<uint8>> savestates;
std::vector<uint8> lag_history;
private:
// saved data
std::vector<uint8> lag_history;
// not saved data
int next_cleaning_time;
};

View File

@ -10,7 +10,7 @@ THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR IMPLI
History - History of movie modifications
[Singleton]
* stores array of snapshots and pointer to current (last) snapshot
* 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
* implements all restoring operations: undo, redo, revert to any snapshot from the array
@ -35,6 +35,7 @@ extern SELECTION selection;
extern GREENZONE greenzone;
extern TASEDITOR_PROJECT project;
extern PIANO_ROLL piano_roll;
extern POPUP_DISPLAY popup_display;
extern TASEDITOR_LUA taseditor_lua;
extern int joysticks_per_frame[NUM_SUPPORTED_INPUT_TYPES];
@ -44,53 +45,63 @@ extern Window_items_struct window_items[];
char history_save_id[HISTORY_ID_LEN] = "HISTORY";
char history_skipsave_id[HISTORY_ID_LEN] = "HISTORX";
char modCaptions[MODTYPES_TOTAL][20] = {" Init Project",
" Undefined",
" Set",
" Unset",
" Pattern",
" Insert",
" Insert#",
" Delete",
" Truncate",
" Clear",
" Cut",
" Paste",
" PasteInsert",
" Clone",
" Record",
" Import",
" Branch0 to ",
" Branch1 to ",
" Branch2 to ",
" Branch3 to ",
" Branch4 to ",
" Branch5 to ",
" Branch6 to ",
" Branch7 to ",
" Branch8 to ",
" Branch9 to ",
" Marker Branch0 to ",
" Marker Branch1 to ",
" Marker Branch2 to ",
" Marker Branch3 to ",
" Marker Branch4 to ",
" Marker Branch5 to ",
" Marker Branch6 to ",
" Marker Branch7 to ",
" Marker Branch8 to ",
" Marker Branch9 to ",
" Marker Set",
" Marker Remove",
" Marker Pattern",
" Marker Rename",
" Marker Drag",
" Marker Swap",
" Marker Shift",
" LUA Marker Set",
" LUA Marker Remove",
" LUA Marker Rename",
" LUA Change" };
char modCaptions[MODTYPES_TOTAL][20] = {" Initialization",
" Undefined",
" Set",
" Unset",
" Pattern",
" Insert",
" Insert#",
" Delete",
" Truncate",
" Clear",
" Cut",
" Paste",
" PasteInsert",
" Clone",
" Record",
" Import",
" Bookmark0",
" Bookmark1",
" Bookmark2",
" Bookmark3",
" Bookmark4",
" Bookmark5",
" Bookmark6",
" Bookmark7",
" Bookmark8",
" Bookmark9",
" Branch0 to ",
" Branch1 to ",
" Branch2 to ",
" Branch3 to ",
" Branch4 to ",
" Branch5 to ",
" Branch6 to ",
" Branch7 to ",
" Branch8 to ",
" Branch9 to ",
" Marker Branch0 to ",
" Marker Branch1 to ",
" Marker Branch2 to ",
" Marker Branch3 to ",
" Marker Branch4 to ",
" Marker Branch5 to ",
" Marker Branch6 to ",
" Marker Branch7 to ",
" Marker Branch8 to ",
" Marker Branch9 to ",
" Marker Set",
" Marker Remove",
" Marker Pattern",
" Marker Rename",
" Marker Drag",
" Marker Swap",
" Marker Shift",
" LUA Marker Set",
" LUA Marker Remove",
" LUA Marker Rename",
" LUA Change" };
char LuaCaptionPrefix[6] = " LUA ";
char joypadCaptions[4][5] = {"(1P)", "(2P)", "(3P)", "(4P)"};
@ -114,6 +125,8 @@ void HISTORY::init()
void HISTORY::free()
{
snapshots.resize(0);
backup_bookmarks.resize(0);
backup_current_branch.resize(0);
history_total_items = 0;
}
void HISTORY::reset()
@ -124,6 +137,8 @@ void HISTORY::reset()
undo_hint_pos = old_undo_hint_pos = undo_hint_time = -1;
old_show_undo_hint = show_undo_hint = false;
snapshots.resize(history_size);
backup_bookmarks.resize(history_size);
backup_current_branch.resize(history_size);
history_start_pos = 0;
history_cursor_pos = -1;
// create initial snapshot
@ -133,7 +148,7 @@ void HISTORY::reset()
inp.jump_frame = -1;
inp.start_frame = 0;
inp.end_frame = inp.size - 1;
AddSnapshotToHistory(inp);
AddItemToHistory(inp);
UpdateHistoryList();
RedrawHistoryList();
}
@ -160,26 +175,36 @@ void HISTORY::HistorySizeChanged()
{
int new_history_size = taseditor_config.undo_levels + 1;
std::vector<SNAPSHOT> new_snapshots(new_history_size);
std::vector<BOOKMARK> new_backup_bookmarks(new_history_size);
std::vector<int8> new_backup_current_branch(new_history_size);
int pos = history_cursor_pos, source_pos = history_cursor_pos;
if (pos >= new_history_size)
pos = new_history_size - 1;
int new_history_cursor_pos = pos;
// copy old "undo" snapshots
// copy old "undo" items
while (pos >= 0)
{
new_snapshots[pos] = snapshots[(history_start_pos + source_pos) % history_size];
new_backup_bookmarks[pos] = backup_bookmarks[(history_start_pos + source_pos) % history_size];
new_backup_current_branch[pos] = backup_current_branch[(history_start_pos + source_pos) % history_size];
pos--;
source_pos--;
}
// copy old "redo" snapshots
int num_redo_snapshots = history_total_items - (history_cursor_pos + 1);
// copy old "redo" items
int num_redo_items = history_total_items - (history_cursor_pos + 1);
int space_available = new_history_size - (new_history_cursor_pos + 1);
int i = (num_redo_snapshots <= space_available) ? num_redo_snapshots : space_available;
int i = (num_redo_items <= space_available) ? num_redo_items : space_available;
int new_history_total_items = new_history_cursor_pos + i + 1;
for (; i > 0; i--)
{
new_snapshots[new_history_cursor_pos + i] = snapshots[(history_start_pos + history_cursor_pos + i) % history_size];
new_backup_bookmarks[new_history_cursor_pos + i] = backup_bookmarks[(history_start_pos + history_cursor_pos + i) % history_size];
new_backup_current_branch[new_history_cursor_pos + i] = backup_current_branch[(history_start_pos + history_cursor_pos + i) % history_size];
}
// finish
snapshots = new_snapshots;
backup_bookmarks = new_backup_bookmarks;
backup_current_branch = new_backup_current_branch;
history_size = new_history_size;
history_start_pos = 0;
history_cursor_pos = new_history_cursor_pos;
@ -198,18 +223,119 @@ int HISTORY::jump(int new_pos)
// make jump
int old_pos = history_cursor_pos;
history_cursor_pos = new_pos;
int real_pos = (history_start_pos + history_cursor_pos) % history_size;
RedrawHistoryList();
int real_pos, mod_type, slot, current_branch = branches.GetCurrentBranch();
bool bookmarks_changed = false, changes_since_current_branch = false;
bool old_changes_since_current_branch = branches.GetChangesSinceCurrentBranch();
// restore Bookmarks/Branches
std::vector<uint8> bookmarks_to_redraw;
std::vector<int> frames_to_redraw;
if (new_pos > old_pos)
{
// redo
for (int i = old_pos + 1; i <= new_pos; ++i)
{
real_pos = (history_start_pos + i) % history_size;
mod_type = snapshots[real_pos].mod_type;
if (mod_type >= MODTYPE_BOOKMARK_0 && mod_type <= MODTYPE_BRANCH_MARKERS_9)
{
current_branch = (mod_type - MODTYPE_BOOKMARK_0) % TOTAL_BOOKMARKS;
changes_since_current_branch = false;
} else
{
changes_since_current_branch = true;
}
if (mod_type >= MODTYPE_BOOKMARK_0 && mod_type <= MODTYPE_BOOKMARK_9)
{
// 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);
bookmarks.bookmarks_array[slot] = backup_bookmarks[real_pos];
frames_to_redraw.push_back(bookmarks.bookmarks_array[slot].snapshot.jump_frame);
bookmarks_to_redraw.push_back(slot);
backup_bookmarks[real_pos] = temp_bookmark;
branches.InvalidateBranchSlot(slot);
bookmarks_changed = true;
}
}
} else
{
// undo
for (int i = old_pos; i > new_pos; i--)
{
real_pos = (history_start_pos + i) % history_size;
mod_type = snapshots[real_pos].mod_type;
if (mod_type >= MODTYPE_BOOKMARK_0 && mod_type <= MODTYPE_BRANCH_MARKERS_9)
current_branch = backup_current_branch[real_pos];
if (mod_type >= MODTYPE_BOOKMARK_0 && mod_type <= MODTYPE_BOOKMARK_9)
{
// 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);
bookmarks.bookmarks_array[slot] = backup_bookmarks[real_pos];
frames_to_redraw.push_back(bookmarks.bookmarks_array[slot].snapshot.jump_frame);
bookmarks_to_redraw.push_back(slot);
backup_bookmarks[real_pos] = temp_bookmark;
branches.InvalidateBranchSlot(slot);
bookmarks_changed = true;
}
}
real_pos = (history_start_pos + new_pos) % history_size;
mod_type = snapshots[real_pos].mod_type;
if (mod_type >= MODTYPE_BOOKMARK_0 && mod_type <= MODTYPE_BRANCH_MARKERS_9)
{
current_branch = (mod_type - MODTYPE_BOOKMARK_0) % TOTAL_BOOKMARKS;
changes_since_current_branch = false;
} else if (GetCategoryOfOperation(mod_type) != CATEGORY_OTHER)
{
changes_since_current_branch = true;
}
}
int old_current_branch = branches.GetCurrentBranch();
if (bookmarks_changed || current_branch != old_current_branch || changes_since_current_branch != old_changes_since_current_branch)
{
branches.HandleHistoryJump(current_branch, changes_since_current_branch);
if (current_branch != old_current_branch)
{
// 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);
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);
bookmarks_to_redraw.push_back(current_branch);
}
}
bookmarks.must_check_item_under_mouse = true;
project.SetProjectChanged();
}
// redraw Piano Roll rows and Bookmarks List rows
for (int i = frames_to_redraw.size() - 1; i >= 0; i--)
piano_roll.RedrawRow(frames_to_redraw[i]);
for (int i = bookmarks_to_redraw.size() - 1; i >= 0; i--)
{
bookmarks.RedrawBookmark(bookmarks_to_redraw[i]);
// if screenshot of the slot is currently shown - reinit and redraw the picture
if (popup_display.screenshot_currently_shown == bookmarks_to_redraw[i])
popup_display.screenshot_currently_shown = ITEM_UNDER_MOUSE_NONE;
}
// create undo_hint
if (new_pos > old_pos)
undo_hint_pos = GetCurrentSnapshot().jump_frame;
undo_hint_pos = GetCurrentSnapshot().jump_frame; // redo
else
undo_hint_pos = GetNextToCurrentSnapshot().jump_frame;
undo_hint_pos = GetNextToCurrentSnapshot().jump_frame; // undo
undo_hint_time = clock() + UNDO_HINT_TIME;
show_undo_hint = true;
// update markers
real_pos = (history_start_pos + history_cursor_pos) % history_size;
// update Markers
bool markers_changed = false;
if (taseditor_config.bind_markers)
{
@ -221,27 +347,26 @@ int HISTORY::jump(int new_pos)
}
}
// update current movie
// update current movie data
int first_change = snapshots[real_pos].findFirstChange(currMovieData);
if (first_change >= 0)
{
snapshots[real_pos].toMovie(currMovieData, first_change);
selection.must_find_current_marker = playback.must_find_current_marker = true;
branches.ChangesMadeSinceBranch();
// and Piano Roll will be redrawn by greenzone invalidation
// Piano Roll Redraw and ProjectChanged will be called by greenzone invalidation
} else if (markers_changed)
{
markers_manager.update();
selection.must_find_current_marker = playback.must_find_current_marker = true;
branches.ChangesMadeSinceBranch();
piano_roll.RedrawList();
piano_roll.FollowUndo();
project.SetProjectChanged();
} else if (taseditor_config.enable_hot_changes)
{
// when using Hot Changes, Piano Roll should be always redrawn, because old changes become less hot
piano_roll.RedrawList();
}
piano_roll.UpdateItemCount();
piano_roll.FollowUndo();
return first_change;
}
@ -249,28 +374,20 @@ void HISTORY::undo()
{
int result = jump(history_cursor_pos - 1);
if (result >= 0)
{
piano_roll.UpdateItemCount();
piano_roll.FollowUndo();
greenzone.InvalidateAndCheck(result);
}
return;
}
void HISTORY::redo()
{
int result = jump(history_cursor_pos + 1);
if (result >= 0)
{
piano_roll.UpdateItemCount();
piano_roll.FollowUndo();
greenzone.InvalidateAndCheck(result);
}
return;
}
// ----------------------------
void HISTORY::AddSnapshotToHistory(SNAPSHOT &inp)
void HISTORY::AddItemToHistory(SNAPSHOT &inp, int cur_branch)
{
// history uses conveyor of snapshots (vector with fixed size) to aviod resizing which is awfully expensive with such large objects as SNAPSHOT
// 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
if (history_total_items >= history_size)
{
// reached the end of available history_size - move history_start_pos (thus deleting oldest snapshot)
@ -283,9 +400,33 @@ void HISTORY::AddSnapshotToHistory(SNAPSHOT &inp)
history_total_items = history_cursor_pos+1;
UpdateHistoryList();
}
// write snapshot
// write data
int real_pos = (history_start_pos + history_cursor_pos) % history_size;
snapshots[real_pos] = inp;
backup_bookmarks[real_pos].free();
backup_current_branch[real_pos] = cur_branch;
RedrawHistoryList();
}
void HISTORY::AddItemToHistory(SNAPSHOT &inp, 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
if (history_total_items >= history_size)
{
// reached the end of available history_size - move history_start_pos (thus deleting oldest snapshot)
history_cursor_pos = history_size-1;
history_start_pos = (history_start_pos + 1) % history_size;
} else
{
// didn't reach the end of history yet
history_cursor_pos++;
history_total_items = history_cursor_pos+1;
UpdateHistoryList();
}
// write data
int real_pos = (history_start_pos + history_cursor_pos) % history_size;
snapshots[real_pos] = inp;
backup_bookmarks[real_pos] = bookm;
backup_current_branch[real_pos] = cur_branch;
RedrawHistoryList();
}
@ -414,7 +555,7 @@ int HISTORY::RegisterChanges(int mod_type, int start, int end, const char* comme
break;
}
}
AddSnapshotToHistory(inp);
AddItemToHistory(inp);
}
branches.ChangesMadeSinceBranch();
}
@ -448,7 +589,7 @@ int HISTORY::RegisterInsertNum(int start, int frames)
// set hotchanges
if (taseditor_config.enable_hot_changes)
inp.inheritHotChanges_InsertNum(&snapshots[real_pos], start, frames);
AddSnapshotToHistory(inp);
AddItemToHistory(inp);
branches.ChangesMadeSinceBranch();
}
return first_changes;
@ -479,7 +620,7 @@ int HISTORY::RegisterPasteInsert(int start, SelectionFrames& inserted_set)
// set hotchanges
if (taseditor_config.enable_hot_changes)
inp.inheritHotChanges_PasteInsert(&snapshots[real_pos], inserted_set);
AddSnapshotToHistory(inp);
AddItemToHistory(inp);
branches.ChangesMadeSinceBranch();
}
return first_changes;
@ -520,18 +661,35 @@ void HISTORY::RegisterMarkersChange(int mod_type, int start, int end, const char
// input hotchanges aren't changed
if (taseditor_config.enable_hot_changes)
inp.copyHotChanges(&GetCurrentSnapshot());
AddSnapshotToHistory(inp);
AddItemToHistory(inp);
branches.ChangesMadeSinceBranch();
project.SetProjectChanged();
}
void HISTORY::RegisterBranching(int mod_type, int first_change, int slot)
void HISTORY::RegisterBookmarkSet(int slot, BOOKMARK& backup_copy, int old_current_branch)
{
// create new snapshot
SNAPSHOT inp;
inp.init(currMovieData, taseditor_config.enable_hot_changes);
// fill description: modification type + jump_frame of the Bookmark
inp.mod_type = MODTYPE_BOOKMARK_0 + slot;
strcat(inp.description, modCaptions[inp.mod_type]);
inp.start_frame = inp.end_frame = inp.jump_frame = bookmarks.bookmarks_array[slot].snapshot.jump_frame;
char framenum[11];
strcat(inp.description, " ");
_itoa(inp.jump_frame, framenum, 10);
strcat(inp.description, framenum);
if (taseditor_config.enable_hot_changes)
inp.copyHotChanges(&GetCurrentSnapshot());
AddItemToHistory(inp, old_current_branch, backup_copy);
}
void HISTORY::RegisterBranching(int mod_type, int first_change, int slot, int old_current_branch)
{
// create new snapshot
SNAPSHOT inp;
inp.init(currMovieData, taseditor_config.enable_hot_changes);
// fill description: modification type + time of the Branch
inp.mod_type = mod_type;
strcat(inp.description, modCaptions[mod_type]);
strcat(inp.description, modCaptions[inp.mod_type]);
strcat(inp.description, bookmarks.bookmarks_array[slot].snapshot.description);
inp.jump_frame = first_change;
inp.start_frame = first_change;
@ -556,7 +714,7 @@ void HISTORY::RegisterBranching(int mod_type, int first_change, int slot)
inp.copyHotChanges(&GetCurrentSnapshot());
}
}
AddSnapshotToHistory(inp);
AddItemToHistory(inp, old_current_branch);
}
void HISTORY::RegisterRecording(int frame_of_change)
{
@ -629,7 +787,7 @@ void HISTORY::RegisterRecording(int frame_of_change)
inp.inheritHotChanges(&snapshots[real_pos]);
inp.fillHotChanges(snapshots[real_pos], frame_of_change, frame_of_change);
}
AddSnapshotToHistory(inp);
AddItemToHistory(inp);
}
branches.ChangesMadeSinceBranch();
}
@ -658,7 +816,7 @@ void HISTORY::RegisterImport(MovieData& md, char* filename)
// do not inherit old hotchanges, because imported input (most likely) doesn't have direct connection with recent edits, so old hotchanges are irrelevant and should not be copied
inp.fillHotChanges(snapshots[real_pos], first_changes);
}
AddSnapshotToHistory(inp);
AddItemToHistory(inp);
inp.toMovie(currMovieData);
piano_roll.UpdateItemCount();
branches.ChangesMadeSinceBranch();
@ -730,7 +888,7 @@ int HISTORY::RegisterLuaChanges(const char* name, int start, bool InsertionDelet
inp.fillHotChanges(snapshots[real_pos], first_changes);
}
}
AddSnapshotToHistory(inp);
AddItemToHistory(inp);
branches.ChangesMadeSinceBranch();
}
return first_changes;
@ -746,11 +904,13 @@ void HISTORY::save(EMUFILE *os, bool really_save)
// write vars
write32le(history_cursor_pos, os);
write32le(history_total_items, os);
// write snapshots starting from history_start_pos
// write items starting from history_start_pos
for (int i = 0; i < history_total_items; ++i)
{
real_pos = (history_start_pos + i) % history_size;
snapshots[real_pos].save(os);
backup_bookmarks[real_pos].save(os);
os->fwrite(&backup_current_branch[real_pos], 1);
playback.SetProgressbar(i, history_total_items);
}
} else
@ -760,12 +920,16 @@ void HISTORY::save(EMUFILE *os, bool really_save)
}
}
// returns true if couldn't load
bool HISTORY::load(EMUFILE *is)
bool HISTORY::load(EMUFILE *is, bool really_load)
{
if (!really_load)
{
reset();
return false;
}
int i = -1;
SNAPSHOT inp;
// delete old snapshots
snapshots.resize(history_size);
BOOKMARK bookm;
// read "HISTORY" string
char save_id[HISTORY_ID_LEN];
if ((int)is->fread(save_id, HISTORY_ID_LEN) < HISTORY_ID_LEN) goto error;
@ -777,47 +941,60 @@ bool HISTORY::load(EMUFILE *is)
return false;
}
if (strcmp(history_save_id, save_id)) goto error; // string is not valid
// delete old items
snapshots.resize(history_size);
backup_bookmarks.resize(history_size);
backup_current_branch.resize(history_size);
// read vars
if (!read32le(&history_cursor_pos, is)) goto error;
if (!read32le(&history_total_items, is)) goto error;
if (history_cursor_pos > history_total_items) goto error;
history_start_pos = 0;
// read snapshots
// read items
int total = history_total_items;
if (history_total_items > history_size)
{
// user can't afford that much undo levels, skip some snapshots
int num_snapshots_to_skip = history_total_items - history_size;
// first try to skip snapshots over history_cursor_pos (future snapshots), because "redo" is less important than "undo"
int num_redo_snapshots = history_total_items-1 - history_cursor_pos;
if (num_snapshots_to_skip >= num_redo_snapshots)
// user can't afford that much undo levels, skip some items
int num_items_to_skip = history_total_items - history_size;
// first try to skip items over history_cursor_pos (future items), because "redo" is less important than "undo"
int num_redo_items = history_total_items-1 - history_cursor_pos;
if (num_items_to_skip >= num_redo_items)
{
// skip all redo snapshots
// skip all redo items
history_total_items = history_cursor_pos+1;
num_snapshots_to_skip -= num_redo_snapshots;
// and still need to skip some undo snapshots
for (i = 0; i < num_snapshots_to_skip; ++i)
num_items_to_skip -= num_redo_items;
// and still need to skip some undo items
for (i = 0; i < num_items_to_skip; ++i)
{
if (inp.skipLoad(is)) goto error;
total -= num_snapshots_to_skip;
history_cursor_pos -= num_snapshots_to_skip;
if (bookm.skipLoad(is)) goto error;
if (is->fseek(1, SEEK_CUR)) goto error; // backup_current_branch
}
total -= num_items_to_skip;
history_cursor_pos -= num_items_to_skip;
}
history_total_items -= num_snapshots_to_skip;
history_total_items -= num_items_to_skip;
}
// load snapshots
// load items
for (i = 0; i < history_total_items; ++i)
{
if (snapshots[i].load(is)) goto error;
if (backup_bookmarks[i].load(is)) goto error;
if (is->fread(&backup_current_branch[i], 1) != 1) goto error;
playback.SetProgressbar(i, history_total_items);
}
// skip redo snapshots if needed
// skip redo items if needed
for (; i < total; ++i)
{
if (inp.skipLoad(is)) goto error;
if (bookm.skipLoad(is)) goto error;
if (is->fseek(1, SEEK_CUR)) goto error; // backup_current_branch
}
// everything went well
// init vars
undo_hint_pos = old_undo_hint_pos = undo_hint_time = -1;
old_show_undo_hint = show_undo_hint = false;
// everything went well
UpdateHistoryList();
RedrawHistoryList();
return false;
@ -855,14 +1032,8 @@ void HISTORY::Click(int row_index)
{
int result = jump(row_index);
if (result >= 0)
{
piano_roll.UpdateItemCount();
piano_roll.FollowUndo();
greenzone.InvalidateAndCheck(result);
return;
}
}
RedrawHistoryList();
}
void HISTORY::UpdateHistoryList()
@ -880,6 +1051,82 @@ void HISTORY::RedrawHistoryList()
InvalidateRect(hwndHistoryList, 0, FALSE);
}
// ----------------------------
int HISTORY::GetCategoryOfOperation(int mod_type)
{
switch (mod_type)
{
case MODTYPE_INIT:
case MODTYPE_UNDEFINED:
return CATEGORY_OTHER;
case MODTYPE_SET:
case MODTYPE_UNSET:
case MODTYPE_PATTERN:
return CATEGORY_INPUT_CHANGE;
case MODTYPE_INSERT:
case MODTYPE_INSERTNUM:
case MODTYPE_DELETE:
case MODTYPE_TRUNCATE:
return CATEGORY_INPUT_MARKERS_CHANGE;
case MODTYPE_CLEAR:
case MODTYPE_CUT:
case MODTYPE_PASTE:
return CATEGORY_INPUT_CHANGE;
case MODTYPE_PASTEINSERT:
case MODTYPE_CLONE:
return CATEGORY_INPUT_MARKERS_CHANGE;
case MODTYPE_RECORD:
case MODTYPE_IMPORT:
return CATEGORY_INPUT_CHANGE;
case MODTYPE_BOOKMARK_0:
case MODTYPE_BOOKMARK_1:
case MODTYPE_BOOKMARK_2:
case MODTYPE_BOOKMARK_3:
case MODTYPE_BOOKMARK_4:
case MODTYPE_BOOKMARK_5:
case MODTYPE_BOOKMARK_6:
case MODTYPE_BOOKMARK_7:
case MODTYPE_BOOKMARK_8:
case MODTYPE_BOOKMARK_9:
return CATEGORY_OTHER;
case MODTYPE_BRANCH_0:
case MODTYPE_BRANCH_1:
case MODTYPE_BRANCH_2:
case MODTYPE_BRANCH_3:
case MODTYPE_BRANCH_4:
case MODTYPE_BRANCH_5:
case MODTYPE_BRANCH_6:
case MODTYPE_BRANCH_7:
case MODTYPE_BRANCH_8:
case MODTYPE_BRANCH_9:
return CATEGORY_INPUT_MARKERS_CHANGE;
case MODTYPE_BRANCH_MARKERS_0:
case MODTYPE_BRANCH_MARKERS_1:
case MODTYPE_BRANCH_MARKERS_2:
case MODTYPE_BRANCH_MARKERS_3:
case MODTYPE_BRANCH_MARKERS_4:
case MODTYPE_BRANCH_MARKERS_5:
case MODTYPE_BRANCH_MARKERS_6:
case MODTYPE_BRANCH_MARKERS_7:
case MODTYPE_BRANCH_MARKERS_8:
case MODTYPE_BRANCH_MARKERS_9:
case MODTYPE_MARKER_SET:
case MODTYPE_MARKER_REMOVE:
case MODTYPE_MARKER_PATTERN:
case MODTYPE_MARKER_RENAME:
case MODTYPE_MARKER_DRAG:
case MODTYPE_MARKER_SWAP:
case MODTYPE_MARKER_SHIFT:
case MODTYPE_LUA_MARKER_SET:
case MODTYPE_LUA_MARKER_REMOVE:
case MODTYPE_LUA_MARKER_RENAME:
return CATEGORY_MARKERS_CHANGE;
case MODTYPE_LUA_CHANGE:
return CATEGORY_INPUT_MARKERS_CHANGE;
}
// if undefined
return CATEGORY_OTHER;
}
SNAPSHOT& HISTORY::GetCurrentSnapshot()
{
return snapshots[(history_start_pos + history_cursor_pos) % history_size];

View File

@ -2,7 +2,7 @@
#define UNDO_HINT_TIME 200
enum
enum MOD_TYPES
{
MODTYPE_INIT,
MODTYPE_UNDEFINED,
@ -20,6 +20,16 @@ enum
MODTYPE_CLONE,
MODTYPE_RECORD,
MODTYPE_IMPORT,
MODTYPE_BOOKMARK_0,
MODTYPE_BOOKMARK_1,
MODTYPE_BOOKMARK_2,
MODTYPE_BOOKMARK_3,
MODTYPE_BOOKMARK_4,
MODTYPE_BOOKMARK_5,
MODTYPE_BOOKMARK_6,
MODTYPE_BOOKMARK_7,
MODTYPE_BOOKMARK_8,
MODTYPE_BOOKMARK_9,
MODTYPE_BRANCH_0,
MODTYPE_BRANCH_1,
MODTYPE_BRANCH_2,
@ -54,6 +64,17 @@ enum
MODTYPES_TOTAL
};
enum CATEGORIES_OF_OPERATIONS
{
CATEGORY_OTHER,
CATEGORY_INPUT_CHANGE,
CATEGORY_MARKERS_CHANGE,
CATEGORY_INPUT_MARKERS_CHANGE,
CATEGORIES_OF_OPERATIONS_TOTAL
};
#define HISTORY_NORMAL_COLOR 0x000000
#define WM_MOUSEWHEEL_RESENT WM_APP+123
@ -72,7 +93,7 @@ public:
void HistorySizeChanged();
void save(EMUFILE *os, bool really_save = true);
bool load(EMUFILE *is);
bool load(EMUFILE *is, bool really_load = true);
int jump(int new_pos);
@ -83,11 +104,14 @@ public:
int RegisterInsertNum(int start, int frames);
int RegisterPasteInsert(int start, SelectionFrames& inserted_set);
void RegisterMarkersChange(int mod_type, int start = 0, int end =-1, const char* comment = 0);
void RegisterBranching(int mod_type, int first_change, int slot);
void RegisterBookmarkSet(int slot, BOOKMARK& backup_copy, int old_current_branch);
void RegisterBranching(int mod_type, int first_change, int slot, int old_current_branch);
void RegisterRecording(int frame_of_change);
void RegisterImport(MovieData& md, char* filename);
int RegisterLuaChanges(const char* name, int start, bool InsertionDeletion_was_made);
int GetCategoryOfOperation(int mod_type);
SNAPSHOT& GetCurrentSnapshot();
SNAPSHOT& GetNextToCurrentSnapshot();
char* GetItemDesc(int pos);
@ -105,9 +129,12 @@ public:
HWND hwndHistoryList;
private:
void AddSnapshotToHistory(SNAPSHOT &inp);
void AddItemToHistory(SNAPSHOT &inp, int cur_branch = 0);
void AddItemToHistory(SNAPSHOT &inp, int cur_branch, BOOKMARK &bookm);
std::vector<SNAPSHOT> snapshots;
std::vector<BOOKMARK> backup_bookmarks;
std::vector<int8> backup_current_branch;
int history_cursor_pos;
int history_start_pos;

View File

@ -80,8 +80,13 @@ void MARKERS_MANAGER::save(EMUFILE *os, bool really_save)
}
}
// returns true if couldn't load
bool MARKERS_MANAGER::load(EMUFILE *is)
bool MARKERS_MANAGER::load(EMUFILE *is, bool really_load)
{
if (!really_load)
{
reset();
return false;
}
// read "MARKERS" string
char save_id[MARKERS_ID_LEN];
if ((int)is->fread(save_id, MARKERS_ID_LEN) < MARKERS_ID_LEN) goto error;

View File

@ -32,7 +32,7 @@ public:
void update();
void save(EMUFILE *os, bool really_save = true);
bool load(EMUFILE *is);
bool load(EMUFILE *is, bool really_load = true);
int GetMarkersSize();
bool SetMarkersSize(int new_size);

View File

@ -717,10 +717,16 @@ void PIANO_ROLL::save(EMUFILE *os, bool really_save)
}
}
// returns true if couldn't load
bool PIANO_ROLL::load(EMUFILE *is)
bool PIANO_ROLL::load(EMUFILE *is, bool really_load)
{
reset();
update();
if (!really_load)
{
// scroll to the beginning
ListView_EnsureVisible(hwndList, 0, FALSE);
return false;
}
// read "PIANO_ROLL" string
char save_id[PIANO_ROLL_ID_LEN];
if ((int)is->fread(save_id, PIANO_ROLL_ID_LEN) < PIANO_ROLL_ID_LEN) goto error;
@ -746,9 +752,9 @@ error:
return true;
}
// ----------------------------------------------------------------------
void PIANO_ROLL::RedrawList()
void PIANO_ROLL::RedrawList(bool erase_bg)
{
InvalidateRect(hwndList, 0, FALSE);
InvalidateRect(hwndList, 0, erase_bg);
must_check_item_under_mouse = true;
}
void PIANO_ROLL::RedrawRow(int index)
@ -1072,16 +1078,13 @@ void PIANO_ROLL::GetDispInfo(NMLVDISPINFO* nmlvDispInfo)
{
case COLUMN_ICONS:
{
if(item.iImage == I_IMAGECALLBACK)
item.iImage = bookmarks.FindBookmarkAtFrame(item.iItem);
if (item.iImage < 0)
{
item.iImage = bookmarks.FindBookmarkAtFrame(item.iItem);
if (item.iImage < 0)
{
if (item.iItem == currFrameCounter)
item.iImage = ARROW_IMAGE_ID;
else if (item.iItem == playback.lost_position_frame-1)
item.iImage = GREEN_ARROW_IMAGE_ID;
}
if (item.iItem == currFrameCounter)
item.iImage = ARROW_IMAGE_ID;
else if (item.iItem == playback.lost_position_frame-1)
item.iImage = GREEN_ARROW_IMAGE_ID;
}
break;
}
@ -1181,7 +1184,7 @@ LONG PIANO_ROLL::CustomDraw(NMLVCUSTOMDRAW* msg)
{
if (!greenzone.savestates[cell_y].empty())
{
if (taseditor_config.show_lag_frames && greenzone.lag_history[cell_y])
if (taseditor_config.show_lag_frames && greenzone.GetLagHistoryAtFrame(cell_y))
msg->clrTextBk = LAG_FRAMENUM_COLOR;
else
msg->clrTextBk = GREENZONE_FRAMENUM_COLOR;
@ -1190,7 +1193,7 @@ LONG PIANO_ROLL::CustomDraw(NMLVCUSTOMDRAW* msg)
|| (!greenzone.savestates[cell_y & EVERY4TH].empty() && (int)greenzone.savestates.size() > (cell_y | 0x3) + 1 && !greenzone.savestates[(cell_y | 0x3) + 1].empty())
|| (!greenzone.savestates[cell_y & EVERY2ND].empty() && !greenzone.savestates[(cell_y | 0x1) + 1].empty()))
{
if (taseditor_config.show_lag_frames && greenzone.lag_history[cell_y])
if (taseditor_config.show_lag_frames && greenzone.GetLagHistoryAtFrame(cell_y))
msg->clrTextBk = PALE_LAG_FRAMENUM_COLOR;
else
msg->clrTextBk = PALE_GREENZONE_FRAMENUM_COLOR;
@ -1221,7 +1224,7 @@ LONG PIANO_ROLL::CustomDraw(NMLVCUSTOMDRAW* msg)
{
if (!greenzone.savestates[cell_y].empty())
{
if (taseditor_config.show_lag_frames && greenzone.lag_history[cell_y])
if (taseditor_config.show_lag_frames && greenzone.GetLagHistoryAtFrame(cell_y))
msg->clrTextBk = LAG_INPUT_COLOR1;
else
msg->clrTextBk = GREENZONE_INPUT_COLOR1;
@ -1230,7 +1233,7 @@ LONG PIANO_ROLL::CustomDraw(NMLVCUSTOMDRAW* msg)
|| (!greenzone.savestates[cell_y & EVERY4TH].empty() && (int)greenzone.savestates.size() > (cell_y | 0x3) + 1 && !greenzone.savestates[(cell_y | 0x3) + 1].empty())
|| (!greenzone.savestates[cell_y & EVERY2ND].empty() && !greenzone.savestates[(cell_y | 0x1) + 1].empty()))
{
if (taseditor_config.show_lag_frames && greenzone.lag_history[cell_y])
if (taseditor_config.show_lag_frames && greenzone.GetLagHistoryAtFrame(cell_y))
msg->clrTextBk = PALE_LAG_INPUT_COLOR1;
else
msg->clrTextBk = PALE_GREENZONE_INPUT_COLOR1;
@ -1260,7 +1263,7 @@ LONG PIANO_ROLL::CustomDraw(NMLVCUSTOMDRAW* msg)
{
if (!greenzone.savestates[cell_y].empty())
{
if (taseditor_config.show_lag_frames && greenzone.lag_history[cell_y])
if (taseditor_config.show_lag_frames && greenzone.GetLagHistoryAtFrame(cell_y))
msg->clrTextBk = LAG_INPUT_COLOR2;
else
msg->clrTextBk = GREENZONE_INPUT_COLOR2;
@ -1269,7 +1272,7 @@ LONG PIANO_ROLL::CustomDraw(NMLVCUSTOMDRAW* msg)
|| (!greenzone.savestates[cell_y & EVERY4TH].empty() && (int)greenzone.savestates.size() > (cell_y | 0x3) + 1 && !greenzone.savestates[(cell_y | 0x3) + 1].empty())
|| (!greenzone.savestates[cell_y & EVERY2ND].empty() && !greenzone.savestates[(cell_y | 0x1) + 1].empty()))
{
if (taseditor_config.show_lag_frames && greenzone.lag_history[cell_y])
if (taseditor_config.show_lag_frames && greenzone.GetLagHistoryAtFrame(cell_y))
msg->clrTextBk = PALE_LAG_INPUT_COLOR2;
else
msg->clrTextBk = PALE_GREENZONE_INPUT_COLOR2;

View File

@ -144,9 +144,9 @@ public:
void update();
void save(EMUFILE *os, bool really_save = true);
bool load(EMUFILE *is);
bool load(EMUFILE *is, bool really_load = true);
void RedrawList();
void RedrawList(bool erase_bg = true);
void RedrawRow(int index);
void RedrawHeader();

View File

@ -133,7 +133,7 @@ void POPUP_DISPLAY::update()
if (clock() > next_update_time)
{
next_update_time = clock() + DISPLAY_UPDATE_TICK;
if (bookmarks.item_under_mouse >= 0 && bookmarks.item_under_mouse < TOTAL_BOOKMARKS)
if (bookmarks.item_under_mouse >= 0 && bookmarks.item_under_mouse < TOTAL_BOOKMARKS && bookmarks.bookmarks_array[bookmarks.item_under_mouse].not_empty)
{
if (taseditor_config.show_branch_screenshots && !hwndScrBmp)
{

View File

@ -260,8 +260,13 @@ void SELECTION::save(EMUFILE *os, bool really_save)
}
}
// returns true if couldn't load
bool SELECTION::load(EMUFILE *is)
bool SELECTION::load(EMUFILE *is, bool really_load)
{
if (!really_load)
{
reset();
return false;
}
// read "SELECTION" string
char save_id[SELECTION_ID_LEN];
if ((int)is->fread(save_id, SELECTION_ID_LEN) < SELECTION_ID_LEN) goto error;
@ -526,33 +531,25 @@ void SELECTION::SelectBetweenMarkers()
return;
}
// selecting circle:
// selecting circle: 1-2-3-4-1-2-3-4...
if (upper_border > upper_marker+1 || lower_border < lower_marker-1 || lower_border > lower_marker)
{
// default: select all between markers
for (int i = upper_marker+1; i < lower_marker; ++i)
{
ListView_SetItemState(piano_roll.hwndList, i, LVIS_SELECTED, LVIS_SELECTED);
}
} else if (upper_border == upper_marker+1 && lower_border == lower_marker-1)
{
// already selected all between markers - now select both markers or at least select the marker that is not outside movie range
// 1 - default: select all between markers, not including lower marker
if (upper_marker < 0) upper_marker = 0;
if (lower_marker >= movie_size) lower_marker = movie_size - 1;
for (int i = upper_marker; i <= lower_marker; ++i)
{
ListView_SetItemState(piano_roll.hwndList, i, LVIS_SELECTED, LVIS_SELECTED);
}
} else if (upper_border <= upper_marker && lower_border >= lower_marker)
{
// selected all between markers and both markers selected too - now deselect lower marker
for (int i = upper_marker; i < lower_marker; ++i)
{
ListView_SetItemState(piano_roll.hwndList, i, LVIS_SELECTED, LVIS_SELECTED);
}
} else if (upper_border == upper_marker && lower_border == lower_marker-1)
{
// selected all between markers and upper marker selected too - now deselect upper marker and (if lower marker < movie_size) reselect lower marker
// 2 - selected all between markers and upper marker selected too: select all between markers, not including markers
for (int i = upper_marker+1; i < lower_marker; ++i)
{
ListView_SetItemState(piano_roll.hwndList, i, LVIS_SELECTED, LVIS_SELECTED);
}
} else if (upper_border == upper_marker+1 && lower_border == lower_marker-1)
{
// 3 - selected all between markers, nut including markers: select all between markers, not including upper marker
if (lower_marker >= movie_size) lower_marker = movie_size - 1;
for (int i = upper_marker+1; i <= lower_marker; ++i)
{
@ -560,8 +557,18 @@ void SELECTION::SelectBetweenMarkers()
}
} else if (upper_border == upper_marker+1 && lower_border == lower_marker)
{
// selected all between markers and lower marker selected too - now deselect lower marker (return to "selected all between markers")
for (int i = upper_marker + 1; i < lower_marker; ++i)
// 4 - selected all between markers and lower marker selected too: select all bertween markers, including markers
if (upper_marker < 0) upper_marker = 0;
if (lower_marker >= movie_size) lower_marker = movie_size - 1;
for (int i = upper_marker; i <= lower_marker; ++i)
{
ListView_SetItemState(piano_roll.hwndList, i, LVIS_SELECTED, LVIS_SELECTED);
}
} else
{
// return to 1
if (upper_marker < 0) upper_marker = 0;
for (int i = upper_marker; i < lower_marker; ++i)
{
ListView_SetItemState(piano_roll.hwndList, i, LVIS_SELECTED, LVIS_SELECTED);
}

View File

@ -20,7 +20,7 @@ public:
void RedrawMarker();
void save(EMUFILE *os, bool really_save = true);
bool load(EMUFILE *is);
bool load(EMUFILE *is, bool really_load = true);
void saveSelection(SelectionFrames& selection, EMUFILE *os);
bool loadSelection(SelectionFrames& selection, EMUFILE *is);
bool skiploadSelection(EMUFILE *is);

View File

@ -432,7 +432,7 @@ int SNAPSHOT::findFirstChange(SNAPSHOT& inp, int start, int end)
break;
}
}
// if current size is less then previous, return size-1 as the frame of difference
// if current size is less then previous, return last frame (=size-1) as the frame of difference
if (size < inp_end) return size-1;
// no changes were found
return -1;

View File

@ -92,12 +92,12 @@ bool TASEDITOR_PROJECT::save(const char* different_name, bool save_binary, bool
if(count1 && count2)
{
// ask user if he wants to fix the checksum before saving
char message[2048];
char message[2048] = {0};
strcpy(message, "Movie ROM:\n");
strncat(message, currMovieData.romFilename.c_str(), 2047 - strlen(message));
strncat(message, "\nMD5: ", 2047 - strlen(message));
strncat(message, md5_movie, 2047 - strlen(message));
strncat(message, "\n\nCurrent ROM: \n", 2047 - strlen(message));
strncat(message, "\n\nCurrent ROM:\n", 2047 - strlen(message));
strncat(message, GameInfo->filename, 2047 - strlen(message));
strncat(message, "\nMD5: ", 2047 - strlen(message));
strncat(message, md5_rom, 2047 - strlen(message));
@ -128,7 +128,8 @@ bool TASEDITOR_PROJECT::save(const char* different_name, bool save_binary, bool
currMovieData.loadFrameCount = currMovieData.records.size();
currMovieData.emuVersion = FCEU_VERSION_NUMERIC;
currMovieData.dump(ofs, save_binary);
// save specified modules
// save header: fm3 version + saved_stuff
write32le(PROJECT_FILE_CURRENT_VERSION, ofs);
unsigned int saved_stuff = 0;
if (save_markers) saved_stuff |= MARKERS_SAVED;
if (save_bookmarks) saved_stuff |= BOOKMARKS_SAVED;
@ -137,6 +138,7 @@ bool TASEDITOR_PROJECT::save(const char* different_name, bool save_binary, bool
if (save_piano_roll) saved_stuff |= PIANO_ROLL_SAVED;
if (save_selection) saved_stuff |= SELECTION_SAVED;
write32le(saved_stuff, ofs);
// save specified modules
markers_manager.save(ofs, save_markers);
bookmarks.save(ofs, save_bookmarks);
greenzone.save(ofs, save_greenzone);
@ -158,6 +160,7 @@ bool TASEDITOR_PROJECT::save(const char* different_name, bool save_binary, bool
}
bool TASEDITOR_PROJECT::load(char* fullname)
{
bool load_all = true;
EMUFILE_FILE ifs(fullname, "rb");
if(ifs.fail())
@ -186,46 +189,79 @@ bool TASEDITOR_PROJECT::load(char* fullname)
if(count1 && count2)
{
// ask user if he really wants to load the project
char message[2048];
char message[2048] = {0};
strcpy(message, "This project was made using different ROM!\n\n");
strcat(message, "Original ROM:\n");
strncat(message, tempMovieData.romFilename.c_str(), 2047 - strlen(message));
strncat(message, "\nMD5: ", 2047 - strlen(message));
strncat(message, md5_original, 2047 - strlen(message));
strncat(message, "\n\nCurrent ROM: \n", 2047 - strlen(message));
strncat(message, "\n\nCurrent ROM:\n", 2047 - strlen(message));
strncat(message, GameInfo->filename, 2047 - strlen(message));
strncat(message, "\nMD5: ", 2047 - strlen(message));
strncat(message, md5_current, 2047 - strlen(message));
strncat(message, "\n\nLoad the project anyway? ", 2047 - strlen(message));
strncat(message, "\n\nLoad the project anyway?", 2047 - strlen(message));
int answer = MessageBox(taseditor_window.hwndTasEditor, message, "ROM Checksum Mismatch", MB_YESNO);
if(answer == IDNO)
if (answer == IDNO)
return false;
}
}
FCEU_printf("\nLoading TAS Editor project %s...\n", fullname);
// load fm3 version from header and check it
unsigned int file_version;
if (read32le(&file_version, &ifs))
{
if (file_version != PROJECT_FILE_CURRENT_VERSION)
{
char message[2048] = {0};
strcpy(message, "This project was saved using different version of TAS Editor!\n\n");
strcat(message, "Original version: ");
char version_num[11];
_itoa(file_version, version_num, 10);
strncat(message, version_num, 2047 - strlen(message));
strncat(message, "\nCurrent version: ", 2047 - strlen(message));
_itoa(PROJECT_FILE_CURRENT_VERSION, version_num, 10);
strncat(message, version_num, 2047 - strlen(message));
strncat(message, "\n\nClick Yes to try loading all data from the file (may crash).\n", 2047 - strlen(message));
strncat(message, "Click No to only load movie data.\n", 2047 - strlen(message));
strncat(message, "Click Cancel to abort loading.", 2047 - strlen(message));
int answer = MessageBox(taseditor_window.hwndTasEditor, message, "FM3 Version Mismatch", MB_YESNOCANCEL);
if (answer == IDCANCEL)
return false;
else if (answer == IDNO)
load_all = false;
}
} else
{
// couldn't even load header, this seems like an FM2
load_all = false;
char message[2048];
strcpy(message, "This file doesn't seem to be an FM3 project.\nIt only contains FM2 movie data. Load it anyway?");
int answer = MessageBox(taseditor_window.hwndTasEditor, message, "Opening FM2 file", MB_YESNO);
if (answer == IDNO)
return false;
}
// save data to currMovieData and continue loading
FCEU_printf("\nLoading TAS Editor project %s...\n", fullname);
currMovieData = tempMovieData;
LoadSubtitles(currMovieData);
// ensure that movie has correct set of ports/fourscore
SetInputType(currMovieData, GetInputType(currMovieData));
} else
{
FCEU_PrintError("Error loading movie data from %s!", fullname);
// do not load the project
// do not alter the project
return false;
}
// ensure that movie has correct set of ports/fourscore
SetInputType(currMovieData, GetInputType(currMovieData));
// load modules
unsigned int saved_stuff;
read32le(&saved_stuff, &ifs);
markers_manager.load(&ifs);
bookmarks.load(&ifs);
greenzone.load(&ifs);
history.load(&ifs);
piano_roll.load(&ifs);
selection.load(&ifs);
if (load_all)
read32le(&saved_stuff, &ifs);
// load modules
markers_manager.load(&ifs, load_all);
bookmarks.load(&ifs, load_all);
greenzone.load(&ifs, load_all);
history.load(&ifs, load_all);
piano_roll.load(&ifs, load_all);
selection.load(&ifs, load_all);
// reset other modules
playback.reset();
recorder.reset();
@ -233,7 +269,7 @@ bool TASEDITOR_PROJECT::load(char* fullname)
popup_display.reset();
reset();
RenameProject(fullname);
// restore cursor
// restore mouse cursor shape
piano_roll.must_check_item_under_mouse = true;
return true;
}

View File

@ -8,12 +8,12 @@
#include "selection.h"
#include "markers_manager.h"
#include "snapshot.h"
#include "bookmarks.h"
#include "branches.h"
#include "history.h"
#include "playback.h"
#include "recorder.h"
#include "greenzone.h"
#include "bookmarks.h"
#include "branches.h"
#include "piano_roll.h"
#include "taseditor_lua.h"
#include "splicer.h"
@ -29,6 +29,8 @@
#define PIANO_ROLL_SAVED 16
#define SELECTION_SAVED 32
#define PROJECT_FILE_CURRENT_VERSION 1
class TASEDITOR_PROJECT
{
public:

View File

@ -90,7 +90,7 @@ Window_items_struct window_items[TASEDITOR_WINDOW_TOTAL_ITEMS] = {
TASEDITOR_FORWARD_FULL, -1, 0, 0, 0, "Send Playback to next Marker (mouse: Shift+Wheel down) (hotkey: Shift+PageDown)", "", false, 0, 0,
IDC_PROGRESS1, -1, 0, 0, 0, "", "", false, 0, 0,
CHECK_FOLLOW_CURSOR, -1, 0, 0, 0, "The Piano Roll will follow Playback cursor movements", "", false, 0, 0,
CHECK_AUTORESTORE_PLAYBACK, -1, 0, 0, 0, "If you change input above Playback, cursor will run where it was before change", "", false, 0, 0,
CHECK_AUTORESTORE_PLAYBACK, -1, 0, 0, 0, "If you change input above Playback, cursor will run to where it was before change", "", false, 0, 0,
IDC_BOOKMARKSLIST, -1, 0, 0, 0, "Right click = set Bookmark, Left click = jump to Bookmark or load Branch", "", false, 0, 0,
IDC_HISTORYLIST, -1, 0, 0, -1, "Click to revert the movie back to that time", "", false, 0, 0,
IDC_RADIO_ALL, -1, 0, 0, 0, "", "", false, 0, 0,

View File

@ -3,7 +3,7 @@
#define TASEDITOR_WINDOW_TOTAL_ITEMS 43
#define PIANOROLL_IN_WINDOWITEMS 2
#define TOOLTIP_TEXT_MAX_LEN 80
#define TOOLTIP_TEXT_MAX_LEN 82
#define TOOLTIPS_AUTOPOP_TIMEOUT 30000
#define PATTERNS_MENU_POS 5

View File

@ -126,8 +126,6 @@ int GetCheckedAutoFirePattern();
int GetCheckedAutoFireOffset();
//Internal variables-------------------------------------
bool AVIenableHUDrecording = false;
bool AVIdisableMovieMessages = false;
char *md5_asciistr(uint8 digest[16]);
static int winwidth, winheight;
static volatile int nofocus = 0;
@ -402,8 +400,8 @@ void UpdateCheckedMenuItems()
//File Menu
CheckMenuItem(fceumenu, ID_FILE_MOVIE_TOGGLEREAD, movie_readonly ? MF_CHECKED : MF_UNCHECKED);
CheckMenuItem(fceumenu, ID_FILE_OPENLUAWINDOW, LuaConsoleHWnd ? MF_CHECKED : MF_UNCHECKED);
CheckMenuItem(fceumenu, ID_AVI_ENABLEHUDRECORDING, AVIenableHUDrecording ? MF_CHECKED : MF_UNCHECKED);
CheckMenuItem(fceumenu, ID_AVI_DISMOVIEMESSAGE, AVIdisableMovieMessages ? MF_CHECKED : MF_UNCHECKED);
CheckMenuItem(fceumenu, ID_AVI_ENABLEHUDRECORDING, FCEUI_AviEnableHUDrecording() ? MF_CHECKED : MF_UNCHECKED);
CheckMenuItem(fceumenu, ID_AVI_DISMOVIEMESSAGE, FCEUI_AviDisableMovieMessages() ? MF_CHECKED : MF_UNCHECKED);
//NES Menu
CheckMenuItem(fceumenu, ID_NES_PAUSE, EmulationPaused ? MF_CHECKED : MF_UNCHECKED);
@ -1635,14 +1633,19 @@ LRESULT FAR PASCAL AppWndProc(HWND hWnd,UINT msg,WPARAM wParam,LPARAM lParam)
loggingSound = false;
break;
case ID_AVI_ENABLEHUDRECORDING:
{
bool AVIenableHUDrecording = FCEUI_AviEnableHUDrecording();
AVIenableHUDrecording ^= 1;
FCEUI_SetAviEnableHUDrecording(AVIenableHUDrecording);
break;
}
case ID_AVI_DISMOVIEMESSAGE:
{
bool AVIdisableMovieMessages = FCEUI_AviDisableMovieMessages();
AVIdisableMovieMessages ^= 1;
FCEUI_SetAviDisableMovieMessages(AVIdisableMovieMessages);
break;
}
case FCEUX_CONTEXT_SCREENSHOT:
case ID_FILE_SCREENSHOT:
FCEUI_SaveSnapshot();
@ -1834,7 +1837,7 @@ LRESULT FAR PASCAL AppWndProc(HWND hWnd,UINT msg,WPARAM wParam,LPARAM lParam)
UpdateCheckedMenuItems();
PushCurrentVideoSettings();
break;
case MENU_DIRECTORIES:
case MENU_DIRECTORIES:
ConfigDirectories();
break;
case MENU_GUI_OPTIONS:
@ -2257,9 +2260,14 @@ adelikat: Outsourced this to a remappable hotkey
EnableMenuItem(fceumenu,ID_FILE_CLOSELUAWINDOWS,MF_BYCOMMAND | (LuaConsoleHWnd?MF_ENABLED:MF_GRAYED));
if (FCEUMOV_Mode(MOVIEMODE_TASEDITOR))
{
EnableMenuItem(fceumenu, MENU_PAL, false);
EnableMenuItem(fceumenu, ID_NEWPPU, false);
EnableMenuItem(fceumenu, ID_OLDPPU, false);
EnableMenuItem(fceumenu, MENU_PAL, MF_GRAYED);
EnableMenuItem(fceumenu, ID_NEWPPU, MF_GRAYED);
EnableMenuItem(fceumenu, ID_OLDPPU, MF_GRAYED);
} else
{
EnableMenuItem(fceumenu, MENU_PAL, MF_ENABLED);
EnableMenuItem(fceumenu, ID_NEWPPU, MF_ENABLED);
EnableMenuItem(fceumenu, ID_OLDPPU, MF_ENABLED);
}
CheckMenuRadioItem(fceumenu, ID_NEWPPU, ID_OLDPPU, newppu ? ID_NEWPPU : ID_OLDPPU, MF_BYCOMMAND);

View File

@ -422,7 +422,7 @@ bool FCEUSS_SaveMS(EMUFILE* outstream, int compressionLevel)
int error = Z_OK;
uint8* cbuf = (uint8*)memory_savestate.buf();
uLongf comprlen = -1;
if(compressionLevel != Z_NO_COMPRESSION && compressSavestates)
if(compressionLevel != Z_NO_COMPRESSION && (compressSavestates || FCEUMOV_Mode(MOVIEMODE_TASEDITOR)))
{
// worst case compression: zlib says "0.1% larger than sourceLen plus 12 bytes"
comprlen = (len>>9)+12 + len;