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-11-18 14:50:43 +01:00
# include <hex/api/shortcut_manager.hpp>
2022-08-08 21:23:52 +02:00
# include <hex/api/project_file_manager.hpp>
2023-05-11 18:44:50 +02:00
# include <hex/api/layout_manager.hpp>
2023-11-16 21:38:20 +01:00
# include <hex/api/achievement_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-10-13 23:46:48 +02:00
# include <hex/helpers/debugging.hpp>
2023-03-12 18:27:29 +01:00
2023-05-11 18:44:50 +02:00
# include <content/global_actions.hpp>
2023-12-26 00:22:47 +01:00
# include <toasts/toast_notification.hpp>
2023-12-23 21:09:41 +01:00
# include <popups/popup_text_input.hpp>
2023-12-11 15:54:22 +01:00
# include <hex/api/workspace_manager.hpp>
2022-05-27 20:42:07 +02:00
2023-03-12 18:27:29 +01:00
# include <wolv/io/file.hpp>
2023-05-11 18:44:50 +02:00
# include <romfs/romfs.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 {
2023-12-27 16:33:49 +01:00
static bool s_demoWindowOpen = false ;
2022-01-18 00:10:10 +01:00
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-05-20 21:23:15 +02:00
bool noRunningTaskAndWritableProvider ( ) {
return noRunningTasks ( ) & & ImHexApi : : Provider : : isValid ( ) & & ImHexApi : : Provider : : get ( ) - > isWritable ( ) ;
}
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-12-26 00:22:47 +01:00
ui : : ToastError : : 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-12-26 00:22:47 +01:00
ui : : ToastError : : 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-12-26 00:22:47 +01:00
ui : : ToastError : : 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-12-26 00:22:47 +01:00
ui : : ToastError : : 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-12-26 00:22:47 +01:00
ui : : ToastError : : 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 importIPSPatch ( ) {
fs : : openFileBrowser ( fs : : DialogMode : : Open , { } , [ ] ( const auto & path ) {
2023-12-23 21:09:41 +01:00
TaskManager : : createTask ( " hex.ui.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-11-25 12:43:48 +01:00
auto patch = Patches : : fromIPSPatch ( patchData ) ;
2023-03-20 13:11:43 +01:00
if ( ! patch . has_value ( ) ) {
handleIPSError ( patch . error ( ) ) ;
return ;
}
2022-05-27 20:42:07 +02:00
2023-11-25 12:43:48 +01:00
task . setMaxValue ( patch - > get ( ) . 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-11-25 12:43:48 +01:00
u64 count = 0 ;
for ( auto & [ address , value ] : patch - > get ( ) ) {
provider - > write ( address , & value , sizeof ( value ) ) ;
count + = 1 ;
task . update ( count ) ;
2023-03-20 13:11:43 +01:00
}
2022-05-27 20:42:07 +02:00
2023-11-25 12:43:48 +01:00
provider - > getUndoStack ( ) . groupOperations ( count , " hex.builtin.undo_operation.patches " ) ;
2023-03-20 13:11:43 +01:00
} ) ;
} ) ;
}
void importIPS32Patch ( ) {
fs : : openFileBrowser ( fs : : DialogMode : : Open , { } , [ ] ( const auto & path ) {
2023-12-23 21:09:41 +01:00
TaskManager : : createTask ( " hex.ui.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-11-25 12:43:48 +01:00
auto patch = Patches : : fromIPS32Patch ( patchData ) ;
2023-03-20 13:11:43 +01:00
if ( ! patch . has_value ( ) ) {
handleIPSError ( patch . error ( ) ) ;
return ;
}
2022-11-25 10:47:11 +01:00
2023-11-25 12:43:48 +01:00
task . setMaxValue ( patch - > get ( ) . 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-11-25 12:43:48 +01:00
u64 count = 0 ;
for ( auto & [ address , value ] : patch - > get ( ) ) {
provider - > write ( address , & value , sizeof ( value ) ) ;
count + = 1 ;
task . update ( count ) ;
2023-03-20 13:11:43 +01:00
}
2022-05-27 20:42:07 +02:00
2023-11-25 12:43:48 +01:00
provider - > getUndoStack ( ) . groupOperations ( count , " hex.builtin.undo_operation.patches " ) ;
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 importModifiedFile ( ) {
fs : : openFileBrowser ( fs : : DialogMode : : Open , { } , [ ] ( const auto & path ) {
2023-12-23 21:09:41 +01:00
TaskManager : : createTask ( " hex.ui.common.processing " , TaskManager : : NoProgress , [ path ] ( auto & task ) {
2023-03-20 13:11:43 +01:00
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-12-26 00:22:47 +01:00
ui : : ToastError : : 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-11-25 12:43:48 +01:00
u64 count = 0 ;
2023-03-20 13:11:43 +01:00
for ( auto & [ address , value ] : patches ) {
2023-11-25 12:43:48 +01:00
provider - > write ( address , & value , sizeof ( value ) ) ;
count + = 1 ;
task . update ( count ) ;
2023-03-20 13:11:43 +01:00
}
2022-05-27 20:42:07 +02:00
2023-11-25 12:43:48 +01:00
provider - > getUndoStack ( ) . groupOperations ( count , " hex.builtin.undo_operation.patches " ) ;
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-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 ) {
2023-12-23 21:09:41 +01:00
TaskManager : : createTask ( " hex.ui.common.processing " , TaskManager : : NoProgress , [ path ] ( auto & ) {
2023-03-20 13:11:43 +01:00
wolv : : io : : File outputFile ( path , wolv : : io : : File : : Mode : : Create ) ;
if ( ! outputFile . isValid ( ) ) {
TaskManager : : doLater ( [ ] {
2023-12-26 00:22:47 +01:00
ui : : ToastError : : 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-11-09 14:57:58 +01:00
void drawExportLanguageMenu ( ) {
for ( const auto & formatter : ContentRegistry : : DataFormatter : : impl : : getEntries ( ) ) {
2023-11-21 14:38:01 +01:00
if ( ImGui : : MenuItem ( Lang ( formatter . unlocalizedName ) , nullptr , false , ImHexApi : : Provider : : get ( ) - > getActualSize ( ) > 0 ) ) {
2023-11-09 14:57:58 +01:00
fs : : openFileBrowser ( fs : : DialogMode : : Save , { } , [ & formatter ] ( const auto & path ) {
TaskManager : : createTask ( " Exporting data " , TaskManager : : NoProgress , [ & formatter , path ] ( auto & ) {
auto provider = ImHexApi : : Provider : : get ( ) ;
auto selection = ImHexApi : : HexEditor : : getSelection ( )
. value_or (
ImHexApi : : HexEditor : : ProviderRegion {
{ provider - > getBaseAddress ( ) , provider - > getSize ( ) } ,
provider
} ) ;
auto result = formatter . callback ( provider , selection . getStartAddress ( ) , selection . getSize ( ) ) ;
wolv : : io : : File file ( path , wolv : : io : : File : : Mode : : Create ) ;
if ( ! file . isValid ( ) ) {
TaskManager : : doLater ( [ ] {
2023-12-26 00:22:47 +01:00
ui : : ToastError : : open ( " hex.builtin.menu.file.export.as_language.popup.export_error " _lang ) ;
2023-11-09 14:57:58 +01:00
} ) ;
return ;
}
file . writeString ( result ) ;
} ) ;
} ) ;
}
}
}
2023-11-22 08:26:31 +01:00
void exportReport ( ) {
2023-12-23 21:09:41 +01:00
TaskManager : : createTask ( " hex.ui.common.processing " , TaskManager : : NoProgress , [ ] ( auto & ) {
2023-11-22 08:26:31 +01:00
std : : string data ;
for ( const auto & provider : ImHexApi : : Provider : : getProviders ( ) ) {
data + = hex : : format ( " # {} " , provider - > getName ( ) ) ;
data + = " \n \n " ;
for ( const auto & generator : ContentRegistry : : Reports : : impl : : getGenerators ( ) ) {
data + = generator . callback ( provider ) ;
data + = " \n \n " ;
}
data + = " \n \n " ;
}
TaskManager : : doLater ( [ data ] {
fs : : openFileBrowser ( fs : : DialogMode : : Save , { { " Markdown File " , " md " } } , [ & data ] ( const auto & path ) {
auto file = wolv : : io : : File ( path , wolv : : io : : File : : Mode : : Create ) ;
if ( ! file . isValid ( ) ) {
2023-12-26 00:22:47 +01:00
ui : : ToastError : : open ( " hex.builtin.menu.file.export.report.popup.export_error " _lang ) ;
2023-11-22 08:26:31 +01:00
return ;
}
file . writeString ( data ) ;
} ) ;
} ) ;
} ) ;
}
2023-03-20 13:11:43 +01:00
void exportIPSPatch ( ) {
auto provider = ImHexApi : : Provider : : get ( ) ;
2022-05-27 20:42:07 +02:00
2023-11-25 12:43:48 +01:00
auto patches = Patches : : fromProvider ( provider ) ;
if ( ! patches . has_value ( ) ) {
handleIPSError ( patches . error ( ) ) ;
return ;
}
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
2023-11-25 12:43:48 +01:00
if ( ! patches - > get ( ) . contains ( 0x00454F45 ) & & patches - > get ( ) . contains ( 0x00454F46 ) ) {
2023-03-20 13:11:43 +01:00
u8 value = 0 ;
provider - > read ( 0x00454F45 , & value , sizeof ( u8 ) ) ;
2023-11-25 12:43:48 +01:00
patches - > get ( ) . at ( 0x00454F45 ) = value ;
2023-03-20 13:11:43 +01:00
}
2022-05-27 20:42:07 +02:00
2023-12-23 21:09:41 +01:00
TaskManager : : createTask ( " hex.ui.common.processing " , TaskManager : : NoProgress , [ patches ] ( auto & ) {
2023-11-25 12:43:48 +01:00
auto data = patches - > toIPSPatch ( ) ;
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-12-26 00:22:47 +01:00
ui : : ToastError : : 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-12-27 16:33:49 +01:00
if ( data . has_value ( ) ) {
2023-03-23 11:23:07 +01:00
file . writeVector ( data . value ( ) ) ;
2023-12-27 16:33:49 +01:00
} else {
2023-03-20 13:11:43 +01:00
handleIPSError ( data . error ( ) ) ;
}
2023-11-16 21:38:20 +01:00
AchievementManager : : unlockAchievement ( " hex.builtin.achievement.hex_editor " , " hex.builtin.achievement.hex_editor.create_patch.name " ) ;
2023-03-20 13:11:43 +01:00
} ) ;
} ) ;
} ) ;
}
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-11-25 12:43:48 +01:00
auto patches = Patches : : fromProvider ( provider ) ;
if ( ! patches . has_value ( ) ) {
handleIPSError ( patches . error ( ) ) ;
return ;
}
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
2023-11-25 12:43:48 +01:00
if ( ! patches - > get ( ) . contains ( 0x45454F45 ) & & patches - > get ( ) . contains ( 0x45454F46 ) ) {
2023-03-20 13:11:43 +01:00
u8 value = 0 ;
provider - > read ( 0x45454F45 , & value , sizeof ( u8 ) ) ;
2023-11-25 12:43:48 +01:00
patches - > get ( ) . at ( 0x45454F45 ) = value ;
2023-03-20 13:11:43 +01:00
}
2023-02-02 14:13:37 +01:00
2023-12-23 21:09:41 +01:00
TaskManager : : createTask ( " hex.ui.common.processing " , TaskManager : : NoProgress , [ patches ] ( auto & ) {
2023-11-25 12:43:48 +01:00
auto data = patches - > toIPS32Patch ( ) ;
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-12-26 00:22:47 +01:00
ui : : ToastError : : 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-12-27 16:33:49 +01:00
if ( data . has_value ( ) ) {
2023-03-23 11:23:07 +01:00
file . writeVector ( data . value ( ) ) ;
2023-12-27 16:33:49 +01:00
} else {
2023-03-20 13:11:43 +01:00
handleIPSError ( data . error ( ) ) ;
2023-11-16 21:38:20 +01:00
}
AchievementManager : : unlockAchievement ( " hex.builtin.achievement.hex_editor " , " hex.builtin.achievement.hex_editor.create_patch.name " ) ;
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-07-05 20:49:57 +02:00
/**
* @ brief returns true if there is a currently selected provider , and it is possibl to dump data from it
*/
bool isProviderDumpable ( ) {
auto provider = ImHexApi : : Provider : : get ( ) ;
return ImHexApi : : Provider : : isValid ( ) & & provider - > isDumpable ( ) ;
}
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 */
2024-01-08 21:51:48 +01:00
ContentRegistry : : Interface : : addMenuItem ( { " hex.builtin.menu.file " , " hex.builtin.menu.file.create_file " } , ICON_VS_FILE , 1050 , CTRLCMD + Keys : : N , [ ] {
2023-03-20 13:11:43 +01:00
auto newProvider = hex : : ImHexApi : : Provider : : createProvider ( " hex.builtin.provider.mem_file " , true ) ;
if ( newProvider ! = nullptr & & ! newProvider - > open ( ) )
hex : : ImHexApi : : Provider : : remove ( newProvider ) ;
else
2023-12-08 10:29:44 +01:00
EventProviderOpened : : post ( newProvider ) ;
2023-03-20 13:11:43 +01:00
} , noRunningTasks ) ;
/* Open File */
2024-01-08 21:51:48 +01:00
ContentRegistry : : Interface : : addMenuItem ( { " hex.builtin.menu.file " , " hex.builtin.menu.file.open_file " } , ICON_VS_OPEN_PREVIEW , 1100 , CTRLCMD + Keys : : O , [ ] {
2023-12-08 10:29:44 +01:00
RequestOpenWindow : : post ( " Open File " ) ;
2023-03-20 13:11:43 +01:00
} , noRunningTasks ) ;
/* Open Other */
2024-01-08 21:51:48 +01:00
ContentRegistry : : Interface : : addMenuItemSubMenu ( { " hex.builtin.menu.file " , " hex.builtin.menu.file.open_other " } , ICON_VS_TELESCOPE , 1150 , [ ] {
2023-03-21 15:33:43 +01:00
for ( const auto & unlocalizedProviderName : ContentRegistry : : Provider : : impl : : getEntries ( ) ) {
2023-11-21 14:38:01 +01:00
if ( ImGui : : MenuItem ( Lang ( unlocalizedProviderName ) ) )
2023-03-20 13:11:43 +01:00
ImHexApi : : Provider : : createProvider ( unlocalizedProviderName ) ;
}
} , noRunningTasks ) ;
2022-05-27 20:42:07 +02:00
2023-03-20 13:11:43 +01:00
/* Reload Provider */
2024-01-08 21:51:48 +01:00
ContentRegistry : : Interface : : addMenuItem ( { " hex.builtin.menu.file " , " hex.builtin.menu.file.reload_provider " } , ICON_VS_ARROW_SWAP , 1250 , CTRLCMD + Keys : : R , [ ] {
2023-03-20 13:11:43 +01:00
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 */
2024-01-08 21:51:48 +01:00
ContentRegistry : : Interface : : addMenuItemSubMenu ( { " hex.builtin.menu.file " , " hex.builtin.menu.file.project " } , ICON_VS_NOTEBOOK , 1400 , [ ] { } , noRunningTasks ) ;
ContentRegistry : : Interface : : addMenuItem ( { " hex.builtin.menu.file " , " hex.builtin.menu.file.project " , " hex.builtin.menu.file.project.open " } , ICON_VS_ROOT_FOLDER_OPENED , 1410 ,
2023-03-20 13:11:43 +01:00
ALT + Keys : : O ,
openProject , noRunningTasks ) ;
2024-01-08 21:51:48 +01:00
ContentRegistry : : Interface : : addMenuItem ( { " hex.builtin.menu.file " , " hex.builtin.menu.file.project " , " hex.builtin.menu.file.project.save " } , ICON_VS_SAVE , 1450 ,
2023-03-20 13:11:43 +01:00
ALT + Keys : : S ,
saveProject , [ & ] { return noRunningTaskAndValidProvider ( ) & & ProjectFile : : hasPath ( ) ; } ) ;
2024-01-08 21:51:48 +01:00
ContentRegistry : : Interface : : addMenuItem ( { " hex.builtin.menu.file " , " hex.builtin.menu.file.project " , " hex.builtin.menu.file.project.save_as " } , ICON_VS_SAVE_AS , 1500 ,
2023-03-20 13:11:43 +01:00
ALT + SHIFT + Keys : : S ,
2023-06-03 23:18:43 +02:00
saveProjectAs , noRunningTaskAndValidProvider ) ;
2023-03-20 13:11:43 +01:00
ContentRegistry : : Interface : : addMenuItemSeparator ( { " hex.builtin.menu.file " } , 2000 ) ;
/* Import */
{
2024-01-08 21:51:48 +01:00
ContentRegistry : : Interface : : addMenuItemSubMenu ( { " hex.builtin.menu.file " , " hex.builtin.menu.file.import " } , ICON_VS_SIGN_IN , 2140 , [ ] { } , noRunningTaskAndWritableProvider ) ;
2023-03-20 13:11:43 +01:00
/* IPS */
2024-01-08 21:51:48 +01:00
ContentRegistry : : Interface : : addMenuItem ( { " hex.builtin.menu.file " , " hex.builtin.menu.file.import " , " hex.builtin.menu.file.import.ips " } , ICON_VS_GIT_PULL_REQUEST_NEW_CHANGES , 2150 ,
2023-03-20 13:11:43 +01:00
Shortcut : : None ,
importIPSPatch ,
2023-05-20 21:23:15 +02:00
noRunningTaskAndWritableProvider ) ;
2023-03-20 13:11:43 +01:00
/* IPS32 */
2024-01-08 21:51:48 +01:00
ContentRegistry : : Interface : : addMenuItem ( { " hex.builtin.menu.file " , " hex.builtin.menu.file.import " , " hex.builtin.menu.file.import.ips32 " } , ICON_VS_GIT_PULL_REQUEST_NEW_CHANGES , 2200 ,
2023-03-20 13:11:43 +01:00
Shortcut : : None ,
importIPS32Patch ,
2023-05-20 21:23:15 +02:00
noRunningTaskAndWritableProvider ) ;
2023-03-20 13:11:43 +01:00
/* Modified File */
2024-01-08 21:51:48 +01:00
ContentRegistry : : Interface : : addMenuItem ( { " hex.builtin.menu.file " , " hex.builtin.menu.file.import " , " hex.builtin.menu.file.import.modified_file " } , ICON_VS_FILES , 2300 ,
2023-03-20 13:11:43 +01:00
Shortcut : : None ,
importModifiedFile ,
2023-05-20 21:23:15 +02:00
noRunningTaskAndWritableProvider ) ;
2023-03-20 13:11:43 +01:00
}
/* Export */
2023-07-05 20:49:57 +02:00
/* Only make them accessible if the current provider is dumpable */
2023-03-20 13:11:43 +01:00
{
2024-01-08 21:51:48 +01:00
ContentRegistry : : Interface : : addMenuItemSubMenu ( { " hex.builtin.menu.file " , " hex.builtin.menu.file.export " } , ICON_VS_SIGN_OUT , 6000 , [ ] { } , isProviderDumpable ) ;
2023-03-20 13:11:43 +01:00
/* Base 64 */
2024-01-08 21:51:48 +01:00
ContentRegistry : : Interface : : addMenuItem ( { " hex.builtin.menu.file " , " hex.builtin.menu.file.export " , " hex.builtin.menu.file.export.base64 " } , 6005 ,
2023-03-20 13:11:43 +01:00
Shortcut : : None ,
exportBase64 ,
2023-07-05 20:49:57 +02:00
isProviderDumpable ) ;
2023-03-20 13:11:43 +01:00
2023-11-22 08:26:31 +01:00
/* Language */
2024-01-08 21:51:48 +01:00
ContentRegistry : : Interface : : addMenuItemSubMenu ( { " hex.builtin.menu.file " , " hex.builtin.menu.file.export " , " hex.builtin.menu.file.export.as_language " } , ICON_VS_CODE , 6010 ,
2023-11-09 14:57:58 +01:00
drawExportLanguageMenu ,
isProviderDumpable ) ;
2023-11-22 08:26:31 +01:00
/* Report */
2024-01-08 21:51:48 +01:00
ContentRegistry : : Interface : : addMenuItem ( { " hex.builtin.menu.file " , " hex.builtin.menu.file.export " , " hex.builtin.menu.file.export.report " } , ICON_VS_MARKDOWN , 6020 ,
2023-11-22 08:26:31 +01:00
Shortcut : : None ,
exportReport ,
ImHexApi : : Provider : : isValid ) ;
2023-03-20 13:11:43 +01:00
ContentRegistry : : Interface : : addMenuItemSeparator ( { " hex.builtin.menu.file " , " hex.builtin.menu.file.export " } , 6050 ) ;
/* IPS */
2024-01-08 21:51:48 +01:00
ContentRegistry : : Interface : : addMenuItem ( { " hex.builtin.menu.file " , " hex.builtin.menu.file.export " , " hex.builtin.menu.file.export.ips " } , ICON_VS_GIT_PULL_REQUEST_NEW_CHANGES , 6100 ,
2023-03-20 13:11:43 +01:00
Shortcut : : None ,
exportIPSPatch ,
2023-07-05 20:49:57 +02:00
isProviderDumpable ) ;
2023-03-20 13:11:43 +01:00
/* IPS32 */
2024-01-08 21:51:48 +01:00
ContentRegistry : : Interface : : addMenuItem ( { " hex.builtin.menu.file " , " hex.builtin.menu.file.export " , " hex.builtin.menu.file.export.ips32 " } , ICON_VS_GIT_PULL_REQUEST_NEW_CHANGES , 6150 ,
2023-03-20 13:11:43 +01:00
Shortcut : : None ,
exportIPS32Patch ,
2023-07-05 20:49:57 +02:00
isProviderDumpable ) ;
2023-03-20 13:11:43 +01:00
}
ContentRegistry : : Interface : : addMenuItemSeparator ( { " hex.builtin.menu.file " } , 10000 ) ;
/* Close Provider */
2024-01-08 21:51:48 +01:00
ContentRegistry : : Interface : : addMenuItem ( { " hex.builtin.menu.file " , " hex.builtin.menu.file.close " } , ICON_VS_CHROME_CLOSE , 10050 , CTRLCMD + Keys : : W , [ ] {
2023-03-20 13:11:43 +01:00
ImHexApi : : Provider : : remove ( ImHexApi : : Provider : : get ( ) ) ;
} , noRunningTaskAndValidProvider ) ;
/* Quit ImHex */
2024-01-08 21:51:48 +01:00
ContentRegistry : : Interface : : addMenuItem ( { " hex.builtin.menu.file " , " hex.builtin.menu.file.quit " } , ICON_VS_CLOSE_ALL , 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 */
2024-01-08 21:51:48 +01:00
ContentRegistry : : Interface : : addMenuItem ( { " hex.builtin.menu.edit " , " hex.builtin.menu.edit.undo " } , ICON_VS_DISCARD , 1000 , CTRLCMD + Keys : : Z , [ ] {
2023-03-20 13:11:43 +01:00
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 */
2024-01-08 21:51:48 +01:00
ContentRegistry : : Interface : : addMenuItem ( { " hex.builtin.menu.edit " , " hex.builtin.menu.edit.redo " } , ICON_VS_REDO , 1050 , CTRLCMD + Keys : : Y , [ ] {
2023-03-20 13:11:43 +01:00
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
2024-01-15 20:52:08 +01:00
ContentRegistry : : Interface : : addMenuItem ( { " hex.builtin.menu.view " , " hex.builtin.menu.view.always_on_top " } , ICON_VS_PINNED , 1000 , Keys : : F10 , [ ] {
static bool state = false ;
state = ! state ;
glfwSetWindowAttrib ( ImHexApi : : System : : getMainWindowHandle ( ) , GLFW_FLOATING , state ) ;
} , [ ] { return true ; } , [ ] { return glfwGetWindowAttrib ( ImHexApi : : System : : getMainWindowHandle ( ) , GLFW_FLOATING ) ; } ) ;
ContentRegistry : : Interface : : addMenuItem ( { " hex.builtin.menu.view " , " hex.builtin.menu.view.fullscreen " } , ICON_VS_SCREEN_FULL , 2000 , Keys : : F11 , [ ] {
static bool state = false ;
static ImVec2 position , size ;
state = ! state ;
const auto window = ImHexApi : : System : : getMainWindowHandle ( ) ;
if ( state ) {
position = ImHexApi : : System : : getMainWindowPosition ( ) ;
size = ImHexApi : : System : : getMainWindowSize ( ) ;
const auto monitor = glfwGetPrimaryMonitor ( ) ;
const auto videoMode = glfwGetVideoMode ( monitor ) ;
glfwSetWindowMonitor ( window , monitor , 0 , 0 , videoMode - > width , videoMode - > height , videoMode - > refreshRate ) ;
} else {
glfwSetWindowMonitor ( window , nullptr , position . x , position . y , size . x , size . y , 0 ) ;
}
} , [ ] { return true ; } , [ ] { return glfwGetWindowMonitor ( ImHexApi : : System : : getMainWindowHandle ( ) ) ! = nullptr ; } ) ;
ContentRegistry : : Interface : : addMenuItemSeparator ( { " hex.builtin.menu.view " } , 3000 ) ;
ContentRegistry : : Interface : : addMenuItemSubMenu ( { " hex.builtin.menu.view " } , 4000 , [ ] {
2023-03-21 15:33:43 +01:00
for ( auto & [ name , view ] : ContentRegistry : : Views : : impl : : getEntries ( ) ) {
2023-10-30 21:53:44 +01:00
if ( view - > hasViewMenuItemEntry ( ) ) {
auto & state = view - > getWindowOpenState ( ) ;
2024-01-09 00:24:17 +01:00
if ( ImGui : : MenuItemEx ( Lang ( view - > getUnlocalizedName ( ) ) , view - > getIcon ( ) , " " , state , ImHexApi : : Provider : : isValid ( ) & & ! LayoutManager : : isLayoutLocked ( ) ) )
state = ! state ;
2023-10-30 21:53:44 +01:00
}
2022-01-18 00:10:10 +01:00
}
2023-03-20 13:11:43 +01:00
# if defined(DEBUG)
2023-12-18 13:55:32 +01:00
ImGui : : Separator ( ) ;
2023-10-13 23:46:48 +02:00
2023-12-27 16:33:49 +01:00
ImGui : : MenuItem ( " hex.builtin.menu.view.demo " _lang , " " , & s_demoWindowOpen ) ;
2023-10-13 23:46:48 +02:00
ImGui : : MenuItem ( " hex.builtin.menu.view.debug " _lang , " " , & hex : : dbg : : impl : : getDebugWindowState ( ) ) ;
2023-03-20 13:11:43 +01:00
# endif
} ) ;
2022-05-27 20:42:07 +02:00
}
static void createLayoutMenu ( ) {
2023-05-11 18:44:50 +02:00
LayoutManager : : reload ( ) ;
2023-12-11 15:54:22 +01:00
ContentRegistry : : Interface : : registerMainMenuItem ( " hex.builtin.menu.workspace " , 4000 ) ;
2022-01-18 00:10:10 +01:00
2024-01-08 21:51:48 +01:00
ContentRegistry : : Interface : : addMenuItemSubMenu ( { " hex.builtin.menu.workspace " , " hex.builtin.menu.workspace.layout " } , ICON_VS_LAYOUT , 1050 , [ ] { } , ImHexApi : : Provider : : isValid ) ;
2023-12-11 15:54:22 +01:00
ContentRegistry : : Interface : : addMenuItem ( { " hex.builtin.menu.workspace " , " hex.builtin.menu.workspace.layout " , " hex.builtin.menu.workspace.layout.save " } , 1100 , Shortcut : : None , [ ] {
2023-12-23 21:09:41 +01:00
ui : : PopupTextInput : : open ( " hex.builtin.popup.save_layout.title " , " hex.builtin.popup.save_layout.desc " , [ ] ( const std : : string & name ) {
2023-05-11 18:44:50 +02:00
LayoutManager : : save ( name ) ;
} ) ;
} , ImHexApi : : Provider : : isValid ) ;
2024-01-08 21:51:48 +01:00
ContentRegistry : : Interface : : addMenuItemSubMenu ( { " hex.builtin.menu.workspace " , " hex.builtin.menu.workspace.layout " } , ICON_VS_LAYOUT , 1150 , [ ] {
2023-12-06 13:49:58 +01:00
bool locked = LayoutManager : : isLayoutLocked ( ) ;
2024-01-08 21:51:48 +01:00
if ( ImGui : : MenuItemEx ( " hex.builtin.menu.workspace.layout.lock " _lang , locked ? ICON_VS_UNLOCK : ICON_VS_LOCK , nullptr , locked , ImHexApi : : Provider : : isValid ( ) ) ) {
2023-12-06 13:49:58 +01:00
LayoutManager : : lockLayout ( locked ) ;
ContentRegistry : : Settings : : write ( " hex.builtin.setting.interface " , " hex.builtin.setting.interface.layout_locked " , locked ) ;
}
} ) ;
2023-12-11 15:54:22 +01:00
ContentRegistry : : Interface : : addMenuItemSeparator ( { " hex.builtin.menu.workspace " , " hex.builtin.menu.workspace.layout " } , 1200 ) ;
2022-01-18 00:10:10 +01:00
2023-12-11 15:54:22 +01:00
ContentRegistry : : Interface : : addMenuItemSubMenu ( { " hex.builtin.menu.workspace " , " hex.builtin.menu.workspace.layout " } , 2000 , [ ] {
2023-12-02 14:35:44 +01:00
for ( const auto & path : romfs : : list ( " layouts " ) ) {
if ( ImGui : : MenuItem ( wolv : : util : : capitalizeString ( path . stem ( ) . string ( ) ) . c_str ( ) , " " , false , ImHexApi : : Provider : : isValid ( ) ) ) {
2023-12-11 15:54:22 +01:00
LayoutManager : : loadFromString ( std : : string ( romfs : : get ( path ) . string ( ) ) ) ;
2023-12-02 14:35:44 +01:00
}
2023-05-11 18:44:50 +02:00
}
2022-01-18 00:10:10 +01:00
2023-05-21 10:43:35 +02:00
bool shift = ImGui : : GetIO ( ) . KeyShift ;
2023-05-11 18:44:50 +02:00
for ( auto & [ name , path ] : LayoutManager : : getLayouts ( ) ) {
2023-12-02 14:35:44 +01:00
if ( ImGui : : MenuItem ( hex : : format ( " {}{} " , name , shift ? " " ICON_VS_X : " " ) . c_str ( ) , " " , false , ImHexApi : : Provider : : isValid ( ) ) ) {
2023-05-21 10:43:35 +02:00
if ( shift ) {
wolv : : io : : fs : : remove ( path ) ;
LayoutManager : : reload ( ) ;
2023-12-27 16:33:49 +01:00
} else {
2023-05-21 10:43:35 +02:00
LayoutManager : : load ( path ) ;
2023-12-27 16:33:49 +01:00
}
2022-01-18 00:10:10 +01:00
}
}
} ) ;
2022-05-27 20:42:07 +02:00
}
2022-01-18 00:10:10 +01:00
2023-12-11 15:54:22 +01:00
static void createWorkspaceMenu ( ) {
createLayoutMenu ( ) ;
ContentRegistry : : Interface : : addMenuItemSeparator ( { " hex.builtin.menu.workspace " } , 3000 ) ;
2024-01-08 21:51:48 +01:00
ContentRegistry : : Interface : : addMenuItem ( { " hex.builtin.menu.workspace " , " hex.builtin.menu.workspace.create " } , ICON_VS_REPO_CREATE , 3100 , Shortcut : : None , [ ] {
2023-12-23 21:09:41 +01:00
ui : : PopupTextInput : : open ( " hex.builtin.popup.create_workspace.title " , " hex.builtin.popup.create_workspace.desc " , [ ] ( const std : : string & name ) {
2023-12-11 15:54:22 +01:00
WorkspaceManager : : createWorkspace ( name ) ;
} ) ;
} , ImHexApi : : Provider : : isValid ) ;
ContentRegistry : : Interface : : addMenuItemSubMenu ( { " hex.builtin.menu.workspace " } , 3200 , [ ] {
const auto & workspaces = WorkspaceManager : : getWorkspaces ( ) ;
for ( auto it = workspaces . begin ( ) ; it ! = workspaces . end ( ) ; + + it ) {
const auto & [ name , workspace ] = * it ;
if ( ImGui : : MenuItem ( name . c_str ( ) , " " , it = = WorkspaceManager : : getCurrentWorkspace ( ) , ImHexApi : : Provider : : isValid ( ) ) ) {
WorkspaceManager : : switchWorkspace ( name ) ;
}
}
} ) ;
}
2023-05-12 08:38:32 +02:00
static void createExtrasMenu ( ) {
ContentRegistry : : Interface : : registerMainMenuItem ( " hex.builtin.menu.extras " , 5000 ) ;
}
2023-04-10 01:42:53 +02:00
2023-05-12 08:38:32 +02:00
static void createHelpMenu ( ) {
ContentRegistry : : Interface : : registerMainMenuItem ( " hex.builtin.menu.help " , 6000 ) ;
2022-05-27 20:42:07 +02:00
}
2023-05-12 08:38:32 +02:00
2022-05-27 20:42:07 +02:00
void registerMainMenuEntries ( ) {
createFileMenu ( ) ;
createEditMenu ( ) ;
createViewMenu ( ) ;
2023-12-11 15:54:22 +01:00
createWorkspaceMenu ( ) ;
2023-05-12 08:38:32 +02:00
createExtrasMenu ( ) ;
2022-05-27 20:42:07 +02:00
createHelpMenu ( ) ;
2022-01-23 20:45:51 +01:00
2023-12-08 10:29:44 +01:00
( void ) EventFrameEnd : : subscribe ( [ ] {
2023-12-27 16:33:49 +01:00
if ( s_demoWindowOpen ) {
ImGui : : ShowDemoWindow ( & s_demoWindowOpen ) ;
ImPlot : : ShowDemoWindow ( & s_demoWindowOpen ) ;
2022-01-18 00:10:10 +01:00
}
} ) ;
}
}