2022-01-18 00:10:10 +01:00
# include <hex/api/content_registry.hpp>
# include <imgui.h>
# include <implot.h>
2022-02-01 18:09:40 +01:00
# include <hex/ui/view.hpp>
2023-01-17 08:16:02 +01:00
# include <hex/api/keybinding.hpp>
2022-08-08 21:23:52 +02:00
# include <hex/api/project_file_manager.hpp>
2022-05-27 20:42:07 +02:00
# include <hex/helpers/file.hpp>
# include <hex/helpers/crypto.hpp>
2022-08-08 21:23:52 +02:00
# include <hex/helpers/patches.hpp>
2022-11-25 10:47:11 +01:00
# include "content/global_actions.hpp"
2022-05-27 20:42:07 +02:00
2023-01-17 08:16:02 +01:00
using namespace std : : literals : : string_literals ;
2022-01-18 00:10:10 +01:00
namespace hex : : plugin : : builtin {
static bool g_demoWindowOpen = false ;
2023-01-25 10:38:04 +01:00
void handleIPSError ( IPSError error ) {
switch ( error ) {
case IPSError : : InvalidPatchHeader :
View : : showErrorPopup ( " hex.builtin.menu.file.export.ips.popup.invalid_patch_header_error " _lang ) ;
break ;
case IPSError : : AddressOutOfRange :
View : : showErrorPopup ( " hex.builtin.menu.file.export.ips.popup.address_out_of_range_error " _lang ) ;
break ;
case IPSError : : PatchTooLarge :
View : : showErrorPopup ( " hex.builtin.menu.file.export.ips.popup.patch_too_large_error " _lang ) ;
break ;
case IPSError : : InvalidPatchFormat :
View : : showErrorPopup ( " hex.builtin.menu.file.export.ips.popup.invalid_patch_format_error " _lang ) ;
break ;
case IPSError : : MissingEOF :
View : : showErrorPopup ( " hex.builtin.menu.file.export.ips.popup.missing_eof_error " _lang ) ;
break ;
}
}
2022-05-27 20:42:07 +02:00
static void createFileMenu ( ) {
2022-07-29 11:35:29 +02:00
2022-01-24 20:53:17 +01:00
ContentRegistry : : Interface : : registerMainMenuItem ( " hex.builtin.menu.file " , 1000 ) ;
2022-05-27 20:42:07 +02:00
ContentRegistry : : Interface : : addMenuItem ( " hex.builtin.menu.file " , 1050 , [ & ] {
2022-08-17 16:15:36 +02:00
bool taskRunning = TaskManager : : getRunningTaskCount ( ) > 0 ;
2022-07-29 11:35:29 +02:00
2023-01-17 08:16:02 +01:00
if ( ImGui : : MenuItem ( " hex.builtin.menu.file.create_file " _lang , ( CTRLCMD_NAME + " + N " s ) . c_str ( ) , false , ! taskRunning ) ) {
2022-10-21 12:01:28 +02:00
auto newProvider = hex : : ImHexApi : : Provider : : createProvider ( " hex.builtin.provider.mem_file " , true ) ;
if ( newProvider ! = nullptr & & ! newProvider - > open ( ) )
hex : : ImHexApi : : Provider : : remove ( newProvider ) ;
else
EventManager : : post < EventProviderOpened > ( newProvider ) ;
2022-08-28 20:55:48 +02:00
}
2022-05-27 20:42:07 +02:00
2023-01-17 08:16:02 +01:00
if ( ImGui : : MenuItem ( " hex.builtin.menu.file.open_file " _lang , ( CTRLCMD_NAME + " + O " s ) . c_str ( ) , false , ! taskRunning ) ) {
2022-08-28 20:55:48 +02:00
EventManager : : post < RequestOpenWindow > ( " Open File " ) ;
2022-05-27 20:42:07 +02:00
}
2022-07-29 11:35:29 +02:00
if ( ImGui : : BeginMenu ( " hex.builtin.menu.file.open_other " _lang , ! taskRunning ) ) {
2022-05-27 20:42:07 +02:00
for ( const auto & unlocalizedProviderName : ContentRegistry : : Provider : : getEntries ( ) ) {
if ( ImGui : : MenuItem ( LangEntry ( unlocalizedProviderName ) ) ) {
2022-08-06 22:29:59 +02:00
ImHexApi : : Provider : : createProvider ( unlocalizedProviderName ) ;
2022-05-27 20:42:07 +02:00
}
}
ImGui : : EndMenu ( ) ;
}
2022-09-26 11:49:35 +02:00
2023-01-17 08:16:02 +01:00
if ( ImGui : : MenuItem ( " hex.builtin.menu.file.reload_file " _lang , ( CTRLCMD_NAME + " + R " s ) . c_str ( ) , false , ! taskRunning & & ImHexApi : : Provider : : isValid ( ) ) ) {
2022-09-26 11:49:35 +02:00
auto provider = ImHexApi : : Provider : : get ( ) ;
provider - > close ( ) ;
if ( ! provider - > open ( ) )
ImHexApi : : Provider : : remove ( provider , true ) ;
}
2022-05-27 20:42:07 +02:00
} ) ;
2023-01-19 17:05:54 +01:00
/* File close, quit imhex */
ContentRegistry : : Interface : : addMenuItem ( " hex.builtin.menu.file " , 5000 , [ & ] {
2022-05-27 20:42:07 +02:00
bool providerValid = ImHexApi : : Provider : : isValid ( ) ;
2022-08-17 16:15:36 +02:00
bool taskRunning = TaskManager : : getRunningTaskCount ( ) > 0 ;
2022-05-27 20:42:07 +02:00
2023-01-17 08:16:02 +01:00
if ( ImGui : : MenuItem ( " hex.builtin.menu.file.close " _lang , ( CTRLCMD_NAME + " + W " s ) . c_str ( ) , false , providerValid & & ! taskRunning ) ) {
2022-05-27 20:42:07 +02:00
ImHexApi : : Provider : : remove ( ImHexApi : : Provider : : get ( ) ) ;
}
2023-01-19 17:05:54 +01:00
if ( ImGui : : MenuItem ( " hex.builtin.menu.file.quit " _lang , " Alt + F4 " ) ) {
2022-05-27 20:42:07 +02:00
ImHexApi : : Common : : closeImHex ( ) ;
}
} ) ;
/* Project open / save */
2023-01-19 17:05:54 +01:00
ContentRegistry : : Interface : : addMenuItem ( " hex.builtin.menu.file " , 1150 , [ & ] {
2022-05-27 20:42:07 +02:00
auto provider = ImHexApi : : Provider : : get ( ) ;
bool providerValid = ImHexApi : : Provider : : isValid ( ) ;
2022-08-17 16:15:36 +02:00
bool taskRunning = TaskManager : : getRunningTaskCount ( ) > 0 ;
2022-05-27 20:42:07 +02:00
2022-07-29 11:35:29 +02:00
if ( ImGui : : MenuItem ( " hex.builtin.menu.file.open_project " _lang , " " , false , ! taskRunning ) ) {
2022-11-25 10:47:11 +01:00
openProject ( ) ;
2022-05-27 20:42:07 +02:00
}
2023-01-17 08:16:02 +01:00
if ( ImGui : : MenuItem ( " hex.builtin.menu.file.save_project " _lang , ( ALT_NAME + " + S " s ) . c_str ( ) , false , providerValid & & ProjectFile : : hasPath ( ) ) ) {
2022-11-25 10:47:11 +01:00
saveProject ( ) ;
}
2023-01-17 08:16:02 +01:00
if ( ImGui : : MenuItem ( " hex.builtin.menu.file.save_project_as " _lang , ( ALT_NAME + " + " s + SHIFT_NAME + " + S " s ) . c_str ( ) , false , providerValid & & provider - > isWritable ( ) ) ) {
2022-11-25 10:47:11 +01:00
saveProjectAs ( ) ;
2022-05-27 20:42:07 +02:00
}
} ) ;
/* Import / Export */
ContentRegistry : : Interface : : addMenuItem ( " hex.builtin.menu.file " , 1300 , [ & ] {
auto provider = ImHexApi : : Provider : : get ( ) ;
bool providerValid = ImHexApi : : Provider : : isValid ( ) ;
2022-08-17 16:15:36 +02:00
bool taskRunning = TaskManager : : getRunningTaskCount ( ) > 0 ;
2022-05-27 20:42:07 +02:00
/* Import */
2022-07-29 11:35:29 +02:00
if ( ImGui : : BeginMenu ( " hex.builtin.menu.file.import " _lang , ! taskRunning ) ) {
2022-05-27 20:42:07 +02:00
if ( ImGui : : MenuItem ( " hex.builtin.menu.file.import.base64 " _lang ) ) {
fs : : openFileBrowser ( fs : : DialogMode : : Open , { } , [ ] ( const auto & path ) {
fs : : File inputFile ( path , fs : : File : : Mode : : Read ) ;
if ( ! inputFile . isValid ( ) ) {
View : : showErrorPopup ( " hex.builtin.menu.file.import.base64.popup.open_error " _lang ) ;
return ;
}
auto base64 = inputFile . readBytes ( ) ;
if ( ! base64 . empty ( ) ) {
auto data = crypt : : decode64 ( base64 ) ;
if ( data . empty ( ) )
View : : showErrorPopup ( " hex.builtin.menu.file.import.base64.popup.import_error " _lang ) ;
else {
fs : : openFileBrowser ( fs : : DialogMode : : Save , { } , [ & data ] ( const std : : fs : : path & path ) {
fs : : File outputFile ( path , fs : : File : : Mode : : Create ) ;
if ( ! outputFile . isValid ( ) )
View : : showErrorPopup ( " hex.builtin.menu.file.import.base64.popup.import_error " _lang ) ;
outputFile . write ( data ) ;
} ) ;
}
} else {
View : : showErrorPopup ( " hex.builtin.popup.file_open_error " _lang ) ;
}
} ) ;
}
ImGui : : Separator ( ) ;
if ( ImGui : : MenuItem ( " hex.builtin.menu.file.import.ips " _lang , nullptr , false ) ) {
fs : : openFileBrowser ( fs : : DialogMode : : Open , { } , [ ] ( const auto & path ) {
2022-08-17 16:15:36 +02:00
TaskManager : : createTask ( " hex.builtin.common.processing " , TaskManager : : NoProgress , [ path ] ( auto & task ) {
2022-05-27 20:42:07 +02:00
auto patchData = fs : : File ( path , fs : : File : : Mode : : Read ) . readBytes ( ) ;
auto patch = hex : : loadIPSPatch ( patchData ) ;
2023-01-25 10:38:04 +01:00
if ( ! patch . has_value ( ) ) {
handleIPSError ( patch . error ( ) ) ;
return ;
}
2022-05-27 20:42:07 +02:00
2023-01-25 10:38:04 +01:00
task . setMaxValue ( patch - > size ( ) ) ;
2022-05-27 20:42:07 +02:00
auto provider = ImHexApi : : Provider : : get ( ) ;
u64 progress = 0 ;
2023-01-25 10:38:04 +01:00
for ( auto & [ address , value ] : * patch ) {
2022-05-27 20:42:07 +02:00
provider - > addPatch ( address , & value , 1 ) ;
progress + + ;
task . update ( progress ) ;
}
provider - > createUndoPoint ( ) ;
2022-08-17 16:15:36 +02:00
} ) ;
2022-05-27 20:42:07 +02:00
} ) ;
}
if ( ImGui : : MenuItem ( " hex.builtin.menu.file.import.ips32 " _lang , nullptr , false ) ) {
fs : : openFileBrowser ( fs : : DialogMode : : Open , { } , [ ] ( const auto & path ) {
2022-08-17 16:15:36 +02:00
TaskManager : : createTask ( " hex.builtin.common.processing " , TaskManager : : NoProgress , [ path ] ( auto & task ) {
2022-05-27 20:42:07 +02:00
auto patchData = fs : : File ( path , fs : : File : : Mode : : Read ) . readBytes ( ) ;
2023-01-25 10:38:04 +01:00
auto patch = hex : : loadIPS32Patch ( patchData ) ;
if ( ! patch . has_value ( ) ) {
handleIPSError ( patch . error ( ) ) ;
return ;
}
2022-05-27 20:42:07 +02:00
2023-01-25 10:38:04 +01:00
task . setMaxValue ( patch - > size ( ) ) ;
2022-05-27 20:42:07 +02:00
auto provider = ImHexApi : : Provider : : get ( ) ;
u64 progress = 0 ;
2023-01-25 10:38:04 +01:00
for ( auto & [ address , value ] : * patch ) {
2022-05-27 20:42:07 +02:00
provider - > addPatch ( address , & value , 1 ) ;
progress + + ;
task . update ( progress ) ;
}
provider - > createUndoPoint ( ) ;
2022-08-17 16:15:36 +02:00
} ) ;
2022-05-27 20:42:07 +02:00
} ) ;
}
ImGui : : EndMenu ( ) ;
}
/* Export */
if ( ImGui : : BeginMenu ( " hex.builtin.menu.file.export " _lang , providerValid & & provider - > isWritable ( ) ) ) {
2023-01-25 10:51:00 +01:00
if ( ImGui : : MenuItem ( " hex.builtin.menu.file.export.base64 " _lang ) ) {
fs : : openFileBrowser ( fs : : DialogMode : : Save , { } , [ ] ( const auto & path ) {
TaskManager : : createTask ( " hex.builtin.common.processing " , TaskManager : : NoProgress , [ path ] ( auto & ) {
fs : : File outputFile ( path , fs : : File : : Mode : : Create ) ;
if ( ! outputFile . isValid ( ) ) {
TaskManager : : doLater ( [ ] {
View : : showErrorPopup ( " hex.builtin.menu.file.export.base64.popup.export_error " _lang ) ;
} ) ;
return ;
}
auto provider = ImHexApi : : Provider : : get ( ) ;
std : : vector < u8 > bytes ( 3000 ) ;
for ( u64 address = 0 ; address < provider - > getActualSize ( ) ; address + = 3000 ) {
bytes . resize ( std : : min < u64 > ( 3000 , provider - > getActualSize ( ) - address ) ) ;
provider - > read ( provider - > getBaseAddress ( ) + address , bytes . data ( ) , bytes . size ( ) ) ;
outputFile . write ( crypt : : encode64 ( bytes ) ) ;
}
} ) ;
} ) ;
}
ImGui : : Separator ( ) ;
2022-05-27 20:42:07 +02:00
if ( ImGui : : MenuItem ( " hex.builtin.menu.file.export.ips " _lang , nullptr , false ) ) {
Patches patches = provider - > getPatches ( ) ;
2023-01-25 10:38:04 +01:00
// Make sure there's no patch at address 0x00454F46 because that would cause the patch to contain the sequence "EOF" which signals the end of the patch
2022-05-27 20:42:07 +02:00
if ( ! patches . contains ( 0x00454F45 ) & & patches . contains ( 0x00454F46 ) ) {
u8 value = 0 ;
provider - > read ( 0x00454F45 , & value , sizeof ( u8 ) ) ;
patches [ 0x00454F45 ] = value ;
}
2022-08-17 16:15:36 +02:00
TaskManager : : createTask ( " hex.builtin.common.processing " , TaskManager : : NoProgress , [ patches ] ( auto & ) {
2022-05-27 20:42:07 +02:00
auto data = generateIPSPatch ( patches ) ;
2022-08-17 16:15:36 +02:00
TaskManager : : doLater ( [ data ] {
2022-05-27 20:42:07 +02:00
fs : : openFileBrowser ( fs : : DialogMode : : Save , { } , [ & data ] ( const auto & path ) {
auto file = fs : : File ( path , fs : : File : : Mode : : Create ) ;
if ( ! file . isValid ( ) ) {
2023-01-25 10:38:04 +01:00
View : : showErrorPopup ( " hex.builtin.menu.file.export.ips.popup.export_error " _lang ) ;
2022-05-27 20:42:07 +02:00
return ;
}
2023-01-25 10:38:04 +01:00
if ( data . has_value ( ) )
file . write ( data . value ( ) ) ;
else {
handleIPSError ( data . error ( ) ) ;
}
2022-05-27 20:42:07 +02:00
} ) ;
} ) ;
2022-08-17 16:15:36 +02:00
} ) ;
2022-05-27 20:42:07 +02:00
}
if ( ImGui : : MenuItem ( " hex.builtin.menu.file.export.ips32 " _lang , nullptr , false ) ) {
Patches patches = provider - > getPatches ( ) ;
2023-01-25 10:38:04 +01:00
// Make sure there's no patch at address 0x45454F46 because that would cause the patch to contain the sequence "*EOF" which signals the end of the patch
if ( ! patches . contains ( 0x45454F45 ) & & patches . contains ( 0x45454F46 ) ) {
2022-05-27 20:42:07 +02:00
u8 value = 0 ;
provider - > read ( 0x45454F45 , & value , sizeof ( u8 ) ) ;
patches [ 0x45454F45 ] = value ;
}
2022-08-17 16:15:36 +02:00
TaskManager : : createTask ( " hex.builtin.common.processing " , TaskManager : : NoProgress , [ patches ] ( auto & ) {
2022-05-27 20:42:07 +02:00
auto data = generateIPS32Patch ( patches ) ;
2022-08-17 16:15:36 +02:00
TaskManager : : doLater ( [ data ] {
2022-05-27 20:42:07 +02:00
fs : : openFileBrowser ( fs : : DialogMode : : Save , { } , [ & data ] ( const auto & path ) {
auto file = fs : : File ( path , fs : : File : : Mode : : Create ) ;
if ( ! file . isValid ( ) ) {
2023-01-25 10:38:04 +01:00
View : : showErrorPopup ( " hex.builtin.menu.file.export.ips.popup.export_error " _lang ) ;
2022-05-27 20:42:07 +02:00
return ;
}
2023-01-25 10:38:04 +01:00
if ( data . has_value ( ) )
file . write ( data . value ( ) ) ;
else
handleIPSError ( data . error ( ) ) ;
2022-05-27 20:42:07 +02:00
} ) ;
} ) ;
2022-08-17 16:15:36 +02:00
} ) ;
2022-05-27 20:42:07 +02:00
}
ImGui : : EndMenu ( ) ;
}
} ) ;
}
static void createEditMenu ( ) {
2022-01-24 20:53:17 +01:00
ContentRegistry : : Interface : : registerMainMenuItem ( " hex.builtin.menu.edit " , 2000 ) ;
2022-05-27 20:42:07 +02:00
/* Provider Undo / Redo */
ContentRegistry : : Interface : : addMenuItem ( " hex.builtin.menu.edit " , 1000 , [ & ] {
auto provider = ImHexApi : : Provider : : get ( ) ;
bool providerValid = ImHexApi : : Provider : : isValid ( ) ;
2023-01-17 08:16:02 +01:00
if ( ImGui : : MenuItem ( " hex.builtin.menu.edit.undo " _lang , ( CTRLCMD_NAME + " + Z " s ) . c_str ( ) , false , providerValid & & provider - > canUndo ( ) ) )
2022-05-27 20:42:07 +02:00
provider - > undo ( ) ;
2023-01-17 08:16:02 +01:00
if ( ImGui : : MenuItem ( " hex.builtin.menu.edit.redo " _lang , ( CTRLCMD_NAME + " + Y " s ) . c_str ( ) , false , providerValid & & provider - > canRedo ( ) ) )
2022-05-27 20:42:07 +02:00
provider - > redo ( ) ;
} ) ;
}
static void createViewMenu ( ) {
2022-01-24 20:53:17 +01:00
ContentRegistry : : Interface : : registerMainMenuItem ( " hex.builtin.menu.view " , 3000 ) ;
2022-01-18 00:10:10 +01:00
2022-01-24 20:53:17 +01:00
ContentRegistry : : Interface : : addMenuItem ( " hex.builtin.menu.view " , 1000 , [ ] {
2022-01-18 00:10:10 +01:00
for ( auto & [ name , view ] : ContentRegistry : : Views : : getEntries ( ) ) {
if ( view - > hasViewMenuItemEntry ( ) )
ImGui : : MenuItem ( LangEntry ( view - > getUnlocalizedName ( ) ) , " " , & view - > getWindowOpenState ( ) ) ;
}
2022-01-23 20:45:51 +01:00
} ) ;
2022-01-18 00:10:10 +01:00
2022-05-27 20:42:07 +02:00
# if defined(DEBUG)
ContentRegistry : : Interface : : addMenuItem ( " hex.builtin.menu.view " , 2000 , [ ] {
ImGui : : MenuItem ( " hex.builtin.menu.view.demo " _lang , " " , & g_demoWindowOpen ) ;
} ) ;
# endif
}
static void createLayoutMenu ( ) {
ContentRegistry : : Interface : : registerMainMenuItem ( " hex.builtin.menu.layout " , 4000 ) ;
2022-01-18 00:10:10 +01:00
2022-01-23 20:45:51 +01:00
ContentRegistry : : Interface : : addMenuItem ( " hex.builtin.menu.layout " , 1000 , [ ] {
2022-01-18 00:10:10 +01:00
for ( auto & [ layoutName , func ] : ContentRegistry : : Interface : : getLayouts ( ) ) {
if ( ImGui : : MenuItem ( LangEntry ( layoutName ) , " " , false , ImHexApi : : Provider : : isValid ( ) ) ) {
2022-02-01 18:09:40 +01:00
auto dock = ImHexApi : : System : : getMainDockSpaceId ( ) ;
2022-01-18 00:10:10 +01:00
for ( auto & [ viewName , view ] : ContentRegistry : : Views : : getEntries ( ) ) {
view - > getWindowOpenState ( ) = false ;
}
ImGui : : DockBuilderRemoveNode ( dock ) ;
ImGui : : DockBuilderAddNode ( dock ) ;
func ( dock ) ;
ImGui : : DockBuilderFinish ( dock ) ;
}
}
} ) ;
2022-05-27 20:42:07 +02:00
}
2022-01-18 00:10:10 +01:00
2022-05-27 20:42:07 +02:00
static void createHelpMenu ( ) {
ContentRegistry : : Interface : : registerMainMenuItem ( " hex.builtin.menu.help " , 5000 ) ;
}
void registerMainMenuEntries ( ) {
createFileMenu ( ) ;
createEditMenu ( ) ;
createViewMenu ( ) ;
createLayoutMenu ( ) ;
createHelpMenu ( ) ;
2022-01-23 20:45:51 +01:00
2022-01-24 20:53:17 +01:00
( void ) EventManager : : subscribe < EventFrameEnd > ( [ ] {
2022-01-18 00:10:10 +01:00
if ( g_demoWindowOpen ) {
ImGui : : ShowDemoWindow ( & g_demoWindowOpen ) ;
ImPlot : : ShowDemoWindow ( & g_demoWindowOpen ) ;
}
} ) ;
}
}