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>
2023-03-12 18:27:29 +01:00
2022-05-27 20:42:07 +02:00
# include <hex/helpers/crypto.hpp>
2022-08-08 21:23:52 +02:00
# include <hex/helpers/patches.hpp>
2023-03-12 18:27:29 +01:00
2022-11-25 10:47:11 +01:00
# include "content/global_actions.hpp"
2023-04-08 00:58:53 +02:00
# include <content/popups/popup_notification.hpp>
2023-04-10 01:42:53 +02:00
# include <content/popups/popup_docs_question.hpp>
2022-05-27 20:42:07 +02:00
2023-03-12 18:27:29 +01:00
# include <wolv/io/file.hpp>
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-03-20 13:11:43 +01:00
namespace {
2023-01-25 10:38:04 +01:00
2023-03-20 13:11:43 +01:00
bool noRunningTasks ( ) {
return TaskManager : : getRunningTaskCount ( ) = = 0 ;
}
2022-07-29 11:35:29 +02:00
2023-03-20 13:11:43 +01:00
bool noRunningTaskAndValidProvider ( ) {
return noRunningTasks ( ) & & ImHexApi : : Provider : : isValid ( ) ;
}
2022-05-27 20:42:07 +02:00
2023-03-20 13:11:43 +01:00
}
2022-07-29 11:35:29 +02:00
2023-03-20 13:11:43 +01:00
namespace {
void handleIPSError ( IPSError error ) {
TaskManager : : doLater ( [ error ] {
switch ( error ) {
case IPSError : : InvalidPatchHeader :
2023-04-08 00:58:53 +02:00
PopupError : : open ( " hex.builtin.menu.file.export.ips.popup.invalid_patch_header_error " _lang ) ;
2023-03-20 13:11:43 +01:00
break ;
case IPSError : : AddressOutOfRange :
2023-04-08 00:58:53 +02:00
PopupError : : open ( " hex.builtin.menu.file.export.ips.popup.address_out_of_range_error " _lang ) ;
2023-03-20 13:11:43 +01:00
break ;
case IPSError : : PatchTooLarge :
2023-04-08 00:58:53 +02:00
PopupError : : open ( " hex.builtin.menu.file.export.ips.popup.patch_too_large_error " _lang ) ;
2023-03-20 13:11:43 +01:00
break ;
case IPSError : : InvalidPatchFormat :
2023-04-08 00:58:53 +02:00
PopupError : : open ( " hex.builtin.menu.file.export.ips.popup.invalid_patch_format_error " _lang ) ;
2023-03-20 13:11:43 +01:00
break ;
case IPSError : : MissingEOF :
2023-04-08 00:58:53 +02:00
PopupError : : open ( " hex.builtin.menu.file.export.ips.popup.missing_eof_error " _lang ) ;
2023-03-20 13:11:43 +01:00
break ;
}
} ) ;
}
2022-05-27 20:42:07 +02:00
2023-03-20 13:11:43 +01:00
}
2022-05-27 20:42:07 +02:00
2023-03-20 13:11:43 +01:00
// Import
namespace {
2022-05-27 20:42:07 +02:00
2023-03-20 13:11:43 +01:00
void importBase64 ( ) {
fs : : openFileBrowser ( fs : : DialogMode : : Open , { } , [ ] ( const auto & path ) {
wolv : : io : : File inputFile ( path , wolv : : io : : File : : Mode : : Read ) ;
if ( ! inputFile . isValid ( ) ) {
2023-04-08 00:58:53 +02:00
PopupError : : open ( " hex.builtin.menu.file.import.base64.popup.open_error " _lang ) ;
2023-03-20 13:11:43 +01:00
return ;
2022-05-27 20:42:07 +02:00
}
2023-03-23 11:23:07 +01:00
auto base64 = inputFile . readVector ( ) ;
2022-09-26 11:49:35 +02:00
2023-03-20 13:11:43 +01:00
if ( ! base64 . empty ( ) ) {
auto data = crypt : : decode64 ( base64 ) ;
2022-09-26 11:49:35 +02:00
2023-03-20 13:11:43 +01:00
if ( data . empty ( ) )
2023-04-08 00:58:53 +02:00
PopupError : : open ( " hex.builtin.menu.file.import.base64.popup.import_error " _lang ) ;
2023-03-20 13:11:43 +01:00
else {
fs : : openFileBrowser ( fs : : DialogMode : : Save , { } , [ & data ] ( const std : : fs : : path & path ) {
wolv : : io : : File outputFile ( path , wolv : : io : : File : : Mode : : Create ) ;
2022-05-27 20:42:07 +02:00
2023-03-20 13:11:43 +01:00
if ( ! outputFile . isValid ( ) )
2023-04-08 00:58:53 +02:00
PopupError : : open ( " hex.builtin.menu.file.import.base64.popup.import_error " _lang ) ;
2022-05-27 20:42:07 +02:00
2023-03-23 11:23:07 +01:00
outputFile . writeVector ( data ) ;
2023-03-20 13:11:43 +01:00
} ) ;
}
} else {
2023-04-08 00:58:53 +02:00
PopupError : : open ( " hex.builtin.popup.file_open_error " _lang ) ;
2023-03-20 13:11:43 +01:00
}
} ) ;
}
void importIPSPatch ( ) {
fs : : openFileBrowser ( fs : : DialogMode : : Open , { } , [ ] ( const auto & path ) {
TaskManager : : createTask ( " hex.builtin.common.processing " , TaskManager : : NoProgress , [ path ] ( auto & task ) {
2023-03-23 11:23:07 +01:00
auto patchData = wolv : : io : : File ( path , wolv : : io : : File : : Mode : : Read ) . readVector ( ) ;
2023-03-20 13:11:43 +01:00
auto patch = hex : : loadIPSPatch ( patchData ) ;
if ( ! patch . has_value ( ) ) {
handleIPSError ( patch . error ( ) ) ;
return ;
}
2022-05-27 20:42:07 +02:00
2023-03-20 13:11:43 +01:00
task . setMaxValue ( patch - > size ( ) ) ;
2022-05-27 20:42:07 +02:00
2023-03-20 13:11:43 +01:00
auto provider = ImHexApi : : Provider : : get ( ) ;
2022-05-27 20:42:07 +02:00
2023-03-20 13:11:43 +01:00
u64 progress = 0 ;
for ( auto & [ address , value ] : * patch ) {
provider - > addPatch ( address , & value , 1 ) ;
progress + + ;
task . update ( progress ) ;
}
2022-05-27 20:42:07 +02:00
2023-03-20 13:11:43 +01:00
provider - > createUndoPoint ( ) ;
} ) ;
} ) ;
}
void importIPS32Patch ( ) {
fs : : openFileBrowser ( fs : : DialogMode : : Open , { } , [ ] ( const auto & path ) {
TaskManager : : createTask ( " hex.builtin.common.processing " , TaskManager : : NoProgress , [ path ] ( auto & task ) {
2023-03-23 11:23:07 +01:00
auto patchData = wolv : : io : : File ( path , wolv : : io : : File : : Mode : : Read ) . readVector ( ) ;
2023-03-20 13:11:43 +01:00
auto patch = hex : : loadIPS32Patch ( patchData ) ;
if ( ! patch . has_value ( ) ) {
handleIPSError ( patch . error ( ) ) ;
return ;
}
2022-11-25 10:47:11 +01:00
2023-03-20 13:11:43 +01:00
task . setMaxValue ( patch - > size ( ) ) ;
2022-05-27 20:42:07 +02:00
2023-03-20 13:11:43 +01:00
auto provider = ImHexApi : : Provider : : get ( ) ;
2022-05-27 20:42:07 +02:00
2023-03-20 13:11:43 +01:00
u64 progress = 0 ;
for ( auto & [ address , value ] : * patch ) {
provider - > addPatch ( address , & value , 1 ) ;
progress + + ;
task . update ( progress ) ;
}
2022-05-27 20:42:07 +02:00
2023-03-20 13:11:43 +01:00
provider - > createUndoPoint ( ) ;
} ) ;
} ) ;
}
2022-05-27 20:42:07 +02:00
2023-03-20 13:11:43 +01:00
void importModifiedFile ( ) {
fs : : openFileBrowser ( fs : : DialogMode : : Open , { } , [ ] ( const auto & path ) {
TaskManager : : createTask ( " hex.builtin.common.processing " , TaskManager : : NoProgress , [ path ] ( auto & task ) {
auto provider = ImHexApi : : Provider : : get ( ) ;
2023-03-23 11:23:07 +01:00
auto patchData = wolv : : io : : File ( path , wolv : : io : : File : : Mode : : Read ) . readVector ( ) ;
2022-05-27 20:42:07 +02:00
2023-03-20 13:11:43 +01:00
if ( patchData . size ( ) ! = provider - > getActualSize ( ) ) {
2023-04-08 00:58:53 +02:00
PopupError : : open ( " hex.builtin.menu.file.import.modified_file.popup.invalid_size " _lang ) ;
2023-03-20 13:11:43 +01:00
return ;
}
2022-05-27 20:42:07 +02:00
2023-03-20 13:11:43 +01:00
const auto baseAddress = provider - > getBaseAddress ( ) ;
std : : map < u64 , u8 > patches ;
for ( u64 i = 0 ; i < patchData . size ( ) ; i + + ) {
u8 value = 0 ;
provider - > read ( baseAddress + i , & value , 1 ) ;
2022-05-27 20:42:07 +02:00
2023-03-20 13:11:43 +01:00
if ( value ! = patchData [ i ] )
patches [ baseAddress + i ] = patchData [ i ] ;
}
2022-05-27 20:42:07 +02:00
2023-03-20 13:11:43 +01:00
task . setMaxValue ( patches . size ( ) ) ;
2022-05-27 20:42:07 +02:00
2023-03-20 13:11:43 +01:00
u64 progress = 0 ;
for ( auto & [ address , value ] : patches ) {
provider - > addPatch ( address , & value , 1 ) ;
progress + + ;
task . update ( progress ) ;
}
2022-05-27 20:42:07 +02:00
2023-03-20 13:11:43 +01:00
provider - > createUndoPoint ( ) ;
} ) ;
} ) ;
}
2022-05-27 20:42:07 +02:00
2023-03-20 13:11:43 +01:00
}
2022-05-27 20:42:07 +02:00
2023-03-20 13:11:43 +01:00
// Export
namespace {
2022-05-27 20:42:07 +02:00
2023-03-20 13:11:43 +01:00
void exportBase64 ( ) {
fs : : openFileBrowser ( fs : : DialogMode : : Save , { } , [ ] ( const auto & path ) {
TaskManager : : createTask ( " hex.builtin.common.processing " , TaskManager : : NoProgress , [ path ] ( auto & ) {
wolv : : io : : File outputFile ( path , wolv : : io : : File : : Mode : : Create ) ;
if ( ! outputFile . isValid ( ) ) {
TaskManager : : doLater ( [ ] {
2023-04-08 00:58:53 +02:00
PopupError : : open ( " hex.builtin.menu.file.export.base64.popup.export_error " _lang ) ;
2022-08-17 16:15:36 +02:00
} ) ;
2023-03-20 13:11:43 +01:00
return ;
}
2022-05-27 20:42:07 +02:00
2023-03-20 13:11:43 +01:00
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 ( ) ) ;
2022-05-27 20:42:07 +02:00
2023-03-23 11:23:07 +01:00
outputFile . writeVector ( crypt : : encode64 ( bytes ) ) ;
2023-03-20 13:11:43 +01:00
}
} ) ;
} ) ;
}
2022-05-27 20:42:07 +02:00
2023-03-20 13:11:43 +01:00
void exportIPSPatch ( ) {
auto provider = ImHexApi : : Provider : : get ( ) ;
2022-05-27 20:42:07 +02:00
2023-03-20 13:11:43 +01:00
Patches patches = provider - > getPatches ( ) ;
2022-05-27 20:42:07 +02:00
2023-03-20 13:11:43 +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
if ( ! patches . contains ( 0x00454F45 ) & & patches . contains ( 0x00454F46 ) ) {
u8 value = 0 ;
provider - > read ( 0x00454F45 , & value , sizeof ( u8 ) ) ;
patches [ 0x00454F45 ] = value ;
}
2022-05-27 20:42:07 +02:00
2023-03-20 13:11:43 +01:00
TaskManager : : createTask ( " hex.builtin.common.processing " , TaskManager : : NoProgress , [ patches ] ( auto & ) {
auto data = generateIPSPatch ( patches ) ;
2023-02-02 14:13:37 +01:00
2023-03-20 13:11:43 +01:00
TaskManager : : doLater ( [ data ] {
fs : : openFileBrowser ( fs : : DialogMode : : Save , { } , [ & data ] ( const auto & path ) {
auto file = wolv : : io : : File ( path , wolv : : io : : File : : Mode : : Create ) ;
if ( ! file . isValid ( ) ) {
2023-04-08 00:58:53 +02:00
PopupError : : open ( " hex.builtin.menu.file.export.ips.popup.export_error " _lang ) ;
2023-03-20 13:11:43 +01:00
return ;
}
2023-02-02 14:13:37 +01:00
2023-03-20 13:11:43 +01:00
if ( data . has_value ( ) )
2023-03-23 11:23:07 +01:00
file . writeVector ( data . value ( ) ) ;
2023-03-20 13:11:43 +01:00
else {
handleIPSError ( data . error ( ) ) ;
}
} ) ;
} ) ;
} ) ;
}
2023-02-02 14:13:37 +01:00
2023-03-20 13:11:43 +01:00
void exportIPS32Patch ( ) {
auto provider = ImHexApi : : Provider : : get ( ) ;
2023-02-02 14:13:37 +01:00
2023-03-20 13:11:43 +01:00
Patches patches = provider - > getPatches ( ) ;
2023-02-02 14:13:37 +01:00
2023-03-20 13:11:43 +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 ) ) {
u8 value = 0 ;
provider - > read ( 0x45454F45 , & value , sizeof ( u8 ) ) ;
patches [ 0x45454F45 ] = value ;
}
2023-02-02 14:13:37 +01:00
2023-03-20 13:11:43 +01:00
TaskManager : : createTask ( " hex.builtin.common.processing " , TaskManager : : NoProgress , [ patches ] ( auto & ) {
auto data = generateIPS32Patch ( patches ) ;
2023-02-02 14:13:37 +01:00
2023-03-20 13:11:43 +01:00
TaskManager : : doLater ( [ data ] {
fs : : openFileBrowser ( fs : : DialogMode : : Save , { } , [ & data ] ( const auto & path ) {
auto file = wolv : : io : : File ( path , wolv : : io : : File : : Mode : : Create ) ;
if ( ! file . isValid ( ) ) {
2023-04-08 00:58:53 +02:00
PopupError : : open ( " hex.builtin.menu.file.export.ips.popup.export_error " _lang ) ;
2023-03-20 13:11:43 +01:00
return ;
}
2023-02-02 14:13:37 +01:00
2023-03-20 13:11:43 +01:00
if ( data . has_value ( ) )
2023-03-23 11:23:07 +01:00
file . writeVector ( data . value ( ) ) ;
2023-03-20 13:11:43 +01:00
else
handleIPSError ( data . error ( ) ) ;
2023-02-02 14:13:37 +01:00
} ) ;
2023-03-20 13:11:43 +01:00
} ) ;
} ) ;
}
2022-05-27 20:42:07 +02:00
2023-03-20 13:11:43 +01:00
}
2022-05-27 20:42:07 +02:00
2023-01-25 10:51:00 +01:00
2023-03-20 13:11:43 +01:00
static void createFileMenu ( ) {
2023-01-25 10:38:04 +01:00
2023-03-20 13:11:43 +01:00
ContentRegistry : : Interface : : registerMainMenuItem ( " hex.builtin.menu.file " , 1000 ) ;
2022-05-27 20:42:07 +02:00
2023-03-20 13:11:43 +01:00
/* Create File */
ContentRegistry : : Interface : : addMenuItem ( { " hex.builtin.menu.file " , " hex.builtin.menu.file.create_file " } , 1050 , CTRLCMD + Keys : : N , [ ] {
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 ) ;
} , noRunningTasks ) ;
/* Open File */
ContentRegistry : : Interface : : addMenuItem ( { " hex.builtin.menu.file " , " hex.builtin.menu.file.open_file " } , 1100 , CTRLCMD + Keys : : O , [ ] {
EventManager : : post < RequestOpenWindow > ( " Open File " ) ;
} , noRunningTasks ) ;
/* Open Other */
2023-03-28 10:13:41 +02:00
ContentRegistry : : Interface : : addMenuItemSubMenu ( { " hex.builtin.menu.file " , " hex.builtin.menu.file.open_other " } , 1150 , [ ] {
2023-03-21 15:33:43 +01:00
for ( const auto & unlocalizedProviderName : ContentRegistry : : Provider : : impl : : getEntries ( ) ) {
2023-03-20 13:11:43 +01:00
if ( ImGui : : MenuItem ( LangEntry ( unlocalizedProviderName ) ) )
ImHexApi : : Provider : : createProvider ( unlocalizedProviderName ) ;
}
} , noRunningTasks ) ;
2022-05-27 20:42:07 +02:00
2023-03-20 13:11:43 +01:00
/* Reload Provider */
ContentRegistry : : Interface : : addMenuItem ( { " hex.builtin.menu.file " , " hex.builtin.menu.file.reload_provider " } , 1250 , CTRLCMD + Keys : : R , [ ] {
auto provider = ImHexApi : : Provider : : get ( ) ;
2023-01-25 10:38:04 +01:00
2023-03-20 13:11:43 +01:00
provider - > close ( ) ;
if ( ! provider - > open ( ) )
ImHexApi : : Provider : : remove ( provider , true ) ;
} , noRunningTaskAndValidProvider ) ;
2022-05-27 20:42:07 +02:00
2023-03-20 13:11:43 +01:00
/* Project open / save */
ContentRegistry : : Interface : : addMenuItem ( { " hex.builtin.menu.file " , " hex.builtin.menu.file.project " , " hex.builtin.menu.file.project.open " } , 1400 ,
ALT + Keys : : O ,
openProject , noRunningTasks ) ;
ContentRegistry : : Interface : : addMenuItem ( { " hex.builtin.menu.file " , " hex.builtin.menu.file.project " , " hex.builtin.menu.file.project.save " } , 1450 ,
ALT + Keys : : S ,
saveProject , [ & ] { return noRunningTaskAndValidProvider ( ) & & ProjectFile : : hasPath ( ) ; } ) ;
ContentRegistry : : Interface : : addMenuItem ( { " hex.builtin.menu.file " , " hex.builtin.menu.file.project " , " hex.builtin.menu.file.project.save_as " } , 1500 ,
ALT + SHIFT + Keys : : S ,
saveProjectAs , [ & ] { return ImHexApi : : Provider : : isValid ( ) & & ImHexApi : : Provider : : get ( ) - > isWritable ( ) ; } ) ;
ContentRegistry : : Interface : : addMenuItemSeparator ( { " hex.builtin.menu.file " } , 2000 ) ;
/* Import */
{
/* Base 64 */
ContentRegistry : : Interface : : addMenuItem ( { " hex.builtin.menu.file " , " hex.builtin.menu.file.import " , " hex.builtin.menu.file.import.base64 " } , 2050 ,
Shortcut : : None ,
importBase64 ,
noRunningTasks ) ;
ContentRegistry : : Interface : : addMenuItemSeparator ( { " hex.builtin.menu.file " , " hex.builtin.menu.file.import " } , 2100 ) ;
/* IPS */
ContentRegistry : : Interface : : addMenuItem ( { " hex.builtin.menu.file " , " hex.builtin.menu.file.import " , " hex.builtin.menu.file.import.ips " } , 2150 ,
Shortcut : : None ,
importIPSPatch ,
ImHexApi : : Provider : : isValid ) ;
/* IPS32 */
ContentRegistry : : Interface : : addMenuItem ( { " hex.builtin.menu.file " , " hex.builtin.menu.file.import " , " hex.builtin.menu.file.import.ips32 " } , 2200 ,
Shortcut : : None ,
importIPS32Patch ,
ImHexApi : : Provider : : isValid ) ;
/* Modified File */
ContentRegistry : : Interface : : addMenuItem ( { " hex.builtin.menu.file " , " hex.builtin.menu.file.import " , " hex.builtin.menu.file.import.modified_file " } , 2300 ,
Shortcut : : None ,
importModifiedFile ,
[ & ] { return noRunningTaskAndValidProvider ( ) & & ImHexApi : : Provider : : get ( ) - > isWritable ( ) ; } ) ;
}
/* Export */
{
/* Base 64 */
ContentRegistry : : Interface : : addMenuItem ( { " hex.builtin.menu.file " , " hex.builtin.menu.file.export " , " hex.builtin.menu.file.export.base64 " } , 6000 ,
Shortcut : : None ,
exportBase64 ,
ImHexApi : : Provider : : isValid ) ;
ContentRegistry : : Interface : : addMenuItemSeparator ( { " hex.builtin.menu.file " , " hex.builtin.menu.file.export " } , 6050 ) ;
/* IPS */
ContentRegistry : : Interface : : addMenuItem ( { " hex.builtin.menu.file " , " hex.builtin.menu.file.export " , " hex.builtin.menu.file.export.ips " } , 6100 ,
Shortcut : : None ,
exportIPSPatch ,
ImHexApi : : Provider : : isValid ) ;
/* IPS32 */
ContentRegistry : : Interface : : addMenuItem ( { " hex.builtin.menu.file " , " hex.builtin.menu.file.export " , " hex.builtin.menu.file.export.ips32 " } , 6150 ,
Shortcut : : None ,
exportIPS32Patch ,
ImHexApi : : Provider : : isValid ) ;
}
ContentRegistry : : Interface : : addMenuItemSeparator ( { " hex.builtin.menu.file " } , 10000 ) ;
/* Close Provider */
ContentRegistry : : Interface : : addMenuItem ( { " hex.builtin.menu.file " , " hex.builtin.menu.file.close " } , 10050 , CTRLCMD + Keys : : W , [ ] {
ImHexApi : : Provider : : remove ( ImHexApi : : Provider : : get ( ) ) ;
} , noRunningTaskAndValidProvider ) ;
/* Quit ImHex */
ContentRegistry : : Interface : : addMenuItem ( { " hex.builtin.menu.file " , " hex.builtin.menu.file.quit " } , 10100 , ALT + Keys : : F4 , [ ] {
2023-03-21 15:33:43 +01:00
ImHexApi : : System : : closeImHex ( ) ;
2022-05-27 20:42:07 +02:00
} ) ;
}
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
2023-03-20 13:11:43 +01:00
/* Undo */
ContentRegistry : : Interface : : addMenuItem ( { " hex.builtin.menu.edit " , " hex.builtin.menu.edit.undo " } , 1000 , CTRLCMD + Keys : : Z , [ ] {
auto provider = ImHexApi : : Provider : : get ( ) ;
2022-05-27 20:42:07 +02:00
provider - > undo ( ) ;
2023-03-20 13:11:43 +01:00
} , [ & ] { return ImHexApi : : Provider : : isValid ( ) & & ImHexApi : : Provider : : get ( ) - > canUndo ( ) ; } ) ;
/* Redo */
ContentRegistry : : Interface : : addMenuItem ( { " hex.builtin.menu.edit " , " hex.builtin.menu.edit.redo " } , 1050 , CTRLCMD + Keys : : Y , [ ] {
auto provider = ImHexApi : : Provider : : get ( ) ;
2022-05-27 20:42:07 +02:00
provider - > redo ( ) ;
2023-03-20 13:11:43 +01:00
} , [ & ] { return ImHexApi : : Provider : : isValid ( ) & & ImHexApi : : Provider : : get ( ) - > canRedo ( ) ; } ) ;
2022-05-27 20:42:07 +02:00
}
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
2023-03-20 13:11:43 +01:00
ContentRegistry : : Interface : : addMenuItemSubMenu ( { " hex.builtin.menu.view " } , 1000 , [ ] {
2023-03-21 15:33:43 +01:00
for ( auto & [ name , view ] : ContentRegistry : : Views : : impl : : getEntries ( ) ) {
2022-01-18 00:10:10 +01:00
if ( view - > hasViewMenuItemEntry ( ) )
ImGui : : MenuItem ( LangEntry ( view - > getUnlocalizedName ( ) ) , " " , & view - > getWindowOpenState ( ) ) ;
}
2023-03-20 13:11:43 +01:00
ImGui : : Separator ( ) ;
# if defined(DEBUG)
2022-05-27 20:42:07 +02:00
ImGui : : MenuItem ( " hex.builtin.menu.view.demo " _lang , " " , & g_demoWindowOpen ) ;
2023-03-20 13:11:43 +01:00
# endif
} ) ;
2022-05-27 20:42:07 +02:00
}
static void createLayoutMenu ( ) {
ContentRegistry : : Interface : : registerMainMenuItem ( " hex.builtin.menu.layout " , 4000 ) ;
2022-01-18 00:10:10 +01:00
2023-03-20 13:11:43 +01:00
ContentRegistry : : Interface : : addMenuItemSubMenu ( { " hex.builtin.menu.layout " } , 1000 , [ ] {
2023-03-21 15:33:43 +01:00
for ( auto & [ layoutName , func ] : ContentRegistry : : Interface : : impl : : getLayouts ( ) ) {
2022-01-18 00:10:10 +01:00
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
2023-03-21 15:33:43 +01:00
for ( auto & [ viewName , view ] : ContentRegistry : : Views : : impl : : getEntries ( ) ) {
2022-01-18 00:10:10 +01:00
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 ) ;
2023-04-10 01:42:53 +02:00
ContentRegistry : : Interface : : addMenuItem ( { " hex.builtin.menu.help " , " hex.builtin.menu.help.ask_for_help " } , 5000 , CTRLCMD + SHIFT + Keys : : D , [ ] {
PopupDocsQuestion : : open ( ) ;
} ) ;
2022-05-27 20:42:07 +02:00
}
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 ) ;
}
} ) ;
}
}