1903 lines
60 KiB
C++
1903 lines
60 KiB
C++
#include <fstream>
|
|
#include <sstream>
|
|
#include "taseditlib/taseditproj.h"
|
|
#include "utils/xstring.h"
|
|
#include "Win32InputBox.h"
|
|
#include "keyboard.h"
|
|
#include "joystick.h"
|
|
#include "help.h"
|
|
#include "main.h"
|
|
#include "tasedit.h"
|
|
|
|
using namespace std;
|
|
|
|
HWND hwndTasEdit = 0;
|
|
HMENU hmenu, hrmenu;
|
|
|
|
bool TASEdit_focus = false;
|
|
bool Tasedit_rewind_now = false;
|
|
|
|
// all Taseditor functional modules
|
|
TASEDIT_PROJECT project;
|
|
INPUT_HISTORY history;
|
|
PLAYBACK playback;
|
|
RECORDER recorder;
|
|
GREENZONE greenzone;
|
|
MARKERS markers;
|
|
BOOKMARKS bookmarks;
|
|
SCREENSHOT_DISPLAY screenshot_display;
|
|
TASEDIT_LIST tasedit_list;
|
|
TASEDIT_SELECTION selection;
|
|
|
|
// saved FCEU config
|
|
int saved_eoptions;
|
|
int saved_EnableAutosave;
|
|
extern int EnableAutosave;
|
|
|
|
extern EMOVIEMODE movieMode; // maybe we need normal setter for movieMode, to encapsulate it
|
|
extern void UpdateCheckedMenuItems();
|
|
|
|
// vars saved in cfg file (need dedicated storage class?)
|
|
int TasEdit_wndx, TasEdit_wndy;
|
|
bool TASEdit_follow_playback = true;
|
|
bool TASEdit_turbo_seek = true;
|
|
bool TASEdit_show_lag_frames = true;
|
|
bool TASEdit_show_markers = true;
|
|
bool TASEdit_show_branch_screenshots = true;
|
|
bool TASEdit_bind_markers = true;
|
|
bool TASEdit_use_1p_rec = true;
|
|
bool TASEdit_combine_consecutive_rec = true;
|
|
bool TASEdit_superimpose_affects_paste = true;
|
|
int TASEdit_superimpose = BST_UNCHECKED;
|
|
bool TASEdit_branch_full_movie = true;
|
|
bool TASEdit_branch_only_when_rec = false;
|
|
bool TASEdit_view_branches_tree = false;
|
|
bool TASEdit_branch_scr_hud = true;
|
|
bool TASEdit_restore_position = false;
|
|
int TASEdit_greenzone_capacity = GREENZONE_CAPACITY_DEFAULT;
|
|
int TasEdit_undo_levels = UNDO_LEVELS_DEFAULT;
|
|
int TASEdit_autosave_period = AUTOSAVE_PERIOD_DEFAULT;
|
|
extern bool muteTurbo;
|
|
bool TASEdit_enable_hot_changes = true;
|
|
bool TASEdit_jump_to_undo = true;
|
|
int TASEdit_last_export_type = EXPORT_TYPE_1P;
|
|
bool TASEdit_savecompact_binary = true;
|
|
bool TASEdit_savecompact_markers = true;
|
|
bool TASEdit_savecompact_bookmarks = true;
|
|
bool TASEdit_savecompact_greenzone = false;
|
|
bool TASEdit_savecompact_history = false;
|
|
bool TASEdit_savecompact_selection = false;
|
|
bool TASEdit_savecompact_list = true;
|
|
|
|
// Recent Menu
|
|
HMENU recent_projects_menu;
|
|
char* recent_projects[] = { 0, 0, 0, 0, 0, 0, 0, 0, 0, 0 };
|
|
const unsigned int MENU_FIRST_RECENT_PROJECT = 55000;
|
|
const unsigned int MAX_NUMBER_OF_RECENT_PROJECTS = sizeof(recent_projects)/sizeof(*recent_projects);
|
|
|
|
// resources
|
|
string tasedithelp = "{16CDE0C4-02B0-4A60-A88D-076319909A4D}"; //Name of TAS Editor Help page
|
|
char buttonNames[NUM_JOYPAD_BUTTONS][2] = {"A", "B", "S", "T", "U", "D", "L", "R"};
|
|
char windowCaptioBase[] = "TAS Editor";
|
|
extern char recordingCaptions[5][30];
|
|
|
|
// enterframe function
|
|
void UpdateTasEdit()
|
|
{
|
|
if(!hwndTasEdit) return;
|
|
|
|
tasedit_list.update();
|
|
markers.update();
|
|
greenzone.update();
|
|
playback.update();
|
|
recorder.update();
|
|
bookmarks.update();
|
|
screenshot_display.update();
|
|
selection.update();
|
|
history.update();
|
|
project.update();
|
|
}
|
|
|
|
void RedrawWindowCaption()
|
|
{
|
|
char new_caption[300];
|
|
strcpy(new_caption, windowCaptioBase);
|
|
if (!movie_readonly)
|
|
strcat(new_caption, recordingCaptions[recorder.multitrack_recording_joypad]);
|
|
// add project name
|
|
std::string projectname = project.GetProjectName();
|
|
if (!projectname.empty())
|
|
{
|
|
strcat(new_caption, " - ");
|
|
strcat(new_caption, projectname.c_str());
|
|
}
|
|
// and * if project has unsaved changes
|
|
if (project.GetProjectChanged())
|
|
strcat(new_caption, "*");
|
|
SetWindowText(hwndTasEdit, new_caption);
|
|
}
|
|
void RedrawTasedit()
|
|
{
|
|
InvalidateRect(hwndTasEdit, 0, FALSE);
|
|
}
|
|
|
|
void StrayClickMenu(LPNMITEMACTIVATE info)
|
|
{
|
|
POINT pt = info->ptAction;
|
|
ClientToScreen(tasedit_list.hwndList, &pt);
|
|
HMENU sub = GetSubMenu(hrmenu, CONTEXTMENU_STRAY);
|
|
TrackPopupMenu(sub, 0, pt.x, pt.y, 0, hwndTasEdit, 0);
|
|
}
|
|
void RightClickMenu(LPNMITEMACTIVATE info)
|
|
{
|
|
POINT pt = info->ptAction;
|
|
ClientToScreen(tasedit_list.hwndList, &pt);
|
|
|
|
SelectionFrames* current_selection = selection.MakeStrobe();
|
|
if (current_selection->size() == 0)
|
|
{
|
|
StrayClickMenu(info);
|
|
return;
|
|
}
|
|
HMENU sub = GetSubMenu(hrmenu, CONTEXTMENU_SELECTED);
|
|
// inspect current selection and disable inappropriate menu items
|
|
SelectionFrames::iterator current_selection_begin(current_selection->begin());
|
|
SelectionFrames::iterator current_selection_end(current_selection->end());
|
|
bool set_found = false, unset_found = false;
|
|
for(SelectionFrames::iterator it(current_selection_begin); it != current_selection_end; it++)
|
|
{
|
|
if(markers.GetMarker(*it))
|
|
set_found = true;
|
|
else
|
|
unset_found = true;
|
|
}
|
|
if (set_found)
|
|
EnableMenuItem(sub, ID_SELECTED_REMOVEMARKER, MF_BYCOMMAND | MF_ENABLED);
|
|
else
|
|
EnableMenuItem(sub, ID_SELECTED_REMOVEMARKER, MF_BYCOMMAND | MF_DISABLED | MF_GRAYED);
|
|
if (unset_found)
|
|
EnableMenuItem(sub, ID_SELECTED_SETMARKER, MF_BYCOMMAND | MF_ENABLED);
|
|
else
|
|
EnableMenuItem(sub, ID_SELECTED_SETMARKER, MF_BYCOMMAND | MF_DISABLED | MF_GRAYED);
|
|
|
|
TrackPopupMenu(sub, 0, pt.x, pt.y, 0, hwndTasEdit, 0);
|
|
}
|
|
void RightClick(LPNMITEMACTIVATE info)
|
|
{
|
|
int index = info->iItem;
|
|
if(index == -1)
|
|
StrayClickMenu(info);
|
|
else if (selection.CheckFrameSelected(index))
|
|
RightClickMenu(info);
|
|
}
|
|
|
|
void ToggleJoypadBit(int column_index, int row_index, UINT KeyFlags)
|
|
{
|
|
int joy = (column_index - COLUMN_JOYPAD1_A) / NUM_JOYPAD_BUTTONS;
|
|
int bit = (column_index - COLUMN_JOYPAD1_A) % NUM_JOYPAD_BUTTONS;
|
|
if (KeyFlags & (LVKF_SHIFT|LVKF_CONTROL))
|
|
{
|
|
//update multiple rows
|
|
SelectionFrames* current_selection = selection.MakeStrobe();
|
|
SelectionFrames::iterator current_selection_end(current_selection->end());
|
|
for(SelectionFrames::iterator it(current_selection->begin()); it != current_selection_end; it++)
|
|
{
|
|
currMovieData.records[*it].toggleBit(joy,bit);
|
|
}
|
|
greenzone.InvalidateAndCheck(history.RegisterChanges(MODTYPE_CHANGE, *current_selection->begin(), *current_selection->rbegin()));
|
|
} else
|
|
{
|
|
//update one row
|
|
currMovieData.records[row_index].toggleBit(joy,bit);
|
|
if (currMovieData.records[row_index].checkBit(joy,bit))
|
|
greenzone.InvalidateAndCheck(history.RegisterChanges(MODTYPE_SET, row_index, row_index));
|
|
else
|
|
greenzone.InvalidateAndCheck(history.RegisterChanges(MODTYPE_UNSET, row_index, row_index));
|
|
}
|
|
|
|
}
|
|
|
|
void SingleClick(LPNMITEMACTIVATE info)
|
|
{
|
|
int row_index = info->iItem;
|
|
if(row_index == -1) return;
|
|
int column_index = info->iSubItem;
|
|
|
|
if(column_index == COLUMN_ICONS)
|
|
{
|
|
// click on the "icons" column - jump to the frame
|
|
selection.ClearSelection();
|
|
playback.jump(row_index);
|
|
} else if(column_index == COLUMN_FRAMENUM || column_index == COLUMN_FRAMENUM2)
|
|
{
|
|
// click on the "frame number" column - set marker if clicked with Alt
|
|
if (info->uKeyFlags & LVKF_ALT)
|
|
{
|
|
// reverse MARKER_FLAG_BIT in pointed frame
|
|
markers.ToggleMarker(row_index);
|
|
if (markers.GetMarker(row_index))
|
|
history.RegisterChanges(MODTYPE_MARKER_SET, row_index);
|
|
else
|
|
history.RegisterChanges(MODTYPE_MARKER_UNSET, row_index);
|
|
project.SetProjectChanged();
|
|
tasedit_list.RedrawRow(row_index);
|
|
}
|
|
}
|
|
else if(column_index >= COLUMN_JOYPAD1_A && column_index <= COLUMN_JOYPAD4_R)
|
|
{
|
|
ToggleJoypadBit(column_index, row_index, info->uKeyFlags);
|
|
}
|
|
}
|
|
|
|
void DoubleClick(LPNMITEMACTIVATE info)
|
|
{
|
|
int row_index = info->iItem;
|
|
if(row_index == -1) return;
|
|
int column_index = info->iSubItem;
|
|
|
|
if(column_index == COLUMN_ICONS || column_index == COLUMN_FRAMENUM || column_index == COLUMN_FRAMENUM2)
|
|
{
|
|
// double click sends playback to the frame
|
|
selection.ClearSelection();
|
|
playback.jump(row_index);
|
|
} else if(column_index >= COLUMN_JOYPAD1_A && column_index <= COLUMN_JOYPAD4_R)
|
|
{
|
|
ToggleJoypadBit(column_index, row_index, info->uKeyFlags);
|
|
}
|
|
}
|
|
|
|
void CloneFrames()
|
|
{
|
|
SelectionFrames* current_selection = selection.MakeStrobe();
|
|
int frames = current_selection->size();
|
|
if (!frames) return;
|
|
|
|
currMovieData.records.reserve(currMovieData.getNumRecords() + frames);
|
|
//insert frames before each selection, but consecutive selection lines are accounted as single region
|
|
frames = 1;
|
|
SelectionFrames::reverse_iterator next_it;
|
|
SelectionFrames::reverse_iterator current_selection_rend = current_selection->rend();
|
|
for(SelectionFrames::reverse_iterator it(current_selection->rbegin()); it != current_selection_rend; it++)
|
|
{
|
|
next_it = it;
|
|
next_it++;
|
|
if (next_it == current_selection_rend || (int)*next_it < ((int)*it - 1))
|
|
{
|
|
// end of current region
|
|
currMovieData.cloneRegion(*it, frames);
|
|
if (TASEdit_bind_markers)
|
|
markers.insertEmpty(*it, frames);
|
|
frames = 1;
|
|
} else frames++;
|
|
}
|
|
markers.update();
|
|
greenzone.InvalidateAndCheck(history.RegisterChanges(MODTYPE_CLONE, *current_selection->begin()));
|
|
}
|
|
|
|
void InsertFrames()
|
|
{
|
|
SelectionFrames* current_selection = selection.MakeStrobe();
|
|
int frames = current_selection->size();
|
|
if (!frames) return;
|
|
|
|
//to keep this from being even slower than it would otherwise be, go ahead and reserve records
|
|
currMovieData.records.reserve(currMovieData.getNumRecords() + frames);
|
|
|
|
//insert frames before each selection, but consecutive selection lines are accounted as single region
|
|
frames = 1;
|
|
SelectionFrames::reverse_iterator next_it;
|
|
SelectionFrames::reverse_iterator current_selection_rend = current_selection->rend();
|
|
for(SelectionFrames::reverse_iterator it(current_selection->rbegin()); it != current_selection_rend; it++)
|
|
{
|
|
next_it = it;
|
|
next_it++;
|
|
if (next_it == current_selection_rend || (int)*next_it < ((int)*it - 1))
|
|
{
|
|
// end of current region
|
|
currMovieData.insertEmpty(*it,frames);
|
|
if (TASEdit_bind_markers)
|
|
markers.insertEmpty(*it,frames);
|
|
frames = 1;
|
|
} else frames++;
|
|
}
|
|
markers.update();
|
|
greenzone.InvalidateAndCheck(history.RegisterChanges(MODTYPE_INSERT, *current_selection->begin()));
|
|
}
|
|
|
|
void InsertNumFrames()
|
|
{
|
|
SelectionFrames* current_selection = selection.MakeStrobe();
|
|
int frames = current_selection->size();
|
|
if(CWin32InputBox::GetInteger("Insert number of Frames", "How many frames?", frames, hwndTasEdit) == IDOK)
|
|
{
|
|
if (frames > 0)
|
|
{
|
|
int index;
|
|
if (current_selection->size())
|
|
{
|
|
// insert at selection
|
|
index = *current_selection->begin();
|
|
selection.ClearSelection();
|
|
} else
|
|
{
|
|
// insert at playback cursor
|
|
index = currFrameCounter;
|
|
}
|
|
currMovieData.insertEmpty(index, frames);
|
|
if (TASEdit_bind_markers)
|
|
markers.insertEmpty(index, frames);
|
|
// select inserted rows
|
|
tasedit_list.update();
|
|
selection.SetRegionSelection(index, index + frames - 1);
|
|
greenzone.InvalidateAndCheck(history.RegisterChanges(MODTYPE_INSERT, index));
|
|
}
|
|
}
|
|
}
|
|
|
|
void DeleteFrames()
|
|
{
|
|
SelectionFrames* current_selection = selection.MakeStrobe();
|
|
if (current_selection->size() == 0) return;
|
|
|
|
int start_index = *current_selection->begin();
|
|
int end_index = *current_selection->rbegin();
|
|
SelectionFrames::reverse_iterator current_selection_rend = current_selection->rend();
|
|
//delete frames on each selection, going backwards
|
|
for(SelectionFrames::reverse_iterator it(current_selection->rbegin()); it != current_selection_rend; it++)
|
|
{
|
|
currMovieData.records.erase(currMovieData.records.begin() + *it);
|
|
if (TASEdit_bind_markers)
|
|
markers.EraseMarker(*it);
|
|
}
|
|
// check if user deleted all frames
|
|
if (!currMovieData.getNumRecords())
|
|
playback.StartFromZero();
|
|
// reduce list
|
|
tasedit_list.update();
|
|
|
|
int result = history.RegisterChanges(MODTYPE_DELETE, start_index);
|
|
if (result >= 0)
|
|
{
|
|
greenzone.InvalidateAndCheck(result);
|
|
} else if (greenzone.greenZoneCount >= currMovieData.getNumRecords())
|
|
{
|
|
greenzone.InvalidateAndCheck(currMovieData.getNumRecords()-1);
|
|
} else tasedit_list.RedrawList();
|
|
}
|
|
|
|
void ClearFrames(SelectionFrames* current_selection)
|
|
{
|
|
bool cut = true;
|
|
if (!current_selection)
|
|
{
|
|
cut = false;
|
|
current_selection = selection.MakeStrobe();
|
|
if (current_selection->size() == 0) return;
|
|
}
|
|
|
|
//clear input on each selected frame
|
|
SelectionFrames::iterator current_selection_end(current_selection->end());
|
|
for(SelectionFrames::iterator it(current_selection->begin()); it != current_selection_end; it++)
|
|
{
|
|
currMovieData.records[*it].clear();
|
|
}
|
|
if (cut)
|
|
greenzone.InvalidateAndCheck(history.RegisterChanges(MODTYPE_CUT, *current_selection->begin(), *current_selection->rbegin()));
|
|
else
|
|
greenzone.InvalidateAndCheck(history.RegisterChanges(MODTYPE_CLEAR, *current_selection->begin(), *current_selection->rbegin()));
|
|
}
|
|
|
|
void Truncate()
|
|
{
|
|
int frame = selection.GetCurrentSelectionBeginning();
|
|
if (frame < 0) frame = currFrameCounter;
|
|
|
|
if (currMovieData.getNumRecords() > frame+1)
|
|
{
|
|
currMovieData.truncateAt(frame+1);
|
|
if (TASEdit_bind_markers)
|
|
markers.truncateAt(frame+1);
|
|
tasedit_list.update();
|
|
int result = history.RegisterChanges(MODTYPE_TRUNCATE, frame+1);
|
|
if (result >= 0)
|
|
{
|
|
greenzone.InvalidateAndCheck(result);
|
|
} else if (greenzone.greenZoneCount >= currMovieData.getNumRecords())
|
|
{
|
|
greenzone.InvalidateAndCheck(currMovieData.getNumRecords()-1);
|
|
} else tasedit_list.RedrawList();
|
|
}
|
|
}
|
|
|
|
void ColumnSet(int column)
|
|
{
|
|
if (column == COLUMN_FRAMENUM || column == COLUMN_FRAMENUM2)
|
|
FrameColumnSet();
|
|
else
|
|
InputColumnSet(column);
|
|
}
|
|
void FrameColumnSet()
|
|
{
|
|
SelectionFrames* current_selection = selection.MakeStrobe();
|
|
if (current_selection->size() == 0) return;
|
|
SelectionFrames::iterator current_selection_begin(current_selection->begin());
|
|
SelectionFrames::iterator current_selection_end(current_selection->end());
|
|
|
|
// inspect the selected frames, if they are all set, then unset all, else set all
|
|
bool unset_found = false, changes_made = false;
|
|
for(SelectionFrames::iterator it(current_selection_begin); it != current_selection_end; it++)
|
|
{
|
|
if(!markers.GetMarker(*it))
|
|
{
|
|
unset_found = true;
|
|
break;
|
|
}
|
|
}
|
|
if (unset_found)
|
|
{
|
|
|
|
// set all
|
|
for(SelectionFrames::iterator it(current_selection_begin); it != current_selection_end; it++)
|
|
{
|
|
if(!markers.GetMarker(*it))
|
|
{
|
|
changes_made = true;
|
|
markers.SetMarker(*it);
|
|
tasedit_list.RedrawRow(*it);
|
|
}
|
|
}
|
|
if (changes_made)
|
|
history.RegisterChanges(MODTYPE_MARKER_SET, *current_selection_begin, *current_selection->rbegin());
|
|
} else
|
|
{
|
|
// unset all
|
|
for(SelectionFrames::iterator it(current_selection_begin); it != current_selection_end; it++)
|
|
{
|
|
if(markers.GetMarker(*it))
|
|
{
|
|
changes_made = true;
|
|
markers.ClearMarker(*it);
|
|
tasedit_list.RedrawRow(*it);
|
|
}
|
|
}
|
|
if (changes_made)
|
|
history.RegisterChanges(MODTYPE_MARKER_UNSET, *current_selection_begin, *current_selection->rbegin());
|
|
}
|
|
if (changes_made)
|
|
project.SetProjectChanged();
|
|
}
|
|
void InputColumnSet(int column)
|
|
{
|
|
int joy = (column - COLUMN_JOYPAD1_A) / NUM_JOYPAD_BUTTONS;
|
|
if (joy < 0 || joy >= NUM_JOYPADS) return;
|
|
int button = (column - COLUMN_JOYPAD1_A) % NUM_JOYPAD_BUTTONS;
|
|
|
|
SelectionFrames* current_selection = selection.MakeStrobe();
|
|
if (current_selection->size() == 0) return;
|
|
SelectionFrames::iterator current_selection_begin(current_selection->begin());
|
|
SelectionFrames::iterator current_selection_end(current_selection->end());
|
|
|
|
//inspect the selected frames, if they are all set, then unset all, else set all
|
|
bool newValue = false;
|
|
for(SelectionFrames::iterator it(current_selection_begin); it != current_selection_end; it++)
|
|
{
|
|
if(!(currMovieData.records[*it].checkBit(joy,button)))
|
|
{
|
|
newValue = true;
|
|
break;
|
|
}
|
|
}
|
|
// apply newValue
|
|
for(SelectionFrames::iterator it(current_selection_begin); it != current_selection_end; it++)
|
|
currMovieData.records[*it].setBitValue(joy,button,newValue);
|
|
if (newValue)
|
|
{
|
|
greenzone.InvalidateAndCheck(history.RegisterChanges(MODTYPE_SET, *current_selection_begin, *current_selection->rbegin()));
|
|
} else
|
|
{
|
|
greenzone.InvalidateAndCheck(history.RegisterChanges(MODTYPE_UNSET, *current_selection_begin, *current_selection->rbegin()));
|
|
}
|
|
}
|
|
|
|
|
|
bool Copy(SelectionFrames* current_selection)
|
|
{
|
|
if (!current_selection)
|
|
{
|
|
current_selection = selection.MakeStrobe();
|
|
if (current_selection->size() == 0) return false;
|
|
}
|
|
|
|
SelectionFrames::iterator current_selection_begin(current_selection->begin());
|
|
SelectionFrames::iterator current_selection_end(current_selection->end());
|
|
int cframe = (*current_selection_begin) - 1;
|
|
try
|
|
{
|
|
int range = (*current_selection->rbegin() - *current_selection_begin) + 1;
|
|
|
|
std::stringstream clipString;
|
|
clipString << "TAS " << range << std::endl;
|
|
|
|
for(SelectionFrames::iterator it(current_selection_begin); it != current_selection_end; it++)
|
|
{
|
|
if (*it > cframe+1)
|
|
{
|
|
clipString << '+' << (*it-cframe) << '|';
|
|
}
|
|
cframe=*it;
|
|
|
|
int cjoy=0;
|
|
for (int joy = 0; joy < NUM_JOYPADS; ++joy)
|
|
{
|
|
while (currMovieData.records[*it].joysticks[joy] && cjoy<joy)
|
|
{
|
|
clipString << '|';
|
|
++cjoy;
|
|
}
|
|
for (int bit=0; bit<NUM_JOYPAD_BUTTONS; ++bit)
|
|
{
|
|
if (currMovieData.records[*it].joysticks[joy] & (1<<bit))
|
|
{
|
|
clipString << buttonNames[bit];
|
|
}
|
|
}
|
|
}
|
|
clipString << std::endl;
|
|
|
|
if (!OpenClipboard(hwndTasEdit))
|
|
return false;
|
|
EmptyClipboard();
|
|
|
|
HGLOBAL hGlobal = GlobalAlloc(GMEM_MOVEABLE, clipString.str().size()+1);
|
|
|
|
if (hGlobal==INVALID_HANDLE_VALUE)
|
|
{
|
|
CloseClipboard();
|
|
return false;
|
|
}
|
|
char *pGlobal = (char*)GlobalLock(hGlobal);
|
|
strcpy(pGlobal, clipString.str().c_str());
|
|
GlobalUnlock(hGlobal);
|
|
SetClipboardData(CF_TEXT, hGlobal);
|
|
|
|
CloseClipboard();
|
|
}
|
|
|
|
}
|
|
catch (std::bad_alloc e)
|
|
{
|
|
return false;
|
|
}
|
|
// copied successfully
|
|
selection.MemorizeClipboardSelection();
|
|
return true;
|
|
}
|
|
void Cut()
|
|
{
|
|
SelectionFrames* current_selection = selection.MakeStrobe();
|
|
if (current_selection->size() == 0) return;
|
|
|
|
if (Copy(current_selection))
|
|
{
|
|
ClearFrames(current_selection);
|
|
}
|
|
}
|
|
bool Paste()
|
|
{
|
|
SelectionFrames* current_selection = selection.MakeStrobe();
|
|
if (current_selection->size() == 0) return false;
|
|
|
|
if (!OpenClipboard(hwndTasEdit)) return false;
|
|
|
|
SelectionFrames::iterator current_selection_begin(current_selection->begin());
|
|
bool result = false;
|
|
int pos = *current_selection_begin;
|
|
HANDLE hGlobal = GetClipboardData(CF_TEXT);
|
|
if (hGlobal)
|
|
{
|
|
char *pGlobal = (char*)GlobalLock((HGLOBAL)hGlobal);
|
|
|
|
// TAS recording info starts with "TAS "
|
|
if (pGlobal[0]=='T' && pGlobal[1]=='A' && pGlobal[2]=='S')
|
|
{
|
|
// Extract number of frames
|
|
int range;
|
|
sscanf (pGlobal+3, "%d", &range);
|
|
if (currMovieData.getNumRecords() < pos+range)
|
|
{
|
|
currMovieData.insertEmpty(currMovieData.getNumRecords(),pos+range-currMovieData.getNumRecords());
|
|
markers.update();
|
|
}
|
|
|
|
pGlobal = strchr(pGlobal, '\n');
|
|
int joy = 0;
|
|
uint8 new_buttons = 0;
|
|
char* frame;
|
|
--pos;
|
|
while (pGlobal++ && *pGlobal!='\0')
|
|
{
|
|
// Detect skipped frames in paste
|
|
frame = pGlobal;
|
|
if (frame[0]=='+')
|
|
{
|
|
pos += atoi(frame+1);
|
|
while (*frame && *frame != '\n' && *frame!='|')
|
|
++frame;
|
|
if (*frame=='|') ++frame;
|
|
} else
|
|
{
|
|
++pos;
|
|
}
|
|
|
|
if (!TASEdit_superimpose_affects_paste || TASEdit_superimpose == BST_UNCHECKED)
|
|
{
|
|
currMovieData.records[pos].joysticks[0] = 0;
|
|
currMovieData.records[pos].joysticks[1] = 0;
|
|
currMovieData.records[pos].joysticks[2] = 0;
|
|
currMovieData.records[pos].joysticks[3] = 0;
|
|
}
|
|
// read this frame input
|
|
joy = 0;
|
|
new_buttons = 0;
|
|
while (*frame && *frame != '\n' && *frame !='\r')
|
|
{
|
|
switch (*frame)
|
|
{
|
|
case '|': // Joystick mark
|
|
// flush buttons to movie data
|
|
if (TASEdit_superimpose_affects_paste && (TASEdit_superimpose == BST_CHECKED || (TASEdit_superimpose == BST_INDETERMINATE && new_buttons == 0)))
|
|
currMovieData.records[pos].joysticks[joy] |= new_buttons;
|
|
else
|
|
currMovieData.records[pos].joysticks[joy] = new_buttons;
|
|
++joy;
|
|
new_buttons = 0;
|
|
break;
|
|
default:
|
|
for (int bit=0; bit<NUM_JOYPAD_BUTTONS; ++bit)
|
|
{
|
|
if (*frame == buttonNames[bit][0])
|
|
{
|
|
new_buttons |= (1<<bit);
|
|
break;
|
|
}
|
|
}
|
|
break;
|
|
}
|
|
++frame;
|
|
}
|
|
// before going to next frame, flush buttons to movie data
|
|
if (TASEdit_superimpose_affects_paste && (TASEdit_superimpose == BST_CHECKED || (TASEdit_superimpose == BST_INDETERMINATE && new_buttons == 0)))
|
|
currMovieData.records[pos].joysticks[joy] |= new_buttons;
|
|
else
|
|
currMovieData.records[pos].joysticks[joy] = new_buttons;
|
|
|
|
// find CRLF
|
|
pGlobal = strchr(pGlobal, '\n');
|
|
}
|
|
|
|
greenzone.InvalidateAndCheck(history.RegisterChanges(MODTYPE_PASTE, *current_selection_begin));
|
|
result = true;
|
|
}
|
|
GlobalUnlock(hGlobal);
|
|
}
|
|
CloseClipboard();
|
|
return result;
|
|
}
|
|
bool PasteInsert()
|
|
{
|
|
SelectionFrames* current_selection = selection.MakeStrobe();
|
|
if (current_selection->size() == 0) return false;
|
|
|
|
if (!OpenClipboard(hwndTasEdit)) return false;
|
|
|
|
SelectionFrames::iterator current_selection_begin(current_selection->begin());
|
|
bool result = false;
|
|
int pos = *current_selection_begin;
|
|
HANDLE hGlobal = GetClipboardData(CF_TEXT);
|
|
if (hGlobal)
|
|
{
|
|
char *pGlobal = (char*)GlobalLock((HGLOBAL)hGlobal);
|
|
|
|
// TAS recording info starts with "TAS "
|
|
if (pGlobal[0]=='T' && pGlobal[1]=='A' && pGlobal[2]=='S')
|
|
{
|
|
// make sure markers have the same size as movie
|
|
markers.update();
|
|
// init inserted_set (for input history hot changes)
|
|
selection.GetInsertedSet().clear();
|
|
|
|
// Extract number of frames
|
|
int range;
|
|
sscanf (pGlobal+3, "%d", &range);
|
|
|
|
|
|
pGlobal = strchr(pGlobal, '\n');
|
|
char* frame;
|
|
int joy=0;
|
|
--pos;
|
|
while (pGlobal++ && *pGlobal!='\0')
|
|
{
|
|
// Detect skipped frames in paste
|
|
frame = pGlobal;
|
|
if (frame[0]=='+')
|
|
{
|
|
pos += atoi(frame+1);
|
|
if (currMovieData.getNumRecords() < pos)
|
|
{
|
|
currMovieData.insertEmpty(currMovieData.getNumRecords(), pos - currMovieData.getNumRecords());
|
|
markers.update();
|
|
}
|
|
while (*frame && *frame != '\n' && *frame != '|')
|
|
++frame;
|
|
if (*frame=='|') ++frame;
|
|
} else
|
|
{
|
|
++pos;
|
|
}
|
|
|
|
// insert new frame
|
|
currMovieData.insertEmpty(pos, 1);
|
|
if (TASEdit_bind_markers) markers.insertEmpty(pos, 1);
|
|
selection.GetInsertedSet().insert(pos);
|
|
|
|
// read this frame input
|
|
int joy = 0;
|
|
while (*frame && *frame != '\n' && *frame !='\r')
|
|
{
|
|
switch (*frame)
|
|
{
|
|
case '|': // Joystick mark
|
|
++joy;
|
|
break;
|
|
default:
|
|
for (int bit = 0; bit < NUM_JOYPAD_BUTTONS; ++bit)
|
|
{
|
|
if (*frame == buttonNames[bit][0])
|
|
{
|
|
currMovieData.records[pos].joysticks[joy] |= (1<<bit);
|
|
break;
|
|
}
|
|
}
|
|
break;
|
|
}
|
|
++frame;
|
|
}
|
|
|
|
pGlobal = strchr(pGlobal, '\n');
|
|
}
|
|
markers.update();
|
|
greenzone.InvalidateAndCheck(history.RegisterChanges(MODTYPE_PASTEINSERT, *current_selection_begin));
|
|
result = true;
|
|
}
|
|
GlobalUnlock(hGlobal);
|
|
}
|
|
CloseClipboard();
|
|
return result;
|
|
}
|
|
|
|
void OpenProject()
|
|
{
|
|
if (!AskSaveProject()) return;
|
|
|
|
OPENFILENAME ofn;
|
|
memset(&ofn, 0, sizeof(ofn));
|
|
ofn.lStructSize = sizeof(ofn);
|
|
ofn.hwndOwner = hwndTasEdit;
|
|
ofn.hInstance = fceu_hInstance;
|
|
ofn.lpstrTitle = "Open TAS Editor Project";
|
|
const char filter[] = "TAS Editor Projects (*.tas)\0*.tas\0\0";
|
|
ofn.lpstrFilter = filter;
|
|
|
|
char nameo[2048];
|
|
strcpy(nameo, mass_replace(GetRomName(), "|", ".").c_str()); //convert | to . for archive filenames
|
|
|
|
ofn.lpstrFile = nameo;
|
|
ofn.nMaxFile = 2048;
|
|
ofn.Flags = OFN_EXPLORER|OFN_HIDEREADONLY|OFN_OVERWRITEPROMPT|OFN_FILEMUSTEXIST;
|
|
string initdir = FCEU_GetPath(FCEUMKF_MOVIE);
|
|
ofn.lpstrInitialDir = initdir.c_str();
|
|
|
|
if(GetOpenFileName(&ofn)) // If it is a valid filename
|
|
{
|
|
// If they haven't put ".tas", stick it on ourselves
|
|
if (!strstr(nameo, ".tas"))
|
|
strcat(nameo, ".tas");
|
|
LoadProject(nameo);
|
|
}
|
|
}
|
|
bool LoadProject(char* fullname)
|
|
{
|
|
// remember to update fourscore status
|
|
bool last_fourscore = currMovieData.fourscore;
|
|
// try to load project
|
|
if (project.load(fullname))
|
|
{
|
|
// update fourscore status
|
|
if (last_fourscore && !currMovieData.fourscore)
|
|
{
|
|
tasedit_list.RemoveFourscore();
|
|
FCEUD_SetInput(currMovieData.fourscore, currMovieData.microphone, (ESI)currMovieData.ports[0], (ESI)currMovieData.ports[1], (ESIFC)currMovieData.ports[2]);
|
|
} else if (!last_fourscore && currMovieData.fourscore)
|
|
{
|
|
tasedit_list.AddFourscore();
|
|
FCEUD_SetInput(currMovieData.fourscore, currMovieData.microphone, (ESI)currMovieData.ports[0], (ESI)currMovieData.ports[1], (ESIFC)currMovieData.ports[2]);
|
|
}
|
|
UpdateRecentProjectsArray(fullname);
|
|
RedrawTasedit();
|
|
RedrawWindowCaption();
|
|
return true;
|
|
} else
|
|
{
|
|
// failed to load
|
|
RedrawTasedit();
|
|
RedrawWindowCaption();
|
|
return false;
|
|
}
|
|
}
|
|
void LoadRecentProject(int slot)
|
|
{
|
|
char*& fname = recent_projects[slot];
|
|
if(fname && AskSaveProject())
|
|
{
|
|
if (!LoadProject(fname))
|
|
{
|
|
int result = MessageBox(hwndTasEdit, "Remove from list?", "Could Not Open Recent Project", MB_YESNO);
|
|
if (result == IDYES)
|
|
RemoveRecentProject(slot);
|
|
}
|
|
}
|
|
}
|
|
|
|
// Saves current project
|
|
bool SaveProjectAs()
|
|
{
|
|
const char filter[] = "TAS Editor Projects (*.tas)\0*.tas\0All Files (*.*)\0*.*\0\0";
|
|
OPENFILENAME ofn;
|
|
memset(&ofn, 0, sizeof(ofn));
|
|
ofn.lStructSize = sizeof(ofn);
|
|
ofn.hwndOwner = hwndTasEdit;
|
|
ofn.hInstance = fceu_hInstance;
|
|
ofn.lpstrTitle = "Save TAS Editor Project As...";
|
|
ofn.lpstrFilter = filter;
|
|
|
|
char nameo[2048];
|
|
if (project.GetProjectName().empty())
|
|
// suggest ROM name for this project
|
|
strcpy(nameo, mass_replace(GetRomName(), "|", ".").c_str()); //convert | to . for archive filenames
|
|
else
|
|
// suggest current name
|
|
strncpy(nameo, project.GetProjectName().c_str(), 2047);
|
|
|
|
ofn.lpstrFile = nameo;
|
|
ofn.lpstrDefExt = "tas";
|
|
ofn.nMaxFile = 2048;
|
|
ofn.Flags = OFN_EXPLORER|OFN_HIDEREADONLY|OFN_OVERWRITEPROMPT;
|
|
string initdir = FCEU_GetPath(FCEUMKF_MOVIE); //Initial directory
|
|
ofn.lpstrInitialDir = initdir.c_str();
|
|
|
|
if(GetSaveFileName(&ofn)) //If it is a valid filename
|
|
{
|
|
project.RenameProject(nameo);
|
|
project.save();
|
|
UpdateRecentProjectsArray(nameo);
|
|
} else return false;
|
|
// saved successfully - remove * mark from caption
|
|
RedrawWindowCaption();
|
|
return true;
|
|
}
|
|
bool SaveProject()
|
|
{
|
|
if (!project.save())
|
|
return SaveProjectAs();
|
|
else
|
|
RedrawWindowCaption();
|
|
return true;
|
|
}
|
|
|
|
void SaveCompact_GetCheckboxes(HWND hwndDlg)
|
|
{
|
|
TASEdit_savecompact_binary = (SendDlgItemMessage(hwndDlg, IDC_CHECK_BINARY, BM_GETCHECK, 0, 0) == BST_CHECKED);
|
|
TASEdit_savecompact_markers = (SendDlgItemMessage(hwndDlg, IDC_CHECK_MARKERS, BM_GETCHECK, 0, 0) == BST_CHECKED);
|
|
TASEdit_savecompact_bookmarks = (SendDlgItemMessage(hwndDlg, IDC_CHECK_BOOKMARKS, BM_GETCHECK, 0, 0) == BST_CHECKED);
|
|
TASEdit_savecompact_greenzone = (SendDlgItemMessage(hwndDlg, IDC_CHECK_GREENZONE, BM_GETCHECK, 0, 0) == BST_CHECKED);
|
|
TASEdit_savecompact_history = (SendDlgItemMessage(hwndDlg, IDC_CHECK_HISTORY, BM_GETCHECK, 0, 0) == BST_CHECKED);
|
|
TASEdit_savecompact_selection = (SendDlgItemMessage(hwndDlg, IDC_CHECK_SELECTION, BM_GETCHECK, 0, 0) == BST_CHECKED);
|
|
TASEdit_savecompact_list = (SendDlgItemMessage(hwndDlg, IDC_CHECK_LIST, BM_GETCHECK, 0, 0) == BST_CHECKED);
|
|
}
|
|
BOOL CALLBACK SaveCompactProc(HWND hwndDlg, UINT message, WPARAM wParam, LPARAM lParam)
|
|
{
|
|
switch (message)
|
|
{
|
|
case WM_INITDIALOG:
|
|
{
|
|
SetWindowPos(hwndDlg, 0, TasEdit_wndx + 100, TasEdit_wndy + 200, 0, 0, SWP_NOSIZE | SWP_NOZORDER | SWP_NOOWNERZORDER);
|
|
CheckDlgButton(hwndDlg, IDC_CHECK_BINARY, TASEdit_savecompact_binary?MF_CHECKED : MF_UNCHECKED);
|
|
CheckDlgButton(hwndDlg, IDC_CHECK_MARKERS, TASEdit_savecompact_markers?MF_CHECKED : MF_UNCHECKED);
|
|
CheckDlgButton(hwndDlg, IDC_CHECK_BOOKMARKS, TASEdit_savecompact_bookmarks?MF_CHECKED : MF_UNCHECKED);
|
|
CheckDlgButton(hwndDlg, IDC_CHECK_GREENZONE, TASEdit_savecompact_greenzone?MF_CHECKED : MF_UNCHECKED);
|
|
CheckDlgButton(hwndDlg, IDC_CHECK_HISTORY, TASEdit_savecompact_history?MF_CHECKED : MF_UNCHECKED);
|
|
CheckDlgButton(hwndDlg, IDC_CHECK_SELECTION, TASEdit_savecompact_selection?MF_CHECKED : MF_UNCHECKED);
|
|
CheckDlgButton(hwndDlg, IDC_CHECK_LIST, TASEdit_savecompact_list?MF_CHECKED : MF_UNCHECKED);
|
|
return TRUE;
|
|
}
|
|
case WM_COMMAND:
|
|
{
|
|
switch (LOWORD(wParam))
|
|
{
|
|
case IDOK:
|
|
SaveCompact_GetCheckboxes(hwndDlg);
|
|
EndDialog(hwndDlg, 1);
|
|
return TRUE;
|
|
case IDCANCEL:
|
|
SaveCompact_GetCheckboxes(hwndDlg);
|
|
EndDialog(hwndDlg, 0);
|
|
return TRUE;
|
|
}
|
|
break;
|
|
}
|
|
case WM_CLOSE:
|
|
case WM_QUIT:
|
|
{
|
|
SaveCompact_GetCheckboxes(hwndDlg);
|
|
break;
|
|
}
|
|
}
|
|
return FALSE;
|
|
}
|
|
|
|
void SaveCompact()
|
|
{
|
|
if (DialogBox(fceu_hInstance, MAKEINTRESOURCE(IDD_TASEDIT_SAVECOMPACT), hwndTasEdit, SaveCompactProc) > 0)
|
|
{
|
|
const char filter[] = "TAS Editor Projects (*.tas)\0*.tas\0All Files (*.*)\0*.*\0\0";
|
|
OPENFILENAME ofn;
|
|
memset(&ofn, 0, sizeof(ofn));
|
|
ofn.lStructSize = sizeof(ofn);
|
|
ofn.hwndOwner = hwndTasEdit;
|
|
ofn.hInstance = fceu_hInstance;
|
|
ofn.lpstrTitle = "Save Compact";
|
|
ofn.lpstrFilter = filter;
|
|
|
|
char nameo[2048];
|
|
if (project.GetProjectName().empty())
|
|
// suggest ROM name for this project
|
|
strcpy(nameo, mass_replace(GetRomName(), "|", ".").c_str()); //convert | to . for archive filenames
|
|
else
|
|
// suggest current name
|
|
strcpy(nameo, project.GetProjectName().c_str());
|
|
// add "-compact"
|
|
strcat(nameo, "-compact");
|
|
|
|
ofn.lpstrFile = nameo;
|
|
ofn.lpstrDefExt = "tas";
|
|
ofn.nMaxFile = 2048;
|
|
ofn.Flags = OFN_EXPLORER|OFN_HIDEREADONLY|OFN_OVERWRITEPROMPT;
|
|
string initdir = FCEU_GetPath(FCEUMKF_MOVIE); //Initial directory
|
|
ofn.lpstrInitialDir = initdir.c_str();
|
|
|
|
if(GetSaveFileName(&ofn)) //If it is a valid filename
|
|
{
|
|
project.save_compact(nameo, TASEdit_savecompact_binary, TASEdit_savecompact_markers, TASEdit_savecompact_bookmarks, TASEdit_savecompact_greenzone, TASEdit_savecompact_history, TASEdit_savecompact_selection, TASEdit_savecompact_list);
|
|
}
|
|
}
|
|
}
|
|
|
|
// returns false if user doesn't want to exit
|
|
bool AskSaveProject()
|
|
{
|
|
bool changes_found = false;
|
|
if (project.GetProjectChanged()) changes_found = true;
|
|
|
|
// ask saving project
|
|
if (changes_found)
|
|
{
|
|
int answer = MessageBox(hwndTasEdit, "Save Project changes?", "TAS Editor", MB_YESNOCANCEL);
|
|
if(answer == IDYES)
|
|
return SaveProject();
|
|
return (answer != IDCANCEL);
|
|
}
|
|
return true;
|
|
}
|
|
|
|
extern bool LoadFM2(MovieData& movieData, EMUFILE* fp, int size, bool stopAfterHeader);
|
|
void Import()
|
|
{
|
|
const char filter[] = "FCEUX Movie Files (*.fm2), TAS Editor Projects (*.tas)\0*.fm2;*.tas\0All Files (*.*)\0*.*\0\0";
|
|
OPENFILENAME ofn;
|
|
memset(&ofn, 0, sizeof(ofn));
|
|
ofn.lStructSize = sizeof(ofn);
|
|
ofn.hwndOwner = hwndTasEdit;
|
|
ofn.hInstance = fceu_hInstance;
|
|
ofn.lpstrTitle = "Import";
|
|
ofn.lpstrFilter = filter;
|
|
char nameo[2048] = {0};
|
|
ofn.lpstrFile = nameo;
|
|
ofn.nMaxFile = 2048;
|
|
ofn.Flags = OFN_EXPLORER|OFN_HIDEREADONLY|OFN_OVERWRITEPROMPT|OFN_FILEMUSTEXIST;
|
|
string initdir = FCEU_GetPath(FCEUMKF_MOVIE);
|
|
ofn.lpstrInitialDir = initdir.c_str();
|
|
|
|
if(GetOpenFileName(&ofn))
|
|
{
|
|
EMUFILE_FILE ifs(nameo, "rb");
|
|
// Load input to temporary moviedata
|
|
MovieData md;
|
|
if (LoadFM2(md, &ifs, ifs.size(), false))
|
|
{
|
|
// loaded successfully, now register input changes
|
|
char drv[512], dir[512], name[1024], ext[512];
|
|
splitpath(nameo, drv, dir, name, ext);
|
|
strcat(name, ext);
|
|
history.RegisterImport(md, name);
|
|
} else
|
|
{
|
|
FCEUD_PrintError("Error loading movie data!");
|
|
}
|
|
}
|
|
}
|
|
|
|
BOOL CALLBACK ExportProc(HWND hwndDlg, UINT message, WPARAM wParam, LPARAM lParam)
|
|
{
|
|
switch (message)
|
|
{
|
|
case WM_INITDIALOG:
|
|
SetWindowPos(hwndDlg, 0, TasEdit_wndx + 100, TasEdit_wndy + 200, 0, 0, SWP_NOSIZE | SWP_NOZORDER | SWP_NOOWNERZORDER);
|
|
switch (TASEdit_last_export_type)
|
|
{
|
|
case EXPORT_TYPE_1P:
|
|
{
|
|
Button_SetCheck(GetDlgItem(hwndDlg, IDC_RADIO_1PLAYER), BST_CHECKED);
|
|
break;
|
|
}
|
|
case EXPORT_TYPE_2P:
|
|
{
|
|
Button_SetCheck(GetDlgItem(hwndDlg, IDC_RADIO_2PLAYERS), BST_CHECKED);
|
|
break;
|
|
}
|
|
case EXPORT_TYPE_FOURSCORE:
|
|
{
|
|
Button_SetCheck(GetDlgItem(hwndDlg, IDC_RADIO_FOURSCORE), BST_CHECKED);
|
|
break;
|
|
}
|
|
}
|
|
return TRUE;
|
|
case WM_COMMAND:
|
|
switch (LOWORD(wParam))
|
|
{
|
|
case IDC_RADIO_1PLAYER:
|
|
TASEdit_last_export_type = EXPORT_TYPE_1P;
|
|
break;
|
|
case IDC_RADIO_2PLAYERS:
|
|
TASEdit_last_export_type = EXPORT_TYPE_2P;
|
|
break;
|
|
case IDC_RADIO_FOURSCORE:
|
|
TASEdit_last_export_type = EXPORT_TYPE_FOURSCORE;
|
|
break;
|
|
case IDOK:
|
|
EndDialog(hwndDlg, 1);
|
|
return TRUE;
|
|
case IDCANCEL:
|
|
EndDialog(hwndDlg, 0);
|
|
return TRUE;
|
|
}
|
|
break;
|
|
}
|
|
return FALSE;
|
|
}
|
|
|
|
void Export()
|
|
{
|
|
if (DialogBox(fceu_hInstance, MAKEINTRESOURCE(IDD_TASEDIT_EXPORT), hwndTasEdit, ExportProc) > 0)
|
|
{
|
|
const char filter[] = "FCEUX Movie File (*.fm2)\0*.fm2\0All Files (*.*)\0*.*\0\0";
|
|
char fname[2048];
|
|
strcpy(fname, project.GetFM2Name().c_str());
|
|
OPENFILENAME ofn;
|
|
memset(&ofn, 0, sizeof(ofn));
|
|
ofn.lStructSize = sizeof(ofn);
|
|
ofn.hwndOwner = hwndTasEdit;
|
|
ofn.hInstance = fceu_hInstance;
|
|
ofn.lpstrTitle = "Export to FM2";
|
|
ofn.lpstrFilter = filter;
|
|
ofn.lpstrFile = fname;
|
|
ofn.lpstrDefExt = "fm2";
|
|
ofn.nMaxFile = 2048;
|
|
ofn.Flags = OFN_EXPLORER|OFN_HIDEREADONLY|OFN_OVERWRITEPROMPT;
|
|
std::string initdir = FCEU_GetPath(FCEUMKF_MOVIE);
|
|
ofn.lpstrInitialDir = initdir.c_str();
|
|
if(GetSaveFileName(&ofn))
|
|
{
|
|
EMUFILE* osRecordingMovie = FCEUD_UTF8_fstream(fname, "wb");
|
|
// create copy of current movie data
|
|
MovieData temp_md = currMovieData;
|
|
// modify the copy according to selected type of export
|
|
switch (TASEdit_last_export_type)
|
|
{
|
|
case EXPORT_TYPE_1P:
|
|
{
|
|
temp_md.fourscore = false;
|
|
temp_md.ports[0] = SI_GAMEPAD;
|
|
temp_md.ports[1] = SI_NONE;
|
|
break;
|
|
}
|
|
case EXPORT_TYPE_2P:
|
|
{
|
|
temp_md.fourscore = false;
|
|
temp_md.ports[0] = SI_GAMEPAD;
|
|
temp_md.ports[1] = SI_GAMEPAD;
|
|
break;
|
|
}
|
|
case EXPORT_TYPE_FOURSCORE:
|
|
{
|
|
temp_md.fourscore = true;
|
|
break;
|
|
}
|
|
}
|
|
temp_md.loadFrameCount = -1;
|
|
// dump to disk
|
|
temp_md.dump(osRecordingMovie, false);
|
|
delete osRecordingMovie;
|
|
osRecordingMovie = 0;
|
|
}
|
|
}
|
|
}
|
|
|
|
BOOL CALLBACK WndprocTasEdit(HWND hwndDlg, UINT uMsg, WPARAM wParam, LPARAM lParam)
|
|
{
|
|
switch(uMsg)
|
|
{
|
|
case WM_PAINT:
|
|
break;
|
|
case WM_INITDIALOG:
|
|
if (TasEdit_wndx==-32000) TasEdit_wndx=0; //Just in case
|
|
if (TasEdit_wndy==-32000) TasEdit_wndy=0;
|
|
SetWindowPos(hwndDlg, 0, TasEdit_wndx, TasEdit_wndy, 0, 0, SWP_NOSIZE | SWP_NOZORDER | SWP_NOOWNERZORDER);
|
|
break;
|
|
case WM_MOVE:
|
|
{
|
|
if (!IsIconic(hwndDlg))
|
|
{
|
|
RECT wrect;
|
|
GetWindowRect(hwndDlg, &wrect);
|
|
TasEdit_wndx = wrect.left;
|
|
TasEdit_wndy = wrect.top;
|
|
WindowBoundsCheckNoResize(TasEdit_wndx, TasEdit_wndy, wrect.right);
|
|
// also move screenshot display if it's open
|
|
screenshot_display.ParentWindowMoved();
|
|
}
|
|
break;
|
|
}
|
|
case WM_NOTIFY:
|
|
switch(wParam)
|
|
{
|
|
case IDC_LIST1:
|
|
switch(((LPNMHDR)lParam)->code)
|
|
{
|
|
case NM_CUSTOMDRAW:
|
|
SetWindowLong(hwndDlg, DWL_MSGRESULT, tasedit_list.CustomDraw((NMLVCUSTOMDRAW*)lParam));
|
|
return TRUE;
|
|
case LVN_GETDISPINFO:
|
|
tasedit_list.GetDispInfo((NMLVDISPINFO*)lParam);
|
|
break;
|
|
case NM_CLICK:
|
|
SingleClick((LPNMITEMACTIVATE)lParam);
|
|
break;
|
|
case NM_DBLCLK:
|
|
DoubleClick((LPNMITEMACTIVATE)lParam);
|
|
break;
|
|
case NM_RCLICK:
|
|
RightClick((LPNMITEMACTIVATE)lParam);
|
|
break;
|
|
case LVN_ITEMCHANGED:
|
|
selection.ItemChanged((LPNMLISTVIEW) lParam);
|
|
break;
|
|
case LVN_ODSTATECHANGED:
|
|
selection.ItemRangeChanged((LPNMLVODSTATECHANGE) lParam);
|
|
break;
|
|
/*
|
|
case LVN_ENDSCROLL:
|
|
// redraw upper and lower list rows (fix for known WinXP bug)
|
|
int start = ListView_GetTopIndex(hwndList);
|
|
ListView_RedrawItems(hwndList,start,start);
|
|
int end = start + listItems - 1;
|
|
ListView_RedrawItems(hwndList,end,end);
|
|
break;
|
|
*/
|
|
}
|
|
break;
|
|
case IDC_BOOKMARKSLIST:
|
|
switch(((LPNMHDR)lParam)->code)
|
|
{
|
|
case NM_CUSTOMDRAW:
|
|
SetWindowLong(hwndDlg, DWL_MSGRESULT, bookmarks.CustomDraw((NMLVCUSTOMDRAW*)lParam));
|
|
return TRUE;
|
|
case LVN_GETDISPINFO:
|
|
bookmarks.GetDispInfo((NMLVDISPINFO*)lParam);
|
|
break;
|
|
case NM_CLICK:
|
|
case NM_DBLCLK:
|
|
bookmarks.LeftClick((LPNMITEMACTIVATE)lParam);
|
|
break;
|
|
case NM_RCLICK:
|
|
bookmarks.RightClick((LPNMITEMACTIVATE)lParam);
|
|
}
|
|
break;
|
|
case IDC_HISTORYLIST:
|
|
switch(((LPNMHDR)lParam)->code)
|
|
{
|
|
case NM_CUSTOMDRAW:
|
|
SetWindowLong(hwndDlg, DWL_MSGRESULT, history.CustomDraw((NMLVCUSTOMDRAW*)lParam));
|
|
return TRUE;
|
|
case LVN_GETDISPINFO:
|
|
history.GetDispInfo((NMLVDISPINFO*)lParam);
|
|
break;
|
|
case NM_CLICK:
|
|
case NM_DBLCLK:
|
|
case NM_RCLICK:
|
|
history.Click((LPNMITEMACTIVATE)lParam);
|
|
break;
|
|
}
|
|
break;
|
|
case TASEDIT_PLAYSTOP:
|
|
switch(((LPNMHDR)lParam)->code)
|
|
{
|
|
case NM_CLICK:
|
|
case NM_DBLCLK:
|
|
playback.ToggleEmulationPause();
|
|
break;
|
|
}
|
|
break;
|
|
}
|
|
break;
|
|
case WM_CLOSE:
|
|
case WM_QUIT:
|
|
ExitTasEdit();
|
|
break;
|
|
case WM_ACTIVATE:
|
|
if(LOWORD(wParam))
|
|
GotFocus();
|
|
else
|
|
LostFocus();
|
|
break;
|
|
case WM_COMMAND:
|
|
{
|
|
unsigned int loword_wparam = LOWORD(wParam);
|
|
// first check clicking Recent submenu item
|
|
if (loword_wparam >= MENU_FIRST_RECENT_PROJECT && loword_wparam < MENU_FIRST_RECENT_PROJECT + MAX_NUMBER_OF_RECENT_PROJECTS)
|
|
{
|
|
LoadRecentProject(loword_wparam - MENU_FIRST_RECENT_PROJECT);
|
|
break;
|
|
}
|
|
// then check all other commands
|
|
switch(loword_wparam)
|
|
{
|
|
case ID_FILE_OPENPROJECT:
|
|
OpenProject();
|
|
break;
|
|
case ACCEL_CTRL_S:
|
|
case ID_FILE_SAVEPROJECT:
|
|
SaveProject();
|
|
break;
|
|
case ID_FILE_SAVEPROJECTAS:
|
|
SaveProjectAs();
|
|
break;
|
|
case ID_FILE_SAVECOMPACT:
|
|
SaveCompact();
|
|
break;
|
|
case ID_FILE_IMPORT:
|
|
Import();
|
|
break;
|
|
case ID_FILE_EXPORTFM2:
|
|
Export();
|
|
break;
|
|
case ID_TASEDIT_FILE_CLOSE:
|
|
ExitTasEdit();
|
|
break;
|
|
case ID_EDIT_SELECTALL:
|
|
selection.SelectAll();
|
|
break;
|
|
case ACCEL_CTRL_X:
|
|
case ID_TASEDIT_CUT:
|
|
Cut();
|
|
break;
|
|
case ACCEL_CTRL_C:
|
|
case ID_TASEDIT_COPY:
|
|
Copy();
|
|
break;
|
|
case ACCEL_CTRL_V:
|
|
case ID_TASEDIT_PASTE:
|
|
Paste();
|
|
break;
|
|
case ACCEL_SHIFT_V:
|
|
case ID_EDIT_PASTEINSERT:
|
|
PasteInsert();
|
|
break;
|
|
case ACCEL_CTRL_DELETE:
|
|
case ID_TASEDIT_DELETE:
|
|
case ID_CONTEXT_SELECTED_DELETEFRAMES:
|
|
DeleteFrames();
|
|
break;
|
|
case ACCEL_CTRL_T:
|
|
case ID_EDIT_TRUNCATE:
|
|
case ID_CONTEXT_SELECTED_TRUNCATE:
|
|
case ID_CONTEXT_STRAY_TRUNCATE:
|
|
Truncate();
|
|
break;
|
|
case ID_HELP_TASEDITHELP:
|
|
OpenHelpWindow(tasedithelp);
|
|
//link to TAS Editor in help menu
|
|
break;
|
|
case ACCEL_INS:
|
|
case ID_EDIT_INSERT:
|
|
case MENU_CONTEXT_STRAY_INSERTFRAMES:
|
|
case ID_CONTEXT_SELECTED_INSERTFRAMES2:
|
|
InsertNumFrames();
|
|
break;
|
|
case ACCEL_CTRL_INSERT:
|
|
case ID_EDIT_INSERTFRAMES:
|
|
case ID_CONTEXT_SELECTED_INSERTFRAMES:
|
|
InsertFrames();
|
|
break;
|
|
case ACCEL_DEL:
|
|
case ID_EDIT_CLEAR:
|
|
case ID_CONTEXT_SELECTED_CLEARFRAMES:
|
|
ClearFrames();
|
|
break;
|
|
case TASEDIT_PLAYSTOP:
|
|
playback.ToggleEmulationPause();
|
|
break;
|
|
case ACCEL_CTRL_F:
|
|
case CHECK_FOLLOW_CURSOR:
|
|
//switch "Follow playback" flag
|
|
TASEdit_follow_playback ^= 1;
|
|
CheckDlgButton(hwndTasEdit, CHECK_FOLLOW_CURSOR, TASEdit_follow_playback?MF_CHECKED : MF_UNCHECKED);
|
|
// if switched off then jump to selection
|
|
if (TASEdit_follow_playback)
|
|
tasedit_list.FollowPlayback();
|
|
else if (selection.GetCurrentSelectionSize())
|
|
tasedit_list.FollowSelection();
|
|
else if (playback.GetPauseFrame())
|
|
tasedit_list.FollowPauseframe();
|
|
break;
|
|
case CHECK_TURBO_SEEK:
|
|
//switch "Turbo seek" flag
|
|
TASEdit_turbo_seek ^= 1;
|
|
CheckDlgButton(hwndTasEdit, CHECK_TURBO_SEEK, TASEdit_turbo_seek?MF_CHECKED : MF_UNCHECKED);
|
|
// if currently seeking, apply this option immediately
|
|
if (playback.pause_frame)
|
|
turbo = TASEdit_turbo_seek;
|
|
break;
|
|
case ID_VIEW_SHOW_LAG_FRAMES:
|
|
TASEdit_show_lag_frames ^= 1;
|
|
CheckMenuItem(hmenu, ID_VIEW_SHOW_LAG_FRAMES, TASEdit_show_lag_frames?MF_CHECKED : MF_UNCHECKED);
|
|
tasedit_list.RedrawList();
|
|
bookmarks.RedrawBookmarksList();
|
|
break;
|
|
case ID_VIEW_SHOW_MARKERS:
|
|
TASEdit_show_markers ^= 1;
|
|
CheckMenuItem(hmenu, ID_VIEW_SHOW_MARKERS, TASEdit_show_markers?MF_CHECKED : MF_UNCHECKED);
|
|
tasedit_list.RedrawList(); // no need to redraw Bookmarks, as Markers are only shown in main list
|
|
break;
|
|
case ID_VIEW_SHOWBRANCHSCREENSHOTS:
|
|
//switch "Show Branch Screenshots" flag
|
|
TASEdit_show_branch_screenshots ^= 1;
|
|
CheckMenuItem(hmenu, ID_VIEW_SHOWBRANCHSCREENSHOTS, TASEdit_show_branch_screenshots?MF_CHECKED : MF_UNCHECKED);
|
|
break;
|
|
case ID_VIEW_ENABLEHOTCHANGES:
|
|
TASEdit_enable_hot_changes ^= 1;
|
|
CheckMenuItem(hmenu, ID_VIEW_ENABLEHOTCHANGES, TASEdit_enable_hot_changes?MF_CHECKED : MF_UNCHECKED);
|
|
tasedit_list.RedrawList(); // redraw buttons text
|
|
break;
|
|
case ID_VIEW_JUMPWHENMAKINGUNDO:
|
|
TASEdit_jump_to_undo ^= 1;
|
|
CheckMenuItem(hmenu, ID_VIEW_JUMPWHENMAKINGUNDO, TASEdit_jump_to_undo?MF_CHECKED : MF_UNCHECKED);
|
|
break;
|
|
case ACCEL_CTRL_P:
|
|
case CHECK_AUTORESTORE_PLAYBACK:
|
|
//switch "Auto-restore last playback position" flag
|
|
TASEdit_restore_position ^= 1;
|
|
CheckDlgButton(hwndTasEdit,CHECK_AUTORESTORE_PLAYBACK,TASEdit_restore_position?BST_CHECKED:BST_UNCHECKED);
|
|
break;
|
|
case ID_CONFIG_SETGREENZONECAPACITY:
|
|
{
|
|
int new_capacity = TASEdit_greenzone_capacity;
|
|
if(CWin32InputBox::GetInteger("Greenzone capacity", "Keep savestates for how many frames?\n(actual limit of savestates can be 5 times more than the number provided)", new_capacity, hwndDlg) == IDOK)
|
|
{
|
|
if (new_capacity < GREENZONE_CAPACITY_MIN)
|
|
new_capacity = GREENZONE_CAPACITY_MIN;
|
|
else if (new_capacity > GREENZONE_CAPACITY_MAX)
|
|
new_capacity = GREENZONE_CAPACITY_MAX;
|
|
if (new_capacity < TASEdit_greenzone_capacity)
|
|
{
|
|
TASEdit_greenzone_capacity = new_capacity;
|
|
greenzone.GreenzoneCleaning();
|
|
} else TASEdit_greenzone_capacity = new_capacity;
|
|
}
|
|
break;
|
|
}
|
|
case ID_CONFIG_SETMAXUNDOLEVELS:
|
|
{
|
|
int new_size = TasEdit_undo_levels;
|
|
if(CWin32InputBox::GetInteger("Max undo levels", "Keep history of how many changes?", new_size, hwndDlg) == IDOK)
|
|
{
|
|
if (new_size < UNDO_LEVELS_MIN)
|
|
new_size = UNDO_LEVELS_MIN;
|
|
else if (new_size > UNDO_LEVELS_MAX)
|
|
new_size = UNDO_LEVELS_MAX;
|
|
if (new_size != TasEdit_undo_levels)
|
|
{
|
|
TasEdit_undo_levels = new_size;
|
|
history.reset();
|
|
selection.reset();
|
|
// hot changes were cleared, so update list
|
|
tasedit_list.RedrawList();
|
|
}
|
|
}
|
|
break;
|
|
}
|
|
case ID_CONFIG_SETAUTOSAVEPERIOD:
|
|
{
|
|
int new_period = TASEdit_autosave_period;
|
|
if(CWin32InputBox::GetInteger("Autosave period", "How many minutes may the project stay not saved after being changed?\n(0 = no autosaves)", new_period, hwndDlg) == IDOK)
|
|
{
|
|
if (new_period < AUTOSAVE_PERIOD_MIN)
|
|
new_period = AUTOSAVE_PERIOD_MIN;
|
|
else if (new_period > AUTOSAVE_PERIOD_MAX)
|
|
new_period = AUTOSAVE_PERIOD_MAX;
|
|
TASEdit_autosave_period = new_period;
|
|
project.SheduleNextAutosave();
|
|
}
|
|
break;
|
|
}
|
|
case ID_CONFIG_BRANCHESRESTOREFULLMOVIE:
|
|
//switch "Branches restore entire Movie" flag
|
|
TASEdit_branch_full_movie ^= 1;
|
|
CheckMenuItem(hmenu, ID_CONFIG_BRANCHESRESTOREFULLMOVIE, TASEdit_branch_full_movie?MF_CHECKED : MF_UNCHECKED);
|
|
break;
|
|
case ID_CONFIG_BRANCHESWORKONLYWHENRECORDING:
|
|
//switch "Branches work only when Recording" flag
|
|
TASEdit_branch_only_when_rec ^= 1;
|
|
CheckMenuItem(hmenu, ID_CONFIG_BRANCHESWORKONLYWHENRECORDING, TASEdit_branch_only_when_rec?MF_CHECKED : MF_UNCHECKED);
|
|
bookmarks.RedrawBookmarksCaption();
|
|
break;
|
|
case ID_CONFIG_HUDINBRANCHSCREENSHOTS:
|
|
//switch "HUD in Branch screenshots" flag
|
|
TASEdit_branch_scr_hud ^= 1;
|
|
CheckMenuItem(hmenu, ID_CONFIG_HUDINBRANCHSCREENSHOTS, TASEdit_branch_scr_hud?MF_CHECKED : MF_UNCHECKED);
|
|
break;
|
|
case ID_CONFIG_BINDMARKERSTOINPUT:
|
|
//switch "Bind Markers to Input" flag
|
|
TASEdit_bind_markers ^= 1;
|
|
CheckMenuItem(hmenu, ID_CONFIG_BINDMARKERSTOINPUT, TASEdit_bind_markers?MF_CHECKED : MF_UNCHECKED);
|
|
break;
|
|
case ID_CONFIG_USE1PFORRECORDING:
|
|
//switch "Use 1P keys for single Recordings" flag
|
|
TASEdit_use_1p_rec ^= 1;
|
|
CheckMenuItem(hmenu, ID_CONFIG_USE1PFORRECORDING, TASEdit_use_1p_rec?MF_CHECKED : MF_UNCHECKED);
|
|
break;
|
|
case ID_CONFIG_COMBINECONSECUTIVERECORDINGS:
|
|
//switch "Combine consecutive Recordings" flag
|
|
TASEdit_combine_consecutive_rec ^= 1;
|
|
CheckMenuItem(hmenu, ID_CONFIG_COMBINECONSECUTIVERECORDINGS, TASEdit_combine_consecutive_rec?MF_CHECKED : MF_UNCHECKED);
|
|
break;
|
|
case ID_CONFIG_SUPERIMPOSE_AFFECTS_PASTE:
|
|
TASEdit_superimpose_affects_paste ^= 1;
|
|
CheckMenuItem(hmenu, ID_CONFIG_SUPERIMPOSE_AFFECTS_PASTE, TASEdit_superimpose_affects_paste?MF_CHECKED : MF_UNCHECKED);
|
|
break;
|
|
case ID_CONFIG_MUTETURBO:
|
|
muteTurbo ^= 1;
|
|
CheckMenuItem(hmenu, ID_CONFIG_MUTETURBO, muteTurbo?MF_CHECKED : MF_UNCHECKED);
|
|
break;
|
|
case IDC_PROGRESS_BUTTON:
|
|
// click on progressbar - stop seeking
|
|
if (playback.GetPauseFrame()) playback.SeekingStop();
|
|
break;
|
|
case IDC_BRANCHES_BUTTON:
|
|
// click on "Bookmarks/Branches" - switch "View Tree of branches"
|
|
TASEdit_view_branches_tree ^= 1;
|
|
bookmarks.RedrawBookmarksCaption();
|
|
break;
|
|
case IDC_RADIO1:
|
|
// switch to readonly, no need to recheck radiobuttons
|
|
if (!movie_readonly) FCEUI_MovieToggleReadOnly();
|
|
break;
|
|
case IDC_RADIO2:
|
|
// switch to read+write for all, no need to recheck radiobuttons
|
|
if (movie_readonly) FCEUI_MovieToggleReadOnly();
|
|
recorder.multitrack_recording_joypad = MULTITRACK_RECORDING_ALL;
|
|
break;
|
|
case IDC_RADIO3:
|
|
// switch to read+write for 1P, no need to recheck radiobuttons
|
|
if (movie_readonly) FCEUI_MovieToggleReadOnly();
|
|
recorder.multitrack_recording_joypad = MULTITRACK_RECORDING_1P;
|
|
break;
|
|
case IDC_RADIO4:
|
|
// switch to read+write for 2P, no need to recheck radiobuttons
|
|
if (movie_readonly) FCEUI_MovieToggleReadOnly();
|
|
recorder.multitrack_recording_joypad = MULTITRACK_RECORDING_2P;
|
|
break;
|
|
case IDC_RADIO5:
|
|
// switch to read+write for 3P, no need to recheck radiobuttons
|
|
if (movie_readonly) FCEUI_MovieToggleReadOnly();
|
|
recorder.multitrack_recording_joypad = MULTITRACK_RECORDING_3P;
|
|
break;
|
|
case IDC_RADIO6:
|
|
// switch to read+write for 4P, no need to recheck radiobuttons
|
|
if (movie_readonly) FCEUI_MovieToggleReadOnly();
|
|
recorder.multitrack_recording_joypad = MULTITRACK_RECORDING_4P;
|
|
break;
|
|
case IDC_SUPERIMPOSE:
|
|
// 3 states of "Superimpose" checkbox
|
|
if (TASEdit_superimpose == BST_UNCHECKED)
|
|
TASEdit_superimpose = BST_CHECKED;
|
|
else if (TASEdit_superimpose == BST_CHECKED)
|
|
TASEdit_superimpose = BST_INDETERMINATE;
|
|
else TASEdit_superimpose = BST_UNCHECKED;
|
|
CheckDlgButton(hwndTasEdit, IDC_SUPERIMPOSE, TASEdit_superimpose);
|
|
break;
|
|
case ACCEL_CTRL_A:
|
|
case ID_EDIT_SELECTMIDMARKERS:
|
|
case ID_SELECTED_SELECTMIDMARKERS:
|
|
selection.SelectMidMarkers();
|
|
break;
|
|
case ACCEL_SHIFT_INS:
|
|
case ID_EDIT_CLONEFRAMES:
|
|
case ID_SELECTED_CLONE:
|
|
CloneFrames();
|
|
break;
|
|
case ACCEL_CTRL_Z:
|
|
case ID_EDIT_UNDO:
|
|
{
|
|
int result = history.undo();
|
|
if (result >= 0)
|
|
{
|
|
tasedit_list.update();
|
|
tasedit_list.FollowUndo();
|
|
greenzone.InvalidateAndCheck(result);
|
|
}
|
|
break;
|
|
}
|
|
case ACCEL_CTRL_Y:
|
|
case ID_EDIT_REDO:
|
|
{
|
|
int result = history.redo();
|
|
if (result >= 0)
|
|
{
|
|
tasedit_list.update();
|
|
tasedit_list.FollowUndo();
|
|
greenzone.InvalidateAndCheck(result);
|
|
}
|
|
break;
|
|
}
|
|
case ID_EDIT_SELECTIONUNDO:
|
|
case ACCEL_CTRL_Q:
|
|
{
|
|
selection.undo();
|
|
tasedit_list.FollowSelection();
|
|
break;
|
|
}
|
|
case ID_EDIT_SELECTIONREDO:
|
|
case ACCEL_CTRL_W:
|
|
{
|
|
selection.redo();
|
|
tasedit_list.FollowSelection();
|
|
break;
|
|
}
|
|
case ID_EDIT_RESELECTCLIPBOARD:
|
|
case ACCEL_CTRL_B:
|
|
{
|
|
selection.ReselectClipboard();
|
|
tasedit_list.FollowSelection();
|
|
break;
|
|
}
|
|
case IDC_TEXT_SELECTION_BUTTON:
|
|
{
|
|
tasedit_list.FollowSelection();
|
|
break;
|
|
}
|
|
case ID_SELECTED_SETMARKER:
|
|
{
|
|
SelectionFrames* current_selection = selection.MakeStrobe();
|
|
if (current_selection->size())
|
|
{
|
|
SelectionFrames::iterator current_selection_begin(current_selection->begin());
|
|
SelectionFrames::iterator current_selection_end(current_selection->end());
|
|
bool changes_made = false;
|
|
for(SelectionFrames::iterator it(current_selection_begin); it != current_selection_end; it++)
|
|
{
|
|
if(!markers.GetMarker(*it))
|
|
{
|
|
changes_made = true;
|
|
markers.SetMarker(*it);
|
|
tasedit_list.RedrawRow(*it);
|
|
}
|
|
}
|
|
if (changes_made)
|
|
{
|
|
history.RegisterChanges(MODTYPE_MARKER_SET, *current_selection_begin, *current_selection->rbegin());
|
|
project.SetProjectChanged();
|
|
}
|
|
}
|
|
break;
|
|
}
|
|
case ID_SELECTED_REMOVEMARKER:
|
|
{
|
|
SelectionFrames* current_selection = selection.MakeStrobe();
|
|
if (current_selection->size())
|
|
{
|
|
SelectionFrames::iterator current_selection_begin(current_selection->begin());
|
|
SelectionFrames::iterator current_selection_end(current_selection->end());
|
|
bool changes_made = false;
|
|
for(SelectionFrames::iterator it(current_selection_begin); it != current_selection_end; it++)
|
|
{
|
|
if(markers.GetMarker(*it))
|
|
{
|
|
changes_made = true;
|
|
markers.ClearMarker(*it);
|
|
tasedit_list.RedrawRow(*it);
|
|
}
|
|
}
|
|
if (changes_made)
|
|
{
|
|
history.RegisterChanges(MODTYPE_MARKER_UNSET, *current_selection_begin, *current_selection->rbegin());
|
|
project.SetProjectChanged();
|
|
}
|
|
}
|
|
break;
|
|
}
|
|
|
|
}
|
|
break;
|
|
}
|
|
case WM_SYSKEYDOWN:
|
|
{
|
|
if (wParam == VK_F10)
|
|
return 0;
|
|
break;
|
|
}
|
|
default:
|
|
break;
|
|
}
|
|
return FALSE;
|
|
}
|
|
|
|
bool EnterTasEdit()
|
|
{
|
|
if(!FCEU_IsValidUI(FCEUI_TASEDIT)) return false;
|
|
if(!hwndTasEdit)
|
|
{
|
|
hwndTasEdit = CreateDialog(fceu_hInstance,"TASEDIT", hAppWnd, WndprocTasEdit);
|
|
if(hwndTasEdit)
|
|
{
|
|
// save "eoptions"
|
|
saved_eoptions = eoptions;
|
|
// set "Run in background"
|
|
eoptions |= EO_BGRUN;
|
|
GotFocus();
|
|
// "Set high-priority thread"
|
|
eoptions |= EO_HIGHPRIO;
|
|
DoPriority();
|
|
// clear "Disable speed throttling"
|
|
eoptions &= ~EO_NOTHROTTLE;
|
|
// switch off autosaves
|
|
saved_EnableAutosave = EnableAutosave;
|
|
EnableAutosave = 0;
|
|
UpdateCheckedMenuItems();
|
|
|
|
hmenu = GetMenu(hwndTasEdit);
|
|
hrmenu = LoadMenu(fceu_hInstance,"TASEDITCONTEXTMENUS");
|
|
recent_projects_menu = CreateMenu();
|
|
UpdateRecentProjectsMenu();
|
|
// check option ticks
|
|
CheckDlgButton(hwndTasEdit, CHECK_FOLLOW_CURSOR, TASEdit_follow_playback?MF_CHECKED : MF_UNCHECKED);
|
|
CheckDlgButton(hwndTasEdit, CHECK_TURBO_SEEK, TASEdit_turbo_seek?MF_CHECKED : MF_UNCHECKED);
|
|
CheckMenuItem(hmenu, ID_VIEW_SHOW_LAG_FRAMES, TASEdit_show_lag_frames?MF_CHECKED : MF_UNCHECKED);
|
|
CheckMenuItem(hmenu, ID_VIEW_SHOW_MARKERS, TASEdit_show_markers?MF_CHECKED : MF_UNCHECKED);
|
|
CheckMenuItem(hmenu, ID_VIEW_SHOWBRANCHSCREENSHOTS, TASEdit_show_branch_screenshots?MF_CHECKED : MF_UNCHECKED);
|
|
CheckMenuItem(hmenu, ID_VIEW_JUMPWHENMAKINGUNDO, TASEdit_jump_to_undo?MF_CHECKED : MF_UNCHECKED);
|
|
CheckMenuItem(hmenu, ID_VIEW_ENABLEHOTCHANGES, TASEdit_enable_hot_changes?MF_CHECKED : MF_UNCHECKED);
|
|
CheckMenuItem(hmenu, ID_CONFIG_BRANCHESRESTOREFULLMOVIE, TASEdit_branch_full_movie?MF_CHECKED : MF_UNCHECKED);
|
|
CheckMenuItem(hmenu, ID_CONFIG_BRANCHESWORKONLYWHENRECORDING, TASEdit_branch_only_when_rec?MF_CHECKED : MF_UNCHECKED);
|
|
CheckMenuItem(hmenu, ID_CONFIG_HUDINBRANCHSCREENSHOTS, TASEdit_branch_scr_hud?MF_CHECKED : MF_UNCHECKED);
|
|
CheckMenuItem(hmenu, ID_CONFIG_BINDMARKERSTOINPUT, TASEdit_bind_markers?MF_CHECKED : MF_UNCHECKED);
|
|
CheckMenuItem(hmenu, ID_CONFIG_USE1PFORRECORDING, TASEdit_use_1p_rec?MF_CHECKED : MF_UNCHECKED);
|
|
CheckMenuItem(hmenu, ID_CONFIG_COMBINECONSECUTIVERECORDINGS, TASEdit_combine_consecutive_rec?MF_CHECKED : MF_UNCHECKED);
|
|
CheckMenuItem(hmenu, ID_CONFIG_SUPERIMPOSE_AFFECTS_PASTE, TASEdit_superimpose_affects_paste?MF_CHECKED : MF_UNCHECKED);
|
|
CheckMenuItem(hmenu, ID_CONFIG_MUTETURBO, muteTurbo?MF_CHECKED : MF_UNCHECKED);
|
|
CheckDlgButton(hwndTasEdit,CHECK_AUTORESTORE_PLAYBACK,TASEdit_restore_position?BST_CHECKED:BST_UNCHECKED);
|
|
CheckDlgButton(hwndTasEdit, IDC_SUPERIMPOSE, TASEdit_superimpose);
|
|
|
|
SetWindowPos(hwndTasEdit, HWND_TOP, 0, 0, 0, 0, SWP_NOSIZE|SWP_NOMOVE|SWP_NOOWNERZORDER);
|
|
// init modules
|
|
greenzone.init();
|
|
playback.init();
|
|
// either start new movie or use current movie
|
|
if (FCEUMOV_Mode(MOVIEMODE_INACTIVE))
|
|
{
|
|
FCEUI_StopMovie();
|
|
CreateCleanMovie();
|
|
playback.StartFromZero();
|
|
} else
|
|
{
|
|
// use current movie to create a new project
|
|
if (currMovieData.savestate.size() != 0)
|
|
{
|
|
FCEUD_PrintError("This version of TAS Editor doesn't work with movies starting from savestate.");
|
|
// delete savestate, but preserve input
|
|
currMovieData.savestate.clear();
|
|
}
|
|
FCEUI_StopMovie();
|
|
greenzone.TryDumpIncremental(lagFlag != 0);
|
|
}
|
|
// switch to taseditor mode
|
|
movieMode = MOVIEMODE_TASEDIT;
|
|
currMovieData.ports[0] = SI_GAMEPAD;
|
|
currMovieData.ports[1] = SI_GAMEPAD;
|
|
//force the input configuration stored in the movie to apply
|
|
FCEUD_SetInput(currMovieData.fourscore, currMovieData.microphone, (ESI)currMovieData.ports[0], (ESI)currMovieData.ports[1], (ESIFC)currMovieData.ports[2]);
|
|
// init variables
|
|
recorder.init();
|
|
tasedit_list.init();
|
|
markers.init();
|
|
project.init();
|
|
bookmarks.init();
|
|
screenshot_display.init();
|
|
history.init();
|
|
selection.init();
|
|
SetFocus(history.hwndHistoryList); // set focus only once, to show selection cursor
|
|
SetFocus(tasedit_list.hwndList);
|
|
FCEU_DispMessage("TAS Editor engaged", 0);
|
|
return true;
|
|
} else return false;
|
|
} else return true;
|
|
}
|
|
|
|
bool ExitTasEdit()
|
|
{
|
|
if (!AskSaveProject()) return false;
|
|
|
|
DestroyWindow(hwndTasEdit);
|
|
hwndTasEdit = 0;
|
|
TASEdit_focus = false;
|
|
// restore "eoptions"
|
|
eoptions = saved_eoptions;
|
|
// restore autosaves
|
|
EnableAutosave = saved_EnableAutosave;
|
|
DoPriority();
|
|
UpdateCheckedMenuItems();
|
|
// clear "Background TAS Editor input"
|
|
KeyboardClearBackgroundAccessBit(KEYBACKACCESS_TASEDIT);
|
|
JoystickClearBackgroundAccessBit(JOYBACKACCESS_TASEDIT);
|
|
// release memory
|
|
tasedit_list.free();
|
|
markers.free();
|
|
greenzone.free();
|
|
bookmarks.free();
|
|
screenshot_display.free();
|
|
history.free();
|
|
playback.SeekingStop();
|
|
selection.free();
|
|
// switch off taseditor mode
|
|
movieMode = MOVIEMODE_INACTIVE;
|
|
FCEU_DispMessage("TAS Editor disengaged", 0);
|
|
CreateCleanMovie();
|
|
return true;
|
|
}
|
|
|
|
void GotFocus()
|
|
{
|
|
TASEdit_focus = true;
|
|
// set "Background TAS Editor input"
|
|
KeyboardSetBackgroundAccessBit(KEYBACKACCESS_TASEDIT);
|
|
JoystickSetBackgroundAccessBit(JOYBACKACCESS_TASEDIT);
|
|
}
|
|
void LostFocus()
|
|
{
|
|
TASEdit_focus = false;
|
|
// clear "Background TAS Editor input"
|
|
KeyboardClearBackgroundAccessBit(KEYBACKACCESS_TASEDIT);
|
|
JoystickClearBackgroundAccessBit(JOYBACKACCESS_TASEDIT);
|
|
}
|
|
// --------------------------------------------------------------------------------------------
|
|
void UpdateRecentProjectsMenu()
|
|
{
|
|
MENUITEMINFO moo;
|
|
int x;
|
|
moo.cbSize = sizeof(moo);
|
|
moo.fMask = MIIM_SUBMENU | MIIM_STATE;
|
|
GetMenuItemInfo(GetSubMenu(hmenu, 0), ID_TASEDIT_FILE_RECENT, FALSE, &moo);
|
|
moo.hSubMenu = recent_projects_menu;
|
|
moo.fState = recent_projects[0] ? MFS_ENABLED : MFS_GRAYED;
|
|
SetMenuItemInfo(GetSubMenu(hmenu, 0), ID_TASEDIT_FILE_RECENT, FALSE, &moo);
|
|
|
|
// Remove all recent files submenus
|
|
for(x = 0; x < MAX_NUMBER_OF_RECENT_PROJECTS; x++)
|
|
{
|
|
RemoveMenu(recent_projects_menu, MENU_FIRST_RECENT_PROJECT + x, MF_BYCOMMAND);
|
|
}
|
|
// Recreate the menus
|
|
for(x = MAX_NUMBER_OF_RECENT_PROJECTS - 1; x >= 0; x--)
|
|
{
|
|
// Skip empty strings
|
|
if(!recent_projects[x]) continue;
|
|
|
|
string tmp = recent_projects[x];
|
|
// clamp this string to 128 chars
|
|
if(tmp.size() > 128)
|
|
tmp = tmp.substr(0, 128);
|
|
|
|
moo.cbSize = sizeof(moo);
|
|
moo.fMask = MIIM_DATA | MIIM_ID | MIIM_TYPE;
|
|
// Insert the menu item
|
|
moo.cch = tmp.size();
|
|
moo.fType = 0;
|
|
moo.wID = MENU_FIRST_RECENT_PROJECT + x;
|
|
moo.dwTypeData = (LPSTR)tmp.c_str();
|
|
InsertMenuItem(recent_projects_menu, 0, 1, &moo);
|
|
}
|
|
|
|
// if recent_projects is empty, "Recent" manu should be grayed
|
|
int i;
|
|
for (i = 0; i < MAX_NUMBER_OF_RECENT_PROJECTS; ++i)
|
|
if (recent_projects[i]) break;
|
|
if (i < MAX_NUMBER_OF_RECENT_PROJECTS)
|
|
EnableMenuItem(hmenu, ID_TASEDIT_FILE_RECENT, MF_ENABLED);
|
|
else
|
|
EnableMenuItem(hmenu, ID_TASEDIT_FILE_RECENT, MF_GRAYED);
|
|
|
|
DrawMenuBar(hwndTasEdit);
|
|
}
|
|
void UpdateRecentProjectsArray(const char* addString)
|
|
{
|
|
// find out if the filename is already in the recent files list
|
|
for(unsigned int x = 0; x < MAX_NUMBER_OF_RECENT_PROJECTS; x++)
|
|
{
|
|
if(recent_projects[x])
|
|
{
|
|
if(!strcmp(recent_projects[x], addString)) // Item is already in list
|
|
{
|
|
// If the filename is in the file list don't add it again, move it up in the list instead
|
|
char* tmp = recent_projects[x]; // save pointer
|
|
for(int y = x; y; y--)
|
|
// Move items down.
|
|
recent_projects[y] = recent_projects[y - 1];
|
|
// Put item on top.
|
|
recent_projects[0] = tmp;
|
|
UpdateRecentProjectsMenu();
|
|
return;
|
|
}
|
|
}
|
|
}
|
|
// The filename wasn't found in the list. That means we need to add it.
|
|
// If there's no space left in the recent files list, get rid of the last item in the list
|
|
if(recent_projects[MAX_NUMBER_OF_RECENT_PROJECTS-1])
|
|
free(recent_projects[MAX_NUMBER_OF_RECENT_PROJECTS-1]);
|
|
// Move other items down
|
|
for(unsigned int x = MAX_NUMBER_OF_RECENT_PROJECTS-1; x; x--)
|
|
recent_projects[x] = recent_projects[x-1];
|
|
// Add new item
|
|
recent_projects[0] = (char*)malloc(strlen(addString) + 1);
|
|
strcpy(recent_projects[0], addString);
|
|
|
|
UpdateRecentProjectsMenu();
|
|
}
|
|
void RemoveRecentProject(unsigned int which)
|
|
{
|
|
if (which >= MAX_NUMBER_OF_RECENT_PROJECTS) return;
|
|
// Remove the item
|
|
if(recent_projects[which])
|
|
free(recent_projects[which]);
|
|
// If the item is not the last one in the list, shift the remaining ones up
|
|
if (which < MAX_NUMBER_OF_RECENT_PROJECTS-1)
|
|
{
|
|
// Move the remaining items up
|
|
for(unsigned int x = which+1; x < MAX_NUMBER_OF_RECENT_PROJECTS; ++x)
|
|
{
|
|
recent_projects[x-1] = recent_projects[x]; // Shift each remaining item up by 1
|
|
}
|
|
}
|
|
recent_projects[MAX_NUMBER_OF_RECENT_PROJECTS-1] = 0; // Clear out the last item since it is empty now
|
|
|
|
UpdateRecentProjectsMenu();
|
|
}
|