FifoPlayerDlg.cpp 29 KB

1234567891011121314151617181920212223242526272829303132333435363738394041424344454647484950515253545556575859606162636465666768697071727374757677787980818283848586878889909192939495969798991001011021031041051061071081091101111121131141151161171181191201211221231241251261271281291301311321331341351361371381391401411421431441451461471481491501511521531541551561571581591601611621631641651661671681691701711721731741751761771781791801811821831841851861871881891901911921931941951961971981992002012022032042052062072082092102112122132142152162172182192202212222232242252262272282292302312322332342352362372382392402412422432442452462472482492502512522532542552562572582592602612622632642652662672682692702712722732742752762772782792802812822832842852862872882892902912922932942952962972982993003013023033043053063073083093103113123133143153163173183193203213223233243253263273283293303313323333343353363373383393403413423433443453463473483493503513523533543553563573583593603613623633643653663673683693703713723733743753763773783793803813823833843853863873883893903913923933943953963973983994004014024034044054064074084094104114124134144154164174184194204214224234244254264274284294304314324334344354364374384394404414424434444454464474484494504514524534544554564574584594604614624634644654664674684694704714724734744754764774784794804814824834844854864874884894904914924934944954964974984995005015025035045055065075085095105115125135145155165175185195205215225235245255265275285295305315325335345355365375385395405415425435445455465475485495505515525535545555565575585595605615625635645655665675685695705715725735745755765775785795805815825835845855865875885895905915925935945955965975985996006016026036046056066076086096106116126136146156166176186196206216226236246256266276286296306316326336346356366376386396406416426436446456466476486496506516526536546556566576586596606616626636646656666676686696706716726736746756766776786796806816826836846856866876886896906916926936946956966976986997007017027037047057067077087097107117127137147157167177187197207217227237247257267277287297307317327337347357367377387397407417427437447457467477487497507517527537547557567577587597607617627637647657667677687697707717727737747757767777787797807817827837847857867877887897907917927937947957967977987998008018028038048058068078088098108118128138148158168178188198208218228238248258268278288298308318328338348358368378388398408418428438448458468478488498508518528538548558568578588598608618628638648658668678688698708718728738748758768778788798808818828838848858868878888898908918928938948958968978988999009019029039049059069079089099109119129139149159169179189199209219229239249259269279289299309319329339349359369379389399409419429439449459469479489499509519529539549559569579589599609619629639649659669679689699709719729739749759769779789799809819829839849859869879889899909919929939949959969979989991000
  1. // Copyright 2011 Dolphin Emulator Project
  2. // Licensed under GPLv2+
  3. // Refer to the license.txt file included.
  4. #include <algorithm>
  5. #include <cstddef>
  6. #include <mutex>
  7. #include <string>
  8. #include <vector>
  9. #include <wx/button.h>
  10. #include <wx/checkbox.h>
  11. #include <wx/clipbrd.h>
  12. #include <wx/dataobj.h>
  13. #include <wx/dialog.h>
  14. #include <wx/filedlg.h>
  15. #include <wx/listbox.h>
  16. #include <wx/msgdlg.h>
  17. #include <wx/notebook.h>
  18. #include <wx/panel.h>
  19. #include <wx/sizer.h>
  20. #include <wx/spinbutt.h>
  21. #include <wx/spinctrl.h>
  22. #include <wx/statbox.h>
  23. #include <wx/stattext.h>
  24. #include <wx/textctrl.h>
  25. #include "Common/CommonTypes.h"
  26. #include "Core/FifoPlayer/FifoDataFile.h"
  27. #include "Core/FifoPlayer/FifoPlaybackAnalyzer.h"
  28. #include "Core/FifoPlayer/FifoPlayer.h"
  29. #include "Core/FifoPlayer/FifoRecorder.h"
  30. #include "DolphinWX/FifoPlayerDlg.h"
  31. #include "DolphinWX/WxUtils.h"
  32. #include "VideoCommon/BPMemory.h"
  33. #include "VideoCommon/OpcodeDecoding.h"
  34. wxDEFINE_EVENT(RECORDING_FINISHED_EVENT, wxCommandEvent);
  35. wxDEFINE_EVENT(FRAME_WRITTEN_EVENT, wxCommandEvent);
  36. static std::recursive_mutex sMutex;
  37. wxEvtHandler *volatile FifoPlayerDlg::m_EvtHandler = nullptr;
  38. FifoPlayerDlg::FifoPlayerDlg(wxWindow * const parent) :
  39. wxDialog(parent, wxID_ANY, _("FIFO Player")),
  40. m_search_result_idx(0), m_FramesToRecord(1)
  41. {
  42. CreateGUIControls();
  43. sMutex.lock();
  44. m_EvtHandler = GetEventHandler();
  45. sMutex.unlock();
  46. FifoPlayer::GetInstance().SetFileLoadedCallback(FileLoaded);
  47. FifoPlayer::GetInstance().SetFrameWrittenCallback(FrameWritten);
  48. }
  49. FifoPlayerDlg::~FifoPlayerDlg()
  50. {
  51. FifoPlayer::GetInstance().SetFrameWrittenCallback(nullptr);
  52. sMutex.lock();
  53. m_EvtHandler = nullptr;
  54. sMutex.unlock();
  55. }
  56. void FifoPlayerDlg::CreateGUIControls()
  57. {
  58. wxBoxSizer* sMain;
  59. sMain = new wxBoxSizer(wxVERTICAL);
  60. m_Notebook = new wxNotebook(this, wxID_ANY);
  61. {
  62. m_PlayPage = new wxPanel(m_Notebook, wxID_ANY, wxDefaultPosition, wxDefaultSize, wxTAB_TRAVERSAL);
  63. wxBoxSizer* sPlayPage;
  64. sPlayPage = new wxBoxSizer(wxVERTICAL);
  65. wxStaticBoxSizer* sPlayInfo;
  66. sPlayInfo = new wxStaticBoxSizer(new wxStaticBox(m_PlayPage, wxID_ANY, _("File Info")), wxVERTICAL);
  67. m_NumFramesLabel = new wxStaticText(m_PlayPage, wxID_ANY, wxEmptyString);
  68. m_NumFramesLabel->Wrap(-1);
  69. sPlayInfo->Add(m_NumFramesLabel, 0, wxALL, 5);
  70. m_CurrentFrameLabel = new wxStaticText(m_PlayPage, wxID_ANY, wxEmptyString);
  71. m_CurrentFrameLabel->Wrap(-1);
  72. sPlayInfo->Add(m_CurrentFrameLabel, 0, wxALL, 5);
  73. m_NumObjectsLabel = new wxStaticText(m_PlayPage, wxID_ANY, wxEmptyString);
  74. m_NumObjectsLabel->Wrap(-1);
  75. sPlayInfo->Add(m_NumObjectsLabel, 0, wxALL, 5);
  76. sPlayPage->Add(sPlayInfo, 1, wxEXPAND, 5);
  77. wxStaticBoxSizer* sFrameRange;
  78. sFrameRange = new wxStaticBoxSizer(new wxStaticBox(m_PlayPage, wxID_ANY, _("Frame Range")), wxHORIZONTAL);
  79. m_FrameFromLabel = new wxStaticText(m_PlayPage, wxID_ANY, _("From"));
  80. m_FrameFromLabel->Wrap(-1);
  81. sFrameRange->Add(m_FrameFromLabel, 0, wxALL, 5);
  82. m_FrameFromCtrl = new wxSpinCtrl(m_PlayPage, wxID_ANY, wxEmptyString, wxDefaultPosition, wxDefaultSize, wxSP_ARROW_KEYS, 0, 10, 0);
  83. sFrameRange->Add(m_FrameFromCtrl, 0, wxALL, 5);
  84. m_FrameToLabel = new wxStaticText(m_PlayPage, wxID_ANY, _("To"));
  85. m_FrameToLabel->Wrap(-1);
  86. sFrameRange->Add(m_FrameToLabel, 0, wxALL, 5);
  87. m_FrameToCtrl = new wxSpinCtrl(m_PlayPage, wxID_ANY, wxEmptyString, wxDefaultPosition, wxSize(-1, -1), wxSP_ARROW_KEYS, 0, 10, 0);
  88. sFrameRange->Add(m_FrameToCtrl, 0, wxALL, 5);
  89. sPlayPage->Add(sFrameRange, 0, wxEXPAND, 5);
  90. wxStaticBoxSizer* sObjectRange;
  91. sObjectRange = new wxStaticBoxSizer(new wxStaticBox(m_PlayPage, wxID_ANY, _("Object Range")), wxHORIZONTAL);
  92. m_ObjectFromLabel = new wxStaticText(m_PlayPage, wxID_ANY, _("From"));
  93. m_ObjectFromLabel->Wrap(-1);
  94. sObjectRange->Add(m_ObjectFromLabel, 0, wxALL, 5);
  95. m_ObjectFromCtrl = new wxSpinCtrl(m_PlayPage, wxID_ANY, wxEmptyString, wxDefaultPosition, wxDefaultSize, wxSP_ARROW_KEYS, 0, 10000, 0);
  96. sObjectRange->Add(m_ObjectFromCtrl, 0, wxALL, 5);
  97. m_ObjectToLabel = new wxStaticText(m_PlayPage, wxID_ANY, _("To"));
  98. m_ObjectToLabel->Wrap(-1);
  99. sObjectRange->Add(m_ObjectToLabel, 0, wxALL, 5);
  100. m_ObjectToCtrl = new wxSpinCtrl(m_PlayPage, wxID_ANY, wxEmptyString, wxDefaultPosition, wxDefaultSize, wxSP_ARROW_KEYS, 0, 10000, 0);
  101. sObjectRange->Add(m_ObjectToCtrl, 0, wxALL, 5);
  102. sPlayPage->Add(sObjectRange, 0, wxEXPAND, 5);
  103. wxStaticBoxSizer* sPlayOptions;
  104. sPlayOptions = new wxStaticBoxSizer(new wxStaticBox(m_PlayPage, wxID_ANY, _("Playback Options")), wxVERTICAL);
  105. m_EarlyMemoryUpdates = new wxCheckBox(m_PlayPage, wxID_ANY, _("Early Memory Updates"));
  106. sPlayOptions->Add(m_EarlyMemoryUpdates, 0, wxALL, 5);
  107. sPlayPage->Add(sPlayOptions, 0, wxEXPAND, 5);
  108. sPlayPage->AddStretchSpacer();
  109. m_PlayPage->SetSizer(sPlayPage);
  110. m_PlayPage->Layout();
  111. sPlayPage->Fit(m_PlayPage);
  112. m_Notebook->AddPage(m_PlayPage, _("Play"), true);
  113. }
  114. {
  115. m_RecordPage = new wxPanel(m_Notebook, wxID_ANY, wxDefaultPosition, wxDefaultSize, wxTAB_TRAVERSAL);
  116. wxBoxSizer* sRecordPage;
  117. sRecordPage = new wxBoxSizer(wxVERTICAL);
  118. wxStaticBoxSizer* sRecordInfo;
  119. sRecordInfo = new wxStaticBoxSizer(new wxStaticBox(m_RecordPage, wxID_ANY, _("Recording Info")), wxVERTICAL);
  120. m_RecordingFifoSizeLabel = new wxStaticText(m_RecordPage, wxID_ANY, wxEmptyString);
  121. m_RecordingFifoSizeLabel->Wrap(-1);
  122. sRecordInfo->Add(m_RecordingFifoSizeLabel, 0, wxALL, 5);
  123. m_RecordingMemSizeLabel = new wxStaticText(m_RecordPage, wxID_ANY, wxEmptyString);
  124. m_RecordingMemSizeLabel->Wrap(-1);
  125. sRecordInfo->Add(m_RecordingMemSizeLabel, 0, wxALL, 5);
  126. m_RecordingFramesLabel = new wxStaticText(m_RecordPage, wxID_ANY, wxEmptyString);
  127. m_RecordingFramesLabel->Wrap(-1);
  128. sRecordInfo->Add(m_RecordingFramesLabel, 0, wxALL, 5);
  129. sRecordPage->Add(sRecordInfo, 0, wxEXPAND, 5);
  130. wxBoxSizer* sRecordButtons;
  131. sRecordButtons = new wxBoxSizer(wxHORIZONTAL);
  132. m_RecordStop = new wxButton(m_RecordPage, wxID_ANY, _("Record"));
  133. sRecordButtons->Add(m_RecordStop, 0, wxALL, 5);
  134. m_Save = new wxButton(m_RecordPage, wxID_ANY, _("Save"));
  135. sRecordButtons->Add(m_Save, 0, wxALL, 5);
  136. sRecordPage->Add(sRecordButtons, 0, wxEXPAND, 5);
  137. wxStaticBoxSizer* sRecordingOptions;
  138. sRecordingOptions = new wxStaticBoxSizer(new wxStaticBox(m_RecordPage, wxID_ANY, _("Recording Options")), wxHORIZONTAL);
  139. m_FramesToRecordLabel = new wxStaticText(m_RecordPage, wxID_ANY, _("Frames To Record"));
  140. m_FramesToRecordLabel->Wrap(-1);
  141. sRecordingOptions->Add(m_FramesToRecordLabel, 0, wxALL, 5);
  142. wxString initialNum = wxString::Format("%d", m_FramesToRecord);
  143. m_FramesToRecordCtrl = new wxSpinCtrl(m_RecordPage, wxID_ANY, initialNum, wxDefaultPosition, wxDefaultSize, wxSP_ARROW_KEYS, 0, 10000, 1);
  144. sRecordingOptions->Add(m_FramesToRecordCtrl, 0, wxALL, 5);
  145. sRecordPage->Add(sRecordingOptions, 0, wxEXPAND, 5);
  146. sRecordPage->AddStretchSpacer();
  147. m_RecordPage->SetSizer(sRecordPage);
  148. m_RecordPage->Layout();
  149. sRecordPage->Fit(m_RecordPage);
  150. m_Notebook->AddPage(m_RecordPage, _("Record"), false);
  151. }
  152. // Analyze page
  153. {
  154. m_AnalyzePage = new wxPanel(m_Notebook, wxID_ANY, wxDefaultPosition, wxDefaultSize, wxTAB_TRAVERSAL);
  155. wxBoxSizer* sAnalyzePage;
  156. sAnalyzePage = new wxBoxSizer(wxVERTICAL);
  157. wxStaticBoxSizer* sFrameInfoSizer;
  158. sFrameInfoSizer = new wxStaticBoxSizer(new wxStaticBox(m_AnalyzePage, wxID_ANY, _("Frame Info")), wxVERTICAL);
  159. wxBoxSizer* sListsSizer = new wxBoxSizer(wxHORIZONTAL);
  160. m_framesList = new wxListBox(m_AnalyzePage, wxID_ANY);
  161. m_framesList->SetMinSize(wxSize(100, 250));
  162. sListsSizer->Add(m_framesList, 0, wxALL, 5);
  163. m_objectsList = new wxListBox(m_AnalyzePage, wxID_ANY);
  164. m_objectsList->SetMinSize(wxSize(110, 250));
  165. sListsSizer->Add(m_objectsList, 0, wxALL, 5);
  166. m_objectCmdList = new wxListBox(m_AnalyzePage, wxID_ANY);
  167. m_objectCmdList->SetMinSize(wxSize(175, 250));
  168. sListsSizer->Add(m_objectCmdList, 0, wxALL, 5);
  169. sFrameInfoSizer->Add(sListsSizer, 0, wxALL, 5);
  170. m_objectCmdInfo = new wxStaticText(m_AnalyzePage, wxID_ANY, wxString());
  171. sFrameInfoSizer->Add(m_objectCmdInfo, 0, wxALL, 5);
  172. sAnalyzePage->Add(sFrameInfoSizer, 0, wxEXPAND, 5);
  173. wxStaticBoxSizer* sSearchSizer = new wxStaticBoxSizer(new wxStaticBox(m_AnalyzePage, wxID_ANY, _("Search current Object")), wxVERTICAL);
  174. wxBoxSizer* sSearchField = new wxBoxSizer(wxHORIZONTAL);
  175. sSearchField->Add(new wxStaticText(m_AnalyzePage, wxID_ANY, _("Search for hex Value:")), 0, wxALIGN_CENTER_VERTICAL, 5);
  176. // TODO: ugh, wxValidator sucks - but we should use it anyway.
  177. m_searchField = new wxTextCtrl(m_AnalyzePage, wxID_ANY, wxEmptyString, wxDefaultPosition, wxDefaultSize, wxTE_PROCESS_ENTER);
  178. m_numResultsText = new wxStaticText(m_AnalyzePage, wxID_ANY, wxEmptyString);
  179. sSearchField->Add(m_searchField, 0, wxALL, 5);
  180. sSearchField->Add(m_numResultsText, 0, wxALIGN_CENTER_VERTICAL, 5);
  181. wxBoxSizer* sSearchButtons = new wxBoxSizer(wxHORIZONTAL);
  182. m_beginSearch = new wxButton(m_AnalyzePage, wxID_ANY, _("Search"));
  183. m_findNext = new wxButton(m_AnalyzePage, wxID_ANY, _("Find next"));
  184. m_findPrevious = new wxButton(m_AnalyzePage, wxID_ANY, _("Find previous"));
  185. ResetSearch();
  186. sSearchButtons->Add(m_beginSearch, 0, wxALL, 5);
  187. sSearchButtons->Add(m_findNext, 0, wxALL, 5);
  188. sSearchButtons->Add(m_findPrevious, 0, wxALL, 5);
  189. sSearchSizer->Add(sSearchField, 0, wxEXPAND, 5);
  190. sSearchSizer->Add(sSearchButtons, 0, wxEXPAND, 5);
  191. sAnalyzePage->Add(sSearchSizer, 0, wxEXPAND, 5);
  192. sAnalyzePage->AddStretchSpacer();
  193. m_AnalyzePage->SetSizer(sAnalyzePage);
  194. m_AnalyzePage->Layout();
  195. sAnalyzePage->Fit(m_AnalyzePage);
  196. m_Notebook->AddPage(m_AnalyzePage, _("Analyze"), false);
  197. }
  198. sMain->Add(m_Notebook, 1, wxEXPAND | wxALL, 5);
  199. wxBoxSizer* sButtons;
  200. sButtons = new wxBoxSizer(wxHORIZONTAL);
  201. wxBoxSizer* sCloseButtonExpander;
  202. sCloseButtonExpander = new wxBoxSizer(wxHORIZONTAL);
  203. sButtons->Add(sCloseButtonExpander, 1, wxEXPAND, 5);
  204. m_Close = new wxButton(this, wxID_ANY, _("Close"));
  205. sButtons->Add(m_Close, 0, wxALL, 5);
  206. sMain->Add(sButtons, 0, wxEXPAND, 5);
  207. SetSizer(sMain);
  208. Layout();
  209. sMain->Fit(this);
  210. Center(wxBOTH);
  211. // Connect Events
  212. Bind(wxEVT_PAINT, &FifoPlayerDlg::OnPaint, this);
  213. m_FrameFromCtrl->Bind(wxEVT_SPINCTRL, &FifoPlayerDlg::OnFrameFrom, this);
  214. m_FrameToCtrl->Bind(wxEVT_SPINCTRL, &FifoPlayerDlg::OnFrameTo, this);
  215. m_ObjectFromCtrl->Bind(wxEVT_SPINCTRL, &FifoPlayerDlg::OnObjectFrom, this);
  216. m_ObjectToCtrl->Bind(wxEVT_SPINCTRL, &FifoPlayerDlg::OnObjectTo, this);
  217. m_EarlyMemoryUpdates->Bind(wxEVT_CHECKBOX, &FifoPlayerDlg::OnCheckEarlyMemoryUpdates, this);
  218. m_RecordStop->Bind(wxEVT_BUTTON, &FifoPlayerDlg::OnRecordStop, this);
  219. m_Save->Bind(wxEVT_BUTTON, &FifoPlayerDlg::OnSaveFile, this);
  220. m_FramesToRecordCtrl->Bind(wxEVT_SPINCTRL, &FifoPlayerDlg::OnNumFramesToRecord, this);
  221. Bind(wxEVT_BUTTON, &FifoPlayerDlg::OnCloseClick, this);
  222. m_framesList->Bind(wxEVT_LISTBOX, &FifoPlayerDlg::OnFrameListSelectionChanged, this);
  223. m_objectsList->Bind(wxEVT_LISTBOX, &FifoPlayerDlg::OnObjectListSelectionChanged, this);
  224. m_objectCmdList->Bind(wxEVT_LISTBOX, &FifoPlayerDlg::OnObjectCmdListSelectionChanged, this);
  225. m_beginSearch->Bind(wxEVT_BUTTON, &FifoPlayerDlg::OnBeginSearch, this);
  226. m_findNext->Bind(wxEVT_BUTTON, &FifoPlayerDlg::OnFindNextClick, this);
  227. m_findPrevious->Bind(wxEVT_BUTTON, &FifoPlayerDlg::OnFindPreviousClick, this);
  228. m_searchField->Bind(wxEVT_TEXT_ENTER, &FifoPlayerDlg::OnBeginSearch, this);
  229. m_searchField->Bind(wxEVT_TEXT, &FifoPlayerDlg::OnSearchFieldTextChanged, this);
  230. // Setup command copying
  231. wxAcceleratorEntry entry;
  232. entry.Set(wxACCEL_CTRL, (int)'C', wxID_COPY);
  233. wxAcceleratorTable accel(1, &entry);
  234. m_objectCmdList->SetAcceleratorTable(accel);
  235. m_objectCmdList->Bind(wxEVT_MENU, &FifoPlayerDlg::OnObjectCmdListSelectionCopy, this, wxID_COPY);
  236. Bind(RECORDING_FINISHED_EVENT, &FifoPlayerDlg::OnRecordingFinished, this);
  237. Bind(FRAME_WRITTEN_EVENT, &FifoPlayerDlg::OnFrameWritten, this);
  238. Show();
  239. }
  240. void FifoPlayerDlg::OnPaint(wxPaintEvent& event)
  241. {
  242. UpdatePlayGui();
  243. UpdateRecorderGui();
  244. UpdateAnalyzerGui();
  245. event.Skip();
  246. }
  247. void FifoPlayerDlg::OnFrameFrom(wxSpinEvent& event)
  248. {
  249. FifoPlayer &player = FifoPlayer::GetInstance();
  250. player.SetFrameRangeStart(event.GetPosition());
  251. m_FrameFromCtrl->SetValue(player.GetFrameRangeStart());
  252. m_FrameToCtrl->SetValue(player.GetFrameRangeEnd());
  253. }
  254. void FifoPlayerDlg::OnFrameTo(wxSpinEvent& event)
  255. {
  256. FifoPlayer &player = FifoPlayer::GetInstance();
  257. player.SetFrameRangeEnd(event.GetPosition());
  258. m_FrameFromCtrl->SetValue(player.GetFrameRangeStart());
  259. m_FrameToCtrl->SetValue(player.GetFrameRangeEnd());
  260. }
  261. void FifoPlayerDlg::OnObjectFrom(wxSpinEvent& event)
  262. {
  263. FifoPlayer::GetInstance().SetObjectRangeStart(event.GetPosition());
  264. }
  265. void FifoPlayerDlg::OnObjectTo(wxSpinEvent& event)
  266. {
  267. FifoPlayer::GetInstance().SetObjectRangeEnd(event.GetPosition());
  268. }
  269. void FifoPlayerDlg::OnCheckEarlyMemoryUpdates(wxCommandEvent& event)
  270. {
  271. FifoPlayer::GetInstance().SetEarlyMemoryUpdates(event.IsChecked());
  272. }
  273. void FifoPlayerDlg::OnSaveFile(wxCommandEvent& WXUNUSED(event))
  274. {
  275. // Pointer to the file data that was created as a result of recording.
  276. FifoDataFile *file = FifoRecorder::GetInstance().GetRecordedFile();
  277. if (file)
  278. {
  279. // Bring up a save file dialog. The location the user chooses will be assigned to this variable.
  280. wxString path = wxSaveFileSelector(_("Dolphin FIFO"), "dff", wxEmptyString, this);
  281. // Has a valid file path
  282. if (!path.empty())
  283. {
  284. // Attempt to save the file to the path the user chose
  285. wxBeginBusyCursor();
  286. bool result = file->Save(WxStrToStr(path));
  287. wxEndBusyCursor();
  288. // Wasn't able to save the file, shit's whack, yo.
  289. if (!result)
  290. WxUtils::ShowErrorDialog(_("Error saving file."));
  291. }
  292. }
  293. }
  294. void FifoPlayerDlg::OnRecordStop(wxCommandEvent& WXUNUSED(event))
  295. {
  296. FifoRecorder& recorder = FifoRecorder::GetInstance();
  297. // Recorder is still recording
  298. if (recorder.IsRecording())
  299. {
  300. // Then stop recording
  301. recorder.StopRecording();
  302. // and change the button label accordingly.
  303. m_RecordStop->SetLabel(_("Record"));
  304. }
  305. else // Recorder is actually about to start recording
  306. {
  307. // So start recording
  308. recorder.StartRecording(m_FramesToRecord, RecordingFinished);
  309. // and change the button label accordingly.
  310. m_RecordStop->SetLabel(_("Stop"));
  311. }
  312. }
  313. void FifoPlayerDlg::OnNumFramesToRecord(wxSpinEvent& event)
  314. {
  315. m_FramesToRecord = event.GetPosition();
  316. // Entering 0 frames in the control indicates infinite frames to record
  317. // The fifo recorder takes any value < 0 to be infinite frames
  318. if (m_FramesToRecord < 1)
  319. m_FramesToRecord = -1;
  320. }
  321. void FifoPlayerDlg::OnBeginSearch(wxCommandEvent& event)
  322. {
  323. wxString str_search_val = m_searchField->GetValue();
  324. if (m_framesList->GetSelection() == -1)
  325. return;
  326. // TODO: Limited to even str lengths...
  327. if (!str_search_val.empty() && str_search_val.length() % 2)
  328. {
  329. m_numResultsText->SetLabel(_("Invalid search string (only even string lengths supported)"));
  330. return;
  331. }
  332. unsigned int const val_length = str_search_val.length() / 2;
  333. std::vector<u8> search_val(val_length);
  334. for (unsigned int i = 0; i < val_length; ++i)
  335. {
  336. wxString char_str = str_search_val.Mid(2*i, 2);
  337. unsigned long val = 0;
  338. if (!char_str.ToULong(&val, 16))
  339. {
  340. m_numResultsText->SetLabel(_("Invalid search string (couldn't convert to number)"));
  341. return;
  342. }
  343. search_val[i] = (u8)val;
  344. }
  345. search_results.clear();
  346. int const frame_idx = m_framesList->GetSelection();
  347. FifoPlayer& player = FifoPlayer::GetInstance();
  348. const AnalyzedFrameInfo& frame = player.GetAnalyzedFrameInfo(frame_idx);
  349. const FifoFrameInfo& fifo_frame = player.GetFile()->GetFrame(frame_idx);
  350. // TODO: Support searching through the last object... How do we know were the cmd data ends?
  351. // TODO: Support searching for bit patterns
  352. int obj_idx = m_objectsList->GetSelection();
  353. if (obj_idx == -1)
  354. {
  355. m_numResultsText->SetLabel(_("Invalid search parameters (no object selected)"));
  356. return;
  357. }
  358. const u8* const start_ptr = &fifo_frame.fifoData[frame.objectStarts[obj_idx]];
  359. const u8* const end_ptr = &fifo_frame.fifoData[frame.objectStarts[obj_idx + 1]];
  360. for (const u8* ptr = start_ptr; ptr < end_ptr - val_length + 1; ++ptr)
  361. {
  362. if (std::equal(search_val.begin(), search_val.end(), ptr))
  363. {
  364. SearchResult result;
  365. result.frame_idx = frame_idx;
  366. result.obj_idx = m_objectsList->GetSelection();
  367. result.cmd_idx = 0;
  368. for (unsigned int cmd_idx = 1; cmd_idx < m_objectCmdOffsets.size(); ++cmd_idx)
  369. {
  370. if (ptr < start_ptr + m_objectCmdOffsets[cmd_idx])
  371. {
  372. result.cmd_idx = cmd_idx - 1;
  373. break;
  374. }
  375. }
  376. search_results.push_back(result);
  377. }
  378. }
  379. ChangeSearchResult(0);
  380. m_beginSearch->Disable();
  381. m_numResultsText->SetLabel(wxString::Format(_("Found %u results for \'"), (u32)search_results.size()) + m_searchField->GetValue() + "\'");
  382. }
  383. void FifoPlayerDlg::OnSearchFieldTextChanged(wxCommandEvent& event)
  384. {
  385. ResetSearch();
  386. }
  387. void FifoPlayerDlg::OnFindNextClick(wxCommandEvent& event)
  388. {
  389. int cur_cmd_index = m_objectCmdList->GetSelection();
  390. if (cur_cmd_index == -1)
  391. {
  392. ChangeSearchResult(0);
  393. return;
  394. }
  395. for (auto it = search_results.begin(); it != search_results.end(); ++it)
  396. {
  397. if (it->cmd_idx > cur_cmd_index)
  398. {
  399. ChangeSearchResult(it - search_results.begin());
  400. return;
  401. }
  402. }
  403. }
  404. void FifoPlayerDlg::OnFindPreviousClick(wxCommandEvent& event)
  405. {
  406. int cur_cmd_index = m_objectCmdList->GetSelection();
  407. if (cur_cmd_index == -1)
  408. {
  409. ChangeSearchResult(search_results.size() - 1);
  410. return;
  411. }
  412. for (auto it = search_results.rbegin(); it != search_results.rend(); ++it)
  413. {
  414. if (it->cmd_idx < cur_cmd_index)
  415. {
  416. ChangeSearchResult(search_results.size() - 1 - (it - search_results.rbegin()));
  417. return;
  418. }
  419. }
  420. }
  421. void FifoPlayerDlg::ChangeSearchResult(unsigned int result_idx)
  422. {
  423. if (result_idx < search_results.size()) // if index is valid
  424. {
  425. m_search_result_idx = result_idx;
  426. int prev_frame = m_framesList->GetSelection();
  427. int prev_obj = m_objectsList->GetSelection();
  428. int prev_cmd = m_objectCmdList->GetSelection();
  429. m_framesList->SetSelection(search_results[result_idx].frame_idx);
  430. m_objectsList->SetSelection(search_results[result_idx].obj_idx);
  431. m_objectCmdList->SetSelection(search_results[result_idx].cmd_idx);
  432. wxCommandEvent ev(wxEVT_LISTBOX);
  433. if (prev_frame != m_framesList->GetSelection())
  434. {
  435. ev.SetInt(m_framesList->GetSelection());
  436. OnFrameListSelectionChanged(ev);
  437. }
  438. if (prev_obj != m_objectsList->GetSelection())
  439. {
  440. ev.SetInt(m_objectsList->GetSelection());
  441. OnObjectListSelectionChanged(ev);
  442. }
  443. if (prev_cmd != m_objectCmdList->GetSelection())
  444. {
  445. ev.SetInt(m_objectCmdList->GetSelection());
  446. OnObjectCmdListSelectionChanged(ev);
  447. }
  448. m_findNext->Enable(result_idx + 1 < search_results.size());
  449. m_findPrevious->Enable(result_idx != 0);
  450. }
  451. else if (search_results.size())
  452. {
  453. ChangeSearchResult(search_results.size() - 1);
  454. }
  455. }
  456. void FifoPlayerDlg::ResetSearch()
  457. {
  458. m_beginSearch->Enable(m_searchField->GetLineLength(0) > 0);
  459. m_findNext->Disable();
  460. m_findPrevious->Disable();
  461. search_results.clear();
  462. }
  463. void FifoPlayerDlg::OnFrameListSelectionChanged(wxCommandEvent& event)
  464. {
  465. FifoPlayer& player = FifoPlayer::GetInstance();
  466. m_objectsList->Clear();
  467. if (event.GetInt() != -1)
  468. {
  469. size_t num_objects = player.GetAnalyzedFrameInfo(event.GetInt()).objectStarts.size();
  470. for (size_t i = 0; i < num_objects; ++i)
  471. m_objectsList->Append(wxString::Format("Object %u", (u32)i));
  472. }
  473. // Update object list
  474. wxCommandEvent ev = wxCommandEvent(wxEVT_LISTBOX);
  475. ev.SetInt(-1);
  476. OnObjectListSelectionChanged(ev);
  477. ResetSearch();
  478. }
  479. void FifoPlayerDlg::OnObjectListSelectionChanged(wxCommandEvent& event)
  480. {
  481. FifoPlayer& player = FifoPlayer::GetInstance();
  482. int frame_idx = m_framesList->GetSelection();
  483. int object_idx = event.GetInt();
  484. m_objectCmdList->Clear();
  485. m_objectCmdOffsets.clear();
  486. if (frame_idx != -1 && object_idx != -1)
  487. {
  488. const AnalyzedFrameInfo& frame = player.GetAnalyzedFrameInfo(frame_idx);
  489. const FifoFrameInfo& fifo_frame = player.GetFile()->GetFrame(frame_idx);
  490. const u8* objectdata_start = &fifo_frame.fifoData[frame.objectStarts[object_idx]];
  491. const u8* objectdata_end = &fifo_frame.fifoData[frame.objectEnds[object_idx]];
  492. u8* objectdata = (u8*)objectdata_start;
  493. const int obj_offset = objectdata_start - &fifo_frame.fifoData[frame.objectStarts[0]];
  494. int cmd = *objectdata++;
  495. int stream_size = Common::swap16(objectdata);
  496. objectdata += 2;
  497. wxString newLabel = wxString::Format("%08X: %02X %04X ", obj_offset, cmd, stream_size);
  498. if (stream_size && ((objectdata_end - objectdata) % stream_size))
  499. newLabel += _("NOTE: Stream size doesn't match actual data length\n");
  500. while (objectdata < objectdata_end)
  501. {
  502. newLabel += wxString::Format("%02X", *objectdata++);
  503. }
  504. m_objectCmdList->Append(newLabel);
  505. m_objectCmdOffsets.push_back(0);
  506. // Between objectdata_end and next_objdata_start, there are register setting commands
  507. if (object_idx + 1 < (int)frame.objectStarts.size())
  508. {
  509. const u8* next_objdata_start = &fifo_frame.fifoData[frame.objectStarts[object_idx+1]];
  510. while (objectdata < next_objdata_start)
  511. {
  512. m_objectCmdOffsets.push_back(objectdata - objectdata_start);
  513. int new_offset = objectdata - &fifo_frame.fifoData[frame.objectStarts[0]];
  514. int command = *objectdata++;
  515. switch (command)
  516. {
  517. case GX_NOP:
  518. newLabel = "NOP";
  519. break;
  520. case 0x44:
  521. newLabel = "0x44";
  522. break;
  523. case GX_CMD_INVL_VC:
  524. newLabel = "GX_CMD_INVL_VC";
  525. break;
  526. case GX_LOAD_CP_REG:
  527. {
  528. u32 cmd2 = *objectdata++;
  529. u32 value = Common::swap32(objectdata);
  530. objectdata += 4;
  531. newLabel = wxString::Format("CP %02X %08X", cmd2, value);
  532. }
  533. break;
  534. case GX_LOAD_XF_REG:
  535. {
  536. u32 cmd2 = Common::swap32(objectdata);
  537. objectdata += 4;
  538. u8 streamSize = ((cmd2 >> 16) & 15) + 1;
  539. const u8* stream_start = objectdata;
  540. const u8* stream_end = stream_start + streamSize * 4;
  541. newLabel = wxString::Format("XF %08X ", cmd2);
  542. while (objectdata < stream_end)
  543. {
  544. newLabel += wxString::Format("%02X", *objectdata++);
  545. if (((objectdata - stream_start) % 4) == 0)
  546. newLabel += " ";
  547. }
  548. }
  549. break;
  550. case GX_LOAD_INDX_A:
  551. case GX_LOAD_INDX_B:
  552. case GX_LOAD_INDX_C:
  553. case GX_LOAD_INDX_D:
  554. objectdata += 4;
  555. newLabel = wxString::Format("LOAD INDX %s", (command == GX_LOAD_INDX_A) ? "A" :
  556. (command == GX_LOAD_INDX_B) ? "B" :
  557. (command == GX_LOAD_INDX_C) ? "C" : "D");
  558. break;
  559. case GX_CMD_CALL_DL:
  560. // The recorder should have expanded display lists into the fifo stream and skipped the call to start them
  561. // That is done to make it easier to track where memory is updated
  562. _assert_(false);
  563. objectdata += 8;
  564. newLabel = wxString::Format("CALL DL");
  565. break;
  566. case GX_LOAD_BP_REG:
  567. {
  568. u32 cmd2 = Common::swap32(objectdata);
  569. objectdata += 4;
  570. newLabel = wxString::Format("BP %02X %06X", cmd2 >> 24, cmd2 & 0xFFFFFF);
  571. }
  572. break;
  573. default:
  574. newLabel = _("Unexpected 0x80 call? Aborting...");
  575. objectdata = (u8*)next_objdata_start;
  576. break;
  577. }
  578. newLabel = wxString::Format("%08X: ", new_offset) + newLabel;
  579. m_objectCmdList->Append(newLabel);
  580. }
  581. }
  582. }
  583. // Update command list
  584. wxCommandEvent ev = wxCommandEvent(wxEVT_LISTBOX);
  585. ev.SetInt(-1);
  586. OnObjectCmdListSelectionChanged(ev);
  587. ResetSearch();
  588. }
  589. void FifoPlayerDlg::OnObjectCmdListSelectionChanged(wxCommandEvent& event)
  590. {
  591. const int frame_idx = m_framesList->GetSelection();
  592. const int object_idx = m_objectsList->GetSelection();
  593. if (event.GetInt() == -1 || frame_idx == -1 || object_idx == -1)
  594. {
  595. m_objectCmdInfo->SetLabel(wxEmptyString);
  596. return;
  597. }
  598. FifoPlayer& player = FifoPlayer::GetInstance();
  599. const AnalyzedFrameInfo& frame = player.GetAnalyzedFrameInfo(frame_idx);
  600. const FifoFrameInfo& fifo_frame = player.GetFile()->GetFrame(frame_idx);
  601. const u8* cmddata = &fifo_frame.fifoData[frame.objectStarts[object_idx]] + m_objectCmdOffsets[event.GetInt()];
  602. // TODO: Not sure whether we should bother translating the descriptions
  603. wxString newLabel;
  604. if (*cmddata == GX_LOAD_BP_REG)
  605. {
  606. std::string name;
  607. std::string desc;
  608. GetBPRegInfo(cmddata + 1, &name, &desc);
  609. newLabel = _("BP register ");
  610. newLabel += (name.empty()) ? wxString::Format(_("UNKNOWN_%02X"), *(cmddata + 1)) : StrToWxStr(name);
  611. newLabel += ":\n";
  612. if (desc.empty())
  613. newLabel += _("No description available");
  614. else
  615. newLabel += StrToWxStr(desc);
  616. }
  617. else if (*cmddata == GX_LOAD_CP_REG)
  618. {
  619. newLabel = _("CP register ");
  620. }
  621. else if (*cmddata == GX_LOAD_XF_REG)
  622. {
  623. newLabel = _("XF register ");
  624. }
  625. else
  626. {
  627. newLabel = _("No description available");
  628. }
  629. m_objectCmdInfo->SetLabel(newLabel);
  630. Layout();
  631. Fit();
  632. }
  633. void FifoPlayerDlg::OnObjectCmdListSelectionCopy(wxCommandEvent& WXUNUSED(event))
  634. {
  635. if (wxTheClipboard->Open())
  636. {
  637. wxTheClipboard->SetData(new wxTextDataObject(m_objectCmdList->GetStringSelection()));
  638. wxTheClipboard->Close();
  639. }
  640. }
  641. void FifoPlayerDlg::OnCloseClick(wxCommandEvent& WXUNUSED(event))
  642. {
  643. Hide();
  644. }
  645. void FifoPlayerDlg::OnRecordingFinished(wxEvent&)
  646. {
  647. m_RecordStop->SetLabel(_("Record"));
  648. m_RecordStop->Enable();
  649. UpdateRecorderGui();
  650. }
  651. void FifoPlayerDlg::OnFrameWritten(wxEvent&)
  652. {
  653. m_CurrentFrameLabel->SetLabel(CreateCurrentFrameLabel());
  654. m_NumObjectsLabel->SetLabel(CreateFileObjectCountLabel());
  655. }
  656. void FifoPlayerDlg::UpdatePlayGui()
  657. {
  658. m_NumFramesLabel->SetLabel(CreateFileFrameCountLabel());
  659. m_CurrentFrameLabel->SetLabel(CreateCurrentFrameLabel());
  660. m_NumObjectsLabel->SetLabel(CreateFileObjectCountLabel());
  661. FifoPlayer &player = FifoPlayer::GetInstance();
  662. FifoDataFile *file = player.GetFile();
  663. u32 frameCount = 0;
  664. if (file)
  665. frameCount = file->GetFrameCount();
  666. m_FrameFromCtrl->SetRange(0, frameCount);
  667. m_FrameFromCtrl->SetValue(player.GetFrameRangeStart());
  668. m_FrameToCtrl->SetRange(0, frameCount);
  669. m_FrameToCtrl->SetValue(player.GetFrameRangeEnd());
  670. m_ObjectFromCtrl->SetValue(player.GetObjectRangeStart());
  671. m_ObjectToCtrl->SetValue(player.GetObjectRangeEnd());
  672. }
  673. void FifoPlayerDlg::UpdateRecorderGui()
  674. {
  675. m_RecordingFifoSizeLabel->SetLabel(CreateRecordingFifoSizeLabel());
  676. m_RecordingMemSizeLabel->SetLabel(CreateRecordingMemSizeLabel());
  677. m_RecordingFramesLabel->SetLabel(CreateRecordingFrameCountLabel());
  678. m_Save->Enable(GetSaveButtonEnabled());
  679. }
  680. void FifoPlayerDlg::UpdateAnalyzerGui()
  681. {
  682. FifoPlayer &player = FifoPlayer::GetInstance();
  683. FifoDataFile* file = player.GetFile();
  684. size_t num_frames = (file) ? player.GetFile()->GetFrameCount() : 0U;
  685. if (m_framesList->GetCount() != num_frames)
  686. {
  687. m_framesList->Clear();
  688. for (size_t i = 0; i < num_frames; ++i)
  689. {
  690. m_framesList->Append(wxString::Format("Frame %u", (u32)i));
  691. }
  692. wxCommandEvent ev = wxCommandEvent(wxEVT_LISTBOX);
  693. ev.SetInt(-1);
  694. OnFrameListSelectionChanged(ev);
  695. }
  696. }
  697. wxString FifoPlayerDlg::CreateFileFrameCountLabel() const
  698. {
  699. FifoDataFile *file = FifoPlayer::GetInstance().GetFile();
  700. if (file)
  701. return CreateIntegerLabel(file->GetFrameCount(), _("Frame"));
  702. return _("No file loaded");
  703. }
  704. wxString FifoPlayerDlg::CreateCurrentFrameLabel() const
  705. {
  706. FifoDataFile *file = FifoPlayer::GetInstance().GetFile();
  707. if (file)
  708. return _("Frame ") + wxString::Format("%u", FifoPlayer::GetInstance().GetCurrentFrameNum());
  709. return wxEmptyString;
  710. }
  711. wxString FifoPlayerDlg::CreateFileObjectCountLabel() const
  712. {
  713. FifoDataFile *file = FifoPlayer::GetInstance().GetFile();
  714. if (file)
  715. return CreateIntegerLabel(FifoPlayer::GetInstance().GetFrameObjectCount(), _("Object"));
  716. return wxEmptyString;
  717. }
  718. wxString FifoPlayerDlg::CreateRecordingFifoSizeLabel() const
  719. {
  720. FifoDataFile *file = FifoRecorder::GetInstance().GetRecordedFile();
  721. if (file)
  722. {
  723. size_t fifoBytes = 0;
  724. for (size_t i = 0; i < file->GetFrameCount(); ++i)
  725. fifoBytes += file->GetFrame(i).fifoDataSize;
  726. return CreateIntegerLabel(fifoBytes, _("FIFO Byte"));
  727. }
  728. return _("No recorded file");
  729. }
  730. wxString FifoPlayerDlg::CreateRecordingMemSizeLabel() const
  731. {
  732. FifoDataFile *file = FifoRecorder::GetInstance().GetRecordedFile();
  733. if (file)
  734. {
  735. size_t memBytes = 0;
  736. for (size_t frameNum = 0; frameNum < file->GetFrameCount(); ++frameNum)
  737. {
  738. const std::vector<MemoryUpdate>& memUpdates = file->GetFrame(frameNum).memoryUpdates;
  739. for (auto& memUpdate : memUpdates)
  740. memBytes += memUpdate.size;
  741. }
  742. return CreateIntegerLabel(memBytes, _("Memory Byte"));
  743. }
  744. return wxEmptyString;
  745. }
  746. wxString FifoPlayerDlg::CreateRecordingFrameCountLabel() const
  747. {
  748. FifoDataFile *file = FifoRecorder::GetInstance().GetRecordedFile();
  749. if (file)
  750. {
  751. size_t numFrames = file->GetFrameCount();
  752. return CreateIntegerLabel(numFrames, _("Frame"));
  753. }
  754. return wxEmptyString;
  755. }
  756. wxString FifoPlayerDlg::CreateIntegerLabel(size_t size, const wxString& label) const
  757. {
  758. wxString postfix;
  759. if (size != 1)
  760. postfix = _("s");
  761. return wxString::Format("%u ", (u32)size) + label + postfix;
  762. }
  763. bool FifoPlayerDlg::GetSaveButtonEnabled() const
  764. {
  765. return (FifoRecorder::GetInstance().GetRecordedFile() != nullptr);
  766. }
  767. void FifoPlayerDlg::RecordingFinished()
  768. {
  769. sMutex.lock();
  770. if (m_EvtHandler)
  771. {
  772. wxCommandEvent event(RECORDING_FINISHED_EVENT);
  773. m_EvtHandler->AddPendingEvent(event);
  774. }
  775. sMutex.unlock();
  776. }
  777. void FifoPlayerDlg::FileLoaded()
  778. {
  779. sMutex.lock();
  780. if (m_EvtHandler)
  781. {
  782. wxPaintEvent event;
  783. m_EvtHandler->AddPendingEvent(event);
  784. }
  785. sMutex.unlock();
  786. }
  787. void FifoPlayerDlg::FrameWritten()
  788. {
  789. sMutex.lock();
  790. if (m_EvtHandler)
  791. {
  792. wxCommandEvent event(FRAME_WRITTEN_EVENT);
  793. m_EvtHandler->AddPendingEvent(event);
  794. }
  795. sMutex.unlock();
  796. }