Compare commits

...

99 Commits

Author SHA1 Message Date
WerWolv
7a9d7b59e8 Added overriding of endianess for individual variables 2020-12-06 21:40:57 +01:00
WerWolv
4720cf9fbe Added possible support for MacOS. Completely untested.
Relevant: #32
2020-12-06 13:48:56 +01:00
WerWolv
68f93c5e3d Fixed possible crash when loading files, relax pattern detection requirements
This fixes #20
2020-12-05 22:30:09 +01:00
WerWolv
7b8330f8f8 Added command line support / dropping files onto executable
Closes #36
2020-12-05 22:10:03 +01:00
WerWolv
63696b9f5f Merge pull request #38 from Thog/feature/linux-ci
CI: Add a GitHub Action to build on Ubuntu 20.04 LTS
2020-12-05 20:32:45 +01:00
Mary
727b3c6b10 CI: Add a GitHub Action to build on Ubuntu 20.04 LTS
Also clean up MSYS2 action.
2020-12-05 20:03:22 +01:00
WerWolv
05cb3831a2 Merge pull request #35 from Thog/fix/llvm-custom-lib-dir
Make sure to add LLVM_LIBRARY_DIR to link directories
2020-12-05 18:55:28 +01:00
Mary
5b51375404 Make sure to add LLVM_LIBRARY_DIR to link directories
This fix build on Debian and Ubuntu systems.

Close #28
Close #8
2020-12-05 18:13:47 +01:00
WerWolv
398e845e8d Merge pull request #33 from Thog/fix/capstone-401-support
Abstract capstone architectures
2020-12-05 17:43:18 +01:00
Mary
45c29888b4 Abstract capstone architectures
This allows to support older version of Capstone (example 4.0.1).

Should help with ubuntu building issues.
2020-12-05 17:32:30 +01:00
WerWolv
82c5bd528c Added more compile information 2020-12-05 15:39:28 +01:00
WerWolv
45d0b614c2 Added sponsoring links to Readme. Thank you <3 2020-12-05 15:10:57 +01:00
WerWolv
4725ff6d5f Don't open a console window in release build
This fixes #21
2020-12-05 14:09:32 +01:00
WerWolv
7158d4d4ad Always allow cursor movement with arrow keys
Implements #22
2020-12-05 12:55:37 +01:00
WerWolv
502d90b117 Merge remote-tracking branch 'origin/master' 2020-12-05 12:54:42 +01:00
WerWolv
f0c95b3f29 Merge pull request #29 from Thog/feature/python3-detect
Detect python version at build time
2020-12-05 12:54:30 +01:00
Mary
d3dccace37 Detect python version at build time
This remove the hardcoded version in CMakeLists.txt and
loader_script_handler.cpp.

Fixing building on Arch Linux and probably other systems.
2020-12-05 12:46:50 +01:00
WerWolv
a30453616b Turned cheat sheet popups into actual windows 2020-12-05 11:34:49 +01:00
WerWolv
168ba2ff9f Denote invalid GUIDs in data inspector
Addresses #24
2020-12-05 11:00:56 +01:00
WerWolv
2ca9a8fc79 Fixed data inspector displaying zero at the end of data
Fixes #25
2020-12-05 10:42:42 +01:00
WerWolv
6456a68805 Fixed crash when array size variable has a value of zero 2020-12-05 10:36:30 +01:00
WerWolv
e82b607fa1 Merge pull request #16 from Thog/feature/github-actions
CI: Add a GitHub Action to build on Windows
2020-12-04 13:40:36 +01:00
Mary
7168c9ed59 Limit make to 4 threads and move build.yml to build_win.yml 2020-12-04 13:17:54 +01:00
WerWolv
d7af6934b6 Added ImHex-Patterns repository to the readme 2020-12-04 09:26:19 +01:00
Mary
b4a5a936a0 CI: Add a GitHub Action to build on Windows
This build in any commits and pull requests.
2020-12-04 01:28:26 +01:00
WerWolv
6129e0d696 Added libwinpthread dll to copy instructions
Because smh not everybody has qemu installed
2020-12-03 23:18:41 +01:00
WerWolv
a0c8424800 Create FUNDING.yml 2020-12-03 21:10:38 +01:00
WerWolv
dc839e1ad1 Fixed typos, love you @foone :P 2020-12-03 20:56:02 +01:00
WerWolv
59155256a9 Create LICENSE 2020-12-03 15:34:58 +01:00
WerWolv
29b7995e7f Formatting 2020-12-03 09:18:55 +01:00
WerWolv
ae9ec8851c Credit where it's due 2020-12-03 09:17:55 +01:00
WerWolv
7c2df736df Improved readme 2020-12-01 20:47:57 +01:00
WerWolv
192e7d5060 Added Python API function to create structs and unions 2020-12-01 18:21:29 +01:00
WerWolv
6aed140ecf Use custom "argc" and "argv" exposing. Fixes #6 2020-12-01 18:19:49 +01:00
WerWolv
76d56c9ed4 Added python version requirement to cmake. Fixes #5 2020-12-01 18:18:15 +01:00
WerWolv
17096055f8 Added create_struct and create_union function to Python API 2020-12-01 16:41:38 +01:00
WerWolv
c6134bc038 Added basic python-based load scripts 2020-12-01 02:21:40 +01:00
WerWolv
16f885f469 Added read/write command to math evaluator 2020-11-30 21:45:48 +01:00
WerWolv
00072d0216 Improved pattern language cheat sheet, added math evaluator cheat sheet 2020-11-30 21:44:40 +01:00
WerWolv
4878f70e58 Added project files 2020-11-30 00:03:12 +01:00
WerWolv
9e8523470d Added applying of IPS and IPS32 patches 2020-11-29 15:09:36 +01:00
WerWolv
7316be0bc2 Added patches display window 2020-11-29 02:06:41 +01:00
WerWolv
8a4b663890 Prevent shortcuts from applying to closed windows 2020-11-29 01:18:12 +01:00
WerWolv
3276ad9979 Open file dialog to not pop up if hex view wasn't open 2020-11-29 01:17:54 +01:00
WerWolv
0890043bf4 Make Help view no longer appear in View menu 2020-11-28 22:01:50 +01:00
WerWolv
c90ef343c1 Added math evaluator / calculator to tools window 2020-11-28 21:55:52 +01:00
WerWolv
0f6e276113 Updated screenshots 2020-11-28 17:21:34 +01:00
WerWolv
1395c95831 Same treatment for the disassembly window 2020-11-28 16:15:12 +01:00
WerWolv
33b70a550f Improved look and feel of hash window. Added "Match selection" feature 2020-11-28 15:53:11 +01:00
WerWolv
985e924e9d Added simple bookmarks / comments feature. No saving yet though 2020-11-28 00:33:26 +01:00
WerWolv
3827919a32 Added error messages and error display to pattern language editor 2020-11-27 21:20:23 +01:00
WerWolv
d55bea7c46 Added character literals to pattern language 2020-11-27 14:18:28 +01:00
WerWolv
2efe326fdb Added Save, Save As and implemented error message for Base64 importing 2020-11-27 13:45:56 +01:00
WerWolv
d43bd23e1a Added default error message window 2020-11-27 13:45:27 +01:00
WerWolv
015ec12215 Improved byte write speed by a lot 2020-11-27 13:44:52 +01:00
WerWolv
fde9dc7961 Various small fixes 2020-11-27 09:09:59 +01:00
WerWolv
ed572ececf Added patching system and IPS/IPS32 patch exporting 2020-11-27 09:09:48 +01:00
WerWolv
fd2b79bea9 Removed cxxabi.h include 2020-11-24 18:12:48 +01:00
WerWolv
acc10930c2 Added MSVC symbol demangling, switched to LLVM libs for demangling 2020-11-24 18:12:08 +01:00
WerWolv
f17d6c2359 Added copy string and copy demangled string to strings window 2020-11-24 02:59:49 +01:00
WerWolv
e21211f3f6 Added back default debug/release specific flags 2020-11-24 02:00:48 +01:00
WerWolv
58deaa6b29 Added importing from base64 encoded file 2020-11-24 02:00:22 +01:00
WerWolv
fcd88b4b3b Shrink the color picker to a bit more reasonable size 2020-11-24 00:01:44 +01:00
WerWolv
3bd987ff2c Streamline view creation, save all view states when quitting 2020-11-23 23:57:19 +01:00
WerWolv
45bcdc8c46 Added toggle for ImGui demo window in Debug mode 2020-11-23 22:23:06 +01:00
WerWolv
0d0b2d6962 Indent variable name instead of color in pattern data view 2020-11-23 22:14:11 +01:00
WerWolv
fd6a41d219 Make selecting memory by clicking on a table item update the inspector 2020-11-23 16:26:01 +01:00
WerWolv
b052429a73 Improved ASCII and Wide char display in data inspector 2020-11-23 16:19:58 +01:00
WerWolv
1996f401e0 Fixed last block searched for strings to yield invalid results 2020-11-23 16:19:31 +01:00
WerWolv
3b3f2226f1 Remove collapse button from all windows 2020-11-23 15:51:58 +01:00
WerWolv
5c0f6a1e50 Enabled ImGui's viewports support 2020-11-23 15:51:40 +01:00
WerWolv
b7438f6ab8 Massively improved look and feel of pattern data, string and disassembly tables 2020-11-23 15:22:26 +01:00
WerWolv
84f80b3e06 Select region when clicking on string, disassembly or pattern data item 2020-11-23 13:10:14 +01:00
WerWolv
c281372b8d Improved table coloring 2020-11-23 13:08:24 +01:00
WerWolv
788a3cfc5e Merge pull request #3 from misson20000/bugfixes
a few bugfixes
2020-11-23 08:25:15 +01:00
misson20000
01eeb53af0 Fix out-of-bounds vector write on copy hex string 2020-11-22 18:00:46 -08:00
misson20000
7cf69128ea Actually fix time_t decoding crash on Linux 2020-11-22 18:00:17 -08:00
WerWolv
c977baecc4 Fixed crash when snprintf failed in formatter 2020-11-23 00:35:26 +01:00
WerWolv
2ab2f5e675 Improved block size text 2020-11-23 00:34:53 +01:00
WerWolv
6a38f1e9f3 Truncate file size to 2 digits after comma 2020-11-23 00:22:51 +01:00
WerWolv
f0eba69c4a Fixed time_t decoding crash on Linux 2020-11-23 00:20:29 +01:00
WerWolv
cea366e135 Fixed crash when scrolling disassembler options child off screen 2020-11-23 00:12:53 +01:00
WerWolv
d752c7434f Fixed crash when no patterns folder exists 2020-11-23 00:12:33 +01:00
WerWolv
4402120ffc Added the capstone disassembler and a disassembler window 2020-11-22 23:07:50 +01:00
WerWolv
b3fffdf530 Fixed automatic pattern loading 2020-11-22 23:06:17 +01:00
WerWolv
8d6d959e17 No need to manually set table colors 2020-11-22 20:41:54 +01:00
WerWolv
43f5cc622e Allow loading of huge files efficiently 2020-11-22 19:43:35 +01:00
WerWolv
5f025bcbcc Fixed UTF-8 decoding to not work 2020-11-22 16:22:23 +01:00
WerWolv
8297e22f10 Added global big and little endian support to the pattern parser 2020-11-22 16:22:02 +01:00
WerWolv
989eade5d7 Added big and little endian support to inspector 2020-11-22 15:32:37 +01:00
WerWolv
cd4de2ff96 Fixed data inspector to only show unsigned values 2020-11-22 12:50:49 +01:00
WerWolv
73d66365e9 Updated cheat sheet page 2020-11-22 11:20:01 +01:00
WerWolv
fbd4e593d2 Make array and pointer pattern data display more consistent with other types 2020-11-22 02:25:25 +01:00
WerWolv
033ef3889a Fixed enums failing to validate 2020-11-22 02:25:03 +01:00
WerWolv
0ce1b5d40b Added simple pointer type inside structs 2020-11-21 23:00:09 +01:00
WerWolv
ed4ed6b433 Added array sizes based on other local variables 2020-11-21 20:19:33 +01:00
WerWolv
4cd18b8358 Added auto loading patterns based on MIME types 2020-11-21 14:39:16 +01:00
WerWolv
fb85f272a1 Added pragmas to pattern language 2020-11-21 14:39:01 +01:00
WerWolv
28bb28b79c Also rename data inspector window 2020-11-21 14:37:09 +01:00
72 changed files with 5238 additions and 994 deletions

5
.github/FUNDING.yml vendored Normal file
View File

@@ -0,0 +1,5 @@
# Sponsor links
patreon: werwolv
custom: https://werwolv.net/donate
github: WerWolv

44
.github/workflows/build_linux.yml vendored Normal file
View File

@@ -0,0 +1,44 @@
name: build-linux
on:
push:
pull_request:
env:
BUILD_TYPE: Release
jobs:
build:
runs-on: ubuntu-20.04
steps:
- name: Checkout
uses: actions/checkout@v2
with:
fetch-depth: 0
- name: Setup
shell: bash
run: |
sudo apt-get update
sudo apt-get install -y build-essential \
gcc-10 \
g++-10 \
pkg-config \
cmake \
make \
libglfw3-dev \
libglm-dev \
libmagic-dev \
libssl-dev \
libcapstone-dev \
llvm-dev \
nlohmann-json3-dev \
python3-dev \
- name: Build
shell: bash
run: |
mkdir build
cd build
CC=gcc-10 CXX=g++-10 cmake ..
make -j 4

40
.github/workflows/build_win.yml vendored Normal file
View File

@@ -0,0 +1,40 @@
name: build-win
on:
push:
pull_request:
env:
BUILD_TYPE: Release
jobs:
build:
runs-on: windows-latest
strategy:
fail-fast: false
matrix:
include: [
{ msystem: MINGW64, arch: x86_64 },
# currently fail
#{ msystem: MINGW32, arch: i686 }
]
steps:
- name: Checkout
uses: actions/checkout@v2
with:
fetch-depth: 0
- name: Setup
uses: msys2/setup-msys2@v2
with:
msystem: ${{ matrix.msystem }}
install: mingw-w64-${{ matrix.arch }}-gcc mingw-w64-${{ matrix.arch }}-cmake mingw-w64-${{ matrix.arch }}-make mingw-w64-${{ matrix.arch }}-capstone mingw-w64-${{ matrix.arch }}-glfw mingw-w64-${{ matrix.arch }}-glm mingw-w64-${{ matrix.arch }}-file mingw-w64-${{ matrix.arch }}-llvm mingw-w64-${{ matrix.arch }}-nlohmann-json mingw-w64-${{ matrix.arch }}-openssl mingw-w64-${{ matrix.arch }}-polly mingw-w64-${{ matrix.arch }}-python
update: true
- name: Build
shell: msys2 {0}
run: |
mkdir build
cd build
cmake -G "MinGW Makefiles" ..
mingw32-make -j 4

8
.idea/modules.xml generated
View File

@@ -1,8 +0,0 @@
<?xml version="1.0" encoding="UTF-8"?>
<project version="4">
<component name="ProjectModuleManager">
<modules>
<module fileurl="file://$PROJECT_DIR$/.idea/HexEditor.iml" filepath="$PROJECT_DIR$/.idea/HexEditor.iml" />
</modules>
</component>
</project>

View File

@@ -1,24 +1,57 @@
cmake_minimum_required(VERSION 3.16)
project(HexEditor)
SET(CMAKE_FIND_PACKAGE_SORT_ORDER NATURAL)
SET(CMAKE_FIND_PACKAGE_SORT_DIRECTION DEC)
set(CMAKE_CXX_STANDARD 20)
find_package(PkgConfig REQUIRED)
pkg_search_module(GLFW REQUIRED glfw3)
pkg_search_module(GLM REQUIRED glm)
pkg_search_module(CRYPTO REQUIRED libcrypto)
pkg_search_module(CAPSTONE REQUIRED capstone)
find_package(OpenGL REQUIRED)
find_package(LLVM REQUIRED CONFIG)
find_package(nlohmann_json REQUIRED)
find_package(Python COMPONENTS Interpreter Development)
include_directories(include ${GLFW_INCLUDE_DIRS} libs/ImGui/include libs/glad/include)
SET(CMAKE_CXX_FLAGS "${CMAKE_C_FLAGS} -DIMGUI_IMPL_OPENGL_LOADER_GLAD")
if(Python_VERSION LESS 3)
message(STATUS ${PYTHON_VERSION_MAJOR_MINOR})
message(FATAL_ERROR "No valid version of Python 3 was found.")
endif()
llvm_map_components_to_libnames(_llvm_libs demangle)
llvm_expand_dependencies(llvm_libs ${_llvm_libs})
include_directories(include ${GLFW_INCLUDE_DIRS} ${CAPSTONE_INCLUDE_DIRS} ${LLVM_INCLUDE_DIRS} libs/ImGui/include libs/glad/include ${Python_INCLUDE_DIRS})
# Get Python major and minor
string(REPLACE "." ";" PYTHON_VERSION_MAJOR_MINOR ${Python_VERSION})
list(REMOVE_AT PYTHON_VERSION_MAJOR_MINOR 2)
list(JOIN PYTHON_VERSION_MAJOR_MINOR "." PYTHON_VERSION_MAJOR_MINOR)
SET(CMAKE_CXX_FLAGS "${CMAKE_C_FLAGS} -DIMGUI_IMPL_OPENGL_LOADER_GLAD -DPYTHON_VERSION_MAJOR_MINOR=\"\\\"${PYTHON_VERSION_MAJOR_MINOR}\"\\\"")
if (WIN32)
SET(CMAKE_CXX_FLAGS "${CMAKE_CXX_FLAGS} -static-libstdc++ -static-libgcc -static")
SET(CMAKE_CXX_FLAGS_RELEASE "${CMAKE_CXX_FLAGS_RELEASE} -Wl,-subsystem,windows")
endif (WIN32)
SET(CMAKE_CXX_FLAGS_RELEASE "${CMAKE_CXX_FLAGS_RELEASE} -DRELEASE")
SET(CMAKE_CXX_FLAGS_DEBUG "${CMAKE_CXX_FLAGS_DEBUG} -DDEBUG")
add_executable(ImHex
source/main.cpp
source/window.cpp
source/utils.cpp
source/crypto.cpp
source/helpers/utils.cpp
source/helpers/crypto.cpp
source/helpers/patches.cpp
source/helpers/math_evaluator.cpp
source/helpers/project_file_handler.cpp
source/helpers/loader_script_handler.cpp
source/lang/preprocessor.cpp
source/lang/lexer.cpp
@@ -26,7 +59,7 @@ add_executable(ImHex
source/lang/validator.cpp
source/lang/evaluator.cpp
source/provider/file_provider.cpp
source/providers/file_provider.cpp
source/views/view_hexeditor.cpp
source/views/view_pattern.cpp
@@ -37,6 +70,9 @@ add_executable(ImHex
source/views/view_tools.cpp
source/views/view_strings.cpp
source/views/view_data_inspector.cpp
source/views/view_disassembler.cpp
source/views/view_bookmarks.cpp
source/views/view_patches.cpp
libs/glad/source/glad.c
@@ -52,10 +88,12 @@ add_executable(ImHex
resource.rc
)
target_link_directories(ImHex PRIVATE ${LLVM_LIBRARY_DIR})
if (WIN32)
target_link_libraries(ImHex libglfw3.a libgcc.a libstdc++.a libmagic.a libgnurx.a libtre.a libintl.a libiconv.a shlwapi.lib ws2_32.lib libcrypto.a libwinpthread.a)
target_link_libraries(ImHex libglfw3.a libgcc.a libstdc++.a libmagic.a libgnurx.a libtre.a libintl.a libiconv.a shlwapi.lib libcrypto.a libwinpthread.a libcapstone.a ${llvm_libs} ${Python_LIBRARIES} nlohmann_json::nlohmann_json)
endif (WIN32)
if (UNIX)
target_link_libraries(ImHex libglfw.so libmagic.so libcrypto.so libdl.so)
target_link_libraries(ImHex libglfw.so libmagic.so libcrypto.so libdl.so libcapstone.so ${llvm_libs} ${Python_LIBRARIES} nlohmann_json::nlohmann_json)
endif (UNIX)

339
LICENSE Normal file
View File

@@ -0,0 +1,339 @@
GNU GENERAL PUBLIC LICENSE
Version 2, June 1991
Copyright (C) 1989, 1991 Free Software Foundation, Inc.,
51 Franklin Street, Fifth Floor, Boston, MA 02110-1301 USA
Everyone is permitted to copy and distribute verbatim copies
of this license document, but changing it is not allowed.
Preamble
The licenses for most software are designed to take away your
freedom to share and change it. By contrast, the GNU General Public
License is intended to guarantee your freedom to share and change free
software--to make sure the software is free for all its users. This
General Public License applies to most of the Free Software
Foundation's software and to any other program whose authors commit to
using it. (Some other Free Software Foundation software is covered by
the GNU Lesser General Public License instead.) You can apply it to
your programs, too.
When we speak of free software, we are referring to freedom, not
price. Our General Public Licenses are designed to make sure that you
have the freedom to distribute copies of free software (and charge for
this service if you wish), that you receive source code or can get it
if you want it, that you can change the software or use pieces of it
in new free programs; and that you know you can do these things.
To protect your rights, we need to make restrictions that forbid
anyone to deny you these rights or to ask you to surrender the rights.
These restrictions translate to certain responsibilities for you if you
distribute copies of the software, or if you modify it.
For example, if you distribute copies of such a program, whether
gratis or for a fee, you must give the recipients all the rights that
you have. You must make sure that they, too, receive or can get the
source code. And you must show them these terms so they know their
rights.
We protect your rights with two steps: (1) copyright the software, and
(2) offer you this license which gives you legal permission to copy,
distribute and/or modify the software.
Also, for each author's protection and ours, we want to make certain
that everyone understands that there is no warranty for this free
software. If the software is modified by someone else and passed on, we
want its recipients to know that what they have is not the original, so
that any problems introduced by others will not reflect on the original
authors' reputations.
Finally, any free program is threatened constantly by software
patents. We wish to avoid the danger that redistributors of a free
program will individually obtain patent licenses, in effect making the
program proprietary. To prevent this, we have made it clear that any
patent must be licensed for everyone's free use or not licensed at all.
The precise terms and conditions for copying, distribution and
modification follow.
GNU GENERAL PUBLIC LICENSE
TERMS AND CONDITIONS FOR COPYING, DISTRIBUTION AND MODIFICATION
0. This License applies to any program or other work which contains
a notice placed by the copyright holder saying it may be distributed
under the terms of this General Public License. The "Program", below,
refers to any such program or work, and a "work based on the Program"
means either the Program or any derivative work under copyright law:
that is to say, a work containing the Program or a portion of it,
either verbatim or with modifications and/or translated into another
language. (Hereinafter, translation is included without limitation in
the term "modification".) Each licensee is addressed as "you".
Activities other than copying, distribution and modification are not
covered by this License; they are outside its scope. The act of
running the Program is not restricted, and the output from the Program
is covered only if its contents constitute a work based on the
Program (independent of having been made by running the Program).
Whether that is true depends on what the Program does.
1. You may copy and distribute verbatim copies of the Program's
source code as you receive it, in any medium, provided that you
conspicuously and appropriately publish on each copy an appropriate
copyright notice and disclaimer of warranty; keep intact all the
notices that refer to this License and to the absence of any warranty;
and give any other recipients of the Program a copy of this License
along with the Program.
You may charge a fee for the physical act of transferring a copy, and
you may at your option offer warranty protection in exchange for a fee.
2. You may modify your copy or copies of the Program or any portion
of it, thus forming a work based on the Program, and copy and
distribute such modifications or work under the terms of Section 1
above, provided that you also meet all of these conditions:
a) You must cause the modified files to carry prominent notices
stating that you changed the files and the date of any change.
b) You must cause any work that you distribute or publish, that in
whole or in part contains or is derived from the Program or any
part thereof, to be licensed as a whole at no charge to all third
parties under the terms of this License.
c) If the modified program normally reads commands interactively
when run, you must cause it, when started running for such
interactive use in the most ordinary way, to print or display an
announcement including an appropriate copyright notice and a
notice that there is no warranty (or else, saying that you provide
a warranty) and that users may redistribute the program under
these conditions, and telling the user how to view a copy of this
License. (Exception: if the Program itself is interactive but
does not normally print such an announcement, your work based on
the Program is not required to print an announcement.)
These requirements apply to the modified work as a whole. If
identifiable sections of that work are not derived from the Program,
and can be reasonably considered independent and separate works in
themselves, then this License, and its terms, do not apply to those
sections when you distribute them as separate works. But when you
distribute the same sections as part of a whole which is a work based
on the Program, the distribution of the whole must be on the terms of
this License, whose permissions for other licensees extend to the
entire whole, and thus to each and every part regardless of who wrote it.
Thus, it is not the intent of this section to claim rights or contest
your rights to work written entirely by you; rather, the intent is to
exercise the right to control the distribution of derivative or
collective works based on the Program.
In addition, mere aggregation of another work not based on the Program
with the Program (or with a work based on the Program) on a volume of
a storage or distribution medium does not bring the other work under
the scope of this License.
3. You may copy and distribute the Program (or a work based on it,
under Section 2) in object code or executable form under the terms of
Sections 1 and 2 above provided that you also do one of the following:
a) Accompany it with the complete corresponding machine-readable
source code, which must be distributed under the terms of Sections
1 and 2 above on a medium customarily used for software interchange; or,
b) Accompany it with a written offer, valid for at least three
years, to give any third party, for a charge no more than your
cost of physically performing source distribution, a complete
machine-readable copy of the corresponding source code, to be
distributed under the terms of Sections 1 and 2 above on a medium
customarily used for software interchange; or,
c) Accompany it with the information you received as to the offer
to distribute corresponding source code. (This alternative is
allowed only for noncommercial distribution and only if you
received the program in object code or executable form with such
an offer, in accord with Subsection b above.)
The source code for a work means the preferred form of the work for
making modifications to it. For an executable work, complete source
code means all the source code for all modules it contains, plus any
associated interface definition files, plus the scripts used to
control compilation and installation of the executable. However, as a
special exception, the source code distributed need not include
anything that is normally distributed (in either source or binary
form) with the major components (compiler, kernel, and so on) of the
operating system on which the executable runs, unless that component
itself accompanies the executable.
If distribution of executable or object code is made by offering
access to copy from a designated place, then offering equivalent
access to copy the source code from the same place counts as
distribution of the source code, even though third parties are not
compelled to copy the source along with the object code.
4. You may not copy, modify, sublicense, or distribute the Program
except as expressly provided under this License. Any attempt
otherwise to copy, modify, sublicense or distribute the Program is
void, and will automatically terminate your rights under this License.
However, parties who have received copies, or rights, from you under
this License will not have their licenses terminated so long as such
parties remain in full compliance.
5. You are not required to accept this License, since you have not
signed it. However, nothing else grants you permission to modify or
distribute the Program or its derivative works. These actions are
prohibited by law if you do not accept this License. Therefore, by
modifying or distributing the Program (or any work based on the
Program), you indicate your acceptance of this License to do so, and
all its terms and conditions for copying, distributing or modifying
the Program or works based on it.
6. Each time you redistribute the Program (or any work based on the
Program), the recipient automatically receives a license from the
original licensor to copy, distribute or modify the Program subject to
these terms and conditions. You may not impose any further
restrictions on the recipients' exercise of the rights granted herein.
You are not responsible for enforcing compliance by third parties to
this License.
7. If, as a consequence of a court judgment or allegation of patent
infringement or for any other reason (not limited to patent issues),
conditions are imposed on you (whether by court order, agreement or
otherwise) that contradict the conditions of this License, they do not
excuse you from the conditions of this License. If you cannot
distribute so as to satisfy simultaneously your obligations under this
License and any other pertinent obligations, then as a consequence you
may not distribute the Program at all. For example, if a patent
license would not permit royalty-free redistribution of the Program by
all those who receive copies directly or indirectly through you, then
the only way you could satisfy both it and this License would be to
refrain entirely from distribution of the Program.
If any portion of this section is held invalid or unenforceable under
any particular circumstance, the balance of the section is intended to
apply and the section as a whole is intended to apply in other
circumstances.
It is not the purpose of this section to induce you to infringe any
patents or other property right claims or to contest validity of any
such claims; this section has the sole purpose of protecting the
integrity of the free software distribution system, which is
implemented by public license practices. Many people have made
generous contributions to the wide range of software distributed
through that system in reliance on consistent application of that
system; it is up to the author/donor to decide if he or she is willing
to distribute software through any other system and a licensee cannot
impose that choice.
This section is intended to make thoroughly clear what is believed to
be a consequence of the rest of this License.
8. If the distribution and/or use of the Program is restricted in
certain countries either by patents or by copyrighted interfaces, the
original copyright holder who places the Program under this License
may add an explicit geographical distribution limitation excluding
those countries, so that distribution is permitted only in or among
countries not thus excluded. In such case, this License incorporates
the limitation as if written in the body of this License.
9. The Free Software Foundation may publish revised and/or new versions
of the General Public License from time to time. Such new versions will
be similar in spirit to the present version, but may differ in detail to
address new problems or concerns.
Each version is given a distinguishing version number. If the Program
specifies a version number of this License which applies to it and "any
later version", you have the option of following the terms and conditions
either of that version or of any later version published by the Free
Software Foundation. If the Program does not specify a version number of
this License, you may choose any version ever published by the Free Software
Foundation.
10. If you wish to incorporate parts of the Program into other free
programs whose distribution conditions are different, write to the author
to ask for permission. For software which is copyrighted by the Free
Software Foundation, write to the Free Software Foundation; we sometimes
make exceptions for this. Our decision will be guided by the two goals
of preserving the free status of all derivatives of our free software and
of promoting the sharing and reuse of software generally.
NO WARRANTY
11. BECAUSE THE PROGRAM IS LICENSED FREE OF CHARGE, THERE IS NO WARRANTY
FOR THE PROGRAM, TO THE EXTENT PERMITTED BY APPLICABLE LAW. EXCEPT WHEN
OTHERWISE STATED IN WRITING THE COPYRIGHT HOLDERS AND/OR OTHER PARTIES
PROVIDE THE PROGRAM "AS IS" WITHOUT WARRANTY OF ANY KIND, EITHER EXPRESSED
OR IMPLIED, INCLUDING, BUT NOT LIMITED TO, THE IMPLIED WARRANTIES OF
MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE. THE ENTIRE RISK AS
TO THE QUALITY AND PERFORMANCE OF THE PROGRAM IS WITH YOU. SHOULD THE
PROGRAM PROVE DEFECTIVE, YOU ASSUME THE COST OF ALL NECESSARY SERVICING,
REPAIR OR CORRECTION.
12. IN NO EVENT UNLESS REQUIRED BY APPLICABLE LAW OR AGREED TO IN WRITING
WILL ANY COPYRIGHT HOLDER, OR ANY OTHER PARTY WHO MAY MODIFY AND/OR
REDISTRIBUTE THE PROGRAM AS PERMITTED ABOVE, BE LIABLE TO YOU FOR DAMAGES,
INCLUDING ANY GENERAL, SPECIAL, INCIDENTAL OR CONSEQUENTIAL DAMAGES ARISING
OUT OF THE USE OR INABILITY TO USE THE PROGRAM (INCLUDING BUT NOT LIMITED
TO LOSS OF DATA OR DATA BEING RENDERED INACCURATE OR LOSSES SUSTAINED BY
YOU OR THIRD PARTIES OR A FAILURE OF THE PROGRAM TO OPERATE WITH ANY OTHER
PROGRAMS), EVEN IF SUCH HOLDER OR OTHER PARTY HAS BEEN ADVISED OF THE
POSSIBILITY OF SUCH DAMAGES.
END OF TERMS AND CONDITIONS
How to Apply These Terms to Your New Programs
If you develop a new program, and you want it to be of the greatest
possible use to the public, the best way to achieve this is to make it
free software which everyone can redistribute and change under these terms.
To do so, attach the following notices to the program. It is safest
to attach them to the start of each source file to most effectively
convey the exclusion of warranty; and each file should have at least
the "copyright" line and a pointer to where the full notice is found.
<one line to give the program's name and a brief idea of what it does.>
Copyright (C) <year> <name of author>
This program is free software; you can redistribute it and/or modify
it under the terms of the GNU General Public License as published by
the Free Software Foundation; either version 2 of the License, or
(at your option) any later version.
This program is distributed in the hope that it will be useful,
but WITHOUT ANY WARRANTY; without even the implied warranty of
MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
GNU General Public License for more details.
You should have received a copy of the GNU General Public License along
with this program; if not, write to the Free Software Foundation, Inc.,
51 Franklin Street, Fifth Floor, Boston, MA 02110-1301 USA.
Also add information on how to contact you by electronic and paper mail.
If the program is interactive, make it output a short notice like this
when it starts in an interactive mode:
Gnomovision version 69, Copyright (C) year name of author
Gnomovision comes with ABSOLUTELY NO WARRANTY; for details type `show w'.
This is free software, and you are welcome to redistribute it
under certain conditions; type `show c' for details.
The hypothetical commands `show w' and `show c' should show the appropriate
parts of the General Public License. Of course, the commands you use may
be called something other than `show w' and `show c'; they could even be
mouse-clicks or menu items--whatever suits your program.
You should also get your employer (if you work as a programmer) or your
school, if any, to sign a "copyright disclaimer" for the program, if
necessary. Here is a sample; alter the names:
Yoyodyne, Inc., hereby disclaims all copyright interest in the program
`Gnomovision' (which makes passes at compilers) written by James Hacker.
<signature of Ty Coon>, 1 April 1989
Ty Coon, President of Vice
This General Public License does not permit incorporating your program into
proprietary programs. If your program is a subroutine library, you may
consider it more useful to permit linking proprietary applications with the
library. If this is what you want to do, use the GNU Lesser General
Public License instead of this License.

125
README.md
View File

@@ -1,7 +1,128 @@
# ImHex
A Hex editor written in C++ using OpenGL, GLFW and Dear ImGui
A Hex Editor for Reverse Engineers, Programmers and people that value their eye sight when working at 3 AM.
## Supporting
If you like my work, consider supporting me on GitHub Sponsors, Patreon or PayPal. Thanks a lot!
<a href="https://github.com/sponsors/WerWolv"><img src="https://werwolv.net/assets/github_banner.png" alt="GitHub donate button" /> </a>
<a href="https://www.patreon.com/werwolv"><img src="https://c5.patreon.com/external/logo/become_a_patron_button.png" alt="Patreon donate button" /> </a>
<a href="https://werwolv.net/donate"><img src="https://werwolv.net/assets/paypal_banner.png" alt="PayPal donate button" /> </a>
## Features
- Featureful hex view
- Byte patching
- Patch management
- Copy bytes as feature
- Bytes
- Hex string
- C, C++, C#, Rust, Python, Java & JavaScript array
- ASCII-Art hex view
- HTML self contained div
- String and hex search
- Colorful highlighting
- Goto from start, end and current cursor position
- Custom C++-like pattern language for parsing highlighting a file's content
- Automatic loading based on MIME type
- arrays, pointers, structs, unions, enums, bitfields, using declarations, litte and big endian support
- Useful error messages, syntax highlighting and error marking
- Data importing
- Base64 files
- IPS and IPS32 patches
- Data exporting
- IPS and IPS32 patches
- Data Exporting
- Data inspector allowing interpretation of data as many different types (little and big endian)
- Huge file support with fast and efficient loading
- Strings search
- Copying of strings
- Copying of demangled strings
- File hashing support
- CRC16 and CRC32 with custom initial values and polynomials
- MD4, MD5
- SHA-1, SHA-224, SHA-256, SHA-384, SHA-512
- Disassembler supporting many different architectures
- ARM32 (ARM, Thumb, Cortex-M, aarch32)
- ARM64
- MIPS (MIPS32, MIPS64, MIPS32R6, Micro)
- x86 (16 bit, 32 bit, 64 bit)
- PowerPC (32 bit, 64 bit)
- Sparc
- SystemZ
- XCore
- M68K
- TMS320C64X
- M680X
- Ethereum
- Bookmarks
- Region highlighting
- Comments
- Data Analyzer
- File magic based file parser and MIME type database
- Byte distribution graph
- Entropy graph
- Highest and avarage entropy
- Encrypted / Compressed file detection
- Helpful tools
- Itanium and MSVC demangler
- ASCII table
- Regex replacer
- Mathematical expression evaluator (Calculator)
- Hexadecimal Color picker
- Built-in cheat sheet for pattern language and Math evaluator
- Doesn't burn out your retinas when used in late-night sessions
## Screenshots
![](https://i.imgur.com/ClEsNwL.png)
![](https://i.imgur.com/xH7xJ4g.png)
![](https://i.imgur.com/fhVJYEa.png)
## Additional Files
For format patterns, includable libraries and magic files, check of the [ImHex-Patterns](https://github.com/WerWolv/ImHex-Patterns) repository here. Feel free to PR your own files there as well!
## Compiling
This guide assumes you're either on Windows using mingw or on Arch Linux
You need a C++20 compatible compiler such as GCC 10.2.0 to compile ImHex.
The following libraries are needed to compile ImHex. All of them can be found in the default pacman repositories
```
GLFW3 ( (sudo) pacman -S glfw )
libmagic, libgnurx, libtre, libintl, libiconv ( (sudo) pacman -S file )
libcrypto ( (sudo) pacman -S openssl )
capstone ( (sudo) pacman -S capstone )
libLLVMDemangle ( (sudo) pacman -S llvm llvm-libs )
nlohmann json ( (sudo) pacman -S nlohmann-json )
Python3 ( (sudo) pacman -S python3 )
All in one: sudo pacman -S glfw file openssl capstone llvm llvm-libs nlohmann-json python3
All in one for mingw: pacman -S mingw-w64-x86_64-gcc mingw-w64-x86_64-cmake mingw-w64-x86_64-make mingw-w64-x86_64-capstone mingw-w64-x86_64-glfw mingw-w64-x86_64-glm mingw-w64-x86_64-file mingw-w64-x86_64-llvm mingw-w64-x86_64-nlohmann-json mingw-w64-x86_64-openssl mingw-w64-x86_64-polly mingw-w64-x86_64-python
```
After all the libraries are installed, run the following commands to build ImHex
```
mkdir build
cd build
cmake ..
make -j
```
On Windows, download the python standard library from https://github.com/python/cpython/tree/master/Lib and place the files and folders in `lib/python3.8` next to your built executable. Don't forget to also copy the `libpython3.8.dll` and `libwinpthread-1.dll` from your mingw setup next to the executable.
On both Windows and Linux, copy the files from `python_libs` in the `lib` folder next to your built executable.
Place your magic databases in the `magic` folder next to your built executable
Place your patterns in the `pattern` folder next to your built executable
Place your include pattern files in the `include` folder next to your built executable
## Credits
- Thanks a lot to ocornut for their amazing [Dear ImGui](https://github.com/ocornut/imgui) which is used for building the entire interface
- Thanks to orconut as well for their hex editor view used as base for this project.
- Thanks to BalazsJako for their incredible [ImGuiColorTextEdit](https://github.com/BalazsJako/ImGuiColorTextEdit) used for the pattern language syntax highlighting
- Thanks to AirGuanZ for their amazing [imgui-filebrowser](https://github.com/AirGuanZ/imgui-filebrowser) used for loading and saving files
- Thanks to nlohmann for their [json](https://github.com/nlohmann/json) library used for project files
- Thanks to aquynh for [capstone](https://github.com/aquynh/capstone) which is the base of the disassembly window

View File

@@ -5,6 +5,7 @@
#include <array>
#include <optional>
#include <string>
#include <vector>
namespace hex {
@@ -21,4 +22,6 @@ namespace hex {
std::array<u32, 12> sha384(prv::Provider* &data, u64 offset, size_t size);
std::array<u32, 16> sha512(prv::Provider* &data, u64 offset, size_t size);
std::vector<u8> decode64(const std::vector<u8> &input);
std::vector<u8> encode64(const std::vector<u8> &input);
}

View File

@@ -0,0 +1,54 @@
#pragma once
#include <capstone/capstone.h>
#include <hex.hpp>
namespace hex {
enum class Architecture : s32 {
ARM,
ARM64,
MIPS,
X86,
PPC,
SPARC,
SYSZ,
XCORE,
M68K,
TMS320C64X,
M680X,
EVM,
MAX,
MIN = ARM
};
class Disassembler {
public:
static constexpr cs_arch toCapstoneArchictecture(Architecture architecture) {
return static_cast<cs_arch>(architecture);
}
static inline bool isSupported(Architecture architecture) {
return cs_support(toCapstoneArchictecture(architecture));
}
constexpr static const char * const ArchitectureNames[] = { "ARM32", "ARM64", "MIPS", "x86", "PowerPC", "Sparc", "SystemZ", "XCore", "68K", "TMS320C64x", "680X", "Ethereum" };
static inline s32 getArchitectureSupportedCount() {
static s32 supportedCount = -1;
if (supportedCount != -1) {
return supportedCount;
}
for (supportedCount = static_cast<s32>(Architecture::MIN); supportedCount < static_cast<s32>(Architecture::MAX); supportedCount++) {
if (!cs_support(supportedCount)) {
break;
}
}
return supportedCount;
}
};
}

View File

@@ -6,10 +6,20 @@
namespace hex {
enum class Events {
FileLoaded,
DataChanged,
PatternChanged,
FileDropped,
ByteSelected
WindowClosing,
RegionSelected,
SelectionChangeRequest,
AddBookmark,
AppendPatternLanguageCode,
ProjectFileStore,
ProjectFileLoad
};
struct EventHandler {

View File

@@ -0,0 +1,33 @@
#pragma once
#include <string>
#include <string_view>
struct _object;
typedef struct _object PyObject;
namespace hex {
namespace prv { class Provider; }
class LoaderScript {
public:
LoaderScript() = delete;
static bool processFile(std::string_view scriptPath);
static void setFilePath(std::string_view filePath) { LoaderScript::s_filePath = filePath; }
static void setDataProvider(prv::Provider* provider) { LoaderScript::s_dataProvider = provider; }
private:
static inline std::string s_filePath;
static inline prv::Provider* s_dataProvider;
static PyObject* Py_getFilePath(PyObject *self, PyObject *args);
static PyObject* Py_addPatch(PyObject *self, PyObject *args);
static PyObject* Py_addBookmark(PyObject *self, PyObject *args);
static PyObject* Py_addStruct(PyObject *self, PyObject *args);
static PyObject* Py_addUnion(PyObject *self, PyObject *args);
};
}

View File

@@ -0,0 +1,88 @@
#pragma once
#include <hex.hpp>
#include <string>
#include <vector>
#include <queue>
#include <unordered_map>
#include <functional>
#include <optional>
namespace hex {
enum class TokenType {
Number,
Variable,
Function,
Operator,
Bracket
};
enum class Operator : u16 {
Invalid = 0x000,
Assign = 0x010,
Or = 0x020,
Xor = 0x030,
And = 0x040,
BitwiseOr = 0x050,
BitwiseXor = 0x060,
BitwiseAnd = 0x070,
Equals = 0x080,
NotEquals = 0x081,
GreaterThan = 0x090,
LessThan = 0x091,
GreaterThanOrEquals = 0x092,
LessThanOrEquals = 0x093,
ShiftLeft = 0x0A0,
ShiftRight = 0x0A1,
Addition = 0x0B0,
Subtraction = 0x0B1,
Multiplication = 0x0C0,
Division = 0x0C1,
Modulus = 0x0C2,
Exponentiation = 0x1D0,
Combine = 0x0E0,
BitwiseNot = 0x0F0,
Not = 0x0F1
};
enum class BracketType : std::uint8_t {
Left,
Right
};
struct Token {
TokenType type;
long double number;
Operator op;
BracketType bracketType;
std::string name;
std::vector<long double> arguments;
};
class MathEvaluator {
public:
MathEvaluator() = default;
std::optional<long double> evaluate(std::string input);
void registerStandardVariables();
void registerStandardFunctions();
void setVariable(std::string name, long double value);
void setFunction(std::string name, std::function<std::optional<long double>(std::vector<long double>)> function, size_t minNumArgs, size_t maxNumArgs);
std::unordered_map<std::string, long double>& getVariables() { return this->m_variables; }
private:
std::queue<Token> parseInput(const char *input);
std::queue<Token> toPostfix(std::queue<Token> inputQueue);
std::optional<long double> evaluate(std::queue<Token> postfixTokens);
std::unordered_map<std::string, long double> m_variables;
std::unordered_map<std::string, std::function<std::optional<long double>(std::vector<long double>)>> m_functions;
};
}

View File

@@ -0,0 +1,17 @@
#pragma once
#include <hex.hpp>
#include <map>
#include <vector>
namespace hex {
using Patches = std::map<u64, u8>;
std::vector<u8> generateIPSPatch(const Patches &patches);
std::vector<u8> generateIPS32Patch(const Patches &patches);
Patches loadIPSPatch(const std::vector<u8> &ipsPatch);
Patches loadIPS32Patch(const std::vector<u8> &ipsPatch);
}

View File

@@ -0,0 +1,46 @@
#pragma once
#include <list>
#include <string>
#include <string_view>
#include "patches.hpp"
#include "utils.hpp"
namespace hex {
class ProjectFile {
public:
ProjectFile() = delete;
static bool load(std::string_view filePath);
static bool store(std::string_view filePath = "");
[[nodiscard]] static bool hasUnsavedChanges() { return ProjectFile::s_hasUnsavedChanged; }
static void markDirty() { if (!ProjectFile::s_currProjectFilePath.empty()) ProjectFile::s_hasUnsavedChanged = true; }
[[nodiscard]] static std::string getProjectFilePath() { return ProjectFile::s_currProjectFilePath; }
[[nodiscard]] static std::string getFilePath() { return ProjectFile::s_filePath; }
static void setFilePath(std::string_view filePath) { ProjectFile::s_hasUnsavedChanged = true; ProjectFile::s_filePath = filePath; }
[[nodiscard]] static std::string getPattern() { return ProjectFile::s_pattern; }
static void setPattern(std::string_view pattern) { ProjectFile::s_hasUnsavedChanged = true; ProjectFile::s_pattern = pattern; }
[[nodiscard]] static const Patches& getPatches() { return ProjectFile::s_patches; }
static void setPatches(const Patches &patches) { ProjectFile::s_hasUnsavedChanged = true; ProjectFile::s_patches = patches; }
[[nodiscard]] static const std::list<Bookmark>& getBookmarks() { return ProjectFile::s_bookmarks; }
static void setBookmarks(const std::list<Bookmark> &bookmarks) { ProjectFile::s_hasUnsavedChanged = true; ProjectFile::s_bookmarks = bookmarks; }
private:
static inline std::string s_currProjectFilePath;
static inline bool s_hasUnsavedChanged = false;
static inline std::string s_filePath;
static inline std::string s_pattern;
static inline Patches s_patches;
static inline std::list<Bookmark> s_bookmarks;
};
}

128
include/helpers/utils.hpp Normal file
View File

@@ -0,0 +1,128 @@
#pragma once
#include <hex.hpp>
#include <array>
#include <functional>
#include <memory>
#include <optional>
#include <string>
#include <vector>
#ifdef __MINGW32__
#include <winsock.h>
#else
#include <arpa/inet.h>
#endif
#include "lang/token.hpp"
namespace hex {
template<typename ... Args>
inline std::string format(const std::string &format, Args ... args) {
ssize_t size = snprintf( nullptr, 0, format.c_str(), args ... );
if( size <= 0 )
return "";
std::vector<char> buffer(size + 1, 0x00);
snprintf(buffer.data(), size + 1, format.c_str(), args ...);
return std::string(buffer.data(), buffer.data() + size);
}
[[nodiscard]] constexpr inline u64 extract(u8 from, u8 to, const u64 &value) {
u64 mask = (std::numeric_limits<u64>::max() >> (63 - (from - to))) << to;
return (value & mask) >> to;
}
[[nodiscard]] constexpr inline u64 signExtend(u64 value, u8 currWidth, u8 targetWidth) {
u64 mask = 1LLU << (currWidth - 1);
return (((value ^ mask) - mask) << (64 - targetWidth)) >> (64 - targetWidth);
}
[[nodiscard]] constexpr inline bool isUnsigned(const lang::Token::TypeToken::Type type) {
return (static_cast<u32>(type) & 0x0F) == 0x00;
}
[[nodiscard]] constexpr inline bool isSigned(const lang::Token::TypeToken::Type type) {
return (static_cast<u32>(type) & 0x0F) == 0x01;
}
[[nodiscard]] constexpr inline bool isFloatingPoint(const lang::Token::TypeToken::Type type) {
return (static_cast<u32>(type) & 0x0F) == 0x02;
}
[[nodiscard]] constexpr inline u32 getTypeSize(const lang::Token::TypeToken::Type type) {
return static_cast<u32>(type) >> 4;
}
std::string toByteString(u64 bytes);
std::string makePrintable(char c);
template<typename T>
struct always_false : std::false_type {};
template<typename T>
constexpr T changeEndianess(T value, std::endian endian) {
if (endian == std::endian::native)
return value;
if constexpr (sizeof(T) == 1)
return value;
else if constexpr (sizeof(T) == 2)
return __builtin_bswap16(value);
else if constexpr (sizeof(T) == 4)
return __builtin_bswap32(value);
else if constexpr (sizeof(T) == 8)
return __builtin_bswap64(value);
else
static_assert(always_false<T>::value, "Invalid type provided!");
}
template<typename T>
constexpr T changeEndianess(T value, size_t size, std::endian endian) {
if (endian == std::endian::native)
return value;
if (size == 1)
return value;
else if (size == 2)
return __builtin_bswap16(value);
else if (size == 4)
return __builtin_bswap32(value);
else if (size == 8)
return __builtin_bswap64(value);
else
throw std::invalid_argument("Invalid value size!");
}
std::vector<u8> readFile(std::string_view path);
class ScopeExit {
public:
ScopeExit(std::function<void()> func) : m_func(func) {}
~ScopeExit() { if (this->m_func != nullptr) this->m_func(); }
void release() {
this->m_func = nullptr;
}
private:
std::function<void()> m_func;
};
struct Region {
u64 address;
size_t size;
};
struct Bookmark {
Region region;
std::vector<char> name;
std::vector<char> comment;
};
}

View File

@@ -16,4 +16,13 @@ using s64 = std::int64_t;
using s128 = __int128_t;
#include "lang/result.hpp"
#include "lang/results.hpp"
#include "lang/results.hpp"
extern int mainArgc;
extern char **mainArgv;
#if defined(__EMX__) || defined (WIN32)
#define MAGIC_PATH_SEPARATOR ";"
#else
#define MAGIC_PATH_SEPARATOR ":"
#endif

View File

@@ -2,6 +2,7 @@
#include "token.hpp"
#include <bit>
#include <optional>
#include <unordered_map>
#include <vector>
@@ -20,36 +21,44 @@ namespace hex::lang {
Scope,
};
explicit ASTNode(Type type) : m_type(type) {}
explicit ASTNode(Type type, u32 lineNumber) : m_type(type), m_lineNumber(lineNumber) {}
virtual ~ASTNode() = default;
Type getType() { return this->m_type; }
u32 getLineNumber() { return this->m_lineNumber; }
private:
Type m_type;
u32 m_lineNumber;
};
class ASTNodeVariableDecl : public ASTNode {
public:
explicit ASTNodeVariableDecl(const Token::TypeToken::Type &type, const std::string &name, const std::string& customTypeName = "", std::optional<u64> offset = { }, size_t arraySize = 1)
: ASTNode(Type::VariableDecl), m_type(type), m_name(name), m_customTypeName(customTypeName), m_offset(offset), m_arraySize(arraySize) { }
explicit ASTNodeVariableDecl(u32 lineNumber, const Token::TypeToken::Type &type, const std::string &name, const std::string& customTypeName = "", std::optional<u64> offset = { }, size_t arraySize = 1, std::optional<std::string> arraySizeVariable = { }, std::optional<u8> pointerSize = { }, std::optional<std::endian> endianess = { })
: ASTNode(Type::VariableDecl, lineNumber), m_type(type), m_name(name), m_customTypeName(customTypeName), m_offset(offset), m_arraySize(arraySize), m_arraySizeVariable(arraySizeVariable), m_pointerSize(pointerSize), m_endianess(endianess) { }
const Token::TypeToken::Type& getVariableType() const { return this->m_type; }
const std::string& getCustomVariableTypeName() const { return this->m_customTypeName; }
const std::string& getVariableName() const { return this->m_name; };
std::optional<u64> getOffset() const { return this->m_offset; }
size_t getArraySize() const { return this->m_arraySize; }
std::optional<std::string> getArraySizeVariable() const { return this->m_arraySizeVariable; }
std::optional<u8> getPointerSize() const { return this->m_pointerSize; }
std::optional<std::endian> getEndianess() const { return this->m_endianess; }
private:
Token::TypeToken::Type m_type;
std::string m_name, m_customTypeName;
std::optional<u64> m_offset;
size_t m_arraySize;
std::optional<std::string> m_arraySizeVariable;
std::optional<u8> m_pointerSize;
std::optional<std::endian> m_endianess = { };
};
class ASTNodeScope : public ASTNode {
public:
explicit ASTNodeScope(std::vector<ASTNode*> nodes) : ASTNode(Type::Scope), m_nodes(nodes) { }
explicit ASTNodeScope(u32 lineNumber, std::vector<ASTNode*> nodes) : ASTNode(Type::Scope, lineNumber), m_nodes(nodes) { }
std::vector<ASTNode*> &getNodes() { return this->m_nodes; }
private:
@@ -58,8 +67,8 @@ namespace hex::lang {
class ASTNodeStruct : public ASTNode {
public:
explicit ASTNodeStruct(std::string name, std::vector<ASTNode*> nodes)
: ASTNode(Type::Struct), m_name(name), m_nodes(nodes) { }
explicit ASTNodeStruct(u32 lineNumber, std::string name, std::vector<ASTNode*> nodes)
: ASTNode(Type::Struct, lineNumber), m_name(name), m_nodes(nodes) { }
const std::string& getName() const { return this->m_name; }
std::vector<ASTNode*> &getNodes() { return this->m_nodes; }
@@ -70,8 +79,8 @@ namespace hex::lang {
class ASTNodeUnion : public ASTNode {
public:
explicit ASTNodeUnion(std::string name, std::vector<ASTNode*> nodes)
: ASTNode(Type::Union), m_name(name), m_nodes(nodes) { }
explicit ASTNodeUnion(u32 lineNumber, std::string name, std::vector<ASTNode*> nodes)
: ASTNode(Type::Union, lineNumber), m_name(name), m_nodes(nodes) { }
const std::string& getName() const { return this->m_name; }
std::vector<ASTNode*> &getNodes() { return this->m_nodes; }
@@ -82,8 +91,8 @@ namespace hex::lang {
class ASTNodeBitField : public ASTNode {
public:
explicit ASTNodeBitField(std::string name, std::vector<std::pair<std::string, size_t>> fields)
: ASTNode(Type::Bitfield), m_name(name), m_fields(fields) { }
explicit ASTNodeBitField(u32 lineNumber, std::string name, std::vector<std::pair<std::string, size_t>> fields)
: ASTNode(Type::Bitfield, lineNumber), m_name(name), m_fields(fields) { }
const std::string& getName() const { return this->m_name; }
std::vector<std::pair<std::string, size_t>> &getFields() { return this->m_fields; }
@@ -94,8 +103,8 @@ namespace hex::lang {
class ASTNodeTypeDecl : public ASTNode {
public:
explicit ASTNodeTypeDecl(const Token::TypeToken::Type &type, const std::string &name, const std::string& customTypeName = "")
: ASTNode(Type::TypeDecl), m_type(type), m_name(name), m_customTypeName(customTypeName) { }
explicit ASTNodeTypeDecl(u32 lineNumber, const Token::TypeToken::Type &type, const std::string &name, const std::string& customTypeName = "")
: ASTNode(Type::TypeDecl, lineNumber), m_type(type), m_name(name), m_customTypeName(customTypeName) { }
const std::string& getTypeName() const { return this->m_name; };
@@ -108,8 +117,8 @@ namespace hex::lang {
class ASTNodeEnum : public ASTNode {
public:
explicit ASTNodeEnum(const Token::TypeToken::Type &type, const std::string &name)
: ASTNode(Type::Enum), m_type(type), m_name(name) { }
explicit ASTNodeEnum(u32 lineNumber, const Token::TypeToken::Type &type, const std::string &name)
: ASTNode(Type::Enum, lineNumber), m_type(type), m_name(name) { }
const std::string& getName() const { return this->m_name; };

View File

@@ -2,9 +2,11 @@
#include <hex.hpp>
#include "providers/provider.hpp"
#include "lang/pattern_data.hpp"
#include "ast_node.hpp"
#include <bit>
#include <string>
#include <unordered_map>
#include <vector>
@@ -13,12 +15,18 @@ namespace hex::lang {
class Evaluator {
public:
Evaluator();
Evaluator(prv::Provider* &provider, std::endian defaultDataEndianess);
std::pair<Result, std::vector<PatternData*>> evaluate(const std::vector<ASTNode*>& ast);
const std::pair<u32, std::string>& getError() { return this->m_error; }
private:
std::unordered_map<std::string, ASTNode*> m_types;
prv::Provider* &m_provider;
std::endian m_defaultDataEndianess;
std::pair<u32, std::string> m_error;
std::pair<PatternData*, size_t> createStructPattern(ASTNodeVariableDecl *varDeclNode, u64 offset);
std::pair<PatternData*, size_t> createUnionPattern(ASTNodeVariableDecl *varDeclNode, u64 offset);

View File

@@ -14,6 +14,11 @@ namespace hex::lang {
Lexer();
std::pair<Result, std::vector<Token>> lex(const std::string& code);
const std::pair<u32, std::string>& getError() { return this->m_error; }
private:
std::pair<u32, std::string> m_error;
};
}

View File

@@ -12,8 +12,38 @@ namespace hex::lang {
public:
Parser();
using TokenIter = std::vector<Token>::const_iterator;
std::pair<Result, std::vector<ASTNode*>> parse(const std::vector<Token> &tokens);
const std::pair<u32, std::string>& getError() { return this->m_error; }
private:
std::pair<u32, std::string> m_error;
ASTNode* parseBuiltinVariableDecl(TokenIter &curr, bool hasEndianDef);
ASTNode* parseCustomTypeVariableDecl(TokenIter &curr, bool hasEndianDef);
ASTNode* parseBuiltinPointerVariableDecl(TokenIter &curr, bool hasEndianDef);
ASTNode* parseCustomTypePointerVariableDecl(TokenIter &curr, bool hasEndianDef);
ASTNode* parseBuiltinArrayDecl(TokenIter &curr, bool hasEndianDef);
ASTNode* parseCustomTypeArrayDecl(TokenIter &curr, bool hasEndianDef);
ASTNode* parseBuiltinVariableArrayDecl(TokenIter &curr, bool hasEndianDef);
ASTNode* parseCustomTypeVariableArrayDecl(TokenIter &curr, bool hasEndianDef);
ASTNode* parsePaddingDecl(TokenIter &curr);
ASTNode* parseFreeBuiltinVariableDecl(TokenIter &curr, bool hasEndianDef);
ASTNode* parseFreeCustomTypeVariableDecl(TokenIter &curr, bool hasEndianDef);
ASTNode* parseStruct(TokenIter &curr);
ASTNode* parseUnion(TokenIter &curr);
ASTNode* parseEnum(TokenIter &curr);
ASTNode *parseBitField(TokenIter &curr);
ASTNode *parseScope(TokenIter &curr);
std::optional<ASTNode*> parseUsingDeclaration(TokenIter &curr);
std::optional<std::vector<ASTNode*>> parseStatement(TokenIter &curr);
std::vector<ASTNode*> parseTillToken(TokenIter &curr, Token::Type endTokenType);
bool tryConsume(TokenIter &curr, std::initializer_list<Token::Type> tokenTypes);
};
}

View File

@@ -7,12 +7,14 @@
#include "imgui_memory_editor.h"
#include "providers/provider.hpp"
#include "utils.hpp"
#include "helpers/utils.hpp"
#include <random>
namespace hex::lang {
using namespace ::std::literals::string_literals;
namespace {
std::string makeDisplayable(u8 *data, size_t size) {
@@ -33,8 +35,8 @@ namespace hex::lang {
public:
enum class Type { Padding, Unsigned, Signed, Float, Character, String, Struct, Union, Array, Enum };
PatternData(Type type, u64 offset, size_t size, const std::string &name, u32 color = 0)
: m_type(type), m_offset(offset), m_size(size), m_color(color), m_name(name) {
PatternData(Type type, u64 offset, size_t size, const std::string &name, std::endian endianess, u32 color = 0)
: m_type(type), m_offset(offset), m_size(size), m_name(name), m_endianess(endianess), m_color(color) {
constexpr u32 Palette[] = { 0x50b4771f, 0x500e7fff, 0x502ca02c, 0x502827d6, 0x50bd6794, 0x504b568c, 0x50c277e3, 0x507f7f7f, 0x5022bdbc, 0x50cfbe17 };
if (color != 0)
@@ -51,8 +53,14 @@ namespace hex::lang {
[[nodiscard]] u64 getOffset() const { return this->m_offset; }
[[nodiscard]] size_t getSize() const { return this->m_size; }
[[nodiscard]] u32 getColor() const { return this->m_color; }
[[nodiscard]] const std::string& getName() const { return this->m_name; }
void setName(std::string name) { this->m_name = name; }
[[nodiscard]] u32 getColor() const { return this->m_color; }
void setColor(u32 color) { this->m_color = color; }
[[nodiscard]] std::endian getEndianess() const { return this->m_endianess; }
void setEndianess(std::endian endianess) { this->m_endianess = endianess; }
virtual void createEntry(prv::Provider* &provider) = 0;
virtual std::string getTypeName() = 0;
@@ -92,6 +100,11 @@ namespace hex::lang {
provider->read(left->getOffset(), leftBuffer.data(), left->getSize());
provider->read(right->getOffset(), rightBuffer.data(), right->getSize());
if (left->m_endianess != std::endian::native)
std::reverse(leftBuffer.begin(), leftBuffer.end());
if (right->m_endianess != std::endian::native)
std::reverse(rightBuffer.begin(), rightBuffer.end());
if (sortSpecs->Specs->SortDirection == ImGuiSortDirection_Ascending)
return leftBuffer > rightBuffer;
else
@@ -118,21 +131,29 @@ namespace hex::lang {
protected:
void createDefaultEntry(std::string value) {
ImGui::TableNextRow();
ImGui::TreeNodeEx(this->getName().c_str(), ImGuiTreeNodeFlags_Leaf | ImGuiTreeNodeFlags_NoTreePushOnOpen | ImGuiTreeNodeFlags_SpanFullWidth);
ImGui::TableNextColumn();
ImGui::ColorButton("color", ImColor(this->getColor()), ImGuiColorEditFlags_NoTooltip);
ImGui::TreeNodeEx(this->getName().c_str(), ImGuiTreeNodeFlags_Leaf | ImGuiTreeNodeFlags_NoTreePushOnOpen | ImGuiTreeNodeFlags_SpanFullWidth | ImGuiTreeNodeFlags_AllowItemOverlap);
ImGui::TableNextColumn();
if (ImGui::Selectable(("##PatternDataLine"s + std::to_string(this->getOffset())).c_str(), false, ImGuiSelectableFlags_SpanAllColumns | ImGuiSelectableFlags_AllowItemOverlap)) {
Region selectRegion = { this->getOffset(), this->getSize() };
View::postEvent(Events::SelectionChangeRequest, &selectRegion);
}
ImGui::SameLine();
ImGui::Text("%s", this->getName().c_str());
ImGui::TableNextColumn();
ImGui::ColorButton("color", ImColor(this->getColor()), ImGuiColorEditFlags_NoTooltip, ImVec2(ImGui::GetColumnWidth(), 14));
ImGui::TableNextColumn();
ImGui::Text("0x%08lx : 0x%08lx", this->getOffset(), this->getOffset() + this->getSize() - 1);
ImGui::TableNextColumn();
ImGui::Text("0x%04lx", this->getSize());
ImGui::TableNextColumn();
ImGui::Text("%s", this->getTypeName().c_str());
ImGui::TextColored(ImColor(0xFF9BC64D), "%s", this->getTypeName().c_str());
ImGui::TableNextColumn();
ImGui::Text("%s", value.c_str());
}
protected:
std::endian m_endianess = std::endian::native;
private:
Type m_type;
u64 m_offset;
@@ -142,11 +163,12 @@ namespace hex::lang {
std::string m_name;
static inline u8 s_paletteOffset = 0;
};
class PatternDataPadding : public PatternData {
public:
PatternDataPadding(u64 offset, size_t size) : PatternData(Type::Padding, offset, size, "", 0x00FFFFFF) { }
PatternDataPadding(u64 offset, size_t size) : PatternData(Type::Padding, offset, size, "", { }, 0x00FFFFFF) { }
void createEntry(prv::Provider* &provider) override {
}
@@ -156,13 +178,65 @@ namespace hex::lang {
}
};
class PatternDataUnsigned : public PatternData {
class PatternDataPointer : public PatternData {
public:
PatternDataUnsigned(u64 offset, size_t size, const std::string &name, u32 color = 0) : PatternData(Type::Unsigned, offset, size, name, color) { }
PatternDataPointer(u64 offset, size_t size, const std::string &name, PatternData *pointedAt, std::endian endianess, u32 color = 0)
: PatternData(Type::Unsigned, offset, size, name, endianess, color), m_pointedAt(pointedAt) {
this->m_pointedAt->setName("*" + this->m_pointedAt->getName());
}
void createEntry(prv::Provider* &provider) override {
u64 data = 0;
provider->read(this->getOffset(), &data, this->getSize());
data = hex::changeEndianess(data, this->getSize(), this->m_endianess);
ImGui::TableNextRow();
ImGui::TableNextColumn();
bool open = ImGui::TreeNodeEx(this->getName().c_str(), ImGuiTreeNodeFlags_SpanFullWidth);
ImGui::TableNextColumn();
ImGui::ColorButton("color", ImColor(this->getColor()), ImGuiColorEditFlags_NoTooltip, ImVec2(ImGui::GetColumnWidth(), 14));
ImGui::TableNextColumn();
ImGui::Text("0x%08lx : 0x%08lx", this->getOffset(), this->getOffset() + this->getSize() - 1);
ImGui::TableNextColumn();
ImGui::Text("0x%04lx", this->getSize());
ImGui::TableNextColumn();
ImGui::TextColored(ImColor(0xFF9BC64D), "%s", this->getTypeName().c_str());
ImGui::TableNextColumn();
ImGui::Text("*(0x%0*llx)", this->getSize() * 2, data);
if (open) {
this->m_pointedAt->createEntry(provider);
ImGui::TreePop();
}
}
virtual std::optional<u32> highlightBytes(size_t offset) {
if (offset >= this->getOffset() && offset < (this->getOffset() + this->getSize()))
return this->getColor();
else if (auto color = this->m_pointedAt->highlightBytes(offset); color.has_value())
return color.value();
else
return { };
}
std::string getTypeName() override {
return "Pointer";
}
private:
PatternData *m_pointedAt;
};
class PatternDataUnsigned : public PatternData {
public:
PatternDataUnsigned(u64 offset, size_t size, const std::string &name, std::endian endianess, u32 color = 0)
: PatternData(Type::Unsigned, offset, size, name, endianess, color) { }
void createEntry(prv::Provider* &provider) override {
u64 data = 0;
provider->read(this->getOffset(), &data, this->getSize());
data = hex::changeEndianess(data, this->getSize(), this->m_endianess);
this->createDefaultEntry(hex::format("%lu (0x%0*lx)", data, this->getSize() * 2, data));
}
@@ -181,11 +255,13 @@ namespace hex::lang {
class PatternDataSigned : public PatternData {
public:
PatternDataSigned(u64 offset, size_t size, const std::string &name, u32 color = 0) : PatternData(Type::Signed, offset, size, name, color) { }
PatternDataSigned(u64 offset, size_t size, const std::string &name, std::endian endianess, u32 color = 0)
: PatternData(Type::Signed, offset, size, name, endianess, color) { }
void createEntry(prv::Provider* &provider) override {
u64 data = 0;
provider->read(this->getOffset(), &data, this->getSize());
data = hex::changeEndianess(data, this->getSize(), this->m_endianess);
s64 signedData = signedData = hex::signExtend(data, this->getSize(), 64);
@@ -206,17 +282,22 @@ namespace hex::lang {
class PatternDataFloat : public PatternData {
public:
PatternDataFloat(u64 offset, size_t size, const std::string &name, u32 color = 0) : PatternData(Type::Float, offset, size, name, color) { }
PatternDataFloat(u64 offset, size_t size, const std::string &name, std::endian endianess, u32 color = 0)
: PatternData(Type::Float, offset, size, name, endianess, color) { }
void createEntry(prv::Provider* &provider) override {
double formatData = 0;
if (this->getSize() == 4) {
float data = 0;
provider->read(this->getOffset(), &data, 4);
data = hex::changeEndianess(data, 4, this->m_endianess);
formatData = data;
} else if (this->getSize() == 8) {
double data = 0;
provider->read(this->getOffset(), &data, 8);
data = hex::changeEndianess(data, 8, this->m_endianess);
formatData = data;
}
@@ -234,7 +315,8 @@ namespace hex::lang {
class PatternDataCharacter : public PatternData {
public:
PatternDataCharacter(u64 offset, size_t size, const std::string &name, u32 color = 0) : PatternData(Type::Character, offset, size, name, color) { }
PatternDataCharacter(u64 offset, size_t size, const std::string &name, std::endian endianess, u32 color = 0)
: PatternData(Type::Character, offset, size, name, endianess, color) { }
void createEntry(prv::Provider* &provider) override {
char character;
@@ -250,7 +332,8 @@ namespace hex::lang {
class PatternDataString : public PatternData {
public:
PatternDataString(u64 offset, size_t size, const std::string &name, u32 color = 0) : PatternData(Type::String, offset, size, name, color) { }
PatternDataString(u64 offset, size_t size, const std::string &name, std::endian endianess, u32 color = 0)
: PatternData(Type::String, offset, size, name, endianess, color) { }
void createEntry(prv::Provider* &provider) override {
std::vector<u8> buffer(this->getSize() + 1, 0x00);
@@ -267,21 +350,29 @@ namespace hex::lang {
class PatternDataArray : public PatternData {
public:
PatternDataArray(u64 offset, size_t size, const std::string &name, const std::vector<PatternData*> & entries, u32 color = 0)
: PatternData(Type::Array, offset, size, name, color), m_entries(entries) { }
PatternDataArray(u64 offset, size_t size, const std::string &name, std::endian endianess, const std::vector<PatternData*> & entries, u32 color = 0)
: PatternData(Type::Array, offset, size, name, endianess, color), m_entries(entries) { }
void createEntry(prv::Provider* &provider) override {
ImGui::TableNextRow(ImGuiTableRowFlags_Headers);
ImGui::TableNextColumn();
ImGui::ColorButton("color", ImColor(this->getColor()), ImGuiColorEditFlags_NoTooltip);
ImGui::TableNextRow();
ImGui::TableNextColumn();
bool open = ImGui::TreeNodeEx(this->getName().c_str(), ImGuiTreeNodeFlags_SpanFullWidth);
ImGui::TableNextColumn();
ImGui::ColorButton("color", ImColor(this->getColor()), ImGuiColorEditFlags_NoTooltip, ImVec2(ImGui::GetColumnWidth(), 14));
ImGui::TableNextColumn();
ImGui::Text("0x%08lx : 0x%08lx", this->getOffset(), this->getOffset() + this->getSize() - 1);
ImGui::TableNextColumn();
ImGui::Text("0x%04lx", this->getSize());
ImGui::TableNextColumn();
ImGui::Text("%s", this->getTypeName().c_str());
ImGui::TextColored(ImColor(0xFF9BC64D), "%s", this->m_entries[0]->getTypeName().c_str());
ImGui::SameLine(0, 0);
ImGui::TextUnformatted("[");
ImGui::SameLine(0, 0);
ImGui::TextColored(ImColor(0xFF00FF00), "%llu", this->m_entries.size());
ImGui::SameLine(0, 0);
ImGui::TextUnformatted("]");
ImGui::TableNextColumn();
ImGui::Text("%s", "{ ... }");
@@ -312,21 +403,20 @@ namespace hex::lang {
class PatternDataStruct : public PatternData {
public:
PatternDataStruct(u64 offset, size_t size, const std::string &name, const std::string &structName, const std::vector<PatternData*> & members, u32 color = 0)
: PatternData(Type::Struct, offset, size, name, color), m_structName(structName), m_members(members), m_sortedMembers(members) { }
PatternDataStruct(u64 offset, size_t size, const std::string &name, std::endian endianess, const std::string &structName, const std::vector<PatternData*> & members, u32 color = 0)
: PatternData(Type::Struct, offset, size, name, endianess, color), m_structName(structName), m_members(members), m_sortedMembers(members) { }
void createEntry(prv::Provider* &provider) override {
ImGui::TableNextRow(ImGuiTableRowFlags_Headers);
ImGui::TableNextColumn();
ImGui::ColorButton("color", ImColor(this->getColor()), ImGuiColorEditFlags_NoTooltip | ImGuiColorEditFlags_AlphaPreview);
ImGui::TableNextRow();
ImGui::TableNextColumn();
bool open = ImGui::TreeNodeEx(this->getName().c_str(), ImGuiTreeNodeFlags_SpanFullWidth);
ImGui::TableNextColumn();
ImGui::TableNextColumn();
ImGui::Text("0x%08lx : 0x%08lx", this->getOffset(), this->getOffset() + this->getSize() - 1);
ImGui::TableNextColumn();
ImGui::Text("0x%04lx", this->getSize());
ImGui::TableNextColumn();
ImGui::Text("%s", this->getTypeName().c_str());
ImGui::TextColored(ImColor(0xFFD69C56), "struct"); ImGui::SameLine(); ImGui::Text("%s", this->m_structName.c_str());
ImGui::TableNextColumn();
ImGui::Text("%s", "{ ... }");
@@ -371,21 +461,21 @@ namespace hex::lang {
class PatternDataUnion : public PatternData {
public:
PatternDataUnion(u64 offset, size_t size, const std::string &name, const std::string &unionName, const std::vector<PatternData*> & members, u32 color = 0)
: PatternData(Type::Union, offset, size, name, color), m_unionName(unionName), m_members(members), m_sortedMembers(members) { }
PatternDataUnion(u64 offset, size_t size, const std::string &name, const std::string &unionName, const std::vector<PatternData*> & members, std::endian endianess, u32 color = 0)
: PatternData(Type::Union, offset, size, name, endianess, color), m_unionName(unionName), m_members(members), m_sortedMembers(members) { }
void createEntry(prv::Provider* &provider) override {
ImGui::TableNextRow(ImGuiTableRowFlags_Headers);
ImGui::TableNextColumn();
ImGui::ColorButton("color", ImColor(this->getColor()), ImGuiColorEditFlags_NoTooltip | ImGuiColorEditFlags_AlphaPreview);
ImGui::TableNextRow();
ImGui::TableNextColumn();
bool open = ImGui::TreeNodeEx(this->getName().c_str(), ImGuiTreeNodeFlags_SpanFullWidth);
ImGui::TableNextColumn();
ImGui::TableNextColumn();
ImGui::Text("0x%08lx : 0x%08lx", this->getOffset(), this->getOffset() + this->getSize() - 1);
ImGui::TableNextColumn();
ImGui::Text("0x%04lx", this->getSize());
ImGui::TableNextColumn();
ImGui::Text("%s", this->getTypeName().c_str());
ImGui::TextColored(ImColor(0xFFD69C56), "union"); ImGui::SameLine(); ImGui::Text("%s", this->m_unionName.c_str());
ImGui::TableNextColumn();
ImGui::Text("%s", "{ ... }");
@@ -430,12 +520,13 @@ namespace hex::lang {
class PatternDataEnum : public PatternData {
public:
PatternDataEnum(u64 offset, size_t size, const std::string &name, const std::string &enumName, std::vector<std::pair<u64, std::string>> enumValues, u32 color = 0)
: PatternData(Type::Enum, offset, size, name, color), m_enumName(enumName), m_enumValues(enumValues) { }
PatternDataEnum(u64 offset, size_t size, const std::string &name, const std::string &enumName, std::vector<std::pair<u64, std::string>> enumValues, std::endian endianess, u32 color = 0)
: PatternData(Type::Enum, offset, size, name, endianess, color), m_enumName(enumName), m_enumValues(enumValues) { }
void createEntry(prv::Provider* &provider) override {
u64 value = 0;
provider->read(this->getOffset(), &value, this->getSize());
value = hex::changeEndianess(value, this->getSize(), this->m_endianess);
std::string valueString = this->m_enumName + "::";
@@ -451,7 +542,25 @@ namespace hex::lang {
if (!foundValue)
valueString += "???";
this->createDefaultEntry(hex::format("%s (0x0*lx)", valueString.c_str(), this->getSize() * 2, value));
ImGui::TableNextRow();
ImGui::TreeNodeEx(this->getName().c_str(), ImGuiTreeNodeFlags_Leaf | ImGuiTreeNodeFlags_NoTreePushOnOpen | ImGuiTreeNodeFlags_SpanFullWidth);
ImGui::TableNextColumn();
if (ImGui::Selectable(("##PatternDataLine"s + std::to_string(this->getOffset())).c_str(), false, ImGuiSelectableFlags_SpanAllColumns)) {
Region selectRegion = { this->getOffset(), this->getSize() };
View::postEvent(Events::SelectionChangeRequest, &selectRegion);
}
ImGui::SameLine();
ImGui::Text("%s", this->getName().c_str());
ImGui::TableNextColumn();
ImGui::ColorButton("color", ImColor(this->getColor()), ImGuiColorEditFlags_NoTooltip, ImVec2(ImGui::GetColumnWidth(), 14));
ImGui::TableNextColumn();
ImGui::Text("0x%08lx : 0x%08lx", this->getOffset(), this->getOffset() + this->getSize() - 1);
ImGui::TableNextColumn();
ImGui::Text("0x%04lx", this->getSize());
ImGui::TableNextColumn();
ImGui::TextColored(ImColor(0xFFD69C56), "enum"); ImGui::SameLine(); ImGui::Text("%s", this->m_enumName.c_str());
ImGui::TableNextColumn();
ImGui::Text("%s", hex::format("%s (0x%0*lx)", valueString.c_str(), this->getSize() * 2, value).c_str());
}
std::string getTypeName() override {
@@ -465,24 +574,24 @@ namespace hex::lang {
class PatternDataBitfield : public PatternData {
public:
PatternDataBitfield(u64 offset, size_t size, const std::string &name, const std::string &bitfieldName, std::vector<std::pair<std::string, size_t>> fields, u32 color = 0)
: PatternData(Type::Enum, offset, size, name, color), m_bitfieldName(bitfieldName), m_fields(fields) { }
PatternDataBitfield(u64 offset, size_t size, const std::string &name, const std::string &bitfieldName, std::vector<std::pair<std::string, size_t>> fields, std::endian endianess, u32 color = 0)
: PatternData(Type::Enum, offset, size, name, endianess, color), m_bitfieldName(bitfieldName), m_fields(fields) { }
void createEntry(prv::Provider* &provider) override {
u64 value = 0;
provider->read(this->getOffset(), &value, this->getSize());
value = hex::changeEndianess(value, this->getSize(), this->m_endianess);
ImGui::TableNextRow(ImGuiTableRowFlags_Headers);
ImGui::TableNextColumn();
ImGui::ColorButton("color", ImColor(0x00FFFFFF), ImGuiColorEditFlags_NoTooltip | ImGuiColorEditFlags_AlphaPreview);
ImGui::TableNextRow();
ImGui::TableNextColumn();
bool open = ImGui::TreeNodeEx(this->getName().c_str(), ImGuiTreeNodeFlags_SpanFullWidth);
ImGui::TableNextColumn();
ImGui::TableNextColumn();
ImGui::Text("0x%08lx : 0x%08lx", this->getOffset(), this->getOffset() + this->getSize() - 1);
ImGui::TableNextColumn();
ImGui::Text("0x%04lx", this->getSize());
ImGui::TableNextColumn();
ImGui::Text("%s", this->getTypeName().c_str());
ImGui::TextColored(ImColor(0xFFD69C56), "bitfield"); ImGui::SameLine(); ImGui::Text("%s", this->m_bitfieldName.c_str());
ImGui::TableNextColumn();
ImGui::Text("{ %llx }", value);
@@ -492,10 +601,10 @@ namespace hex::lang {
ImGui::TableNextRow();
ImGui::TreeNodeEx(this->getName().c_str(), ImGuiTreeNodeFlags_Leaf | ImGuiTreeNodeFlags_NoTreePushOnOpen | ImGuiTreeNodeFlags_SpanFullWidth);
ImGui::TableNextColumn();
ImGui::ColorButton("color", ImColor(this->getColor()), ImGuiColorEditFlags_NoTooltip);
ImGui::TableNextColumn();
ImGui::Text("%s", entryName.c_str());
ImGui::TableNextColumn();
ImGui::ColorButton("color", ImColor(this->getColor()), ImGuiColorEditFlags_NoTooltip, ImVec2(ImGui::GetColumnWidth(), 14));
ImGui::TableNextColumn();
ImGui::Text("0x%08lx : 0x%08lx", this->getOffset() + (bitOffset >> 3), this->getOffset() + ((bitOffset + entrySize) >> 3) - 1);
ImGui::TableNextColumn();
if (entrySize == 1)

View File

@@ -4,9 +4,11 @@
#include "token.hpp"
#include <string>
#include <utility>
#include <functional>
#include <set>
#include <string>
#include <unordered_map>
#include <utility>
namespace hex::lang {
@@ -14,10 +16,20 @@ namespace hex::lang {
public:
Preprocessor();
std::pair<Result, std::string> preprocess(const std::string& code, bool applyDefines = true);
std::pair<Result, std::string> preprocess(const std::string& code, bool initialRun = true);
void addPragmaHandler(std::string pragmaType, std::function<bool(std::string)> function);
void addDefaultPragmaHandlers();
const std::pair<u32, std::string>& getError() { return this->m_error; }
private:
std::unordered_map<std::string, std::function<bool(std::string)>> m_pragmaHandlers;
std::set<std::pair<std::string, std::string>> m_defines;
std::set<std::pair<std::string, std::string>> m_pragmas;
std::pair<u32, std::string> m_error;
};
}

View File

@@ -28,7 +28,9 @@ namespace hex::lang {
Union,
Using,
Enum,
Bitfield
Bitfield,
LittleEndian,
BigEndian
} keyword;
} keywordToken;
struct IdentifierToken {
@@ -38,7 +40,8 @@ namespace hex::lang {
enum class Operator {
AtDeclaration,
Assignment,
Inherit
Inherit,
Star
} op;
} operatorToken;
struct IntegerToken {
@@ -62,5 +65,7 @@ namespace hex::lang {
Padding = 0x1F
} type;
} typeToken;
u32 lineNumber;
};
}

View File

@@ -15,6 +15,11 @@ namespace hex::lang {
Validator();
bool validate(const std::vector<ASTNode*>& ast);
const std::pair<u32, std::string>& getError() { return this->m_error; }
private:
std::pair<u32, std::string> m_error;
};
}

View File

@@ -18,8 +18,11 @@ namespace hex::prv {
bool isWritable() override;
void read(u64 offset, void *buffer, size_t size) override;
void write(u64 offset, void *buffer, size_t size) override;
size_t getSize() override;
void write(u64 offset, const void *buffer, size_t size) override;
void readRaw(u64 offset, void *buffer, size_t size) override;
void writeRaw(u64 offset, const void *buffer, size_t size) override;
size_t getActualSize() override;
std::vector<std::pair<std::string, std::string>> getDataInformation() override;

View File

@@ -2,6 +2,10 @@
#include <hex.hpp>
#include <cmath>
#include <concepts>
#include <map>
#include <optional>
#include <string>
#include <vector>
@@ -9,18 +13,58 @@ namespace hex::prv {
class Provider {
public:
Provider() = default;
constexpr static size_t PageSize = 0x1000'0000;
Provider() {
this->m_patches.emplace_back();
}
virtual ~Provider() = default;
virtual bool isAvailable() = 0;
virtual bool isReadable() = 0;
virtual bool isWritable() = 0;
virtual void read(u64 offset, void *buffer, size_t size) = 0;
virtual void write(u64 offset, void *buffer, size_t size) = 0;
virtual size_t getSize() = 0;
virtual void read(u64 offset, void *buffer, size_t size) { this->readRaw(offset, buffer, size); }
virtual void write(u64 offset, const void *buffer, size_t size) { this->writeRaw(offset, buffer, size); }
virtual void readRaw(u64 offset, void *buffer, size_t size) = 0;
virtual void writeRaw(u64 offset, const void *buffer, size_t size) = 0;
virtual size_t getActualSize() = 0;
std::map<u64, u8>& getPatches() { return this->m_patches.back(); }
void applyPatches() {
for (auto &[patchAddress, patch] : this->m_patches.back())
this->writeRaw(patchAddress, &patch, 1);
}
u32 getPageCount() { return std::ceil(this->getActualSize() / double(PageSize)); }
u32 getCurrentPage() const { return this->m_currPage; }
void setCurrentPage(u32 page) { if (page < getPageCount()) this->m_currPage = page; }
virtual size_t getBaseAddress() {
return PageSize * this->m_currPage;
}
virtual size_t getSize() {
return std::min(this->getActualSize() - PageSize * this->m_currPage, PageSize);
}
virtual std::optional<u32> getPageOfAddress(u64 address) {
u32 page = std::floor(address / double(PageSize));
if (page >= this->getPageCount())
return { };
return page;
}
virtual std::vector<std::pair<std::string, std::string>> getDataInformation() = 0;
protected:
u32 m_currPage = 0;
std::vector<std::map<u64, u8>> m_patches;
};
}

View File

@@ -1,96 +0,0 @@
#pragma once
#include <hex.hpp>
#include <array>
#include <functional>
#include <memory>
#include <optional>
#include <string>
#include <vector>
#include "lang/token.hpp"
namespace hex {
template<typename ... Args>
inline std::string format(const std::string &format, Args ... args) {
size_t size = snprintf( nullptr, 0, format.c_str(), args ... );
if( size <= 0 )
return "";
std::vector<char> buffer(size + 1, 0x00);
snprintf(buffer.data(), size + 1, format.c_str(), args ...);
return std::string(buffer.data(), buffer.data() + size);
}
[[nodiscard]] constexpr inline u64 signExtend(u64 value, u8 currWidth, u8 targetWidth) {
u64 mask = 1LLU << (currWidth - 1);
return (((value ^ mask) - mask) << (64 - targetWidth)) >> (64 - targetWidth);
}
constexpr inline bool isUnsigned(const lang::Token::TypeToken::Type type) {
return (static_cast<u32>(type) & 0x0F) == 0x00;
}
constexpr inline bool isSigned(const lang::Token::TypeToken::Type type) {
return (static_cast<u32>(type) & 0x0F) == 0x01;
}
constexpr inline bool isFloatingPoint(const lang::Token::TypeToken::Type type) {
return (static_cast<u32>(type) & 0x0F) == 0x02;
}
constexpr inline u32 getTypeSize(const lang::Token::TypeToken::Type type) {
return static_cast<u32>(type) >> 4;
}
inline std::string toByteString(u64 bytes) {
double value = bytes;
u8 unitIndex = 0;
while (value > 1024) {
value /= 1024;
unitIndex++;
if (unitIndex == 6)
break;
}
std::string result = std::to_string(value);
switch (unitIndex) {
case 0: result += " Bytes"; break;
case 1: result += " kB"; break;
case 2: result += " MB"; break;
case 3: result += " GB"; break;
case 4: result += " TB"; break;
case 5: result += " PB"; break;
case 6: result += " EB"; break;
default: result = "A lot!";
}
return result;
}
[[nodiscard]] constexpr inline u64 extract(u8 from, u8 to, const u64 &value) {
u64 mask = (std::numeric_limits<u64>::max() >> (63 - (from - to))) << to;
return (value & mask) >> to;
}
class ScopeExit {
public:
ScopeExit(std::function<void()> func) : m_func(func) {}
~ScopeExit() { if (this->m_func != nullptr) this->m_func(); }
void release() {
this->m_func = nullptr;
}
private:
std::function<void()> m_func;
};
}

View File

@@ -4,9 +4,10 @@
#include "imgui.h"
#include "event.hpp"
#include "helpers/event.hpp"
#include <functional>
#include <string>
#include <vector>
@@ -14,7 +15,7 @@ namespace hex {
class View {
public:
View() { }
View(std::string viewName) : m_viewName(viewName) { }
virtual ~View() { }
virtual void createView() = 0;
@@ -29,6 +30,38 @@ namespace hex {
View::s_eventManager.post(eventType, userData);
}
static void drawCommonInterfaces() {
if (ImGui::BeginPopupModal("Error", nullptr, ImGuiWindowFlags_NoResize)) {
ImGui::NewLine();
if (ImGui::BeginChild("##scrolling", ImVec2(300, 100))) {
ImGui::SetCursorPosX((300 - ImGui::CalcTextSize(View::s_errorMessage.c_str(), nullptr, false).x) / 2.0F);
ImGui::TextWrapped("%s", View::s_errorMessage.c_str());
ImGui::EndChild();
}
ImGui::NewLine();
ImGui::SetCursorPosX(75);
if (ImGui::Button("Okay", ImVec2(150, 20)))
ImGui::CloseCurrentPopup();
ImGui::EndPopup();
}
}
static void showErrorPopup(std::string_view errorMessage) {
View::s_errorMessage = errorMessage;
ImGui::OpenPopup("Error");
}
virtual bool hasViewMenuItemEntry() { return true; }
bool& getWindowOpenState() {
return this->m_windowOpen;
}
const std::string getName() const {
return this->m_viewName;
}
protected:
void subscribeEvent(Events eventType, std::function<void(const void*)> callback) {
View::s_eventManager.subscribe(eventType, this, callback);
@@ -42,9 +75,16 @@ namespace hex {
View::s_deferedCalls.push_back(function);
}
private:
std::string m_viewName;
bool m_windowOpen = false;
static inline EventManager s_eventManager;
static inline std::vector<std::function<void()>> s_deferedCalls;
static inline std::string s_errorMessage;
};
}

View File

@@ -0,0 +1,28 @@
#pragma once
#include "views/view.hpp"
#include <vector>
#include <list>
#include "helpers/utils.hpp"
namespace hex {
namespace prv { class Provider; }
class ViewBookmarks : public View {
public:
explicit ViewBookmarks(prv::Provider* &dataProvider);
~ViewBookmarks() override;
void createView() override;
void createMenu() override;
private:
prv::Provider* &m_dataProvider;
std::list<Bookmark> m_bookmarks;
};
}

View File

@@ -2,6 +2,7 @@
#include "views/view.hpp"
#include <bit>
#include <cstdio>
#include <ctime>
#include <string>
@@ -50,9 +51,10 @@ namespace hex {
private:
prv::Provider* &m_dataProvider;
bool m_windowOpen = true;
bool m_shouldInvalidate = true;
std::endian m_endianess = std::endian::native;
PreviewData m_previewData = { 0 };
size_t m_validBytes = 0;
std::vector<std::pair<std::string, std::string>> m_cachedData;

View File

@@ -0,0 +1,50 @@
#pragma once
#include "helpers/disassembler.hpp"
#include "views/view.hpp"
#include <capstone/capstone.h>
#include <cstdio>
#include <string>
#include <vector>
namespace hex {
namespace prv { class Provider; }
struct Disassembly {
u64 address;
u64 offset;
size_t size;
std::string bytes;
std::string mnemonic;
std::string operators;
};
class ViewDisassembler : public View {
public:
explicit ViewDisassembler(prv::Provider* &dataProvider);
~ViewDisassembler() override;
void createView() override;
void createMenu() override;
private:
prv::Provider* &m_dataProvider;
bool m_shouldInvalidate = false;
u64 m_baseAddress = 0;
u64 m_codeRegion[2] = { 0 };
bool m_shouldMatchSelection = false;
Architecture m_architecture = Architecture::ARM;
cs_mode m_modeBasicARM = cs_mode(0), m_modeExtraARM = cs_mode(0), m_modeBasicMIPS = cs_mode(0), m_modeBasicPPC = cs_mode(0), m_modeBasicX86 = cs_mode(0);
bool m_littleEndianMode = true, m_micoMode = false, m_sparcV9Mode = false;
std::vector<Disassembly> m_disassembly;
};
}

View File

@@ -18,11 +18,12 @@ namespace hex {
private:
prv::Provider* &m_dataProvider;
bool m_windowOpen = true;
bool m_shouldInvalidate = true;
int m_currHashFunction = 0;
int m_hashStart = 0, m_hashEnd = 0;
u64 m_hashRegion[2] = { 0 };
bool m_shouldMatchSelection = false;
static constexpr const char* HashFunctionNames[] = { "CRC16", "CRC32", "MD4", "MD5", "SHA-1", "SHA-224", "SHA-256", "SHA-384", "SHA-512" };
};

View File

@@ -22,12 +22,16 @@ namespace hex {
void createView() override;
void createMenu() override;
bool hasViewMenuItemEntry() override { return false; }
private:
bool m_aboutWindowOpen = false;
bool m_patternHelpWindowOpen = false;
bool m_mathHelpWindowOpen = false;
void drawAboutPopup();
void drawPatternHelpPopup();
void drawMathEvaluatorHelp();
};
}

View File

@@ -1,6 +1,6 @@
#pragma once
#include "utils.hpp"
#include "helpers/utils.hpp"
#include "views/view.hpp"
#include "imgui_memory_editor.h"
@@ -45,11 +45,17 @@ namespace hex {
s64 m_gotoAddress = 0;
std::vector<u8> m_dataToSave;
std::string m_loaderScriptScriptPath;
std::string m_loaderScriptFilePath;
void drawSearchPopup();
void drawGotoPopup();
void openFile(std::string path);
bool saveToFile(std::string path, const std::vector<u8>& data);
bool loadFromFile(std::string path, std::vector<u8>& data);
enum class Language { C, Cpp, CSharp, Rust, Python, Java, JavaScript };
void copyBytes();

View File

@@ -21,15 +21,17 @@ namespace hex {
private:
prv::Provider* &m_dataProvider;
bool m_windowOpen = true;
bool m_dataValid = false;
u32 m_blockSize = 0;
float m_averageEntropy = 0;
float m_highestBlockEntropy = 0;
std::vector<float> m_blockEntropy;
std::array<float, 256> m_valueCounts = { 0 };
bool m_shouldInvalidate = true;
bool m_shouldInvalidate = false;
std::pair<u64, u64> m_analyzedRegion = { 0, 0 };
std::string m_fileDescription;
std::string m_mimeType;

View File

@@ -0,0 +1,29 @@
#pragma once
#include <hex.hpp>
#include "imgui.h"
#include "views/view.hpp"
#include <optional>
namespace hex {
namespace prv { class Provider; }
class ViewPatches : public View {
public:
explicit ViewPatches(prv::Provider* &dataProvider);
~ViewPatches() override;
void createView() override;
void createMenu() override;
private:
prv::Provider* &m_dataProvider;
u64 m_selectedPatch;
};
}

View File

@@ -9,7 +9,7 @@
#include <concepts>
#include <cstring>
#include <filesystem>
#include "ImGuiFileBrowser.h"
#include "TextEditor.h"
@@ -27,11 +27,12 @@ namespace hex {
private:
std::vector<lang::PatternData*> &m_patternData;
prv::Provider* &m_dataProvider;
bool m_windowOpen = true;
std::filesystem::path m_possiblePatternFile;
TextEditor m_textEditor;
imgui_addons::ImGuiFileBrowser m_fileBrowser;
void loadPatternFile(std::string path);
void clearPatternData();
void parsePattern(char *buffer);
};

View File

@@ -23,10 +23,10 @@ namespace hex {
void createMenu() override;
private:
prv::Provider* &m_dataProvider;
std::vector<lang::PatternData*> &m_patternData;
std::vector<lang::PatternData*> m_sortedPatternData;
bool m_windowOpen = true;
};
}

View File

@@ -25,12 +25,16 @@ namespace hex {
private:
prv::Provider* &m_dataProvider;
bool m_windowOpen = true;
bool m_shouldInvalidate = false;
std::vector<FoundString> m_foundStrings;
int m_minimumLength = 5;
char *m_filter;
std::string m_selectedString;
std::string m_demangledName;
void createStringContextMenu(const FoundString &foundString);
};
}

View File

@@ -4,6 +4,7 @@
#include "imgui.h"
#include "views/view.hpp"
#include "helpers/math_evaluator.hpp"
#include <array>
#include <string>
@@ -14,17 +15,17 @@ namespace hex {
class ViewTools : public View {
public:
ViewTools();
ViewTools(hex::prv::Provider* &provider);
~ViewTools() override;
void createView() override;
void createMenu() override;
private:
bool m_windowOpen = true;
hex::prv::Provider* &m_dataProvider;
char *m_mangledBuffer = nullptr;
char *m_demangledName = nullptr;
std::string m_demangledName;
bool m_asciiTableShowOctal = false;
@@ -35,10 +36,16 @@ namespace hex {
std::array<float, 4> m_pickedColor;
MathEvaluator m_mathEvaluator;
std::vector<long double> m_mathHistory;
std::string m_lastMathError;
char *m_mathInput = nullptr;
void drawDemangler();
void drawASCIITable();
void drawRegexReplacer();
void drawColorPicker();
void drawMathEvaluator();
};
}

View File

@@ -7,6 +7,7 @@
#include "views/view.hpp"
struct GLFWwindow;
struct ImGuiSettingsHandler;
namespace hex {
@@ -24,8 +25,10 @@ namespace hex {
return static_cast<T*>(this->m_views.back());
}
public:
float m_globalScale = 1.0f, m_fontScale = 1.0f;
friend void *ImHexSettingsHandler_ReadOpenFn(ImGuiContext *ctx, ImGuiSettingsHandler *, const char *);
friend void ImHexSettingsHandler_ReadLine(ImGuiContext*, ImGuiSettingsHandler *handler, void *, const char* line);
friend void ImHexSettingsHandler_ApplyAll(ImGuiContext *ctx, ImGuiSettingsHandler *handler);
friend void ImHexSettingsHandler_WriteAll(ImGuiContext* ctx, ImGuiSettingsHandler *handler, ImGuiTextBuffer *buf);
private:
void frameBegin();
@@ -38,7 +41,10 @@ namespace hex {
GLFWwindow* m_window;
std::vector<View*> m_views;
float m_globalScale = 1.0f, m_fontScale = 1.0f;
bool m_fpsVisible = false;
bool m_demoWindowOpen = false;
static inline std::tuple<int, int> s_currShortcut = { -1, -1 };
};

View File

@@ -46,6 +46,7 @@
#include <stdio.h> // sprintf, scanf
#include <stdint.h> // uint8_t, etc.
#include "helpers/utils.hpp"
#include "views/view.hpp"
@@ -75,7 +76,6 @@ struct MemoryEditor
};
// Settings
bool Open; // = true // set to false when DrawWindow() was closed. ignore if not using DrawWindow().
bool ReadOnly; // = false // disable any editing.
int Cols; // = 16 // number of columns to display.
bool OptShowOptions; // = true // display options button/context menu. when disabled, options will be locked unless you provide your own UI for them.
@@ -106,7 +106,6 @@ struct MemoryEditor
MemoryEditor()
{
// Settings
Open = true;
ReadOnly = false;
Cols = 16;
OptShowOptions = true;
@@ -181,13 +180,12 @@ struct MemoryEditor
}
// Standalone Memory Editor window
void DrawWindow(const char* title, void* mem_data, size_t mem_size, size_t base_display_addr = 0x0000)
void DrawWindow(const char* title, bool *p_open, void* mem_data, size_t mem_size, size_t base_display_addr = 0x0000)
{
Sizes s;
CalcSizes(s, mem_size, base_display_addr);
ImGui::SetNextWindowSizeConstraints(ImVec2(0.0f, 0.0f), ImVec2(s.WindowWidth, FLT_MAX));
if (ImGui::Begin(title, &Open, ImGuiWindowFlags_NoScrollbar))
if (ImGui::Begin(title, p_open, ImGuiWindowFlags_NoScrollbar | ImGuiWindowFlags_NoCollapse))
{
DrawContents(mem_data, mem_size, base_display_addr);
if (ContentsWidthChanged)
@@ -229,7 +227,7 @@ struct MemoryEditor
footer_height += height_separator + ImGui::GetFrameHeightWithSpacing() * 1;
ImGui::BeginChild("offset", ImVec2(0, s.LineHeight), false, ImGuiWindowFlags_NoMove | ImGuiWindowFlags_NoNav);
ImGui::Text("% *c ", s.AddrDigitsCount, ' ');
ImGui::Text("%*c ", s.AddrDigitsCount, ' ');
for (int i = 0; i < Cols; i++) {
float byte_pos_x = s.PosHexStart + s.HexCellWidth * i;
if (OptMidColsCount > 0)
@@ -264,7 +262,9 @@ struct MemoryEditor
DataPreviewAddrEnd = (size_t)-1;
size_t data_editing_addr_backup = DataEditingAddr;
size_t data_preview_addr_backup = DataPreviewAddr;
size_t data_editing_addr_next = (size_t)-1;
size_t data_preview_addr_next = (size_t)-1;
if (DataEditingAddr != (size_t)-1)
{
// Move cursor but only apply on next frame so scrolling with be synchronized (because currently we can't change the scrolling while the window is being rendered)
@@ -272,6 +272,20 @@ struct MemoryEditor
else if (ImGui::IsKeyPressed(ImGui::GetKeyIndex(ImGuiKey_DownArrow)) && DataEditingAddr < mem_size - Cols) { data_editing_addr_next = DataEditingAddr + Cols; DataEditingTakeFocus = true; }
else if (ImGui::IsKeyPressed(ImGui::GetKeyIndex(ImGuiKey_LeftArrow)) && DataEditingAddr > 0) { data_editing_addr_next = DataEditingAddr - 1; DataEditingTakeFocus = true; }
else if (ImGui::IsKeyPressed(ImGui::GetKeyIndex(ImGuiKey_RightArrow)) && DataEditingAddr < mem_size - 1) { data_editing_addr_next = DataEditingAddr + 1; DataEditingTakeFocus = true; }
} else if (DataPreviewAddr != -1) {
// Move cursor but only apply on next frame so scrolling with be synchronized (because currently we can't change the scrolling while the window is being rendered)
if (ImGui::IsKeyPressed(ImGui::GetKeyIndex(ImGuiKey_UpArrow)) && DataPreviewAddr >= (size_t)Cols) { data_preview_addr_next = DataPreviewAddr - Cols; DataPreviewAddr = data_preview_addr_next; if (!ImGui::GetIO().KeyShift) DataPreviewAddrEnd = DataPreviewAddr; }
else if (ImGui::IsKeyPressed(ImGui::GetKeyIndex(ImGuiKey_DownArrow)) && DataPreviewAddr < mem_size - Cols) { data_preview_addr_next = DataPreviewAddr + Cols; DataPreviewAddr = data_preview_addr_next; if (!ImGui::GetIO().KeyShift) DataPreviewAddrEnd = DataPreviewAddr; }
else if (ImGui::IsKeyPressed(ImGui::GetKeyIndex(ImGuiKey_LeftArrow)) && DataPreviewAddr > 0) { data_preview_addr_next = DataPreviewAddr - 1; DataPreviewAddr = data_preview_addr_next; if (!ImGui::GetIO().KeyShift) DataPreviewAddrEnd = DataPreviewAddr; }
else if (ImGui::IsKeyPressed(ImGui::GetKeyIndex(ImGuiKey_RightArrow)) && DataPreviewAddr < mem_size - 1) { data_preview_addr_next = DataPreviewAddr + 1; DataPreviewAddr = data_preview_addr_next; if (!ImGui::GetIO().KeyShift) DataPreviewAddrEnd = DataPreviewAddr; }
}
if (data_preview_addr_next != (size_t)-1 && (data_preview_addr_next / Cols) != (data_preview_addr_backup / Cols))
{
// Track cursor movements
const int scroll_offset = ((int)(data_preview_addr_next / Cols) - (int)(data_preview_addr_backup / Cols));
const bool scroll_desired = (scroll_offset < 0 && data_preview_addr_next < visible_start_addr + Cols * 2) || (scroll_offset > 0 && data_preview_addr_next > visible_end_addr - Cols * 2);
if (scroll_desired)
ImGui::SetScrollY(ImGui::GetScrollY() + scroll_offset * s.LineHeight);
}
if (data_editing_addr_next != (size_t)-1 && (data_editing_addr_next / Cols) != (data_editing_addr_backup / Cols))
{
@@ -423,14 +437,16 @@ struct MemoryEditor
DataPreviewAddr = addr;
DataPreviewAddrEnd = addr;
hex::View::postEvent(hex::Events::ByteSelected, &DataPreviewAddr);
hex::Region selectionRegion { addr, 1 };
hex::View::postEvent(hex::Events::RegionSelected, &selectionRegion);
}
if (!ReadOnly && ImGui::IsItemHovered() && ((ImGui::IsMouseClicked(0) && ImGui::GetIO().KeyShift) || ImGui::IsMouseDragging(0))) {
DataPreviewAddrEnd = addr;
size_t dataPreviewStart = std::min(DataPreviewAddr, DataPreviewAddrEnd);
hex::View::postEvent(hex::Events::ByteSelected, &dataPreviewStart);
hex::Region selectionRegion { std::min(DataPreviewAddr, DataPreviewAddrEnd), std::max(DataPreviewAddr, DataPreviewAddrEnd) - std::min(DataPreviewAddr, DataPreviewAddrEnd) + 1 };
hex::View::postEvent(hex::Events::RegionSelected, &selectionRegion);
}
}
}
@@ -510,7 +526,7 @@ struct MemoryEditor
}
else if (data_editing_addr_next != (size_t)-1)
{
DataEditingAddr = DataPreviewAddr = data_editing_addr_next;
DataEditingAddr = DataPreviewAddr = DataPreviewAddrEnd = data_editing_addr_next;
}
if (OptShowOptions)
@@ -547,18 +563,6 @@ struct MemoryEditor
ImGui::SameLine();
ImGui::Text(format_range, s.AddrDigitsCount, base_display_addr, s.AddrDigitsCount, base_display_addr + mem_size - 1);
ImGui::SameLine();
ImGui::PushItemWidth((s.AddrDigitsCount + 1) * s.GlyphWidth + style.FramePadding.x * 2.0f);
if (ImGui::InputText("##addr", AddrInputBuf, 32, ImGuiInputTextFlags_CharsHexadecimal | ImGuiInputTextFlags_EnterReturnsTrue))
{
size_t goto_addr;
if (sscanf(AddrInputBuf, "%" _PRISizeT "X", &goto_addr) == 1)
{
GotoAddr = goto_addr - base_display_addr;
HighlightMin = HighlightMax = (size_t)-1;
}
}
ImGui::PopItemWidth();
if (GotoAddr != (size_t)-1)
{

2
python_libs/lib/imhex.py Normal file
View File

@@ -0,0 +1,2 @@
from _imhex import *
import imhex_python.types as types

View File

@@ -0,0 +1,44 @@
class ImHexTypeMeta(type):
def __new__(cls, name, bases, dct):
return super().__new__(cls, name, bases, dct)
def __getitem__(self, value):
return array(self, value)
class ImHexType(metaclass=ImHexTypeMeta):
pass
class u8(ImHexType):
pass
class u16(ImHexType):
pass
class u32(ImHexType):
pass
class u64(ImHexType):
pass
class u128(ImHexType):
pass
class s8(ImHexType):
pass
class s16(ImHexType):
pass
class s32(ImHexType):
pass
class s64(ImHexType):
pass
class s128(ImHexType):
pass
class float(ImHexType):
pass
class double(ImHexType):
pass
class array(ImHexType):
def __init__(self, array_type, size):
self.array_type = array_type()
self.size = size
array_type : type
size : int

View File

@@ -1,4 +1,4 @@
#include "crypto.hpp"
#include "helpers/crypto.hpp"
#include "providers/provider.hpp"
@@ -6,6 +6,8 @@
#include <openssl/md5.h>
#include <openssl/sha.h>
#include <openssl/evp.h>
#include <array>
#include <span>
@@ -39,7 +41,7 @@ namespace hex {
std::array<u8, 512> buffer = { 0 };
for (u64 bufferOffset = 0; offset < size; offset += buffer.size()) {
const u64 readSize = std::min(buffer.size(), size - bufferOffset);
const u64 readSize = std::min(u64(buffer.size()), size - bufferOffset);
data->read(offset + bufferOffset, buffer.data(), readSize);
for (size_t i = 0; i < readSize; i++) {
@@ -72,7 +74,7 @@ namespace hex {
std::array<u8, 512> buffer = { 0 };
for (u64 bufferOffset = 0; offset < size; offset += buffer.size()) {
const u64 readSize = std::min(buffer.size(), size - bufferOffset);
const u64 readSize = std::min(u64(buffer.size()), size - bufferOffset);
data->read(offset + bufferOffset, buffer.data(), readSize);
for (size_t i = 0; i < readSize; i++) {
@@ -92,7 +94,7 @@ namespace hex {
std::array<u8, 512> buffer = { 0 };
for (u64 bufferOffset = 0; bufferOffset < size; bufferOffset += buffer.size()) {
const u64 readSize = std::min(buffer.size(), size - bufferOffset);
const u64 readSize = std::min(u64(buffer.size()), size - bufferOffset);
data->read(offset + bufferOffset, buffer.data(), readSize);
MD4_Update(&ctx, buffer.data(), readSize);
}
@@ -111,7 +113,7 @@ namespace hex {
std::array<u8, 512> buffer = { 0 };
for (u64 bufferOffset = 0; bufferOffset < size; bufferOffset += buffer.size()) {
const u64 readSize = std::min(buffer.size(), size - bufferOffset);
const u64 readSize = std::min(u64(buffer.size()), size - bufferOffset);
data->read(offset + bufferOffset, buffer.data(), readSize);
MD5_Update(&ctx, buffer.data(), readSize);
}
@@ -129,7 +131,7 @@ namespace hex {
SHA1_Init(&ctx);
std::array<u8, 512> buffer = { 0 };
for (u64 bufferOffset = 0; bufferOffset < size; bufferOffset += buffer.size()) {
const u64 readSize = std::min(buffer.size(), size - bufferOffset);
const u64 readSize = std::min(u64(buffer.size()), size - bufferOffset);
data->read(offset + bufferOffset, buffer.data(), readSize);
SHA1_Update(&ctx, buffer.data(), readSize);
}
@@ -148,7 +150,7 @@ namespace hex {
std::array<u8, 512> buffer = { 0 };
for (u64 bufferOffset = 0; bufferOffset < size; bufferOffset += buffer.size()) {
const u64 readSize = std::min(buffer.size(), size - bufferOffset);
const u64 readSize = std::min(u64(buffer.size()), size - bufferOffset);
data->read(offset + bufferOffset, buffer.data(), readSize);
SHA224_Update(&ctx, buffer.data(), readSize);
}
@@ -167,7 +169,7 @@ namespace hex {
std::array<u8, 512> buffer = { 0 };
for (u64 bufferOffset = 0; bufferOffset < size; bufferOffset += buffer.size()) {
const u64 readSize = std::min(buffer.size(), size - bufferOffset);
const u64 readSize = std::min(u64(buffer.size()), size - bufferOffset);
data->read(offset + bufferOffset, buffer.data(), readSize);
SHA256_Update(&ctx, buffer.data(), readSize);
}
@@ -186,7 +188,7 @@ namespace hex {
std::array<u8, 512> buffer = { 0 };
for (u64 bufferOffset = 0; bufferOffset < size; bufferOffset += buffer.size()) {
const u64 readSize = std::min(buffer.size(), size - bufferOffset);
const u64 readSize = std::min(u64(buffer.size()), size - bufferOffset);
data->read(offset + bufferOffset, buffer.data(), readSize);
SHA384_Update(&ctx, buffer.data(), readSize);
}
@@ -205,7 +207,7 @@ namespace hex {
std::array<u8, 512> buffer = { 0 };
for (u64 bufferOffset = 0; bufferOffset < size; bufferOffset += buffer.size()) {
const u64 readSize = std::min(buffer.size(), size - bufferOffset);
const u64 readSize = std::min(u64(buffer.size()), size - bufferOffset);
data->read(offset + bufferOffset, buffer.data(), readSize);
SHA512_Update(&ctx, buffer.data(), readSize);
}
@@ -215,4 +217,24 @@ namespace hex {
return result;
}
std::vector<u8> decode64(const std::vector<u8> &input) {
size_t outputSize = (3 * input.size()) / 4;
std::vector<u8> output(outputSize + 1, 0x00);
if (EVP_DecodeBlock(output.data(), reinterpret_cast<const unsigned char *>(input.data()), input.size()) != outputSize)
return { };
return output;
}
std::vector<u8> encode64(const std::vector<u8> &input) {
size_t outputSize = 4 * ((input.size() + 2) / 3);
std::vector<u8> output(outputSize + 1, 0x00);
if (EVP_EncodeBlock(output.data(), reinterpret_cast<const unsigned char *>(input.data()), input.size()) != outputSize)
return { };
return output;
}
}

View File

@@ -0,0 +1,228 @@
#include "helpers/loader_script_handler.hpp"
#include "views/view.hpp"
#include "helpers/utils.hpp"
#include "providers/provider.hpp"
#define PY_SSIZE_T_CLEAN
#include <Python.h>
#include <structmember.h>
#include <cstring>
#include <filesystem>
using namespace std::literals::string_literals;
namespace hex {
PyObject* LoaderScript::Py_getFilePath(PyObject *self, PyObject *args) {
return PyUnicode_FromString(LoaderScript::s_filePath.c_str());
}
PyObject* LoaderScript::Py_addPatch(PyObject *self, PyObject *args) {
u64 address;
u8 *patches;
Py_ssize_t count;
if (!PyArg_ParseTuple(args, "K|y#", &address, &patches, &count)) {
PyErr_BadArgument();
return nullptr;
}
if (patches == nullptr || count == 0) {
PyErr_SetString(PyExc_TypeError, "Invalid patch provided");
return nullptr;
}
if (address >= LoaderScript::s_dataProvider->getActualSize()) {
PyErr_SetString(PyExc_IndexError, "address out of range");
return nullptr;
}
LoaderScript::s_dataProvider->write(address, patches, count);
Py_RETURN_NONE;
}
PyObject* LoaderScript::Py_addBookmark(PyObject *self, PyObject *args) {
Bookmark bookmark;
char *name = nullptr;
char *comment = nullptr;
if (!PyArg_ParseTuple(args, "K|n|s|s", &bookmark.region.address, &bookmark.region.size, &name, &comment)) {
PyErr_BadArgument();
return nullptr;
}
if (name == nullptr || comment == nullptr) {
PyErr_SetString(PyExc_IndexError, "address out of range");
return nullptr;
}
std::copy(name, name + std::strlen(name), std::back_inserter(bookmark.name));
std::copy(comment, comment + std::strlen(comment), std::back_inserter(bookmark.comment));
View::postEvent(Events::AddBookmark, &bookmark);
Py_RETURN_NONE;
}
static PyObject* createStructureType(std::string keyword, PyObject *args) {
auto type = PyTuple_GetItem(args, 0);
if (type == nullptr) {
PyErr_BadArgument();
return nullptr;
}
auto instance = PyObject_CallObject(type, nullptr);
if (instance == nullptr) {
PyErr_BadArgument();
return nullptr;
}
hex::ScopeExit instanceCleanup([&]{ Py_DECREF(instance); });
if (instance->ob_type->tp_base == nullptr || instance->ob_type->tp_base->tp_name != "ImHexType"s) {
PyErr_SetString(PyExc_TypeError, "class type must extend from ImHexType");
return nullptr;
}
auto dict = instance->ob_type->tp_dict;
if (dict == nullptr) {
PyErr_BadArgument();
return nullptr;
}
auto annotations = PyDict_GetItemString(dict, "__annotations__");
if (annotations == nullptr) {
PyErr_BadArgument();
return nullptr;
}
auto list = PyDict_Items(annotations);
if (list == nullptr) {
PyErr_BadArgument();
return nullptr;
}
hex::ScopeExit listCleanup([&]{ Py_DECREF(list); });
std::string code = keyword + " " + instance->ob_type->tp_name + " {\n";
for (u16 i = 0; i < PyList_Size(list); i++) {
auto item = PyList_GetItem(list, i);
auto memberName = PyUnicode_AsUTF8(PyTuple_GetItem(item, 0));
auto memberType = PyTuple_GetItem(item, 1);
if (memberType == nullptr) {
PyErr_SetString(PyExc_TypeError, "member needs to have a annotation extending from ImHexType");
return nullptr;
}
// Array already is an object
if (memberType->ob_type->tp_name == "array"s) {
auto arrayType = PyObject_GetAttrString(memberType, "array_type");
if (arrayType == nullptr) {
PyErr_BadArgument();
return nullptr;
}
code += " "s + arrayType->ob_type->tp_name + " " + memberName;
auto arraySize = PyObject_GetAttrString(memberType, "size");
if (arraySize == nullptr) {
PyErr_BadArgument();
return nullptr;
}
if (PyUnicode_Check(arraySize))
code += "["s + PyUnicode_AsUTF8(arraySize) + "];\n";
else if (PyLong_Check(arraySize))
code += "["s + std::to_string(PyLong_AsLong(arraySize)) + "];\n";
else {
PyErr_SetString(PyExc_TypeError, "invalid array size type. Expected string or int");
return nullptr;
}
} else {
auto memberTypeInstance = PyObject_CallObject(memberType, nullptr);
if (memberTypeInstance == nullptr || memberTypeInstance->ob_type->tp_base == nullptr || memberTypeInstance->ob_type->tp_base->tp_name != "ImHexType"s) {
PyErr_SetString(PyExc_TypeError, "member needs to have a annotation extending from ImHexType");
if (memberTypeInstance != nullptr)
Py_DECREF(memberTypeInstance);
return nullptr;
}
code += " "s + memberTypeInstance->ob_type->tp_name + " "s + memberName + ";\n";
Py_DECREF(memberTypeInstance);
}
}
code += "};\n";
View::postEvent(Events::AppendPatternLanguageCode, code.c_str());
Py_RETURN_NONE;
}
PyObject* LoaderScript::Py_addStruct(PyObject *self, PyObject *args) {
return createStructureType("struct", args);
}
PyObject* LoaderScript::Py_addUnion(PyObject *self, PyObject *args) {
return createStructureType("union", args);
}
bool LoaderScript::processFile(std::string_view scriptPath) {
Py_SetProgramName(Py_DecodeLocale(mainArgv[0], nullptr));
if (std::filesystem::exists(std::filesystem::path(mainArgv[0]).parent_path().string() + "/lib/python" PYTHON_VERSION_MAJOR_MINOR))
Py_SetPythonHome(Py_DecodeLocale(std::filesystem::path(mainArgv[0]).parent_path().string().c_str(), nullptr));
PyImport_AppendInittab("_imhex", []() -> PyObject* {
static PyMethodDef ImHexMethods[] = {
{ "get_file_path", &LoaderScript::Py_getFilePath, METH_NOARGS, "Returns the path of the file being loaded." },
{ "patch", &LoaderScript::Py_addPatch, METH_VARARGS, "Patches a region of memory" },
{ "add_bookmark", &LoaderScript::Py_addBookmark, METH_VARARGS, "Adds a bookmark" },
{ "add_struct", &LoaderScript::Py_addStruct, METH_VARARGS, "Adds a struct" },
{ "add_union", &LoaderScript::Py_addUnion, METH_VARARGS, "Adds a union" },
{ nullptr, nullptr, 0, nullptr }
};
static PyModuleDef ImHexModule = {
PyModuleDef_HEAD_INIT, "imhex", nullptr, -1, ImHexMethods, nullptr, nullptr, nullptr, nullptr
};
auto module = PyModule_Create(&ImHexModule);
if (module == nullptr)
return nullptr;
return module;
});
Py_Initialize();
{
auto sysPath = PySys_GetObject("path");
auto path = PyUnicode_FromString("lib");
PyList_Insert(sysPath, 0, path);
}
FILE *scriptFile = fopen(scriptPath.data(), "r");
PyRun_SimpleFile(scriptFile, scriptPath.data());
fclose(scriptFile);
Py_Finalize();
return true;
}
}

View File

@@ -0,0 +1,390 @@
#include "helpers/math_evaluator.hpp"
#include <string>
#include <queue>
#include <stack>
#include <stdexcept>
#include <cmath>
#include <cstdint>
#include <optional>
#include <numbers>
namespace hex {
s16 comparePrecedence(const Operator& a, const Operator& b) {
return (static_cast<s8>(a) & 0x0F0) - (static_cast<s8>(b) & 0x0F0);
}
bool isLeftAssociative(const Operator op) {
return (static_cast<u32>(op) & 0xF00) == 0;
}
std::pair<Operator, size_t> toOperator(std::string input) {
if (input.starts_with("##")) return { Operator::Combine, 2 };
if (input.starts_with("==")) return { Operator::Equals, 2 };
if (input.starts_with("!=")) return { Operator::NotEquals, 2 };
if (input.starts_with(">=")) return { Operator::GreaterThanOrEquals, 2 };
if (input.starts_with("<=")) return { Operator::LessThanOrEquals, 2 };
if (input.starts_with(">>")) return { Operator::ShiftRight, 2 };
if (input.starts_with("<<")) return { Operator::ShiftLeft, 2 };
if (input.starts_with("||")) return { Operator::Or, 2 };
if (input.starts_with("^^")) return { Operator::Xor, 2 };
if (input.starts_with("&&")) return { Operator::And, 2 };
if (input.starts_with("**")) return { Operator::Exponentiation, 2 };
if (input.starts_with(">")) return { Operator::GreaterThan, 1 };
if (input.starts_with("<")) return { Operator::LessThan, 1 };
if (input.starts_with("!")) return { Operator::Not, 1 };
if (input.starts_with("|")) return { Operator::BitwiseOr, 1 };
if (input.starts_with("^")) return { Operator::BitwiseXor, 1 };
if (input.starts_with("&")) return { Operator::BitwiseAnd, 1 };
if (input.starts_with("~")) return { Operator::BitwiseNot, 1 };
if (input.starts_with("+")) return { Operator::Addition, 1 };
if (input.starts_with("-")) return { Operator::Subtraction, 1 };
if (input.starts_with("*")) return { Operator::Multiplication, 1 };
if (input.starts_with("/")) return { Operator::Division, 1 };
if (input.starts_with("%")) return { Operator::Modulus, 1 };
if (input.starts_with("=")) return { Operator::Assign, 1 };
return { Operator::Invalid, 0 };
}
std::queue<Token> MathEvaluator::parseInput(const char *input) {
std::queue<Token> inputQueue;
char *prevPos = const_cast<char*>(input);
for (char *pos = prevPos; *pos != 0x00;) {
if (std::isdigit(*pos) || *pos == '.') {
auto number = std::strtold(pos, &pos);
if (*pos == 'x') {
pos--;
number = std::strtoull(pos, &pos, 0);
}
inputQueue.push(Token{ .type = TokenType::Number, .number = number });
} else if (*pos == '(') {
inputQueue.push(Token{ .type = TokenType::Bracket, .bracketType = BracketType::Left});
pos++;
} else if (*pos == ')') {
inputQueue.push(Token{ .type = TokenType::Bracket, .bracketType = BracketType::Right});
pos++;
} else if (std::isspace(*pos)) {
pos++;
} else {
auto [op, width] = toOperator(pos);
if (op != Operator::Invalid) {
inputQueue.push(Token{ .type = TokenType::Operator, .op = op });
pos += width;
} else {
Token token;
while (std::isalpha(*pos) || *pos == '_') {
token.name += *pos;
pos++;
}
if (*pos == '(') {
pos++;
u32 depth = 1;
std::vector<std::string> expressions;
expressions.emplace_back();
while (*pos != 0x00) {
if (*pos == '(') depth++;
else if (*pos == ')') depth--;
if (depth == 0)
break;
if (depth == 1 && *pos == ',') {
expressions.emplace_back();
pos++;
}
expressions.back() += *pos;
pos++;
}
pos++;
for (const auto &expression : expressions) {
if (expression == "" && expressions.size() > 1)
throw std::invalid_argument("Invalid function call syntax!");
else if (expression == "")
break;
auto inputQueue = parseInput(expression.c_str());
auto postfixTokens = toPostfix(inputQueue);
auto result = evaluate(postfixTokens);
if (!result.has_value())
throw std::invalid_argument("Invalid argument for function!");
token.arguments.push_back(result.value());
}
token.type = TokenType::Function;
inputQueue.push(token);
} else {
token.type = TokenType::Variable;
inputQueue.push(token);
}
}
}
if (prevPos == pos)
throw std::invalid_argument("Invalid syntax!");
prevPos = pos;
}
return inputQueue;
}
std::queue<Token> MathEvaluator::toPostfix(std::queue<Token> inputQueue) {
std::queue<Token> outputQueue;
std::stack<Token> operatorStack;
while (!inputQueue.empty()) {
Token currToken = inputQueue.front();
inputQueue.pop();
if (currToken.type == TokenType::Number || currToken.type == TokenType::Variable || currToken.type == TokenType::Function)
outputQueue.push(currToken);
else if (currToken.type == TokenType::Operator) {
while ((!operatorStack.empty())
&& (operatorStack.top().type == TokenType::Operator && currToken.type == TokenType::Operator && (comparePrecedence(operatorStack.top().op, currToken.op) > 0) || (comparePrecedence(operatorStack.top().op, currToken.op) == 0 && isLeftAssociative(currToken.op)))
&& operatorStack.top().type != TokenType::Bracket) {
outputQueue.push(operatorStack.top());
operatorStack.pop();
}
operatorStack.push(currToken);
} else if (currToken.type == TokenType::Bracket) {
if (currToken.bracketType == BracketType::Left)
operatorStack.push(currToken);
else {
if (operatorStack.empty())
throw std::invalid_argument("Mismatching parenthesis!");
while (operatorStack.top().type != TokenType::Bracket || (operatorStack.top().type == TokenType::Bracket && operatorStack.top().bracketType != BracketType::Left)) {
if (operatorStack.empty())
throw std::invalid_argument("Mismatching parenthesis!");
outputQueue.push(operatorStack.top());
operatorStack.pop();
}
operatorStack.pop();
}
}
}
while (!operatorStack.empty()) {
auto top = operatorStack.top();
if (top.type == TokenType::Bracket)
throw std::invalid_argument("Mismatching parenthesis!");
outputQueue.push(top);
operatorStack.pop();
}
return outputQueue;
}
std::optional<long double> MathEvaluator::evaluate(std::queue<Token> postfixTokens) {
std::stack<long double> evaluationStack;
while (!postfixTokens.empty()) {
auto front = postfixTokens.front();
postfixTokens.pop();
if (front.type == TokenType::Number)
evaluationStack.push(front.number);
else if (front.type == TokenType::Operator) {
long double rightOperand, leftOperand;
if (evaluationStack.size() < 2) {
if ((front.op == Operator::Addition || front.op == Operator::Subtraction || front.op == Operator::Not || front.op == Operator::BitwiseNot) && evaluationStack.size() == 1) {
rightOperand = evaluationStack.top(); evaluationStack.pop();
leftOperand = 0;
}
else throw std::invalid_argument("Not enough operands for operator!");
} else {
rightOperand = evaluationStack.top(); evaluationStack.pop();
leftOperand = evaluationStack.top(); evaluationStack.pop();
}
long double result = std::numeric_limits<long double>::quiet_NaN();
switch (front.op) {
default:
case Operator::Invalid:
throw std::invalid_argument("Invalid operator!");
case Operator::And:
result = static_cast<s64>(leftOperand) && static_cast<s64>(rightOperand);
break;
case Operator::Or:
result = static_cast<s64>(leftOperand) && static_cast<s64>(rightOperand);
break;
case Operator::Xor:
result = (static_cast<s64>(leftOperand) ^ static_cast<s64>(rightOperand)) > 0;
break;
case Operator::GreaterThan:
result = leftOperand > rightOperand;
break;
case Operator::LessThan:
result = leftOperand < rightOperand;
break;
case Operator::GreaterThanOrEquals:
result = leftOperand >= rightOperand;
break;
case Operator::LessThanOrEquals:
result = leftOperand <= rightOperand;
break;
case Operator::Equals:
result = leftOperand == rightOperand;
break;
case Operator::NotEquals:
result = leftOperand != rightOperand;
break;
case Operator::Not:
result = !static_cast<s64>(rightOperand);
break;
case Operator::BitwiseOr:
result = static_cast<s64>(leftOperand) | static_cast<s64>(rightOperand);
break;
case Operator::BitwiseXor:
result = static_cast<s64>(leftOperand) ^ static_cast<s64>(rightOperand);
break;
case Operator::BitwiseAnd:
result = static_cast<s64>(leftOperand) & static_cast<s64>(rightOperand);
break;
case Operator::BitwiseNot:
result = ~static_cast<s64>(rightOperand);
break;
case Operator::ShiftLeft:
result = static_cast<s64>(leftOperand) << static_cast<s64>(rightOperand);
break;
case Operator::ShiftRight:
result = static_cast<s64>(leftOperand) >> static_cast<s64>(rightOperand);
break;
case Operator::Addition:
result = leftOperand + rightOperand;
break;
case Operator::Subtraction:
result = leftOperand - rightOperand;
break;
case Operator::Multiplication:
result = leftOperand * rightOperand;
break;
case Operator::Division:
result = leftOperand / rightOperand;
break;
case Operator::Modulus:
result = std::fmod(leftOperand, rightOperand);
break;
case Operator::Exponentiation:
result = std::pow(leftOperand, rightOperand);
break;
case Operator::Combine:
result = (static_cast<u64>(leftOperand) << (64 - __builtin_clzll(static_cast<u64>(rightOperand)))) | static_cast<u64>(rightOperand);
break;
}
evaluationStack.push(result);
} else if (front.type == TokenType::Variable) {
if (this->m_variables.contains(front.name))
evaluationStack.push(this->m_variables.at(front.name));
else
throw std::invalid_argument("Unknown variable!");
} else if (front.type == TokenType::Function) {
if (!this->m_functions[front.name])
throw std::invalid_argument("Unknown function called!");
auto result = this->m_functions[front.name](front.arguments);
if (result.has_value())
evaluationStack.push(result.value());
} else
throw std::invalid_argument("Parenthesis in postfix expression!");
}
if (evaluationStack.empty())
return { };
else if (evaluationStack.size() > 1)
throw std::invalid_argument("Undigested input left!");
else
return evaluationStack.top();
}
std::optional<long double> MathEvaluator::evaluate(std::string input) {
auto inputQueue = parseInput(input.c_str());
std::string resultVariable = "ans";
{
std::queue<Token> queueCopy = inputQueue;
if (queueCopy.front().type == TokenType::Variable) {
resultVariable = queueCopy.front().name;
queueCopy.pop();
if (queueCopy.front().type != TokenType::Operator || queueCopy.front().op != Operator::Assign)
resultVariable = "ans";
else {
inputQueue.pop();
inputQueue.pop();
}
}
}
auto postfixTokens = toPostfix(inputQueue);
auto result = evaluate(postfixTokens);
if (result.has_value()) {
this->setVariable(resultVariable, result.value());
}
return result;
}
void MathEvaluator::setVariable(std::string name, long double value) {
this->m_variables[name] = value;
}
void MathEvaluator::setFunction(std::string name, std::function<std::optional<long double>(std::vector<long double>)> function, size_t minNumArgs, size_t maxNumArgs) {
this->m_functions[name] = [minNumArgs, maxNumArgs, function](auto args) {
if (args.size() < minNumArgs || args.size() > maxNumArgs)
throw std::invalid_argument("Invalid number of function arguments!");
return function(args);
};
}
void MathEvaluator::registerStandardVariables() {
this->setVariable("ans", 0);
}
void MathEvaluator::registerStandardFunctions() {
this->setFunction("sin", [](auto args){ return std::sin(args[0]); }, 1, 1);
this->setFunction("cos", [](auto args){ return std::cos(args[0]); }, 1, 1);
this->setFunction("tan", [](auto args){ return std::tan(args[0]); }, 1, 1);
this->setFunction("sqrt", [](auto args){ return std::sqrt(args[0]); }, 1, 1);
this->setFunction("ceil", [](auto args){ return std::ceil(args[0]); }, 1, 1);
this->setFunction("floor", [](auto args){ return std::floor(args[0]); }, 1, 1);
this->setFunction("sign", [](auto args){ return (args[0] > 0) ? 1 : (args[0] == 0) ? 0 : -1; }, 1, 1);
this->setFunction("abs", [](auto args){ return std::abs(args[0]); }, 1, 1);
this->setFunction("ln", [](auto args){ return std::log(args[0]); }, 1, 1);
this->setFunction("lb", [](auto args){ return std::log2(args[0]); }, 1, 1);
this->setFunction("log", [](auto args){ return args.size() == 1 ? std::log10(args[0]) : std::log(args[1]) / std::log(args[0]); }, 1, 2);
}
}

218
source/helpers/patches.cpp Normal file
View File

@@ -0,0 +1,218 @@
#include "helpers/patches.hpp"
#include <concepts>
#include <cstring>
#include <string_view>
#include <type_traits>
#include "helpers/utils.hpp"
namespace hex {
static void pushBytesBack(std::vector<u8> &buffer, const char* bytes) {
std::string_view string(bytes);
buffer.resize(buffer.size() + string.length());
std::memcpy((&buffer.back() - string.length()) + 1, string.begin(), string.length());
}
template<typename T>
static void pushBytesBack(std::vector<u8> &buffer, T bytes) {
buffer.resize(buffer.size() + sizeof(T));
std::memcpy((&buffer.back() - sizeof(T)) + 1, &bytes, sizeof(T));
}
std::vector<u8> generateIPSPatch(const Patches &patches) {
std::vector<u8> result;
pushBytesBack(result, "PATCH");
std::vector<u64> addresses;
std::vector<u8> values;
for (const auto &[address, value] : patches) {
addresses.push_back(address);
values.push_back(value);
}
std::optional<u64> startAddress;
std::vector<u8> bytes;
for (u32 i = 0; i < addresses.size(); i++) {
if (!startAddress.has_value())
startAddress = addresses[i];
if (i != addresses.size() - 1 && addresses[i] == (addresses[i + 1] - 1)) {
bytes.push_back(values[i]);
} else {
bytes.push_back(values[i]);
if (bytes.size() > 0xFFFF || startAddress > 0xFF'FFFF)
return { };
u32 address = startAddress.value();
auto addressBytes = reinterpret_cast<u8*>(&address);
result.push_back(addressBytes[2]); result.push_back(addressBytes[1]); result.push_back(addressBytes[0]);
pushBytesBack<u16>(result, changeEndianess<u16>(bytes.size(), std::endian::big));
for (auto byte : bytes)
result.push_back(byte);
bytes.clear();
startAddress = { };
}
}
pushBytesBack(result, "EOF");
return result;
}
std::vector<u8> generateIPS32Patch(const Patches &patches) {
std::vector<u8> result;
pushBytesBack(result, "IPS32");
std::vector<u64> addresses;
std::vector<u8> values;
for (const auto &[address, value] : patches) {
addresses.push_back(address);
values.push_back(value);
}
std::optional<u64> startAddress;
std::vector<u8> bytes;
for (u32 i = 0; i < addresses.size(); i++) {
if (!startAddress.has_value())
startAddress = addresses[i];
if (i != addresses.size() - 1 && addresses[i] == (addresses[i + 1] - 1)) {
bytes.push_back(values[i]);
} else {
bytes.push_back(values[i]);
if (bytes.size() > 0xFFFF || startAddress > 0xFFFF'FFFF)
return { };
u32 address = startAddress.value();
auto addressBytes = reinterpret_cast<u8*>(&address);
result.push_back(addressBytes[3]); result.push_back(addressBytes[2]); result.push_back(addressBytes[1]); result.push_back(addressBytes[0]);
pushBytesBack<u16>(result, changeEndianess<u16>(bytes.size(), std::endian::big));
for (auto byte : bytes)
result.push_back(byte);
bytes.clear();
startAddress = { };
}
}
pushBytesBack(result, "EEOF");
return result;
}
Patches loadIPSPatch(const std::vector<u8> &ipsPatch) {
if (ipsPatch.size() < (5 + 3))
return { };
if (std::memcmp(ipsPatch.data(), "PATCH", 5) != 0)
return { };
Patches result;
bool foundEOF = false;
u32 ipsOffset = 5;
while (ipsOffset < ipsPatch.size() - (5 + 3)) {
u32 offset = ipsPatch[ipsOffset + 2] | (ipsPatch[ipsOffset + 1] << 8) | (ipsPatch[ipsOffset + 0] << 16);
u16 size = ipsPatch[ipsOffset + 4] | (ipsPatch[ipsOffset + 3] << 8);
ipsOffset += 5;
// Handle normal record
if (size > 0x0000) {
if (ipsOffset + size > ipsPatch.size() - 3)
return { };
for (u16 i = 0; i < size; i++)
result[offset + i] = ipsPatch[ipsOffset + i];
ipsOffset += size;
}
// Handle RLE record
else {
if (ipsOffset + 3 > ipsPatch.size() - 3)
return { };
u16 rleSize = ipsPatch[ipsOffset + 0] | (ipsPatch[ipsOffset + 1] << 8);
ipsOffset += 2;
for (u16 i = 0; i < rleSize; i++)
result[offset + i] = ipsPatch[ipsOffset + 0];
ipsOffset += 1;
}
if (std::memcmp(ipsPatch.data(), "EOF", 3))
foundEOF = true;
}
if (foundEOF)
return result;
else
return { };
}
Patches loadIPS32Patch(const std::vector<u8> &ipsPatch) {
if (ipsPatch.size() < (5 + 4))
return { };
if (std::memcmp(ipsPatch.data(), "IPS32", 5) != 0)
return { };
Patches result;
bool foundEEOF = false;
u32 ipsOffset = 5;
while (ipsOffset < ipsPatch.size() - (5 + 4)) {
u32 offset = ipsPatch[ipsOffset + 3] | (ipsPatch[ipsOffset + 2] << 8) | (ipsPatch[ipsOffset + 1] << 16) | (ipsPatch[ipsOffset + 0] << 24);
u16 size = ipsPatch[ipsOffset + 5] | (ipsPatch[ipsOffset + 4] << 8);
ipsOffset += 6;
// Handle normal record
if (size > 0x0000) {
if (ipsOffset + size > ipsPatch.size() - 3)
return { };
for (u16 i = 0; i < size; i++)
result[offset + i] = ipsPatch[ipsOffset + i];
ipsOffset += size;
}
// Handle RLE record
else {
if (ipsOffset + 3 > ipsPatch.size() - 3)
return { };
u16 rleSize = ipsPatch[ipsOffset + 0] | (ipsPatch[ipsOffset + 1] << 8);
ipsOffset += 2;
for (u16 i = 0; i < rleSize; i++)
result[offset + i] = ipsPatch[ipsOffset + 0];
ipsOffset += 1;
}
if (std::memcmp(ipsPatch.data(), "EEOF", 4))
foundEEOF = true;
}
if (foundEEOF)
return result;
else
return { };
}
}

View File

@@ -0,0 +1,85 @@
#include "helpers/project_file_handler.hpp"
#include <fstream>
#include <nlohmann/json.hpp>
using json = nlohmann::json;
namespace hex {
void to_json(json& j, const hex::Bookmark& b) {
j = json{ { "address", b.region.address }, { "size", b.region.size }, { "name", b.name.data() }, { "comment", b.comment.data() } };
}
void from_json(const json& j, hex::Bookmark& b) {
std::string name, comment;
j.at("address").get_to(b.region.address);
j.at("size").get_to(b.region.size);
j.at("name").get_to(name);
j.at("comment").get_to(comment);
std::copy(name.begin(), name.end(), std::back_inserter(b.name));
std::copy(comment.begin(), comment.end(), std::back_inserter(b.comment));
}
bool ProjectFile::load(std::string_view filePath) {
ProjectFile::s_hasUnsavedChanged = false;
json projectFileData;
try {
std::ifstream projectFile(filePath.data());
projectFile >> projectFileData;
ProjectFile::s_filePath = projectFileData["filePath"];
ProjectFile::s_pattern = projectFileData["pattern"];
ProjectFile::s_patches = projectFileData["patches"].get<Patches>();
for (auto &element : projectFileData["bookmarks"].items()) {
ProjectFile::s_bookmarks.push_back(element.value().get<Bookmark>());
}
} catch (json::exception &e) {
return false;
} catch (std::ofstream::failure &e) {
return false;
}
ProjectFile::s_currProjectFilePath = filePath;
return true;
}
bool ProjectFile::store(std::string_view filePath) {
ProjectFile::s_hasUnsavedChanged = false;
json projectFileData;
if (filePath.empty())
filePath = ProjectFile::s_currProjectFilePath;
try {
projectFileData["filePath"] = ProjectFile::s_filePath;
projectFileData["pattern"] = ProjectFile::s_pattern;
projectFileData["patches"] = ProjectFile::s_patches;
for (auto &bookmark : ProjectFile::s_bookmarks) {
projectFileData["bookmarks"].push_back(bookmark);
}
std::ofstream projectFile(filePath.data(), std::fstream::trunc);
projectFile << projectFileData;
} catch (json::exception &e) {
return false;
} catch (std::ifstream::failure &e) {
return false;
}
ProjectFile::s_currProjectFilePath = filePath;
return true;
}
}

93
source/helpers/utils.cpp Normal file
View File

@@ -0,0 +1,93 @@
#include "helpers/utils.hpp"
#include <cstdio>
#include <codecvt>
#include <locale>
namespace hex {
std::string toByteString(u64 bytes) {
double value = bytes;
u8 unitIndex = 0;
while (value > 1024) {
value /= 1024;
unitIndex++;
if (unitIndex == 6)
break;
}
std::string result = hex::format("%.2f", value);
switch (unitIndex) {
case 0: result += " Bytes"; break;
case 1: result += " kB"; break;
case 2: result += " MB"; break;
case 3: result += " GB"; break;
case 4: result += " TB"; break;
case 5: result += " PB"; break;
case 6: result += " EB"; break;
default: result = "A lot!";
}
return result;
}
std::string makePrintable(char c) {
switch (c) {
case 0: return "NUL";
case 1: return "SOH";
case 2: return "STX";
case 3: return "ETX";
case 4: return "EOT";
case 5: return "ENQ";
case 6: return "ACK";
case 7: return "BEL";
case 8: return "BS";
case 9: return "TAB";
case 10: return "LF";
case 11: return "VT";
case 12: return "FF";
case 13: return "CR";
case 14: return "SO";
case 15: return "SI";
case 16: return "DLE";
case 17: return "DC1";
case 18: return "DC2";
case 19: return "DC3";
case 20: return "DC4";
case 21: return "NAK";
case 22: return "SYN";
case 23: return "ETB";
case 24: return "CAN";
case 25: return "EM";
case 26: return "SUB";
case 27: return "ESC";
case 28: return "FS";
case 29: return "GS";
case 30: return "RS";
case 31: return "US";
case 32: return "Space";
case 127: return "DEL";
default: return std::string() + c;
}
}
std::vector<u8> readFile(std::string_view path) {
FILE *file = fopen(path.data(), "rb");
if (file == nullptr) return { };
std::vector<u8> result;
fseek(file, 0, SEEK_END);
result.resize(ftell(file));
rewind(file);
fread(result.data(), 1, result.size(), file);
return result;
}
}

View File

@@ -5,7 +5,7 @@
namespace hex::lang {
Evaluator::Evaluator() {
Evaluator::Evaluator(prv::Provider* &provider, std::endian defaultDataEndianess) : m_provider(provider), m_defaultDataEndianess(defaultDataEndianess) {
}
@@ -14,65 +14,88 @@ namespace hex::lang {
auto structNode = static_cast<ASTNodeStruct*>(this->m_types[varDeclNode->getCustomVariableTypeName()]);
if (structNode == nullptr)
if (structNode == nullptr) {
this->m_error = { varDeclNode->getLineNumber(), hex::format("'%s' does not name a type", varDeclNode->getCustomVariableTypeName().c_str()) };
return { nullptr, 0 };
}
size_t structSize = 0;
for (const auto &node : structNode->getNodes()) {
const auto &member = static_cast<ASTNodeVariableDecl*>(node);
u64 memberOffset = 0;
if (member->getPointerSize().has_value()) {
this->m_provider->read(offset + structSize, &memberOffset, member->getPointerSize().value());
memberOffset = hex::changeEndianess(memberOffset, member->getPointerSize().value(), member->getEndianess().value_or(varDeclNode->getEndianess().value_or(this->m_defaultDataEndianess)));
}
else
memberOffset = offset + structSize;
const auto typeDeclNode = static_cast<ASTNodeTypeDecl*>(this->m_types[member->getCustomVariableTypeName()]);
PatternData *pattern = nullptr;
u64 memberSize = 0;
if (member->getVariableType() == Token::TypeToken::Type::Signed8Bit && member->getArraySize() > 1) {
const auto &[pattern, size] = this->createStringPattern(member, offset + structSize);
if (pattern == nullptr)
return { nullptr, 0 };
members.push_back(pattern);
structSize += size;
std::tie(pattern, memberSize) = this->createStringPattern(member, memberOffset);
} else if (member->getVariableType() == Token::TypeToken::Type::CustomType
&& typeDeclNode != nullptr && typeDeclNode->getAssignedType() == Token::TypeToken::Type::Signed8Bit
&& member->getArraySize() > 1) {
const auto &[pattern, size] = this->createStringPattern(member, offset + structSize);
if (pattern == nullptr)
return { nullptr, 0 };
members.push_back(pattern);
structSize += size;
std::tie(pattern, memberSize) = this->createStringPattern(member, memberOffset);
}
else if (member->getArraySize() > 1) {
const auto &[pattern, size] = this->createArrayPattern(member, offset + structSize);
std::tie(pattern, memberSize) = this->createArrayPattern(member, memberOffset);
}
else if (member->getArraySizeVariable().has_value()) {
std::optional<size_t> arraySize;
if (pattern == nullptr)
for (auto &prevMember : members) {
if (prevMember->getPatternType() == PatternData::Type::Unsigned && prevMember->getName() == member->getArraySizeVariable()) {
u64 value = 0;
this->m_provider->read(prevMember->getOffset(), &value, prevMember->getSize());
value = hex::changeEndianess(value, prevMember->getSize(), prevMember->getEndianess());
arraySize = value;
}
}
if (!arraySize.has_value()) {
this->m_error = { varDeclNode->getLineNumber(), hex::format("'%s' does not name a previous member of '%s'", member->getArraySizeVariable().value().c_str(), varDeclNode->getCustomVariableTypeName().c_str()) };
return { nullptr, 0 };
}
members.push_back(pattern);
structSize += size;
ASTNodeVariableDecl *processedMember = new ASTNodeVariableDecl(member->getLineNumber(), member->getVariableType(), member->getVariableName(), member->getCustomVariableTypeName(), member->getOffset(), arraySize.value());
std::tie(pattern, memberSize) = this->createArrayPattern(processedMember, memberOffset);
}
else if (member->getVariableType() != Token::TypeToken::Type::CustomType) {
const auto &[pattern, size] = this->createBuiltInTypePattern(member, offset + structSize);
if (pattern == nullptr)
return { nullptr, 0 };
members.push_back(pattern);
structSize += size;
std::tie(pattern, memberSize) = this->createBuiltInTypePattern(member, memberOffset);
}
else {
const auto &[pattern, size] = this->createCustomTypePattern(member, offset + structSize);
std::tie(pattern, memberSize) = this->createCustomTypePattern(member, memberOffset);
}
if (pattern == nullptr)
return { nullptr, 0 };
if (pattern == nullptr)
return { nullptr, 0 };
pattern->setEndianess(member->getEndianess().value_or(varDeclNode->getEndianess().value_or(this->m_defaultDataEndianess)));
if (member->getPointerSize().has_value()) {
members.push_back(new PatternDataPointer(offset + structSize, member->getPointerSize().value(), member->getVariableName(), pattern, member->getEndianess().value_or(varDeclNode->getEndianess().value_or(this->m_defaultDataEndianess))));
structSize += member->getPointerSize().value();
}
else {
members.push_back(pattern);
structSize += size;
structSize += memberSize;
}
}
return { new PatternDataStruct(offset, structSize, varDeclNode->getVariableName(), structNode->getName(), members, 0x00FFFFFF), structSize };
return { new PatternDataStruct(offset, structSize, varDeclNode->getVariableName(), varDeclNode->getEndianess().value_or(this->m_defaultDataEndianess), structNode->getName(), members, 0x00FFFFFF), structSize };
}
std::pair<PatternData*, size_t> Evaluator::createUnionPattern(ASTNodeVariableDecl *varDeclNode, u64 offset) {
@@ -80,86 +103,119 @@ namespace hex::lang {
auto unionNode = static_cast<ASTNodeUnion*>(this->m_types[varDeclNode->getCustomVariableTypeName()]);
if (unionNode == nullptr)
if (unionNode == nullptr) {
this->m_error = { varDeclNode->getLineNumber(), hex::format("'%s' does not name a type", varDeclNode->getCustomVariableTypeName().c_str()) };
return { nullptr, 0 };
}
size_t unionSize = 0;
for (const auto &node : unionNode->getNodes()) {
const auto &member = static_cast<ASTNodeVariableDecl*>(node);
u64 memberOffset = 0;
if (member->getPointerSize().has_value()) {
this->m_provider->read(offset + unionSize, &memberOffset, member->getPointerSize().value());
memberOffset = hex::changeEndianess(memberOffset, member->getPointerSize().value(), member->getEndianess().value_or(this->m_defaultDataEndianess));
}
else
memberOffset = offset;
const auto typeDeclNode = static_cast<ASTNodeTypeDecl*>(this->m_types[member->getCustomVariableTypeName()]);
PatternData *pattern = nullptr;
u64 memberSize = 0;
if (member->getVariableType() == Token::TypeToken::Type::Signed8Bit && member->getArraySize() > 1) {
const auto &[pattern, size] = this->createStringPattern(member, offset);
std::tie(pattern, memberSize) = this->createStringPattern(member, memberOffset);
if (pattern == nullptr)
return { nullptr, 0 };
members.push_back(pattern);
unionSize = std::max(size, unionSize);
} else if (member->getVariableType() == Token::TypeToken::Type::CustomType
&& typeDeclNode != nullptr && typeDeclNode->getAssignedType() == Token::TypeToken::Type::Signed8Bit
&& member->getArraySize() > 1) {
const auto &[pattern, size] = this->createStringPattern(member, offset);
std::tie(pattern, memberSize) = this->createStringPattern(member, memberOffset);
if (pattern == nullptr)
return { nullptr, 0 };
members.push_back(pattern);
unionSize = std::max(size, unionSize);
}
else if (member->getArraySize() > 1) {
const auto &[pattern, size] = this->createArrayPattern(member, offset);
std::tie(pattern, memberSize) = this->createArrayPattern(member, memberOffset);
if (pattern == nullptr)
}
else if (member->getArraySizeVariable().has_value()) {
std::optional<size_t> arraySize;
for (auto &prevMember : members) {
if (prevMember->getPatternType() == PatternData::Type::Unsigned && prevMember->getName() == member->getArraySizeVariable()) {
u64 value = 0;
this->m_provider->read(prevMember->getOffset(), &value, prevMember->getSize());
value = hex::changeEndianess(value, prevMember->getSize(), prevMember->getEndianess());
arraySize = value;
}
}
if (!arraySize.has_value()) {
this->m_error = { varDeclNode->getLineNumber(), hex::format("'%s' does not name a previous member of '%s'", member->getArraySizeVariable().value().c_str(), varDeclNode->getCustomVariableTypeName().c_str()) };
return { nullptr, 0 };
}
members.push_back(pattern);
unionSize = std::max(size, unionSize);
if (arraySize.value() == 0) {
this->m_error = { varDeclNode->getLineNumber(), hex::format("Value of '%s' is zero", member->getArraySizeVariable().value().c_str()) };
return { nullptr, 0 };
}
ASTNodeVariableDecl *processedMember = new ASTNodeVariableDecl(member->getLineNumber(), member->getVariableType(), member->getVariableName(), member->getCustomVariableTypeName(), member->getOffset(), arraySize.value());
std::tie(pattern, memberSize) = this->createArrayPattern(processedMember, memberOffset);
}
else if (member->getVariableType() != Token::TypeToken::Type::CustomType) {
const auto &[pattern, size] = this->createBuiltInTypePattern(member, offset);
if (pattern == nullptr)
return { nullptr, 0 };
members.push_back(pattern);
unionSize = std::max(size, unionSize);
std::tie(pattern, memberSize) = this->createBuiltInTypePattern(member, memberOffset);
}
else {
const auto &[pattern, size] = this->createCustomTypePattern(member, offset);
std::tie(pattern, memberSize) = this->createCustomTypePattern(member, memberOffset);
}
if (pattern == nullptr)
return { nullptr, 0 };
if (pattern == nullptr)
return { nullptr, 0 };
pattern->setEndianess(member->getEndianess().value_or(varDeclNode->getEndianess().value_or(this->m_defaultDataEndianess)));
if (member->getPointerSize().has_value()) {
members.push_back(new PatternDataPointer(offset, member->getPointerSize().value(), member->getVariableName(), pattern, member->getEndianess().value_or(this->m_defaultDataEndianess)));
unionSize = std::max(size_t(member->getPointerSize().value()), unionSize);
}
else {
members.push_back(pattern);
unionSize = std::max(size, unionSize);
unionSize = std::max(memberSize, unionSize);
}
}
return { new PatternDataUnion(offset, unionSize, varDeclNode->getVariableName(), unionNode->getName(), members, 0x00FFFFFF), unionSize };
return { new PatternDataUnion(offset, unionSize, varDeclNode->getVariableName(), unionNode->getName(), members, varDeclNode->getEndianess().value_or(this->m_defaultDataEndianess), 0x00FFFFFF), unionSize };
}
std::pair<PatternData*, size_t> Evaluator::createEnumPattern(ASTNodeVariableDecl *varDeclNode, u64 offset) {
std::vector<std::pair<u64, std::string>> enumValues;
auto *enumType = static_cast<ASTNodeEnum*>(this->m_types[varDeclNode->getCustomVariableTypeName()]);
if (enumType == nullptr)
if (enumType == nullptr) {
this->m_error = { varDeclNode->getLineNumber(), hex::format("'%s' does not name a type", varDeclNode->getCustomVariableTypeName().c_str()) };
return { nullptr, 0 };
}
size_t size = getTypeSize(enumType->getUnderlyingType());
return { new PatternDataEnum(offset, size, varDeclNode->getVariableName(), enumType->getName(), enumType->getValues()), size };
return { new PatternDataEnum(offset, size, varDeclNode->getVariableName(), enumType->getName(), enumType->getValues(), varDeclNode->getEndianess().value_or(this->m_defaultDataEndianess)), size };
}
std::pair<PatternData*, size_t> Evaluator::createBitfieldPattern(ASTNodeVariableDecl *varDeclNode, u64 offset) {
auto *bitfieldType = static_cast<ASTNodeBitField*>(this->m_types[varDeclNode->getCustomVariableTypeName()]);
if (bitfieldType == nullptr)
if (bitfieldType == nullptr) {
this->m_error = { varDeclNode->getLineNumber(), hex::format("'%s' does not name a type", varDeclNode->getCustomVariableTypeName().c_str()) };
return { nullptr, 0 };
}
size_t size = 0;
for (auto &[fieldName, fieldSize] : bitfieldType->getFields())
@@ -167,31 +223,51 @@ namespace hex::lang {
size = std::bit_ceil(size) / 8;
return { new PatternDataBitfield(offset, size, varDeclNode->getVariableName(), bitfieldType->getName(), bitfieldType->getFields()), size };
return { new PatternDataBitfield(offset, size, varDeclNode->getVariableName(), bitfieldType->getName(), bitfieldType->getFields(), varDeclNode->getEndianess().value_or(this->m_defaultDataEndianess)), size };
}
std::pair<PatternData*, size_t> Evaluator::createArrayPattern(ASTNodeVariableDecl *varDeclNode, u64 offset) {
std::vector<PatternData*> entries;
size_t arrayOffset = 0;
std::optional<u32> arrayColor;
for (u32 i = 0; i < varDeclNode->getArraySize(); i++) {
ASTNodeVariableDecl *nonArrayVarDeclNode = new ASTNodeVariableDecl(varDeclNode->getVariableType(), "[" + std::to_string(i) + "]", varDeclNode->getCustomVariableTypeName(), varDeclNode->getOffset(), 1);
ASTNodeVariableDecl *nonArrayVarDeclNode = new ASTNodeVariableDecl(varDeclNode->getLineNumber(), varDeclNode->getVariableType(), "[" + std::to_string(i) + "]", varDeclNode->getCustomVariableTypeName(), varDeclNode->getOffset(), 1);
if (varDeclNode->getVariableType() == Token::TypeToken::Type::Padding) {
return { new PatternDataPadding(offset, varDeclNode->getArraySize()), varDeclNode->getArraySize() };
} else if (varDeclNode->getVariableType() != Token::TypeToken::Type::CustomType) {
const auto &[pattern, size] = this->createBuiltInTypePattern(nonArrayVarDeclNode, offset + arrayOffset);
const auto& [pattern, size] = this->createBuiltInTypePattern(nonArrayVarDeclNode, offset + arrayOffset);
if (pattern == nullptr)
if (pattern == nullptr) {
delete nonArrayVarDeclNode;
return { nullptr, 0 };
}
pattern->setEndianess(varDeclNode->getEndianess().value_or(varDeclNode->getEndianess().value_or(this->m_defaultDataEndianess)));
if (!arrayColor.has_value())
arrayColor = pattern->getColor();
pattern->setColor(arrayColor.value());
entries.push_back(pattern);
arrayOffset += size;
} else {
const auto &[pattern, size] = this->createCustomTypePattern(nonArrayVarDeclNode, offset + arrayOffset);
if (pattern == nullptr)
if (pattern == nullptr) {
delete nonArrayVarDeclNode;
return { nullptr, 0 };
}
pattern->setEndianess(varDeclNode->getEndianess().value_or(varDeclNode->getEndianess().value_or(this->m_defaultDataEndianess)));
if (!arrayColor.has_value())
arrayColor = pattern->getColor();
pattern->setColor(arrayColor.value());
entries.push_back(pattern);
arrayOffset += size;
@@ -200,20 +276,22 @@ namespace hex::lang {
delete nonArrayVarDeclNode;
}
return { new PatternDataArray(offset, arrayOffset, varDeclNode->getVariableName(), entries, 0x00FFFFFF), arrayOffset };
return { new PatternDataArray(offset, arrayOffset, varDeclNode->getVariableName(), varDeclNode->getEndianess().value_or(this->m_defaultDataEndianess), entries, arrayColor.value()), arrayOffset };
}
std::pair<PatternData*, size_t> Evaluator::createStringPattern(ASTNodeVariableDecl *varDeclNode, u64 offset) {
size_t arraySize = varDeclNode->getArraySize();
return { new PatternDataString(offset, arraySize, varDeclNode->getVariableName()), arraySize };
return { new PatternDataString(offset, arraySize, varDeclNode->getVariableName(), varDeclNode->getEndianess().value_or(this->m_defaultDataEndianess)), arraySize };
}
std::pair<PatternData*, size_t> Evaluator::createCustomTypePattern(ASTNodeVariableDecl *varDeclNode, u64 offset) {
auto &currType = this->m_types[varDeclNode->getCustomVariableTypeName()];
if (currType == nullptr)
if (currType == nullptr) {
this->m_error = { varDeclNode->getLineNumber(), hex::format("'%s' does not name a type", varDeclNode->getCustomVariableTypeName().c_str()) };
return { nullptr, 0 };
}
switch (currType->getType()) {
case ASTNode::Type::Struct:
@@ -235,8 +313,10 @@ namespace hex::lang {
auto type = varDeclNode->getVariableType();
if (type == Token::TypeToken::Type::CustomType) {
const auto &currType = static_cast<ASTNodeTypeDecl*>(this->m_types[varDeclNode->getCustomVariableTypeName()]);
if (currType == nullptr)
if (currType == nullptr) {
this->m_error = { varDeclNode->getLineNumber(), hex::format("'%s' does not name a type", varDeclNode->getCustomVariableTypeName().c_str()) };
return { nullptr, 0 };
}
type = currType->getAssignedType();
}
@@ -246,21 +326,21 @@ namespace hex::lang {
if (isSigned(type)) {
if (typeSize == 1 && arraySize == 1)
return { new PatternDataCharacter(offset, typeSize, varDeclNode->getVariableName()), 1 };
return { new PatternDataCharacter(offset, typeSize, varDeclNode->getVariableName(), varDeclNode->getEndianess().value_or(this->m_defaultDataEndianess)), 1 };
else if (arraySize > 1)
return createArrayPattern(varDeclNode, offset);
else
return { new PatternDataSigned(offset, typeSize, varDeclNode->getVariableName()), typeSize * arraySize };
return { new PatternDataSigned(offset, typeSize, varDeclNode->getVariableName(), varDeclNode->getEndianess().value_or(this->m_defaultDataEndianess)), typeSize * arraySize };
} else if (isUnsigned(varDeclNode->getVariableType())) {
if (arraySize > 1)
return createArrayPattern(varDeclNode, offset);
else
return { new PatternDataUnsigned(offset, typeSize, varDeclNode->getVariableName()), typeSize * arraySize };
return { new PatternDataUnsigned(offset, typeSize, varDeclNode->getVariableName(), varDeclNode->getEndianess().value_or(this->m_defaultDataEndianess)), typeSize * arraySize };
} else if (isFloatingPoint(varDeclNode->getVariableType())) {
if (arraySize > 1)
return createArrayPattern(varDeclNode, offset);
else
return { new PatternDataFloat(offset, typeSize, varDeclNode->getVariableName()), typeSize * arraySize };
return { new PatternDataFloat(offset, typeSize, varDeclNode->getVariableName(), varDeclNode->getEndianess().value_or(this->m_defaultDataEndianess)), typeSize * arraySize };
}
return { nullptr, 0 };

View File

@@ -69,6 +69,8 @@ namespace hex::lang {
std::vector<Token> tokens;
u32 offset = 0;
u32 lineNumber = 1;
while (offset < code.length()) {
// Handle comments
@@ -85,6 +87,8 @@ namespace hex::lang {
} else if (offset < code.length() && code[offset] == '*') {
offset++;
while (offset < (code.length() - 1)) {
if (code[offset] == '\n') lineNumber++;
if (code[offset] == '*' && code[offset + 1] == '/')
break;
offset++;
@@ -100,99 +104,153 @@ namespace hex::lang {
break;
if (std::isblank(c) || std::isspace(c)) {
if (code[offset] == '\n') lineNumber++;
offset += 1;
} else if (c == ';') {
tokens.push_back({ .type = Token::Type::EndOfExpression });
tokens.push_back({ .type = Token::Type::EndOfExpression, .lineNumber = lineNumber });
offset += 1;
} else if (c == '{') {
tokens.push_back({ .type = Token::Type::ScopeOpen });
tokens.push_back({ .type = Token::Type::ScopeOpen, .lineNumber = lineNumber });
offset += 1;
} else if (c == '}') {
tokens.push_back({ .type = Token::Type::ScopeClose });
tokens.push_back({ .type = Token::Type::ScopeClose, .lineNumber = lineNumber });
offset += 1;
} else if (c == '[') {
tokens.push_back({ .type = Token::Type::ArrayOpen });
tokens.push_back({ .type = Token::Type::ArrayOpen, .lineNumber = lineNumber });
offset += 1;
} else if (c == ']') {
tokens.push_back({.type = Token::Type::ArrayClose});
tokens.push_back({.type = Token::Type::ArrayClose, .lineNumber = lineNumber });
offset += 1;
} else if (c == ',') {
tokens.push_back({ .type = Token::Type::Separator });
tokens.push_back({ .type = Token::Type::Separator, .lineNumber = lineNumber });
offset += 1;
} else if (c == '@') {
tokens.push_back({ .type = Token::Type::Operator, .operatorToken = { .op = Token::OperatorToken::Operator::AtDeclaration } });
tokens.push_back({ .type = Token::Type::Operator, .operatorToken = { .op = Token::OperatorToken::Operator::AtDeclaration }, .lineNumber = lineNumber });
offset += 1;
} else if (c == '=') {
tokens.push_back({ .type = Token::Type::Operator, .operatorToken = { .op = Token::OperatorToken::Operator::Assignment } });
tokens.push_back({ .type = Token::Type::Operator, .operatorToken = { .op = Token::OperatorToken::Operator::Assignment }, .lineNumber = lineNumber });
offset += 1;
} else if (c == ':') {
tokens.push_back({ .type = Token::Type::Operator, .operatorToken = { .op = Token::OperatorToken::Operator::Inherit } });
tokens.push_back({ .type = Token::Type::Operator, .operatorToken = { .op = Token::OperatorToken::Operator::Inherit }, .lineNumber = lineNumber });
offset += 1;
} else if (c == '*') {
tokens.push_back({ .type = Token::Type::Operator, .operatorToken = { .op = Token::OperatorToken::Operator::Star }, .lineNumber = lineNumber });
offset += 1;
} else if (c == '\'') {
offset += 1;
if (offset >= code.length()) {
this->m_error = { lineNumber, "Invalid character literal" };
return { ResultLexicalError, { } };
}
char character = code[offset];
if (character == '\\') {
offset += 1;
if (offset >= code.length()) {
this->m_error = { lineNumber, "Invalid character literal" };
return { ResultLexicalError, { } };
}
if (code[offset] != '\\' && code[offset] != '\'') {
this->m_error = { lineNumber, "Invalid escape sequence" };
return { ResultLexicalError, { } };
}
character = code[offset];
} else {
if (code[offset] == '\\' || code[offset] == '\'' || character == '\n' || character == '\r') {
this->m_error = { lineNumber, "Invalid character literal" };
return { ResultLexicalError, { } };
}
}
offset += 1;
if (offset >= code.length() || code[offset] != '\'') {
this->m_error = { lineNumber, "Missing terminating ' after character literal" };
return { ResultLexicalError, { } };
}
tokens.push_back({ .type = Token::Type::Integer, .integerToken = { .integer = character }, .lineNumber = lineNumber });
offset += 1;
} else if (std::isalpha(c)) {
std::string identifier = matchTillInvalid(&code[offset], [](char c) -> bool { return std::isalnum(c) || c == '_'; });
// Check for reserved keywords
if (identifier == "struct")
tokens.push_back({ .type = Token::Type::Keyword, .keywordToken = { .keyword = Token::KeywordToken::Keyword::Struct } });
tokens.push_back({ .type = Token::Type::Keyword, .keywordToken = { .keyword = Token::KeywordToken::Keyword::Struct }, .lineNumber = lineNumber });
else if (identifier == "union")
tokens.push_back({ .type = Token::Type::Keyword, .keywordToken = { .keyword = Token::KeywordToken::Keyword::Union } });
tokens.push_back({ .type = Token::Type::Keyword, .keywordToken = { .keyword = Token::KeywordToken::Keyword::Union }, .lineNumber = lineNumber });
else if (identifier == "using")
tokens.push_back({ .type = Token::Type::Keyword, .keywordToken = { .keyword = Token::KeywordToken::Keyword::Using } });
tokens.push_back({ .type = Token::Type::Keyword, .keywordToken = { .keyword = Token::KeywordToken::Keyword::Using }, .lineNumber = lineNumber });
else if (identifier == "enum")
tokens.push_back({ .type = Token::Type::Keyword, .keywordToken = { .keyword = Token::KeywordToken::Keyword::Enum } });
tokens.push_back({ .type = Token::Type::Keyword, .keywordToken = { .keyword = Token::KeywordToken::Keyword::Enum }, .lineNumber = lineNumber });
else if (identifier == "bitfield")
tokens.push_back({ .type = Token::Type::Keyword, .keywordToken = { .keyword = Token::KeywordToken::Keyword::Bitfield } });
tokens.push_back({ .type = Token::Type::Keyword, .keywordToken = { .keyword = Token::KeywordToken::Keyword::Bitfield }, .lineNumber = lineNumber });
else if (identifier == "be")
tokens.push_back({ .type = Token::Type::Keyword, .keywordToken = { .keyword = Token::KeywordToken::Keyword::BigEndian }, .lineNumber = lineNumber });
else if (identifier == "le")
tokens.push_back({ .type = Token::Type::Keyword, .keywordToken = { .keyword = Token::KeywordToken::Keyword::LittleEndian }, .lineNumber = lineNumber });
// Check for built-in types
// Check for built-in types
else if (identifier == "u8")
tokens.push_back({ .type = Token::Type::Type, .typeToken = { .type = Token::TypeToken::Type::Unsigned8Bit } });
tokens.push_back({ .type = Token::Type::Type, .typeToken = { .type = Token::TypeToken::Type::Unsigned8Bit }, .lineNumber = lineNumber });
else if (identifier == "s8")
tokens.push_back({ .type = Token::Type::Type, .typeToken = { .type = Token::TypeToken::Type::Signed8Bit } });
tokens.push_back({ .type = Token::Type::Type, .typeToken = { .type = Token::TypeToken::Type::Signed8Bit }, .lineNumber = lineNumber });
else if (identifier == "u16")
tokens.push_back({ .type = Token::Type::Type, .typeToken = { .type = Token::TypeToken::Type::Unsigned16Bit } });
tokens.push_back({ .type = Token::Type::Type, .typeToken = { .type = Token::TypeToken::Type::Unsigned16Bit }, .lineNumber = lineNumber });
else if (identifier == "s16")
tokens.push_back({ .type = Token::Type::Type, .typeToken = { .type = Token::TypeToken::Type::Signed16Bit } });
tokens.push_back({ .type = Token::Type::Type, .typeToken = { .type = Token::TypeToken::Type::Signed16Bit }, .lineNumber = lineNumber });
else if (identifier == "u32")
tokens.push_back({ .type = Token::Type::Type, .typeToken = { .type = Token::TypeToken::Type::Unsigned32Bit } });
tokens.push_back({ .type = Token::Type::Type, .typeToken = { .type = Token::TypeToken::Type::Unsigned32Bit }, .lineNumber = lineNumber });
else if (identifier == "s32")
tokens.push_back({ .type = Token::Type::Type, .typeToken = { .type = Token::TypeToken::Type::Signed32Bit } });
tokens.push_back({ .type = Token::Type::Type, .typeToken = { .type = Token::TypeToken::Type::Signed32Bit }, .lineNumber = lineNumber });
else if (identifier == "u64")
tokens.push_back({ .type = Token::Type::Type, .typeToken = { .type = Token::TypeToken::Type::Unsigned64Bit } });
tokens.push_back({ .type = Token::Type::Type, .typeToken = { .type = Token::TypeToken::Type::Unsigned64Bit }, .lineNumber = lineNumber });
else if (identifier == "s64")
tokens.push_back({ .type = Token::Type::Type, .typeToken = { .type = Token::TypeToken::Type::Signed64Bit } });
tokens.push_back({ .type = Token::Type::Type, .typeToken = { .type = Token::TypeToken::Type::Signed64Bit }, .lineNumber = lineNumber });
else if (identifier == "u128")
tokens.push_back({ .type = Token::Type::Type, .typeToken = { .type = Token::TypeToken::Type::Unsigned128Bit } });
tokens.push_back({ .type = Token::Type::Type, .typeToken = { .type = Token::TypeToken::Type::Unsigned128Bit }, .lineNumber = lineNumber });
else if (identifier == "s128")
tokens.push_back({ .type = Token::Type::Type, .typeToken = { .type = Token::TypeToken::Type::Signed128Bit } });
tokens.push_back({ .type = Token::Type::Type, .typeToken = { .type = Token::TypeToken::Type::Signed128Bit }, .lineNumber = lineNumber });
else if (identifier == "float")
tokens.push_back({ .type = Token::Type::Type, .typeToken = { .type = Token::TypeToken::Type::Float } });
tokens.push_back({ .type = Token::Type::Type, .typeToken = { .type = Token::TypeToken::Type::Float }, .lineNumber = lineNumber });
else if (identifier == "double")
tokens.push_back({ .type = Token::Type::Type, .typeToken = { .type = Token::TypeToken::Type::Double } });
tokens.push_back({ .type = Token::Type::Type, .typeToken = { .type = Token::TypeToken::Type::Double }, .lineNumber = lineNumber });
else if (identifier == "padding")
tokens.push_back({ .type = Token::Type::Type, .typeToken = { .type = Token::TypeToken::Type::Padding } });
tokens.push_back({ .type = Token::Type::Type, .typeToken = { .type = Token::TypeToken::Type::Padding }, .lineNumber = lineNumber });
// If it's not a keyword and a builtin type, it has to be an identifier
else
tokens.push_back({.type = Token::Type::Identifier, .identifierToken = { .identifier = identifier } });
tokens.push_back({.type = Token::Type::Identifier, .identifierToken = { .identifier = identifier }, .lineNumber = lineNumber });
offset += identifier.length();
} else if (std::isdigit(c)) {
char *end = nullptr;
std::strtoull(&code[offset], &end, 0);
auto integer = parseInt(std::string_view(&code[offset], end));
auto integer = parseInt(std::string_view(&code[offset], end - &code[offset]));
if (!integer.has_value())
if (!integer.has_value()) {
this->m_error = { lineNumber, "Invalid integer literal" };
return { ResultLexicalError, {}};
}
tokens.push_back({ .type = Token::Type::Integer, .integerToken = { .integer = integer.value() } });
tokens.push_back({ .type = Token::Type::Integer, .integerToken = { .integer = integer.value() }, .lineNumber = lineNumber });
offset += (end - &code[offset]);
} else return { ResultLexicalError, {}};
} else {
this->m_error = { lineNumber, "Unknown token" };
return { ResultLexicalError, {} };
}
}
tokens.push_back({ .type = Token::Type::EndOfProgram });
tokens.push_back({ .type = Token::Type::EndOfProgram, .lineNumber = lineNumber });
return { ResultSuccess, tokens };
}

View File

@@ -1,5 +1,7 @@
#include "lang/parser.hpp"
#include "helpers/utils.hpp"
#include <optional>
namespace hex::lang {
@@ -8,11 +10,7 @@ namespace hex::lang {
}
using TokenIter = std::vector<Token>::const_iterator;
std::vector<ASTNode*> parseTillToken(TokenIter &curr, Token::Type endTokenType);
bool tryConsume(TokenIter &curr, std::initializer_list<Token::Type> tokenTypes) {
bool Parser::tryConsume(TokenIter &curr, std::initializer_list<Token::Type> tokenTypes) {
std::vector<Token>::const_iterator originalPosition = curr;
for (const auto& type : tokenTypes) {
@@ -27,100 +25,356 @@ namespace hex::lang {
}
ASTNode* parseBuiltinVariableDecl(TokenIter &curr) {
return new ASTNodeVariableDecl(curr[-3].typeToken.type, curr[-2].identifierToken.identifier);
ASTNode* Parser::parseBuiltinVariableDecl(TokenIter &curr, bool hasEndianDef) {
if (hasEndianDef) {
std::endian endianess;
if (curr[-4].keywordToken.keyword == Token::KeywordToken::Keyword::LittleEndian)
endianess = std::endian::little;
else if (curr[-4].keywordToken.keyword == Token::KeywordToken::Keyword::BigEndian)
endianess = std::endian::big;
else {
this->m_error = { curr->lineNumber, "Expected be or le identifier" };
return nullptr;
}
return new ASTNodeVariableDecl(curr[-4].lineNumber, curr[-3].typeToken.type, curr[-2].identifierToken.identifier, "", {}, 1, {}, {}, endianess);
}
else
return new ASTNodeVariableDecl(curr[-3].lineNumber, curr[-3].typeToken.type, curr[-2].identifierToken.identifier);
}
ASTNode* parseCustomTypeVariableDecl(TokenIter &curr) {
return new ASTNodeVariableDecl(Token::TypeToken::Type::CustomType, curr[-2].identifierToken.identifier, curr[-3].identifierToken.identifier);
ASTNode* Parser::parseCustomTypeVariableDecl(TokenIter &curr, bool hasEndianDef) {
if (hasEndianDef) {
std::endian endianess;
if (curr[-4].keywordToken.keyword == Token::KeywordToken::Keyword::LittleEndian)
endianess = std::endian::little;
else if (curr[-4].keywordToken.keyword == Token::KeywordToken::Keyword::BigEndian)
endianess = std::endian::big;
else return nullptr;
return new ASTNodeVariableDecl(curr[-4].lineNumber, Token::TypeToken::Type::CustomType, curr[-2].identifierToken.identifier, curr[-3].identifierToken.identifier, {}, 1, {}, {}, endianess);
}
else
return new ASTNodeVariableDecl(curr[-3].lineNumber, Token::TypeToken::Type::CustomType, curr[-2].identifierToken.identifier, curr[-3].identifierToken.identifier);
}
ASTNode* parseBuiltinArrayDecl(TokenIter &curr) {
return new ASTNodeVariableDecl(curr[-6].typeToken.type, curr[-5].identifierToken.identifier, "", { }, curr[-3].integerToken.integer);
ASTNode* Parser::parseBuiltinPointerVariableDecl(TokenIter &curr, bool hasEndianDef) {
auto pointerType = curr[-2].typeToken.type;
if (!isUnsigned(pointerType)) {
this->m_error = { curr->lineNumber, "Pointer size needs to be a unsigned type" };
return nullptr;
}
if (curr[-5].operatorToken.op != Token::OperatorToken::Operator::Star) {
this->m_error = { curr->lineNumber, "Expected '*' for pointer definition" };
return nullptr;
}
if (curr[-3].operatorToken.op != Token::OperatorToken::Operator::Inherit) {
this->m_error = { curr->lineNumber, "Expected ':' after member name" };
return nullptr;
}
if (hasEndianDef) {
std::endian endianess;
if (curr[-7].keywordToken.keyword == Token::KeywordToken::Keyword::LittleEndian)
endianess = std::endian::little;
else if (curr[-7].keywordToken.keyword == Token::KeywordToken::Keyword::BigEndian)
endianess = std::endian::big;
else return nullptr;
return new ASTNodeVariableDecl(curr[-7].lineNumber, curr[-6].typeToken.type, curr[-4].identifierToken.identifier, "", { }, 1, { }, getTypeSize(pointerType), endianess);
}
else
return new ASTNodeVariableDecl(curr[-6].lineNumber, curr[-6].typeToken.type, curr[-4].identifierToken.identifier, "", { }, 1, { }, getTypeSize(pointerType));
}
ASTNode* parsePaddingDecl(TokenIter &curr) {
return new ASTNodeVariableDecl(curr[-5].typeToken.type, "", "", { }, curr[-3].integerToken.integer);
ASTNode* Parser::parseCustomTypePointerVariableDecl(TokenIter &curr, bool hasEndianDef) {
auto pointerType = curr[-2].typeToken.type;
if (!isUnsigned(pointerType)) {
this->m_error = { curr->lineNumber, "Pointer size needs to be a unsigned type" };
return nullptr;
}
if (curr[-5].operatorToken.op != Token::OperatorToken::Operator::Star) {
this->m_error = { curr->lineNumber, "Expected '*' for pointer definition" };
return nullptr;
}
if (curr[-3].operatorToken.op != Token::OperatorToken::Operator::Inherit) {
this->m_error = { curr->lineNumber, "Expected ':' after member name" };
return nullptr;
}
if (hasEndianDef) {
std::endian endianess;
if (curr[-7].keywordToken.keyword == Token::KeywordToken::Keyword::LittleEndian)
endianess = std::endian::little;
else if (curr[-7].keywordToken.keyword == Token::KeywordToken::Keyword::BigEndian)
endianess = std::endian::big;
else {
this->m_error = { curr->lineNumber, "Expected be or le identifier" };
return nullptr;
}
return new ASTNodeVariableDecl(curr[-7].lineNumber,Token::TypeToken::Type::CustomType, curr[-4].identifierToken.identifier, curr[-6].identifierToken.identifier, { }, 1, { }, getTypeSize(pointerType), endianess);
}
else
return new ASTNodeVariableDecl(curr[-6].lineNumber, Token::TypeToken::Type::CustomType, curr[-4].identifierToken.identifier, curr[-6].identifierToken.identifier, { }, 1, { }, getTypeSize(pointerType));
}
ASTNode* parseCustomTypeArrayDecl(TokenIter &curr) {
return new ASTNodeVariableDecl(Token::TypeToken::Type::CustomType, curr[-5].identifierToken.identifier, curr[-6].identifierToken.identifier, { }, curr[-3].integerToken.integer);
ASTNode* Parser::parseBuiltinArrayDecl(TokenIter &curr, bool hasEndianDef) {
if (hasEndianDef) {
std::endian endianess;
if (curr[-7].keywordToken.keyword == Token::KeywordToken::Keyword::LittleEndian)
endianess = std::endian::little;
else if (curr[-7].keywordToken.keyword == Token::KeywordToken::Keyword::BigEndian)
endianess = std::endian::big;
else {
this->m_error = { curr->lineNumber, "Expected be or le identifier" };
return nullptr;
}
return new ASTNodeVariableDecl(curr[-7].lineNumber, curr[-6].typeToken.type, curr[-5].identifierToken.identifier, "", { }, curr[-3].integerToken.integer, { }, { }, endianess);
}
else
return new ASTNodeVariableDecl(curr[-6].lineNumber, curr[-6].typeToken.type, curr[-5].identifierToken.identifier, "", { }, curr[-3].integerToken.integer);
}
ASTNode* parseFreeBuiltinVariableDecl(TokenIter &curr) {
return new ASTNodeVariableDecl(curr[-5].typeToken.type, curr[-4].identifierToken.identifier, "", curr[-2].integerToken.integer);
ASTNode* Parser::parseCustomTypeArrayDecl(TokenIter &curr, bool hasEndianDef) {
if (hasEndianDef) {
std::endian endianess;
if (curr[-7].keywordToken.keyword == Token::KeywordToken::Keyword::LittleEndian)
endianess = std::endian::little;
else if (curr[-7].keywordToken.keyword == Token::KeywordToken::Keyword::BigEndian)
endianess = std::endian::big;
else {
this->m_error = { curr->lineNumber, "Expected be or le identifier" };
return nullptr;
}
return new ASTNodeVariableDecl(curr[-7].lineNumber, Token::TypeToken::Type::CustomType, curr[-5].identifierToken.identifier, curr[-6].identifierToken.identifier, { }, curr[-3].integerToken.integer, { }, { }, endianess);
}
else
return new ASTNodeVariableDecl(curr[-6].lineNumber, Token::TypeToken::Type::CustomType, curr[-5].identifierToken.identifier, curr[-6].identifierToken.identifier, { }, curr[-3].integerToken.integer);
}
ASTNode* parseFreeCustomTypeVariableDecl(TokenIter &curr) {
return new ASTNodeVariableDecl(Token::TypeToken::Type::CustomType, curr[-4].identifierToken.identifier, curr[-5].identifierToken.identifier, curr[-2].integerToken.integer);
ASTNode* Parser::parseBuiltinVariableArrayDecl(TokenIter &curr, bool hasEndianDef) {
if (hasEndianDef) {
std::endian endianess;
if (curr[-7].keywordToken.keyword == Token::KeywordToken::Keyword::LittleEndian)
endianess = std::endian::little;
else if (curr[-7].keywordToken.keyword == Token::KeywordToken::Keyword::BigEndian)
endianess = std::endian::big;
else {
this->m_error = { curr->lineNumber, "Expected be or le identifier" };
return nullptr;
}
return new ASTNodeVariableDecl(curr[-7].lineNumber, curr[-6].typeToken.type, curr[-5].identifierToken.identifier, "", { }, 0, curr[-3].identifierToken.identifier, { }, endianess);
}
else
return new ASTNodeVariableDecl(curr[-6].lineNumber, curr[-6].typeToken.type, curr[-5].identifierToken.identifier, "", { }, 0, curr[-3].identifierToken.identifier);
}
ASTNode* parseStruct(TokenIter &curr) {
ASTNode* Parser::parseCustomTypeVariableArrayDecl(TokenIter &curr, bool hasEndianDef) {
if (hasEndianDef) {
std::endian endianess;
if (curr[-7].keywordToken.keyword == Token::KeywordToken::Keyword::LittleEndian)
endianess = std::endian::little;
else if (curr[-7].keywordToken.keyword == Token::KeywordToken::Keyword::BigEndian)
endianess = std::endian::big;
else {
this->m_error = { curr->lineNumber, "Expected be or le identifier" };
return nullptr;
}
return new ASTNodeVariableDecl(curr[-7].lineNumber, Token::TypeToken::Type::CustomType, curr[-5].identifierToken.identifier, curr[-6].identifierToken.identifier, { }, 0, curr[-3].identifierToken.identifier, { }, endianess);
}
else
return new ASTNodeVariableDecl(curr[-6].lineNumber, Token::TypeToken::Type::CustomType, curr[-5].identifierToken.identifier, curr[-6].identifierToken.identifier, { }, 0, curr[-3].identifierToken.identifier);
}
ASTNode* Parser::parsePaddingDecl(TokenIter &curr) {
return new ASTNodeVariableDecl(curr[-5].lineNumber, curr[-5].typeToken.type, "", "", { }, curr[-3].integerToken.integer);
}
ASTNode* Parser::parseFreeBuiltinVariableDecl(TokenIter &curr, bool hasEndianDef) {
if (hasEndianDef) {
std::endian endianess;
if (curr[-6].keywordToken.keyword == Token::KeywordToken::Keyword::LittleEndian)
endianess = std::endian::little;
else if (curr[-6].keywordToken.keyword == Token::KeywordToken::Keyword::BigEndian)
endianess = std::endian::big;
else {
this->m_error = { curr->lineNumber, "Expected be or le identifier" };
return nullptr;
}
return new ASTNodeVariableDecl(curr[-6].lineNumber, curr[-5].typeToken.type, curr[-4].identifierToken.identifier, "", curr[-2].integerToken.integer, 1, { }, { }, endianess);
}
else
return new ASTNodeVariableDecl(curr[-5].lineNumber, curr[-5].typeToken.type, curr[-4].identifierToken.identifier, "", curr[-2].integerToken.integer);
}
ASTNode* Parser::parseFreeCustomTypeVariableDecl(TokenIter &curr, bool hasEndianDef) {
if (hasEndianDef) {
std::endian endianess;
if (curr[-6].keywordToken.keyword == Token::KeywordToken::Keyword::LittleEndian)
endianess = std::endian::little;
else if (curr[-6].keywordToken.keyword == Token::KeywordToken::Keyword::BigEndian)
endianess = std::endian::big;
else {
this->m_error = { curr->lineNumber, "Expected be or le identifier" };
return nullptr;
}
return new ASTNodeVariableDecl(curr[-6].lineNumber, Token::TypeToken::Type::CustomType, curr[-4].identifierToken.identifier, curr[-5].identifierToken.identifier, curr[-2].integerToken.integer, 1, { }, { }, endianess);
}
else
return new ASTNodeVariableDecl(curr[-5].lineNumber, Token::TypeToken::Type::CustomType, curr[-4].identifierToken.identifier, curr[-5].identifierToken.identifier, curr[-2].integerToken.integer);
}
ASTNode* Parser::parseStruct(TokenIter &curr) {
const std::string &structName = curr[-2].identifierToken.identifier;
std::vector<ASTNode*> nodes;
u32 startLineNumber = curr[-3].lineNumber;
while (!tryConsume(curr, {Token::Type::ScopeClose})) {
if (tryConsume(curr, {Token::Type::Type, Token::Type::Identifier, Token::Type::EndOfExpression}))
nodes.push_back(parseBuiltinVariableDecl(curr));
nodes.push_back(parseBuiltinVariableDecl(curr, false));
else if (tryConsume(curr, {Token::Type::Identifier, Token::Type::Identifier, Token::Type::EndOfExpression}))
nodes.push_back(parseCustomTypeVariableDecl(curr));
nodes.push_back(parseCustomTypeVariableDecl(curr, false));
else if (tryConsume(curr, {Token::Type::Type, Token::Type::Identifier, Token::Type::ArrayOpen, Token::Type::Integer, Token::Type::ArrayClose, Token::Type::EndOfExpression}))
nodes.push_back(parseBuiltinArrayDecl(curr));
nodes.push_back(parseBuiltinArrayDecl(curr, false));
else if (tryConsume(curr, {Token::Type::Identifier, Token::Type::Identifier, Token::Type::ArrayOpen, Token::Type::Integer, Token::Type::ArrayClose, Token::Type::EndOfExpression}))
nodes.push_back(parseCustomTypeArrayDecl(curr));
nodes.push_back(parseCustomTypeArrayDecl(curr, false));
else if (tryConsume(curr, {Token::Type::Type, Token::Type::Identifier, Token::Type::ArrayOpen, Token::Type::Identifier, Token::Type::ArrayClose, Token::Type::EndOfExpression}))
nodes.push_back(parseBuiltinVariableArrayDecl(curr, false));
else if (tryConsume(curr, {Token::Type::Identifier, Token::Type::Identifier, Token::Type::ArrayOpen, Token::Type::Identifier, Token::Type::ArrayClose, Token::Type::EndOfExpression}))
nodes.push_back(parseCustomTypeVariableArrayDecl(curr, false));
else if (tryConsume(curr, {Token::Type::Type, Token::Type::ArrayOpen, Token::Type::Integer, Token::Type::ArrayClose, Token::Type::EndOfExpression})) {
if (curr[-5].typeToken.type != Token::TypeToken::Type::Padding) {
for(auto &node : nodes) delete node;
this->m_error = { curr[-5].lineNumber, "No member name provided" };
return nullptr;
}
nodes.push_back(parsePaddingDecl(curr));
} else if (tryConsume(curr, {Token::Type::Type, Token::Type::Operator, Token::Type::Identifier, Token::Type::Operator, Token::Type::Type, Token::Type::EndOfExpression}))
nodes.push_back(parseBuiltinPointerVariableDecl(curr, false));
else if (tryConsume(curr, {Token::Type::Identifier, Token::Type::Operator, Token::Type::Identifier, Token::Type::Operator, Token::Type::Type, Token::Type::EndOfExpression}))
nodes.push_back(parseCustomTypePointerVariableDecl(curr, false));
else if (tryConsume(curr, {Token::Type::Keyword, Token::Type::Type, Token::Type::Identifier, Token::Type::EndOfExpression}))
nodes.push_back(parseBuiltinVariableDecl(curr, true));
else if (tryConsume(curr, {Token::Type::Keyword, Token::Type::Identifier, Token::Type::Identifier, Token::Type::EndOfExpression}))
nodes.push_back(parseCustomTypeVariableDecl(curr, true));
else if (tryConsume(curr, {Token::Type::Keyword, Token::Type::Type, Token::Type::Identifier, Token::Type::ArrayOpen, Token::Type::Integer, Token::Type::ArrayClose, Token::Type::EndOfExpression}))
nodes.push_back(parseBuiltinArrayDecl(curr, true));
else if (tryConsume(curr, {Token::Type::Keyword, Token::Type::Identifier, Token::Type::Identifier, Token::Type::ArrayOpen, Token::Type::Integer, Token::Type::ArrayClose, Token::Type::EndOfExpression}))
nodes.push_back(parseCustomTypeArrayDecl(curr, true));
else if (tryConsume(curr, {Token::Type::Keyword, Token::Type::Type, Token::Type::Identifier, Token::Type::ArrayOpen, Token::Type::Identifier, Token::Type::ArrayClose, Token::Type::EndOfExpression}))
nodes.push_back(parseBuiltinVariableArrayDecl(curr, true));
else if (tryConsume(curr, {Token::Type::Keyword, Token::Type::Identifier, Token::Type::Identifier, Token::Type::ArrayOpen, Token::Type::Identifier, Token::Type::ArrayClose, Token::Type::EndOfExpression}))
nodes.push_back(parseCustomTypeVariableArrayDecl(curr, true));
else if (tryConsume(curr, {Token::Type::Keyword, Token::Type::Type, Token::Type::Operator, Token::Type::Identifier, Token::Type::Operator, Token::Type::Type, Token::Type::EndOfExpression}))
nodes.push_back(parseBuiltinPointerVariableDecl(curr, true));
else if (tryConsume(curr, {Token::Type::Keyword, Token::Type::Identifier, Token::Type::Operator, Token::Type::Identifier, Token::Type::Operator, Token::Type::Type, Token::Type::EndOfExpression}))
nodes.push_back(parseCustomTypePointerVariableDecl(curr, true));
else {
for(auto &node : nodes) delete node;
this->m_error = { curr->lineNumber, "Invalid sequence, expected member declaration" };
return nullptr;
}
else break;
}
if (!tryConsume(curr, {Token::Type::EndOfExpression})) {
this->m_error = { curr->lineNumber, "Expected ';' after struct definition" };
for(auto &node : nodes) delete node;
return nullptr;
}
return new ASTNodeStruct(structName, nodes);
return new ASTNodeStruct(startLineNumber, structName, nodes);
}
ASTNode* parseUnion(TokenIter &curr) {
ASTNode* Parser::parseUnion(TokenIter &curr) {
const std::string &unionName = curr[-2].identifierToken.identifier;
std::vector<ASTNode*> nodes;
u32 startLineNumber = curr[-3].lineNumber;
while (!tryConsume(curr, {Token::Type::ScopeClose})) {
if (tryConsume(curr, {Token::Type::Type, Token::Type::Identifier, Token::Type::EndOfExpression}))
nodes.push_back(parseBuiltinVariableDecl(curr));
nodes.push_back(parseBuiltinVariableDecl(curr, false));
else if (tryConsume(curr, {Token::Type::Identifier, Token::Type::Identifier, Token::Type::EndOfExpression}))
nodes.push_back(parseCustomTypeVariableDecl(curr));
nodes.push_back(parseCustomTypeVariableDecl(curr, false));
else if (tryConsume(curr, {Token::Type::Type, Token::Type::Identifier, Token::Type::ArrayOpen, Token::Type::Integer, Token::Type::ArrayClose, Token::Type::EndOfExpression}))
nodes.push_back(parseBuiltinArrayDecl(curr));
nodes.push_back(parseBuiltinArrayDecl(curr, false));
else if (tryConsume(curr, {Token::Type::Identifier, Token::Type::Identifier, Token::Type::ArrayOpen, Token::Type::Integer, Token::Type::ArrayClose, Token::Type::EndOfExpression}))
nodes.push_back(parseCustomTypeArrayDecl(curr));
else break;
nodes.push_back(parseCustomTypeArrayDecl(curr, false));
else if (tryConsume(curr, {Token::Type::Type, Token::Type::Operator, Token::Type::Identifier, Token::Type::Operator, Token::Type::Type, Token::Type::EndOfExpression}))
nodes.push_back(parseBuiltinPointerVariableDecl(curr, false));
else if (tryConsume(curr, {Token::Type::Identifier, Token::Type::Operator, Token::Type::Identifier, Token::Type::Operator, Token::Type::Type, Token::Type::EndOfExpression}))
nodes.push_back(parseCustomTypePointerVariableDecl(curr, false));
else if (tryConsume(curr, {Token::Type::Keyword, Token::Type::Type, Token::Type::Identifier, Token::Type::EndOfExpression}))
nodes.push_back(parseBuiltinVariableDecl(curr, true));
else if (tryConsume(curr, {Token::Type::Keyword, Token::Type::Identifier, Token::Type::Identifier, Token::Type::EndOfExpression}))
nodes.push_back(parseCustomTypeVariableDecl(curr, true));
else if (tryConsume(curr, {Token::Type::Keyword, Token::Type::Type, Token::Type::Identifier, Token::Type::ArrayOpen, Token::Type::Integer, Token::Type::ArrayClose, Token::Type::EndOfExpression}))
nodes.push_back(parseBuiltinArrayDecl(curr, true));
else if (tryConsume(curr, {Token::Type::Keyword, Token::Type::Identifier, Token::Type::Identifier, Token::Type::ArrayOpen, Token::Type::Integer, Token::Type::ArrayClose, Token::Type::EndOfExpression}))
nodes.push_back(parseCustomTypeArrayDecl(curr, true));
else if (tryConsume(curr, {Token::Type::Keyword, Token::Type::Type, Token::Type::Operator, Token::Type::Identifier, Token::Type::Operator, Token::Type::Type, Token::Type::EndOfExpression}))
nodes.push_back(parseBuiltinPointerVariableDecl(curr, true));
else if (tryConsume(curr, {Token::Type::Keyword, Token::Type::Identifier, Token::Type::Operator, Token::Type::Identifier, Token::Type::Operator, Token::Type::Type, Token::Type::EndOfExpression}))
nodes.push_back(parseCustomTypePointerVariableDecl(curr, true));
else {
for(auto &node : nodes) delete node;
this->m_error = { curr->lineNumber, "Invalid sequence, expected member declaration" };
return nullptr;
}
}
if (!tryConsume(curr, {Token::Type::EndOfExpression})) {
for(auto &node : nodes) delete node;
this->m_error = { curr[-1].lineNumber, "Expected ';' after union definition" };
return nullptr;
}
return new ASTNodeUnion(unionName, nodes);
return new ASTNodeUnion(startLineNumber, unionName, nodes);
}
ASTNode* parseEnum(TokenIter &curr) {
ASTNode* Parser::parseEnum(TokenIter &curr) {
const std::string &enumName = curr[-4].identifierToken.identifier;
const Token::TypeToken::Type underlyingType = curr[-2].typeToken.type;
if (curr[-3].operatorToken.op != Token::OperatorToken::Operator::Inherit)
return nullptr;
u32 startLineNumber = curr[-5].lineNumber;
if ((static_cast<u32>(underlyingType) & 0x0F) != 0x00)
if (curr[-3].operatorToken.op != Token::OperatorToken::Operator::Inherit) {
this->m_error = { curr[-3].lineNumber, "Expected ':' after enum name" };
return nullptr;
}
auto enumNode = new ASTNodeEnum(underlyingType, enumName);
if (!isUnsigned(underlyingType)) {
this->m_error = { curr[-3].lineNumber, "Underlying type needs to be an unsigned type" };
return nullptr;
}
auto enumNode = new ASTNodeEnum(startLineNumber, underlyingType, enumName);
while (!tryConsume(curr, {Token::Type::ScopeClose})) {
if (tryConsume(curr, { Token::Type::Identifier, Token::Type::Separator }) || tryConsume(curr, { Token::Type::Identifier, Token::Type::ScopeClose })) {
@@ -144,67 +398,83 @@ namespace hex::lang {
}
else {
delete enumNode;
this->m_error = { curr->lineNumber, "Expected constant identifier" };
return nullptr;
}
}
if (!tryConsume(curr, {Token::Type::EndOfExpression})) {
delete enumNode;
this->m_error = { curr[-1].lineNumber, "Expected ';' after enum definition" };
return nullptr;
}
return enumNode;
}
ASTNode *parseBitField(TokenIter &curr) {
ASTNode* Parser::parseBitField(TokenIter &curr) {
const std::string &bitfieldName = curr[-2].identifierToken.identifier;
std::vector<std::pair<std::string, size_t>> fields;
u32 startLineNumber = curr[-3].lineNumber;
while (!tryConsume(curr, {Token::Type::ScopeClose})) {
if (tryConsume(curr, {Token::Type::Identifier, Token::Type::Operator, Token::Type::Integer, Token::Type::EndOfExpression})) {
if (curr[-3].operatorToken.op != Token::OperatorToken::Operator::Inherit)
if (curr[-3].operatorToken.op != Token::OperatorToken::Operator::Inherit) {
this->m_error = { curr[-3].lineNumber, "Expected ':' after member name" };
return nullptr;
}
fields.emplace_back(curr[-4].identifierToken.identifier, curr[-2].integerToken.integer);
}
else break;
else {
this->m_error = { curr->lineNumber, "Invalid sequence, expected member declaration" };
return nullptr;
}
}
if (!tryConsume(curr, {Token::Type::EndOfExpression}))
if (!tryConsume(curr, {Token::Type::EndOfExpression})) {
this->m_error = { curr[-1].lineNumber, "Expected ';' after bitfield definition" };
return nullptr;
}
return new ASTNodeBitField(bitfieldName, fields);
return new ASTNodeBitField(startLineNumber, bitfieldName, fields);
}
ASTNode *parseScope(TokenIter &curr) {
return new ASTNodeScope(parseTillToken(curr, Token::Type::ScopeClose));
ASTNode* Parser::parseScope(TokenIter &curr) {
return new ASTNodeScope(curr[-1].lineNumber, parseTillToken(curr, Token::Type::ScopeClose));
}
std::optional<ASTNode*> parseUsingDeclaration(TokenIter &curr) {
std::optional<ASTNode*> Parser::parseUsingDeclaration(TokenIter &curr) {
auto keyword = curr[-5].keywordToken;
auto name = curr[-4].identifierToken;
auto op = curr[-3].operatorToken;
if (keyword.keyword != Token::KeywordToken::Keyword::Using)
if (keyword.keyword != Token::KeywordToken::Keyword::Using) {
this->m_error = { curr[-5].lineNumber, "Invalid keyword. Expected 'using'" };
return { };
}
if (op.op != Token::OperatorToken::Operator::Assignment)
if (op.op != Token::OperatorToken::Operator::Assignment) {
this->m_error = { curr[-3].lineNumber, "Invalid operator. Expected '='" };
return { };
}
if (curr[-2].type == Token::Type::Type) {
auto type = curr[-2].typeToken;
return new ASTNodeTypeDecl(type.type, name.identifier);
return new ASTNodeTypeDecl(curr[-2].lineNumber, type.type, name.identifier);
} else if (curr[-2].type == Token::Type::Identifier) {
auto customType = curr[-2].identifierToken;
return new ASTNodeTypeDecl(Token::TypeToken::Type::CustomType, name.identifier, customType.identifier);
return new ASTNodeTypeDecl(curr[-2].lineNumber, Token::TypeToken::Type::CustomType, name.identifier, customType.identifier);
}
this->m_error = { curr[-2].lineNumber, hex::format("'%s' does not name a type") };
return { };
}
std::optional<std::vector<ASTNode*>> parseStatement(TokenIter &curr) {
std::optional<std::vector<ASTNode*>> Parser::parseStatement(TokenIter &curr) {
std::vector<ASTNode*> program;
// Struct
@@ -285,17 +555,57 @@ namespace hex::lang {
program.push_back(usingDecl.value());
return program;
// Variable declaration with built-in type
// Variable placement declaration with built-in type
} else if (tryConsume(curr, { Token::Type::Type, Token::Type::Identifier, Token::Type::Operator, Token::Type::Integer, Token::Type::EndOfExpression})) {
auto variableDecl = parseFreeBuiltinVariableDecl(curr);
if (curr[-3].operatorToken.op != Token::OperatorToken::Operator::AtDeclaration) {
this->m_error = { curr[-3].lineNumber, "Expected '@' after variable placement declaration" };
for(auto &node : program) delete node;
return { };
}
auto variableDecl = parseFreeBuiltinVariableDecl(curr, false);
program.push_back(variableDecl);
return program;
// Variable declaration with custom type
// Variable placement declaration with custom type
} else if (tryConsume(curr, { Token::Type::Identifier, Token::Type::Identifier, Token::Type::Operator, Token::Type::Integer, Token::Type::EndOfExpression})) {
auto variableDecl = parseFreeCustomTypeVariableDecl(curr);
if (curr[-3].operatorToken.op != Token::OperatorToken::Operator::AtDeclaration) {
this->m_error = { curr[-3].lineNumber, "Expected '@' after variable placement declaration" };
for(auto &node : program) delete node;
return { };
}
auto variableDecl = parseFreeCustomTypeVariableDecl(curr, false);
program.push_back(variableDecl);
return program;
// Variable placement declaration with built-in type and big/little endian setting
} else if (tryConsume(curr, { Token::Type::Keyword, Token::Type::Type, Token::Type::Identifier, Token::Type::Operator, Token::Type::Integer, Token::Type::EndOfExpression})) {
if (curr[-3].operatorToken.op != Token::OperatorToken::Operator::AtDeclaration) {
this->m_error = { curr[-3].lineNumber, "Expected '@' after variable placement declaration" };
for(auto &node : program) delete node;
return { };
}
auto variableDecl = parseFreeBuiltinVariableDecl(curr, true);
program.push_back(variableDecl);
return program;
// Variable placement declaration with custom type and big/little endian setting
} else if (tryConsume(curr, { Token::Type::Keyword, Token::Type::Identifier, Token::Type::Identifier, Token::Type::Operator, Token::Type::Integer, Token::Type::EndOfExpression})) {
if (curr[-3].operatorToken.op != Token::OperatorToken::Operator::AtDeclaration) {
this->m_error = { curr[-3].lineNumber, "Expected '@' after variable placement declaration" };
for(auto &node : program) delete node;
return { };
}
auto variableDecl = parseFreeCustomTypeVariableDecl(curr, true);
program.push_back(variableDecl);
@@ -303,11 +613,12 @@ namespace hex::lang {
}
else {
for(auto &node : program) delete node;
this->m_error = { curr->lineNumber, "Invalid sequence" };
return { };
}
}
std::vector<ASTNode*> parseTillToken(TokenIter &curr, Token::Type endTokenType) {
std::vector<ASTNode*> Parser::parseTillToken(TokenIter &curr, Token::Type endTokenType) {
std::vector<ASTNode*> program;
while (curr->type != endTokenType) {

View File

@@ -6,11 +6,14 @@ namespace hex::lang {
}
std::pair<Result, std::string> Preprocessor::preprocess(const std::string& code, bool applyDefines) {
std::pair<Result, std::string> Preprocessor::preprocess(const std::string& code, bool initialRun) {
u32 offset = 0;
u32 lineNumber = 1;
if (applyDefines)
if (initialRun) {
this->m_defines.clear();
this->m_pragmas.clear();
}
std::string output;
output.reserve(code.length());
@@ -76,45 +79,106 @@ namespace hex::lang {
std::string defineName;
while (!std::isblank(code[offset])) {
defineName += code[offset];
offset += 1;
if (offset >= code.length() || code[offset] == '\n' || code[offset] == '\r')
return { ResultPreprocessingError, "" };
offset += 1;
}
while (std::isblank(code[offset]))
offset += 1;
std::string replaceValue;
do {
while (code[offset] != '\n' && code[offset] != '\r') {
if (offset >= code.length())
return { ResultPreprocessingError, "" };
replaceValue += code[offset];
offset += 1;
} while (code[offset] != '\n' && code[offset] != '\r');
}
if (replaceValue.empty())
return { ResultPreprocessingError, "" };
this->m_defines.emplace(defineName, replaceValue);
}
} else if (code.substr(offset, 6) == "pragma") {
offset += 6;
while (std::isblank(code[offset]))
offset += 1;
std::string pragmaKey;
while (!std::isblank(code[offset])) {
pragmaKey += code[offset];
if (offset >= code.length() || code[offset] == '\n' || code[offset] == '\r')
return { ResultPreprocessingError, "" };
offset += 1;
}
while (std::isblank(code[offset]))
offset += 1;
std::string pragmaValue;
while (code[offset] != '\n' && code[offset] != '\r') {
if (offset >= code.length())
return { ResultPreprocessingError, "" };
pragmaValue += code[offset];
offset += 1;
}
if (pragmaValue.empty())
return { ResultPreprocessingError, "" };
this->m_pragmas.emplace(pragmaKey, pragmaValue);
} else
return { ResultPreprocessingError, "" };
}
if (code[offset] == '\n')
lineNumber++;
output += code[offset];
offset += 1;
}
if (applyDefines) {
if (initialRun) {
// Apply defines
for (const auto &[define, value] : this->m_defines) {
s32 index = 0;
while((index = output.find(define, index)) != std::string::npos) {
if (index > 0) {
output.replace(index, define.length(), value);
index += value.length();
}
output.replace(index, define.length(), value);
index += value.length();
}
}
// Handle pragmas
for (const auto &[type, value] : this->m_pragmas) {
if (this->m_pragmaHandlers.contains(type)) {
if (!this->m_pragmaHandlers[type](value))
return { ResultPreprocessingError, { } };
} else
return { ResultPreprocessingError, { } };
}
}
return { ResultSuccess, output };
}
void Preprocessor::addPragmaHandler(std::string pragmaType, std::function<bool(std::string)> function) {
if (!this->m_pragmaHandlers.contains(pragmaType))
this->m_pragmaHandlers.emplace(pragmaType, function);
}
void Preprocessor::addDefaultPragmaHandlers() {
this->addPragmaHandler("MIME", [](std::string value) {
return !std::all_of(value.begin(), value.end(), isspace) && !value.ends_with('\n') && !value.ends_with('\r');
});
this->addPragmaHandler("endian", [](std::string value) {
return value == "big" || value == "little" || value == "native";
});
}
}

View File

@@ -3,6 +3,8 @@
#include <unordered_set>
#include <string>
#include "helpers/utils.hpp"
namespace hex::lang {
Validator::Validator() {
@@ -19,70 +21,98 @@ namespace hex::lang {
{
// Check for duplicate variable names
auto varDeclNode = static_cast<ASTNodeVariableDecl*>(node);
if (!typeNames.insert(varDeclNode->getVariableName()).second)
if (!typeNames.insert(varDeclNode->getVariableName()).second) {
this->m_error = { varDeclNode->getLineNumber(), hex::format("Redefinition of variable '%s'", varDeclNode->getVariableName().c_str()) };
return false;
}
if (varDeclNode->getArraySize() == 0 && !varDeclNode->getArraySizeVariable().has_value() ||
varDeclNode->getArraySize() != 0 && varDeclNode->getArraySizeVariable().has_value()) {
this->m_error = { varDeclNode->getLineNumber(), "Invalid array size" };
return false;
}
}
break;
case ASTNode::Type::TypeDecl:
{
// Check for duplicate type names
auto typeDeclNode = static_cast<ASTNodeTypeDecl*>(node);
if (!typeNames.insert(typeDeclNode->getTypeName()).second)
if (!typeNames.insert(typeDeclNode->getTypeName()).second) {
this->m_error = { typeDeclNode->getLineNumber(), hex::format("Redefinition of type '%s'", typeDeclNode->getTypeName().c_str()) };
return false;
}
if (typeDeclNode->getAssignedType() == Token::TypeToken::Type::CustomType && !typeNames.contains(typeDeclNode->getAssignedCustomTypeName()))
if (typeDeclNode->getAssignedType() == Token::TypeToken::Type::CustomType && !typeNames.contains(typeDeclNode->getAssignedCustomTypeName())) {
this->m_error = { typeDeclNode->getLineNumber(), "Type declaration without a name" };
return false;
}
}
break;
break;
case ASTNode::Type::Struct:
{
// Check for duplicate type name
auto structNode = static_cast<ASTNodeStruct*>(node);
if (!typeNames.insert(structNode->getName()).second)
if (!typeNames.insert(structNode->getName()).second) {
this->m_error = { structNode->getLineNumber(), hex::format("Redeclaration of type '%s'", structNode->getName().c_str()) };
return false;
}
// Check for duplicate member names
std::unordered_set<std::string> memberNames;
for (const auto &member : structNode->getNodes())
if (!memberNames.insert(static_cast<ASTNodeVariableDecl*>(member)->getVariableName()).second)
if (!memberNames.insert(static_cast<ASTNodeVariableDecl*>(member)->getVariableName()).second) {
this->m_error = { member->getLineNumber(), hex::format("Redeclaration of member '%s'", static_cast<ASTNodeVariableDecl*>(member)->getVariableName().c_str()) };
return false;
}
}
break;
break;
case ASTNode::Type::Enum:
{
// Check for duplicate type name
auto enumNode = static_cast<ASTNodeEnum*>(node);
if (!typeNames.insert(enumNode->getName()).second)
if (!typeNames.insert(enumNode->getName()).second) {
this->m_error = { enumNode->getLineNumber(), hex::format("Redeclaration of type '%s'", enumNode->getName().c_str()) };
return false;
}
// Check for duplicate constant names
std::unordered_set<std::string> constantNames;
for (const auto &[value, name] : enumNode->getValues())
if (!constantNames.insert(name).second)
if (!constantNames.insert(name).second) {
this->m_error = { enumNode->getLineNumber(), hex::format("Redeclaration of enum constant '%s'", name.c_str()) };
return false;
}
}
break;
case ASTNode::Type::Bitfield:
{
// Check for duplicate type name
auto bitfieldNode = static_cast<ASTNodeBitField*>(node);
if (!typeNames.insert(bitfieldNode->getName()).second)
if (!typeNames.insert(bitfieldNode->getName()).second) {
this->m_error = { bitfieldNode->getLineNumber(), hex::format("Redeclaration of type '%s'", bitfieldNode->getName().c_str()) };
return false;
}
size_t bitfieldSize = 0;
// Check for duplicate constant names
std::unordered_set<std::string> flagNames;
for (const auto &[name, size] : bitfieldNode->getFields()) {
if (!flagNames.insert(name).second)
if (!flagNames.insert(name).second) {
this->m_error = { bitfieldNode->getLineNumber(), hex::format("Redeclaration of member '%s'", name.c_str()) };
return false;
}
bitfieldSize += size;
}
if (bitfieldSize > 64)
if (bitfieldSize > 64) {
this->m_error = { bitfieldNode->getLineNumber(), "Bitfield exceeds maximum size of 64 bits" };
return false;
}
}
break;
break;
}
}

View File

@@ -10,12 +10,21 @@
#include "views/view_tools.hpp"
#include "views/view_strings.hpp"
#include "views/view_data_inspector.hpp"
#include "views/view_disassembler.hpp"
#include "views/view_bookmarks.hpp"
#include "views/view_patches.hpp"
#include "providers/provider.hpp"
#include <vector>
int main() {
int mainArgc;
char **mainArgv;
int main(int argc, char **argv) {
mainArgc = argc;
mainArgv = argv;
hex::Window window;
// Shared Data
@@ -30,8 +39,14 @@ int main() {
window.addView<hex::ViewHashes>(dataProvider);
window.addView<hex::ViewInformation>(dataProvider);
window.addView<hex::ViewStrings>(dataProvider);
window.addView<hex::ViewDisassembler>(dataProvider);
window.addView<hex::ViewBookmarks>(dataProvider);
window.addView<hex::ViewPatches>(dataProvider);
window.addView<hex::ViewTools>(dataProvider);
window.addView<hex::ViewHelp>();
window.addView<hex::ViewTools>();
if (argc > 1)
hex::View::postEvent(hex::Events::FileDropped, argv[1]);
window.loop();

View File

@@ -6,23 +6,34 @@
#include <sys/stat.h>
#include <time.h>
#include "utils.hpp"
#include "helpers/utils.hpp"
#include "helpers/project_file_handler.hpp"
#ifdef __APPLE__
#define off64_t off_t
#define fopen64 fopen
#define fseeko64 fseek
#define ftello64 ftell
#endif
namespace hex::prv {
FileProvider::FileProvider(std::string_view path) : Provider(), m_path(path) {
this->m_fileStatsValid = stat(path.data(), &this->m_fileStats) == 0;
this->m_file = fopen(path.data(), "r+b");
this->m_file = fopen64(path.data(), "r+b");
this->m_readable = true;
this->m_writable = true;
if (this->m_file == nullptr) {
this->m_file = fopen(path.data(), "rb");
this->m_file = fopen64(path.data(), "rb");
this->m_writable = false;
}
if (this->m_file != nullptr)
ProjectFile::setFilePath(path);
}
FileProvider::~FileProvider() {
@@ -48,19 +59,42 @@ namespace hex::prv {
if ((offset + size) > this->getSize() || buffer == nullptr || size == 0)
return;
fseeko64(this->m_file, offset, SEEK_SET);
fseeko64(this->m_file, this->getCurrentPage() * PageSize + offset, SEEK_SET);
size_t readSize = fread(buffer, 1, size, this->m_file);
for (u64 i = 0; i < readSize; i++)
if (this->m_patches.back().contains(offset + i))
reinterpret_cast<u8*>(buffer)[i] = this->m_patches.back()[offset + i];
}
void FileProvider::write(u64 offset, const void *buffer, size_t size) {
if (buffer == nullptr || size == 0)
return;
this->m_patches.push_back(this->m_patches.back());
for (u64 i = 0; i < size; i++)
this->m_patches.back()[offset + i] = reinterpret_cast<const u8*>(buffer)[i];
}
void FileProvider::readRaw(u64 offset, void *buffer, size_t size) {
if ((offset + size) > this->getSize() || buffer == nullptr || size == 0)
return;
fseeko64(this->m_file, this->getCurrentPage() * PageSize + offset, SEEK_SET);
fread(buffer, 1, size, this->m_file);
}
void FileProvider::write(u64 offset, void *buffer, size_t size) {
void FileProvider::writeRaw(u64 offset, const void *buffer, size_t size) {
if (buffer == nullptr || size == 0)
return;
fseeko64(this->m_file, offset, SEEK_SET);
fwrite(buffer, 1, size, this->m_file);
}
size_t FileProvider::getSize() {
size_t FileProvider::getActualSize() {
fseeko64(this->m_file, 0, SEEK_END);
return ftello64(this->m_file);
}
@@ -69,7 +103,7 @@ namespace hex::prv {
std::vector<std::pair<std::string, std::string>> result;
result.emplace_back("File path", this->m_path);
result.emplace_back("Size", hex::toByteString(this->getSize()));
result.emplace_back("Size", hex::toByteString(this->getActualSize()));
if (this->m_fileStatsValid) {
result.emplace_back("Creation time", ctime(&this->m_fileStats.st_ctime));

View File

@@ -1,8 +0,0 @@
#include "utils.hpp"
#include <codecvt>
#include <locale>
namespace hex {
}

View File

@@ -0,0 +1,114 @@
#include "views/view_bookmarks.hpp"
#include "providers/provider.hpp"
#include "helpers/project_file_handler.hpp"
#include <cstring>
namespace hex {
ViewBookmarks::ViewBookmarks(prv::Provider* &dataProvider) : View("Bookmarks"), m_dataProvider(dataProvider) {
View::subscribeEvent(Events::AddBookmark, [this](const void *userData) {
Bookmark bookmark = *reinterpret_cast<const Bookmark*>(userData);
bookmark.name.resize(64);
bookmark.comment.resize(0xF'FFFF);
if (bookmark.name.empty()) {
std::memset(bookmark.name.data(), 0x00, 64);
std::strcpy(bookmark.name.data(), ("Bookmark " + std::to_string(this->m_bookmarks.size() + 1)).c_str());
}
if (bookmark.comment.empty())
std::memset(bookmark.comment.data(), 0x00, 0xF'FFFF);
this->m_bookmarks.push_back(bookmark);
ProjectFile::markDirty();
});
View::subscribeEvent(Events::ProjectFileLoad, [this](const void*) {
this->m_bookmarks = ProjectFile::getBookmarks();
});
View::subscribeEvent(Events::ProjectFileStore, [this](const void*) {
ProjectFile::setBookmarks(this->m_bookmarks);
});
}
ViewBookmarks::~ViewBookmarks() {
View::unsubscribeEvent(Events::AddBookmark);
View::unsubscribeEvent(Events::ProjectFileLoad);
View::unsubscribeEvent(Events::ProjectFileStore);
}
void ViewBookmarks::createView() {
if (ImGui::Begin("Bookmarks", &this->getWindowOpenState())) {
if (ImGui::BeginChild("##scrolling")) {
u32 id = 1;
std::list<Bookmark>::const_iterator bookmarkToRemove = this->m_bookmarks.end();
for (auto iter = this->m_bookmarks.begin(); iter != this->m_bookmarks.end(); iter++) {
auto &[region, name, comment] = *iter;
if (ImGui::CollapsingHeader((std::string(name.data()) + "###" + std::to_string((u64)comment.data())).c_str())) {
ImGui::TextUnformatted("Information");
ImGui::Separator();
ImGui::Text("0x%08lx : 0x%08lx (%lu bytes)", region.address, region.address + region.size - 1, region.size);
{
u8 bytes[10] = { 0 };
this->m_dataProvider->read(region.address, bytes, std::min(region.size, size_t(10)));
std::string bytesString;
for (u8 i = 0; i < std::min(region.size, size_t(10)); i++) {
bytesString += hex::format("%02X ", bytes[i]);
}
if (region.size > 10) {
bytesString.pop_back();
bytesString += "...";
}
ImGui::TextColored(ImColor(0xFF9BC64D), bytesString.c_str());
}
if (ImGui::Button("Jump to"))
View::postEvent(Events::SelectionChangeRequest, &region);
ImGui::SameLine(0, 15);
if (ImGui::Button("Remove"))
bookmarkToRemove = iter;
ImGui::NewLine();
ImGui::TextUnformatted("Name");
ImGui::Separator();
ImGui::PushID(id);
ImGui::InputText("##nolabel", name.data(), 64);
ImGui::PopID();
ImGui::NewLine();
ImGui::TextUnformatted("Comment");
ImGui::Separator();
ImGui::PushID(id + 1);
ImGui::InputTextMultiline("##nolabel", comment.data(), 0xF'FFFF);
ImGui::PopID();
ImGui::NewLine();
id += 2;
}
}
if (bookmarkToRemove != this->m_bookmarks.end()) {
this->m_bookmarks.erase(bookmarkToRemove);
ProjectFile::markDirty();
}
ImGui::EndChild();
}
}
ImGui::End();
}
void ViewBookmarks::createMenu() {
}
}

View File

@@ -1,7 +1,7 @@
#include "views/view_data_inspector.hpp"
#include "providers/provider.hpp"
#include "utils.hpp"
#include "helpers/utils.hpp"
#include <cstring>
@@ -9,26 +9,23 @@ extern int ImTextCharFromUtf8(unsigned int* out_char, const char* in_text, const
namespace hex {
ViewDataInspector::ViewDataInspector(prv::Provider* &dataProvider) : View(), m_dataProvider(dataProvider) {
View::subscribeEvent(Events::ByteSelected, [this](const void* userData){
size_t offset = *static_cast<const size_t*>(userData);
ViewDataInspector::ViewDataInspector(prv::Provider* &dataProvider) : View("Data Inspector"), m_dataProvider(dataProvider) {
View::subscribeEvent(Events::RegionSelected, [this](const void* userData){
Region region = *static_cast<const Region*>(userData);
this->m_validBytes = std::min(this->m_dataProvider->getSize() - offset, sizeof(PreviewData));
this->m_validBytes = std::min(u64(this->m_dataProvider->getSize() - region.address), u64(sizeof(PreviewData)));
std::memset(&this->m_previewData, 0x00, sizeof(PreviewData));
this->m_dataProvider->read(offset, &this->m_previewData, sizeof(PreviewData));
this->m_dataProvider->read(region.address, &this->m_previewData, this->m_validBytes);
this->m_shouldInvalidate = true;
});
}
ViewDataInspector::~ViewDataInspector() {
View::unsubscribeEvent(Events::ByteSelected);
View::unsubscribeEvent(Events::RegionSelected);
}
void ViewDataInspector::createView() {
if (!this->m_windowOpen)
return;
if (this->m_shouldInvalidate) {
this->m_shouldInvalidate = false;
@@ -41,82 +38,89 @@ namespace hex {
this->m_cachedData.emplace_back("Binary (8 bit)", binary);
}
this->m_cachedData.emplace_back("uint8_t", hex::format("%u", this->m_previewData.unsigned8));
this->m_cachedData.emplace_back("int8_t", hex::format("%d", this->m_previewData.unsigned8));
this->m_cachedData.emplace_back("uint16_t", hex::format("%u", this->m_previewData.unsigned16));
this->m_cachedData.emplace_back("int16_t", hex::format("%d", this->m_previewData.unsigned16));
this->m_cachedData.emplace_back("uint32_t", hex::format("%lu", this->m_previewData.unsigned32));
this->m_cachedData.emplace_back("int32_t", hex::format("%ld", this->m_previewData.unsigned32));
this->m_cachedData.emplace_back("uint64_t", hex::format("%llu", this->m_previewData.unsigned64));
this->m_cachedData.emplace_back("int64_t", hex::format("%lld", this->m_previewData.unsigned64));
this->m_cachedData.emplace_back("uint8_t", hex::format("%u", hex::changeEndianess(this->m_previewData.unsigned8, this->m_endianess)));
this->m_cachedData.emplace_back("int8_t", hex::format("%d", hex::changeEndianess(this->m_previewData.signed8, this->m_endianess)));
this->m_cachedData.emplace_back("uint16_t", hex::format("%u", hex::changeEndianess(this->m_previewData.unsigned16, this->m_endianess)));
this->m_cachedData.emplace_back("int16_t", hex::format("%d", hex::changeEndianess(this->m_previewData.signed16, this->m_endianess)));
this->m_cachedData.emplace_back("uint32_t", hex::format("%lu", hex::changeEndianess(this->m_previewData.unsigned32, this->m_endianess)));
this->m_cachedData.emplace_back("int32_t", hex::format("%ld", hex::changeEndianess(this->m_previewData.signed32, this->m_endianess)));
this->m_cachedData.emplace_back("uint64_t", hex::format("%llu", hex::changeEndianess(this->m_previewData.unsigned64, this->m_endianess)));
this->m_cachedData.emplace_back("int64_t", hex::format("%lld", hex::changeEndianess(this->m_previewData.signed64, this->m_endianess)));
this->m_cachedData.emplace_back("ANSI Character / char8_t", hex::format("%c", this->m_previewData.ansiChar));
this->m_cachedData.emplace_back("Wide Character / char16_t", hex::format("%lc", this->m_previewData.wideChar));
this->m_cachedData.emplace_back("ASCII Character", hex::format("'%s'", makePrintable(this->m_previewData.ansiChar).c_str()));
this->m_cachedData.emplace_back("Wide Character", hex::format("'%lc'", this->m_previewData.wideChar == 0 ? '\x01' : hex::changeEndianess(this->m_previewData.wideChar, this->m_endianess)));
{
char buffer[5] = { 0 };
char codepointString[5] = { 0 };
u32 codepoint = 0;
std::memcpy(buffer, &this->m_previewData.utf8Char, 4);
u32 utf8 = 0;
ImTextCharFromUtf8(&utf8, buffer, buffer + 4);
this->m_cachedData.emplace_back("UTF-8 code point", hex::format("U+%08lx", utf8));
u8 codepointSize = ImTextCharFromUtf8(&codepoint, buffer, buffer + 4);
std::memcpy(codepointString, &codepoint, std::min(codepointSize, u8(4)));
this->m_cachedData.emplace_back("UTF-8 code point", hex::format("'%s' (U+%04lx)", codepoint == 0xFFFD ? "Invalid" : codepointString, codepoint));
}
this->m_cachedData.emplace_back("float (32 bit)", hex::format("%e", this->m_previewData.float32));
this->m_cachedData.emplace_back("double (64 bit)", hex::format("%e", this->m_previewData.float64));
this->m_cachedData.emplace_back("float (32 bit)", hex::format("%e", hex::changeEndianess(this->m_previewData.float32, this->m_endianess)));
this->m_cachedData.emplace_back("double (64 bit)", hex::format("%e", hex::changeEndianess(this->m_previewData.float64, this->m_endianess)));
#if defined(_WIN64)
{
std::tm * ptm = _localtime32(&this->m_previewData.time32);
auto endianAdjustedTime = hex::changeEndianess(this->m_previewData.time32, this->m_endianess);
std::tm * ptm = _localtime32(&endianAdjustedTime);
char buffer[32];
if (std::strftime(buffer, 32, "%a, %d.%m.%Y %H:%M:%S", ptm))
if (ptm != nullptr && std::strftime(buffer, 32, "%a, %d.%m.%Y %H:%M:%S", ptm))
this->m_cachedData.emplace_back("__time32_t", buffer);
else
this->m_cachedData.emplace_back("__time32_t", "Invalid");
}
{
std::tm * ptm = _localtime64(&this->m_previewData.time64);
auto endianAdjustedTime = hex::changeEndianess(this->m_previewData.time64, this->m_endianess);
std::tm * ptm = _localtime64(&endianAdjustedTime);
char buffer[64];
if (std::strftime(buffer, 64, "%a, %d.%m.%Y %H:%M:%S", ptm) != 0)
if (ptm != nullptr && std::strftime(buffer, 64, "%a, %d.%m.%Y %H:%M:%S", ptm) != 0)
this->m_cachedData.emplace_back("__time64_t", buffer);
else
this->m_cachedData.emplace_back("__time64_t", "Invalid");
}
#else
{
std::tm * ptm = localtime(&this->m_previewData.time);
auto endianAdjustedTime = hex::changeEndianess(this->m_previewData.time, this->m_endianess);
std::tm * ptm = localtime(&endianAdjustedTime);
char buffer[64];
if (std::strftime(buffer, 64, "%a, %d.%m.%Y %H:%M:%S", ptm) != 0)
if (ptm != nullptr && std::strftime(buffer, 64, "%a, %d.%m.%Y %H:%M:%S", ptm) != 0)
this->m_cachedData.emplace_back("time_t", buffer);
else
this->m_cachedData.emplace_back("time_t", "Invalid");
}
#endif
this->m_cachedData.emplace_back("GUID", hex::format("{%08lX-%04hX-%04hX-%02hhX%02hhX-%02hhX%02hhX%02hhX%02hhX%02hhX%02hhX}",
this->m_previewData.guid.data1, this->m_previewData.guid.data2, this->m_previewData.guid.data3,
this->m_cachedData.emplace_back("GUID", hex::format("%s{%08lX-%04hX-%04hX-%02hhX%02hhX-%02hhX%02hhX%02hhX%02hhX%02hhX%02hhX}",
(this->m_previewData.guid.data3 >> 12) <= 5 && ((this->m_previewData.guid.data4[0] >> 4) >= 8 || (this->m_previewData.guid.data4[0] >> 4) == 0) ? "" : "[INVALID] ",
hex::changeEndianess(this->m_previewData.guid.data1, this->m_endianess),
hex::changeEndianess(this->m_previewData.guid.data2, this->m_endianess),
hex::changeEndianess(this->m_previewData.guid.data3, this->m_endianess),
this->m_previewData.guid.data4[0], this->m_previewData.guid.data4[1], this->m_previewData.guid.data4[2], this->m_previewData.guid.data4[3],
this->m_previewData.guid.data4[4], this->m_previewData.guid.data4[5], this->m_previewData.guid.data4[6], this->m_previewData.guid.data4[7]));
}
if (ImGui::Begin("Data Preview", &this->m_windowOpen)) {
if (ImGui::Begin("Data Inspector", &this->getWindowOpenState(), ImGuiWindowFlags_NoCollapse)) {
if (this->m_dataProvider != nullptr && this->m_dataProvider->isReadable()) {
if (ImGui::BeginChild("##scrolling")) {
if (ImGui::BeginTable("##datainspector", 2, ImGuiTableFlags_Borders | ImGuiTableFlags_Resizable | ImGuiTableFlags_RowBg)) {
if (ImGui::BeginChild("##scrolling", ImVec2(0, ImGui::GetWindowHeight() - 60))) {
if (ImGui::BeginTable("##datainspector", 2, ImGuiTableFlags_Borders | ImGuiTableFlags_Resizable | ImGuiTableFlags_RowBg | ImGuiTableFlags_NoBordersInBody)) {
ImGui::TableSetupColumn("Name");
ImGui::TableSetupColumn("Value");
ImGui::TableHeadersRow();
u32 rowCount = 0;
for (const auto &[name, value] : this->m_cachedData) {
ImGui::TableNextRow();
ImGui::TableNextColumn();
ImGui::TextUnformatted(name.c_str());
ImGui::TableNextColumn();
ImGui::TextUnformatted(value.c_str());
ImGui::TableSetBgColor(ImGuiTableBgTarget_RowBg0,
((rowCount % 2) == 0) ? 0xFF101010 : 0xFF303030);
rowCount++;
}
{
@@ -124,11 +128,8 @@ namespace hex {
ImGui::TableNextColumn();
ImGui::TextUnformatted("RGBA Color");
ImGui::TableNextColumn();
ImGui::ColorButton("##nolabel", ImColor(this->m_previewData.unsigned32),
ImGui::ColorButton("##nolabel", ImColor(hex::changeEndianess(this->m_previewData.unsigned32, this->m_endianess)),
ImGuiColorEditFlags_None, ImVec2(ImGui::GetColumnWidth(), 15));
ImGui::TableSetBgColor(ImGuiTableBgTarget_RowBg0,
((rowCount % 2) == 0) ? 0xFF101010 : 0xFF303030);
rowCount++;
}
@@ -136,16 +137,23 @@ namespace hex {
}
}
ImGui::EndChild();
if (ImGui::RadioButton("Little Endian", this->m_endianess == std::endian::little)) {
this->m_endianess = std::endian::little;
this->m_shouldInvalidate = true;
}
ImGui::SameLine();
if (ImGui::RadioButton("Big Endian", this->m_endianess == std::endian::big)) {
this->m_endianess = std::endian::big;
this->m_shouldInvalidate = true;
}
}
}
ImGui::End();
}
void ViewDataInspector::createMenu() {
if (ImGui::BeginMenu("View")) {
ImGui::MenuItem("Data Preview View", "", &this->m_windowOpen);
ImGui::EndMenu();
}
}
}

View File

@@ -0,0 +1,289 @@
#include "views/view_disassembler.hpp"
#include "providers/provider.hpp"
#include "helpers/utils.hpp"
#include <cstring>
using namespace std::literals::string_literals;
namespace hex {
ViewDisassembler::ViewDisassembler(prv::Provider* &dataProvider) : View("Disassembler"), m_dataProvider(dataProvider) {
View::subscribeEvent(Events::DataChanged, [this](const void*){
this->m_shouldInvalidate = true;
});
View::subscribeEvent(Events::RegionSelected, [this](const void *userData) {
Region region = *static_cast<const Region*>(userData);
if (this->m_shouldMatchSelection) {
this->m_codeRegion[0] = region.address;
this->m_codeRegion[1] = region.address + region.size - 1;
}
});
}
ViewDisassembler::~ViewDisassembler() {
View::unsubscribeEvent(Events::DataChanged);
View::unsubscribeEvent(Events::RegionSelected);
}
void ViewDisassembler::createView() {
if (this->m_shouldInvalidate) {
this->m_disassembly.clear();
csh capstoneHandle;
cs_insn *instructions = nullptr;
cs_mode mode = cs_mode(this->m_modeBasicARM | this->m_modeExtraARM | this->m_modeBasicMIPS | this->m_modeBasicX86 | this->m_modeBasicPPC);
if (this->m_littleEndianMode)
mode = cs_mode(mode | CS_MODE_LITTLE_ENDIAN);
else
mode = cs_mode(mode | CS_MODE_BIG_ENDIAN);
if (this->m_micoMode)
mode = cs_mode(mode | CS_MODE_MICRO);
if (this->m_sparcV9Mode)
mode = cs_mode(mode | CS_MODE_V9);
if (cs_open(Disassembler::toCapstoneArchictecture(this->m_architecture), mode, &capstoneHandle) == CS_ERR_OK) {
std::vector<u8> buffer(2048, 0x00);
for (u64 address = 0; address < (this->m_codeRegion[1] - this->m_codeRegion[0] + 1); address += 2048) {
size_t bufferSize = std::min(u64(2048), (this->m_codeRegion[1] - this->m_codeRegion[0] + 1) - address);
this->m_dataProvider->read(this->m_codeRegion[0] + address, buffer.data(), bufferSize);
size_t instructionCount = cs_disasm(capstoneHandle, buffer.data(), bufferSize, this->m_baseAddress + address, 0, &instructions);
if (instructionCount == 0)
break;
u64 usedBytes = 0;
for (u32 instr = 0; instr < instructionCount; instr++) {
Disassembly disassembly = { 0 };
disassembly.address = instructions[instr].address;
disassembly.offset = this->m_codeRegion[0] + address + usedBytes;
disassembly.size = instructions[instr].size;
disassembly.mnemonic = instructions[instr].mnemonic;
disassembly.operators = instructions[instr].op_str;
for (u8 i = 0; i < instructions[instr].size; i++)
disassembly.bytes += hex::format("%02X ", instructions[instr].bytes[i]);
disassembly.bytes.pop_back();
this->m_disassembly.push_back(disassembly);
usedBytes += instructions[instr].size;
}
if (instructionCount < bufferSize)
address -= (bufferSize - usedBytes);
cs_free(instructions, instructionCount);
}
cs_close(&capstoneHandle);
}
this->m_shouldInvalidate = false;
}
if (ImGui::Begin("Disassembler", &this->getWindowOpenState(), ImGuiWindowFlags_NoCollapse)) {
if (this->m_dataProvider != nullptr && this->m_dataProvider->isReadable()) {
ImGui::TextUnformatted("Position");
ImGui::Separator();
ImGui::InputScalar("Base address", ImGuiDataType_U64, &this->m_baseAddress, nullptr, nullptr, "%08llX", ImGuiInputTextFlags_CharsHexadecimal);
ImGui::InputScalarN("Code region", ImGuiDataType_U64, this->m_codeRegion, 2, nullptr, nullptr, "%08llX", ImGuiInputTextFlags_CharsHexadecimal);
ImGui::Checkbox("Match selection", &this->m_shouldMatchSelection);
ImGui::NewLine();
ImGui::TextUnformatted("Settings");
ImGui::Separator();
ImGui::Combo("Architecture", reinterpret_cast<int*>(&this->m_architecture), Disassembler::ArchitectureNames, Disassembler::getArchitectureSupportedCount());
if (ImGui::BeginChild("modes", ImVec2(0, 100), true)) {
if (ImGui::RadioButton("Little Endian", this->m_littleEndianMode))
this->m_littleEndianMode = true;
ImGui::SameLine();
if (ImGui::RadioButton("Big Endian", !this->m_littleEndianMode))
this->m_littleEndianMode = false;
ImGui::NewLine();
switch (this->m_architecture) {
case Architecture::ARM:
this->m_modeBasicMIPS = cs_mode(0);
this->m_modeBasicX86 = cs_mode(0);
this->m_modeBasicPPC = cs_mode(0);
this->m_micoMode = false;
this->m_sparcV9Mode = false;
if (this->m_modeBasicARM == cs_mode(0))
this->m_modeBasicARM = CS_MODE_ARM;
if (ImGui::RadioButton("ARM mode", this->m_modeBasicARM == CS_MODE_ARM))
this->m_modeBasicARM = CS_MODE_ARM;
ImGui::SameLine();
if (ImGui::RadioButton("Thumb mode", this->m_modeBasicARM == CS_MODE_THUMB))
this->m_modeBasicARM = CS_MODE_THUMB;
if (ImGui::RadioButton("Default mode", (this->m_modeExtraARM & (CS_MODE_MCLASS | CS_MODE_V8)) == 0))
this->m_modeExtraARM = cs_mode(0);
ImGui::SameLine();
if (ImGui::RadioButton("Cortex-M mode", (this->m_modeExtraARM & (CS_MODE_MCLASS | CS_MODE_V8)) == CS_MODE_MCLASS))
this->m_modeExtraARM = CS_MODE_MCLASS;
ImGui::SameLine();
if (ImGui::RadioButton("ARMv8 mode", (this->m_modeExtraARM & (CS_MODE_MCLASS | CS_MODE_V8)) == CS_MODE_V8))
this->m_modeExtraARM = CS_MODE_V8;
break;
case Architecture::MIPS:
this->m_modeBasicARM = cs_mode(0);
this->m_modeExtraARM = cs_mode(0);
this->m_modeBasicX86 = cs_mode(0);
this->m_modeBasicPPC = cs_mode(0);
this->m_sparcV9Mode = false;
if (this->m_modeBasicMIPS == cs_mode(0))
this->m_modeBasicMIPS = CS_MODE_MIPS32;
if (ImGui::RadioButton("MIPS32 mode", this->m_modeBasicMIPS == CS_MODE_MIPS32))
this->m_modeBasicMIPS = CS_MODE_MIPS32;
ImGui::SameLine();
if (ImGui::RadioButton("MIPS64 mode", this->m_modeBasicMIPS == CS_MODE_MIPS64))
this->m_modeBasicMIPS = CS_MODE_MIPS64;
ImGui::SameLine();
if (ImGui::RadioButton("MIPS32R6 mode", this->m_modeBasicMIPS == CS_MODE_MIPS32R6))
this->m_modeBasicMIPS = CS_MODE_MIPS32R6;
ImGui::Checkbox("Micro Mode", &this->m_micoMode);
break;
case Architecture::X86:
this->m_modeBasicARM = cs_mode(0);
this->m_modeExtraARM = cs_mode(0);
this->m_modeBasicMIPS = cs_mode(0);
this->m_modeBasicPPC = cs_mode(0);
this->m_micoMode = false;
this->m_sparcV9Mode = false;
if (this->m_modeBasicX86 == cs_mode(0))
this->m_modeBasicX86 = CS_MODE_16;
if (ImGui::RadioButton("16-bit mode", this->m_modeBasicX86 == CS_MODE_16))
this->m_modeBasicX86 = CS_MODE_16;
ImGui::SameLine();
if (ImGui::RadioButton("32-bit mode", this->m_modeBasicX86 == CS_MODE_32))
this->m_modeBasicX86 = CS_MODE_32;
ImGui::SameLine();
if (ImGui::RadioButton("64-bit mode", this->m_modeBasicX86 == CS_MODE_64))
this->m_modeBasicX86 = CS_MODE_64;
break;
case Architecture::PPC:
this->m_modeBasicARM = cs_mode(0);
this->m_modeExtraARM = cs_mode(0);
this->m_modeBasicMIPS = cs_mode(0);
this->m_modeBasicX86 = cs_mode(0);
this->m_micoMode = false;
this->m_sparcV9Mode = false;
if (m_modeBasicPPC == cs_mode(0))
this->m_modeBasicPPC = CS_MODE_32;
if (ImGui::RadioButton("32-bit mode", this->m_modeBasicPPC == CS_MODE_32))
this->m_modeBasicPPC = CS_MODE_32;
ImGui::SameLine();
if (ImGui::RadioButton("64-bit mode", this->m_modeBasicPPC == CS_MODE_64))
this->m_modeBasicPPC = CS_MODE_64;
break;
case Architecture::SPARC:
this->m_modeBasicARM = cs_mode(0);
this->m_modeExtraARM = cs_mode(0);
this->m_modeBasicMIPS = cs_mode(0);
this->m_modeBasicX86 = cs_mode(0);
this->m_modeBasicPPC = cs_mode(0);
this->m_micoMode = false;
ImGui::Checkbox("Sparc V9 mode", &this->m_sparcV9Mode);
break;
case Architecture::ARM64:
case Architecture::SYSZ:
case Architecture::XCORE:
case Architecture::M68K:
case Architecture::TMS320C64X:
case Architecture::M680X:
case Architecture::EVM:
this->m_modeBasicARM = cs_mode(0);
this->m_modeExtraARM = cs_mode(0);
this->m_modeBasicMIPS = cs_mode(0);
this->m_modeBasicX86 = cs_mode(0);
this->m_modeBasicPPC = cs_mode(0);
this->m_micoMode = false;
this->m_sparcV9Mode = false;
break;
}
}
ImGui::EndChild();
ImGui::SetCursorPosX((ImGui::GetContentRegionAvailWidth() - 300) / 2);
if (ImGui::Button("Disassemble", ImVec2(300, 20)))
this->m_shouldInvalidate = true;
ImGui::NewLine();
ImGui::TextUnformatted("Disassembly");
ImGui::Separator();
if (ImGui::BeginTable("##disassembly", 4, ImGuiTableFlags_ScrollY | ImGuiTableFlags_Borders | ImGuiTableFlags_Resizable | ImGuiTableFlags_RowBg | ImGuiTableFlags_Reorderable)) {
ImGui::TableSetupScrollFreeze(0, 1);
ImGui::TableSetupColumn("Address");
ImGui::TableSetupColumn("Offset");
ImGui::TableSetupColumn("Bytes");
ImGui::TableSetupColumn("Disassembly");
ImGuiListClipper clipper;
clipper.Begin(this->m_disassembly.size());
ImGui::TableHeadersRow();
while (clipper.Step()) {
for (u64 i = clipper.DisplayStart; i < clipper.DisplayEnd; i++) {
ImGui::TableNextRow();
ImGui::TableNextColumn();
if (ImGui::Selectable(("##DisassemblyLine"s + std::to_string(i)).c_str(), false, ImGuiSelectableFlags_SpanAllColumns)) {
Region selectRegion = { this->m_disassembly[i].offset, this->m_disassembly[i].size };
View::postEvent(Events::SelectionChangeRequest, &selectRegion);
}
ImGui::SameLine();
ImGui::Text("0x%llx", this->m_disassembly[i].address);
ImGui::TableNextColumn();
ImGui::Text("0x%llx", this->m_disassembly[i].offset);
ImGui::TableNextColumn();
ImGui::TextUnformatted(this->m_disassembly[i].bytes.c_str());
ImGui::TableNextColumn();
ImGui::TextColored(ImColor(0xFFD69C56), "%s", this->m_disassembly[i].mnemonic.c_str());
ImGui::SameLine();
ImGui::TextUnformatted(this->m_disassembly[i].operators.c_str());
}
}
clipper.End();
ImGui::EndTable();
}
}
}
ImGui::End();
}
void ViewDisassembler::createMenu() {
}
}

View File

@@ -2,71 +2,74 @@
#include "providers/provider.hpp"
#include "crypto.hpp"
#include "helpers/crypto.hpp"
#include <vector>
#ifdef __MINGW32__
#include <winsock.h>
#else
#include <arpa/inet.h>
#endif
#include "helpers/utils.hpp"
namespace hex {
ViewHashes::ViewHashes(prv::Provider* &dataProvider) : View(), m_dataProvider(dataProvider) {
ViewHashes::ViewHashes(prv::Provider* &dataProvider) : View("Hashes"), m_dataProvider(dataProvider) {
View::subscribeEvent(Events::DataChanged, [this](const void*){
this->m_shouldInvalidate = true;
});
View::subscribeEvent(Events::RegionSelected, [this](const void *userData) {
Region region = *static_cast<const Region*>(userData);
if (this->m_shouldMatchSelection) {
this->m_hashRegion[0] = region.address;
this->m_hashRegion[1] = region.address + region.size - 1;
this->m_shouldInvalidate = true;
}
});
}
ViewHashes::~ViewHashes() {
View::unsubscribeEvent(Events::DataChanged);
View::unsubscribeEvent(Events::RegionSelected);
}
static void formatBigHexInt(auto dataArray, char *buffer, size_t bufferSize) {
for (int i = 0; i < dataArray.size(); i++)
snprintf(buffer + 8 * i, bufferSize - 8 * i, "%08X", htonl(dataArray[i]));
snprintf(buffer + 8 * i, bufferSize - 8 * i, "%08X", hex::changeEndianess(dataArray[i], std::endian::big));
}
void ViewHashes::createView() {
if (!this->m_windowOpen)
return;
if (ImGui::Begin("Hashing", &this->m_windowOpen)) {
if (ImGui::Begin("Hashing", &this->getWindowOpenState(), ImGuiWindowFlags_NoCollapse)) {
ImGui::BeginChild("##scrolling", ImVec2(0, 0), false, ImGuiWindowFlags_NoMove | ImGuiWindowFlags_NoNav);
ImGui::NewLine();
if (this->m_dataProvider != nullptr && this->m_dataProvider->isAvailable()) {
ImGui::TextUnformatted("Region");
ImGui::Separator();
ImGui::InputScalarN("##nolabel", ImGuiDataType_U64, this->m_hashRegion, 2, nullptr, nullptr, "%08X", ImGuiInputTextFlags_CharsHexadecimal);
if (ImGui::IsItemEdited()) this->m_shouldInvalidate = true;
ImGui::Checkbox("Match selection", &this->m_shouldMatchSelection);
if (ImGui::IsItemEdited()) this->m_shouldInvalidate = true;
ImGui::NewLine();
ImGui::TextUnformatted("Settings");
ImGui::Separator();
if (ImGui::Combo("Hash Function", &this->m_currHashFunction, HashFunctionNames,sizeof(HashFunctionNames) / sizeof(const char *)))
this->m_shouldInvalidate = true;
ImGui::NewLine();
ImGui::Separator();
ImGui::NewLine();
ImGui::InputInt("Begin", &this->m_hashStart, 0, 0, ImGuiInputTextFlags_CharsHexadecimal);
if (ImGui::IsItemEdited()) this->m_shouldInvalidate = true;
ImGui::InputInt("End", &this->m_hashEnd, 0, 0, ImGuiInputTextFlags_CharsHexadecimal);
if (ImGui::IsItemEdited()) this->m_shouldInvalidate = true;
size_t dataSize = this->m_dataProvider->getSize();
if (this->m_hashEnd >= dataSize)
this->m_hashEnd = dataSize - 1;
if (this->m_hashRegion[1] >= dataSize)
this->m_hashRegion[1] = dataSize - 1;
ImGui::NewLine();
ImGui::Separator();
ImGui::NewLine();
if (this->m_hashEnd >= this->m_hashStart) {
if (this->m_hashRegion[1] >= this->m_hashRegion[0]) {
switch (this->m_currHashFunction) {
case 0: // CRC16
{
int polynomial = 0, init = 0;
static int polynomial = 0, init = 0;
ImGui::InputInt("Initial Value", &init, 0, 0, ImGuiInputTextFlags_CharsHexadecimal);
if (ImGui::IsItemEdited()) this->m_shouldInvalidate = true;
@@ -74,23 +77,24 @@ namespace hex {
ImGui::InputInt("Polynomial", &polynomial, 0, 0, ImGuiInputTextFlags_CharsHexadecimal);
if (ImGui::IsItemEdited()) this->m_shouldInvalidate = true;
ImGui::NewLine();
ImGui::Separator();
ImGui::NewLine();
static u16 result = 0;
if (this->m_shouldInvalidate)
result = crc16(this->m_dataProvider, this->m_hashStart, this->m_hashEnd - this->m_hashStart + 1, polynomial, init);
result = crc16(this->m_dataProvider, this->m_hashRegion[0], this->m_hashRegion[1] - this->m_hashRegion[0] + 1, polynomial, init);
char buffer[sizeof(result) * 2 + 1];
snprintf(buffer, sizeof(buffer), "%04X", result);
ImGui::InputText("Hash value", buffer, ImGuiInputTextFlags_ReadOnly);
ImGui::TextUnformatted("Result");
ImGui::Separator();
ImGui::InputText("##nolabel", buffer, ImGuiInputTextFlags_ReadOnly);
}
break;
case 1: // CRC32
{
int polynomial = 0, init = 0;
static int polynomial = 0, init = 0;
ImGui::InputInt("Initial Value", &init, 0, 0, ImGuiInputTextFlags_CharsHexadecimal);
if (ImGui::IsItemEdited()) this->m_shouldInvalidate = true;
@@ -98,18 +102,19 @@ namespace hex {
ImGui::InputInt("Polynomial", &polynomial, 0, 0, ImGuiInputTextFlags_CharsHexadecimal);
if (ImGui::IsItemEdited()) this->m_shouldInvalidate = true;
ImGui::NewLine();
ImGui::Separator();
ImGui::NewLine();
static u32 result = 0;
if (this->m_shouldInvalidate)
result = crc32(this->m_dataProvider, this->m_hashStart, this->m_hashEnd - this->m_hashStart + 1, polynomial, init);
result = crc32(this->m_dataProvider, this->m_hashRegion[0], this->m_hashRegion[1] - this->m_hashRegion[0] + 1, polynomial, init);
char buffer[sizeof(result) * 2 + 1];
snprintf(buffer, sizeof(buffer), "%08X", result);
ImGui::InputText("Hash value", buffer, ImGuiInputTextFlags_ReadOnly);
ImGui::TextUnformatted("Result");
ImGui::Separator();
ImGui::InputText("##nolabel", buffer, ImGuiInputTextFlags_ReadOnly);
}
break;
case 2: // MD4
@@ -117,11 +122,15 @@ namespace hex {
static std::array<u32, 4> result;
if (this->m_shouldInvalidate)
result = md4(this->m_dataProvider, this->m_hashStart, this->m_hashEnd - this->m_hashStart + 1);
result = md4(this->m_dataProvider, this->m_hashRegion[0], this->m_hashRegion[1] - this->m_hashRegion[0] + 1);
char buffer[sizeof(result) * 2 + 1];
formatBigHexInt(result, buffer, sizeof(buffer));
ImGui::InputText("Hash value", buffer, ImGuiInputTextFlags_ReadOnly);
ImGui::NewLine();
ImGui::TextUnformatted("Result");
ImGui::Separator();
ImGui::InputText("##nolabel", buffer, ImGuiInputTextFlags_ReadOnly);
}
break;
case 3: // MD5
@@ -129,11 +138,15 @@ namespace hex {
static std::array<u32, 4> result = { 0 };
if (this->m_shouldInvalidate)
result = md5(this->m_dataProvider, this->m_hashStart, this->m_hashEnd - this->m_hashStart + 1);
result = md5(this->m_dataProvider, this->m_hashRegion[0], this->m_hashRegion[1] - this->m_hashRegion[0] + 1);
char buffer[sizeof(result) * 2 + 1];
formatBigHexInt(result, buffer, sizeof(buffer));
ImGui::InputText("Hash value", buffer, ImGuiInputTextFlags_ReadOnly);
ImGui::NewLine();
ImGui::TextUnformatted("Result");
ImGui::Separator();
ImGui::InputText("##nolabel", buffer, ImGuiInputTextFlags_ReadOnly);
}
break;
case 4: // SHA-1
@@ -141,11 +154,15 @@ namespace hex {
static std::array<u32, 5> result = { 0 };
if (this->m_shouldInvalidate)
result = sha1(this->m_dataProvider, this->m_hashStart, this->m_hashEnd - this->m_hashStart + 1);
result = sha1(this->m_dataProvider, this->m_hashRegion[0], this->m_hashRegion[1] - this->m_hashRegion[0] + 1);
char buffer[sizeof(result) * 2 + 1];
formatBigHexInt(result, buffer, sizeof(buffer));
ImGui::InputText("Hash value", buffer, ImGuiInputTextFlags_ReadOnly);
ImGui::NewLine();
ImGui::TextUnformatted("Result");
ImGui::Separator();
ImGui::InputText("##nolabel", buffer, ImGuiInputTextFlags_ReadOnly);
}
break;
case 5: // SHA-224
@@ -153,11 +170,15 @@ namespace hex {
static std::array<u32, 7> result = { 0 };
if (this->m_shouldInvalidate)
result = sha224(this->m_dataProvider, this->m_hashStart, this->m_hashEnd - this->m_hashStart + 1);
result = sha224(this->m_dataProvider, this->m_hashRegion[0], this->m_hashRegion[1] - this->m_hashRegion[0] + 1);
char buffer[sizeof(result) * 2 + 1];
formatBigHexInt(result, buffer, sizeof(buffer));
ImGui::InputText("Hash value", buffer, ImGuiInputTextFlags_ReadOnly);
ImGui::NewLine();
ImGui::TextUnformatted("Result");
ImGui::Separator();
ImGui::InputText("##nolabel", buffer, ImGuiInputTextFlags_ReadOnly);
}
break;
case 6: // SHA-256
@@ -165,11 +186,15 @@ namespace hex {
static std::array<u32, 8> result;
if (this->m_shouldInvalidate)
result = sha256(this->m_dataProvider, this->m_hashStart, this->m_hashEnd - this->m_hashStart + 1);
result = sha256(this->m_dataProvider, this->m_hashRegion[0], this->m_hashRegion[1] - this->m_hashRegion[0] + 1);
char buffer[sizeof(result) * 2 + 1];
formatBigHexInt(result, buffer, sizeof(buffer));
ImGui::InputText("Hash value", buffer, ImGuiInputTextFlags_ReadOnly);
ImGui::NewLine();
ImGui::TextUnformatted("Result");
ImGui::Separator();
ImGui::InputText("##nolabel", buffer, ImGuiInputTextFlags_ReadOnly);
}
break;
case 7: // SHA-384
@@ -177,11 +202,15 @@ namespace hex {
static std::array<u32, 12> result;
if (this->m_shouldInvalidate)
result = sha384(this->m_dataProvider, this->m_hashStart, this->m_hashEnd - this->m_hashStart + 1);
result = sha384(this->m_dataProvider, this->m_hashRegion[0], this->m_hashRegion[1] - this->m_hashRegion[0] + 1);
char buffer[sizeof(result) * 2 + 1];
formatBigHexInt(result, buffer, sizeof(buffer));
ImGui::InputText("Hash value", buffer, ImGuiInputTextFlags_ReadOnly);
ImGui::NewLine();
ImGui::TextUnformatted("Result");
ImGui::Separator();
ImGui::InputText("##nolabel", buffer, ImGuiInputTextFlags_ReadOnly);
}
break;
case 8: // SHA-512
@@ -189,11 +218,15 @@ namespace hex {
static std::array<u32, 16> result;
if (this->m_shouldInvalidate)
result = sha512(this->m_dataProvider, this->m_hashStart, this->m_hashEnd - this->m_hashStart + 1);
result = sha512(this->m_dataProvider, this->m_hashRegion[0], this->m_hashRegion[1] - this->m_hashRegion[0] + 1);
char buffer[sizeof(result) * 2 + 1];
formatBigHexInt(result, buffer, sizeof(buffer));
ImGui::InputText("Hash value", buffer, ImGuiInputTextFlags_ReadOnly);
ImGui::NewLine();
ImGui::TextUnformatted("Result");
ImGui::Separator();
ImGui::InputText("##nolabel", buffer, ImGuiInputTextFlags_ReadOnly);
}
break;
}
@@ -208,10 +241,7 @@ namespace hex {
}
void ViewHashes::createMenu() {
if (ImGui::BeginMenu("View")) {
ImGui::MenuItem("Hash View", "", &this->m_windowOpen);
ImGui::EndMenu();
}
}
}

View File

@@ -2,14 +2,32 @@
namespace hex {
ViewHelp::ViewHelp() {
ViewHelp::ViewHelp() : View("Help") {
this->getWindowOpenState() = true;
}
ViewHelp::~ViewHelp() {
}
static void drawTitle(const std::string &title) {
ImGui::TextColored(ImVec4(0.6F, 0.6F, 1.0F, 1.0F), title.c_str());
}
static void drawCodeSegment(const std::string &id, const std::string &code) {
ImGui::PushStyleColor(ImGuiCol_ChildBg, ImVec4(0.2F, 0.2F, 0.2F, 0.3F));
ImGui::BeginChild(id.c_str(), ImVec2(-1, ImGui::CalcTextSize(code.c_str()).y));
ImGui::Text("%s", code.c_str());
ImGui::EndChild();
ImGui::NewLine();
ImGui::PopStyleColor();
};
void ViewHelp::drawAboutPopup() {
ImGui::SetNextWindowSizeConstraints(ImVec2(450, 300), ImVec2(450, 300));
if (ImGui::BeginPopupModal("About", &this->m_aboutWindowOpen, ImGuiWindowFlags_NoResize)) {
@@ -32,10 +50,14 @@ namespace hex {
ImGui::BulletText("imgui_club by ocornut");
ImGui::BulletText("ImGui-Addons by gallickgunner");
ImGui::BulletText("ImGuiColorTextEdit by BalazsJako");
ImGui::BulletText("capstone by aquynh");
ImGui::BulletText("JSON for Modern C++ by nlohmann");
ImGui::NewLine();
ImGui::BulletText("GNU libmagic");
ImGui::BulletText("OpenSSL libcrypto");
ImGui::BulletText("GLFW3");
ImGui::BulletText("LLVM");
ImGui::BulletText("Python 3");
ImGui::EndChild();
ImGui::PopStyleColor();
@@ -44,44 +66,38 @@ namespace hex {
}
void ViewHelp::drawPatternHelpPopup() {
ImGui::SetNextWindowSizeConstraints(ImVec2(450, 300), ImVec2(450, 300));
constexpr static auto DrawTitle = [](const std::string &title) {
ImGui::TextColored(ImVec4(0.6F, 0.6F, 1.0F, 1.0F), title.c_str());
};
constexpr static auto DrawCodeSegment = [](const std::string &id, size_t height, const std::string &code) {
ImGui::PushStyleColor(ImGuiCol_ChildBg, ImVec4(0.2F, 0.2F, 0.2F, 0.3F));
ImGui::NewLine();
ImGui::BeginChild(id.c_str(), ImVec2(-1, height));
ImGui::Text("%s", code.c_str());
ImGui::EndChild();
ImGui::NewLine();
ImGui::PopStyleColor();
};
if (!this->m_patternHelpWindowOpen) return;
ImGui::SetNextWindowSizeConstraints(ImVec2(450, 300), ImVec2(2000, 1000));
if (ImGui::BeginPopupModal("Cheat Sheet", &this->m_patternHelpWindowOpen)) {
if (ImGui::Begin("Pattern Language Cheat Sheet", &this->m_patternHelpWindowOpen)) {
ImGui::Text("ImHex Pattern Language Cheat Sheet");
ImGui::Separator();
ImGui::NewLine();
DrawTitle("Preprocessor directives");
drawTitle("Preprocessor directives");
ImGui::TextWrapped(
"Preprocessor directives can be used to alter the code before it's being parsed. Supported are "
"#define to replace one string with another and #include to include a separate file");
DrawCodeSegment("preprocessor", 40,
drawCodeSegment("preprocessor",
"#define HEADER_OFFSET 0x100\n"
"#include <cstdint.hexpat>\n"
"#include \"mypattern.hexpat\""
);
DrawTitle("Built-in types");
drawTitle("Pragma directives");
ImGui::TextWrapped(
"Pragma directives are used to give ImHex additional information about the code being read. Currently "
"the following directives are supported.");
drawCodeSegment("pragma",
"// Allow this file to be loaded as pattern when a file\n"
"// identified as application/x-executable gets loaded\n"
"#pragma MIME application/x-executable\n"
);
drawTitle("Built-in types");
ImGui::TextWrapped(
"The following built-in types are available for use");
DrawCodeSegment("built-in", 80,
drawCodeSegment("built-in",
"u8, s8\n"
"u16, s16\n"
"u32, s32\n"
@@ -90,111 +106,201 @@ namespace hex {
"float, double"
);
DrawTitle("Variables and Arrays");
drawTitle("Variables and Arrays");
ImGui::TextWrapped(
"Normal variables as well as arrays are used to highlight and display values.");
DrawCodeSegment("vars arrays", 30,
"Normal variables as well as arrays are used to highlight and display values. "
"It is possible to create arrays within structs and unions that use the value of a previously "
"declared variable as size. In order to override the native / globally set endianess setting, every "
"type in a variable declaration may be prefixed with be or le to turn only it into a big or little endian type.");
drawCodeSegment("vars arrays",
"u32 variable;\n"
"s8 string[16];"
"s8 string[16];\n"
"u8 customSizedArray[variable];\n"
"be u32 bigEndianVariable;"
);
DrawTitle("Structs");
drawTitle("Structs");
ImGui::TextWrapped(
"To bundle multiple variables together, a struct can be used. To insert padding bytes which won't show "
"up in the pattern data view or be highlighted, use the padding[size] syntax.");
DrawCodeSegment("struct", 85,
drawCodeSegment("struct",
"struct Header {\n"
" u32 magic;\n"
" u8 version;\n"
" padding[4];\n"
" Flags flags;\n"
"}"
"};"
);
DrawTitle("Unions");
drawTitle("Unions");
ImGui::TextWrapped(
"A union is used to make two or more variables occupy the same region of memory. "
"The union will have the size of the biggest contained variable.");
DrawCodeSegment("union", 55,
drawCodeSegment("union",
"union Color {\n"
" u32 rgba;\n"
" Components components;\n"
"}"
"};"
);
DrawTitle("Bitfields");
drawTitle("Pointers");
ImGui::TextWrapped(
"\"Another possible type of member in structs and unions are pointers. They are variables"
"whose value is used as an offset from the start of the file to locate the actual offset. "
"The leading type is treated as the data being pointed to and the trailing type as the size of the pointer.");
drawCodeSegment("pointer",
"Data *data : u16;"
);
drawTitle("Bitfields");
ImGui::TextWrapped(
"To decode values that are stored in fields that don't follow the typical 8 bit alignment, bitfields can be used. "
"The size of these fields get specified in numbers of bits.");
DrawCodeSegment("bitfield", 70,
drawCodeSegment("bitfield",
"bitfield Permission {\n"
" r : 1;\n"
" w : 1;\n"
" x : 1;\n"
"}"
"};"
);
DrawTitle("Enum");
drawTitle("Enum");
ImGui::TextWrapped(
"If a value can only be a few specific values with special meaning, an enum can be used. "
"The underlying type has to be specified using a unsigned, built-in type after the name. "
"Entries can be listed with or without a value. The values start counting at zero and increase by one "
"for every next entry");
DrawCodeSegment("enum", 70,
drawCodeSegment("enum",
"enum OperatingSystem : u8 {\n"
" Windows = 0x10,\n"
" MacOSX,\n"
" Linux\n"
"}"
" Linux = 'L'\n"
"};"
);
DrawTitle("Using declarations");
drawTitle("Using declarations");
ImGui::TextWrapped(
"A using declaration can be used to create type aliases for already existing types. This can be "
"a built-in type, a struct, enum or another alias type.");
DrawCodeSegment("using", 15,
drawCodeSegment("using",
"using magic_t = u32;"
);
DrawTitle("Comments");
drawTitle("Comments");
ImGui::TextWrapped(
"To create a comment the C // or /* */ syntax can be used. //-style comments end at the next new line "
"and /*-style comments only end when at the next */.");
DrawCodeSegment("comment", 55,
drawCodeSegment("comment",
"// This is a single line comment\n\n"
"/* This is a\n"
"multiline comment */"
);
DrawTitle("Variable placement");
drawTitle("Variable placement");
ImGui::TextWrapped(
"In order to highlight bytes and displaying their value in the pattern data window, "
"a variable needs to be created and placed in memory. The following line of code creates"
"a unsigned 32 bit variable named data and places it at offset 0x100."
);
DrawCodeSegment("var placement", 15, "u32 data @ 0x100;");
ImGui::EndPopup();
drawCodeSegment("var placement", "u32 data @ 0x100;");
}
ImGui::End();
}
void ViewHelp::drawMathEvaluatorHelp() {
if (!this->m_mathHelpWindowOpen) return;
ImGui::SetNextWindowSizeConstraints(ImVec2(450, 300), ImVec2(2000, 1000));
if (ImGui::Begin("Calculator Cheat Sheet", &this->m_mathHelpWindowOpen)) {
ImGui::Text("ImHex Math Evaluator Cheat Sheet");
ImGui::Separator();
ImGui::NewLine();
ImGui::TextWrapped("ImGui has a simple math evaluator / calculator built-in. It works by parsing the "
"expressions passed to it and displaying the result in the History table.");
ImGui::NewLine();
drawTitle("Basic Operators");
ImGui::TextWrapped(
"The following basic mathematical operators are supported");
drawCodeSegment("basicOperators",
"+ : Addition\n"
"- : Subtraction\n"
"* : Multiplication\n"
"/ : Division\n"
"% : Modulus\n"
"** : Exponentiation\n"
"() : Brackets\n"
"x = : Variable assignment");
drawTitle("Bitwise Operators");
ImGui::TextWrapped(
"The following bitwise operators are supported");
drawCodeSegment("bitwiseOperators",
"& : Bitwise AND\n"
"| : Bitwise OR\n"
"^ : Bitwise XOR\n"
"~ : Bitwise NOT\n"
"<< : Logical shift left\n"
">> : Logical shift right\n"
"## : Bitwise concatenation");
drawTitle("Boolean Operators");
ImGui::TextWrapped(
"The following bitwise operators are supported");
drawCodeSegment("booleanOperators",
"&& : Boolean AND\n"
"|| : Boolean OR\n"
"^^ : Boolean XOR\n"
"! : Boolean NOT\n"
"== : Equality comparison\n"
"!= : Inequality comparison\n"
"> : Greater than comparison\n"
"< : Less than comparison\n"
">= : Greater than or equals comparison\n"
"<= : Less than or equals comparison");
drawTitle("Built-in Functions");
ImGui::TextWrapped(
"The following functions are built into the evaluator");
drawCodeSegment("functions",
"sin(x) : Sine function\n"
"cos(x) : Cosine function\n"
"tan(x) : Tangent function\n"
"ceil(x) : Rounds value to next bigger integer\n"
"floor(x) : Rounds value to next smaller integer\n"
"sign(x) : Returns the value's sign\n"
"abs(x) : Absolute value\n"
"ln(x) : Natural Logarithm\n"
"lb(x) : Logarithm with base 2\n"
"log(x, b) : Logarithm with custom base. Defaults to base 10\n"
"clear() : Clear History and variables\n"
"read(a) : Read 1 byte from loaded data\n"
"write(a, x) : Write 1 byte to loaded data");
}
ImGui::End();
}
void ViewHelp::createView() {
this->drawAboutPopup();
this->drawPatternHelpPopup();
this->drawMathEvaluatorHelp();
}
void ViewHelp::createMenu() {
if (ImGui::BeginMenu("Help")) {
if (ImGui::MenuItem("About", ""))
View::doLater([this]{
ImGui::OpenPopup("About");
this->m_aboutWindowOpen = true;
});
if (ImGui::MenuItem("About", "")) {
View::doLater([] { ImGui::OpenPopup("About"); });
this->m_aboutWindowOpen = true;
}
ImGui::Separator();
if (ImGui::MenuItem("Cheat Sheet", ""))
View::doLater([this]{
ImGui::OpenPopup("Cheat Sheet");
this->m_patternHelpWindowOpen = true;
});
if (ImGui::MenuItem("Pattern Language Cheat Sheet", "")) {
this->m_patternHelpWindowOpen = true;
}
if (ImGui::MenuItem("Calculator Cheat Sheet", "")) {
this->m_mathHelpWindowOpen = true;
}
ImGui::EndMenu();
}
}

View File

@@ -5,10 +5,18 @@
#include <GLFW/glfw3.h>
#include "helpers/crypto.hpp"
#include "helpers/patches.hpp"
#include "helpers/project_file_handler.hpp"
#include "helpers/loader_script_handler.hpp"
#undef __STRICT_ANSI__
#include <cstdio>
namespace hex {
ViewHexEditor::ViewHexEditor(prv::Provider* &dataProvider, std::vector<lang::PatternData*> &patternData)
: View(), m_dataProvider(dataProvider), m_patternData(patternData) {
: View("Hex Editor"), m_dataProvider(dataProvider), m_patternData(patternData) {
this->m_memoryEditor.ReadFn = [](const ImU8 *data, size_t off) -> ImU8 {
ViewHexEditor *_this = (ViewHexEditor *) data;
@@ -30,6 +38,7 @@ namespace hex {
_this->m_dataProvider->write(off, &d, sizeof(ImU8));
_this->postEvent(Events::DataChanged);
ProjectFile::markDirty();
};
this->m_memoryEditor.HighlightFn = [](const ImU8 *data, size_t off, bool next) -> bool {
@@ -56,6 +65,33 @@ namespace hex {
if (filePath != nullptr)
this->openFile(filePath);
});
View::subscribeEvent(Events::SelectionChangeRequest, [this](const void *userData) {
const Region &region = *reinterpret_cast<const Region*>(userData);
auto page = this->m_dataProvider->getPageOfAddress(region.address);
if (!page.has_value())
return;
this->m_dataProvider->setCurrentPage(page.value());
this->m_memoryEditor.GotoAddr = region.address;
this->m_memoryEditor.DataPreviewAddr = region.address;
this->m_memoryEditor.DataPreviewAddrEnd = region.address + region.size - 1;
View::postEvent(Events::RegionSelected, &region);
});
View::subscribeEvent(Events::ProjectFileLoad, [this](const void *userData) {
this->openFile(ProjectFile::getFilePath());
});
View::subscribeEvent(Events::WindowClosing, [this](const void *userData) {
auto window = const_cast<GLFWwindow*>(static_cast<const GLFWwindow*>(userData));
if (ProjectFile::hasUnsavedChanges()) {
glfwSetWindowShouldClose(window, GLFW_FALSE);
View::doLater([] { ImGui::OpenPopup("Save Changes"); });
}
});
}
ViewHexEditor::~ViewHexEditor() {
@@ -65,30 +101,405 @@ namespace hex {
}
void ViewHexEditor::createView() {
if (!this->m_memoryEditor.Open)
return;
size_t dataSize = (this->m_dataProvider == nullptr || !this->m_dataProvider->isReadable()) ? 0x00 : this->m_dataProvider->getSize();
this->m_memoryEditor.DrawWindow("Hex Editor", this, dataSize);
this->m_memoryEditor.DrawWindow("Hex Editor", &this->getWindowOpenState(), this, dataSize, dataSize == 0 ? 0x00 : this->m_dataProvider->getBaseAddress());
if (dataSize != 0x00) {
ImGui::Begin("Hex Editor");
ImGui::SameLine();
ImGui::Text("Page %d / %d", this->m_dataProvider->getCurrentPage() + 1, this->m_dataProvider->getPageCount());
ImGui::SameLine();
if (ImGui::ArrowButton("prevPage", ImGuiDir_Left)) {
this->m_dataProvider->setCurrentPage(this->m_dataProvider->getCurrentPage() - 1);
Region dataPreview = { std::min(this->m_memoryEditor.DataPreviewAddr, this->m_memoryEditor.DataPreviewAddrEnd), 1 };
View::postEvent(Events::RegionSelected, &dataPreview);
}
ImGui::SameLine();
if (ImGui::ArrowButton("nextPage", ImGuiDir_Right)) {
this->m_dataProvider->setCurrentPage(this->m_dataProvider->getCurrentPage() + 1);
Region dataPreview = { std::min(this->m_memoryEditor.DataPreviewAddr, this->m_memoryEditor.DataPreviewAddrEnd), 1 };
View::postEvent(Events::RegionSelected, &dataPreview);
}
ImGui::End();
this->drawSearchPopup();
this->drawGotoPopup();
}
if (ImGui::BeginPopupModal("Save Changes", nullptr, ImGuiWindowFlags_NoResize)) {
constexpr auto Message = "You have unsaved changes made to your Project. Are you sure you want to exit?";
ImGui::NewLine();
if (ImGui::BeginChild("##scrolling", ImVec2(300, 50))) {
ImGui::SetCursorPosX(10);
ImGui::TextWrapped("%s", Message);
ImGui::EndChild();
}
ImGui::SetCursorPosX(40);
if (ImGui::Button("Yes", ImVec2(100, 20)))
std::exit(0);
ImGui::SameLine();
ImGui::SetCursorPosX(160);
if (ImGui::Button("No", ImVec2(100, 20)))
ImGui::CloseCurrentPopup();
ImGui::EndPopup();
}
if (ImGui::BeginPopupModal("Load File with Loader Script", nullptr, ImGuiWindowFlags_NoResize)) {
constexpr auto Message = "Load a file using a Python loader script.";
ImGui::NewLine();
if (ImGui::BeginChild("##scrolling", ImVec2(500, 20))) {
ImGui::SetCursorPosX(10);
ImGui::TextWrapped("%s", Message);
ImGui::EndChild();
}
ImGui::NewLine();
ImGui::InputText("##nolabel", this->m_loaderScriptScriptPath.data(), this->m_loaderScriptScriptPath.length(), ImGuiInputTextFlags_ReadOnly);
ImGui::SameLine();
if (ImGui::Button("Script"))
ImGui::OpenPopup("Loader Script: Open Script");
ImGui::InputText("##nolabel", this->m_loaderScriptFilePath.data(), this->m_loaderScriptFilePath.length(), ImGuiInputTextFlags_ReadOnly);
ImGui::SameLine();
if (ImGui::Button("File"))
ImGui::OpenPopup("Loader Script: Open File");
if (this->m_fileBrowser.showFileDialog("Loader Script: Open Script", imgui_addons::ImGuiFileBrowser::DialogMode::OPEN, ImVec2(0, 0), ".py")) {
this->m_loaderScriptScriptPath = this->m_fileBrowser.selected_path;
}
if (this->m_fileBrowser.showFileDialog("Loader Script: Open File", imgui_addons::ImGuiFileBrowser::DialogMode::OPEN)) {
this->m_loaderScriptFilePath = this->m_fileBrowser.selected_path;
}
ImGui::NewLine();
ImGui::SetCursorPosX(40);
if (ImGui::Button("Load", ImVec2(100, 20))) {
if (!this->m_loaderScriptScriptPath.empty() && !this->m_loaderScriptFilePath.empty()) {
this->openFile(this->m_loaderScriptFilePath);
LoaderScript::setFilePath(this->m_loaderScriptFilePath);
LoaderScript::setDataProvider(this->m_dataProvider);
LoaderScript::processFile(this->m_loaderScriptScriptPath);
ImGui::CloseCurrentPopup();
}
}
ImGui::SameLine();
ImGui::SetCursorPosX(160);
if (ImGui::Button("Cancel", ImVec2(100, 20))) {
ImGui::CloseCurrentPopup();
}
ImGui::EndPopup();
}
if (this->m_fileBrowser.showFileDialog("Open File", imgui_addons::ImGuiFileBrowser::DialogMode::OPEN)) {
this->openFile(this->m_fileBrowser.selected_path);
}
if (this->m_fileBrowser.showFileDialog("Open Base64 File", imgui_addons::ImGuiFileBrowser::DialogMode::OPEN)) {
std::vector<u8> base64;
this->loadFromFile(this->m_fileBrowser.selected_path, base64);
if (!base64.empty()) {
this->m_dataToSave = decode64(base64);
if (this->m_dataToSave.empty())
View::showErrorPopup("File is not in a valid Base64 format!");
else
ImGui::OpenPopup("Save Data");
} else View::showErrorPopup("Failed to open file!");
}
if (this->m_fileBrowser.showFileDialog("Open Project", imgui_addons::ImGuiFileBrowser::DialogMode::OPEN, ImVec2(0, 0), ".hexproj")) {
ProjectFile::load(this->m_fileBrowser.selected_path);
View::postEvent(Events::ProjectFileLoad);
}
if (this->m_fileBrowser.showFileDialog("Save Project", imgui_addons::ImGuiFileBrowser::DialogMode::SAVE, ImVec2(0, 0), ".hexproj")) {
ProjectFile::store(this->m_fileBrowser.selected_path);
}
if (this->m_fileBrowser.showFileDialog("Export File", imgui_addons::ImGuiFileBrowser::DialogMode::SAVE)) {
this->saveToFile(this->m_fileBrowser.selected_path, this->m_dataToSave);
}
if (this->m_fileBrowser.showFileDialog("Apply IPS Patch", imgui_addons::ImGuiFileBrowser::DialogMode::OPEN)) {
auto patchData = hex::readFile(this->m_fileBrowser.selected_path);
auto patch = hex::loadIPSPatch(patchData);
for (auto &[address, value] : patch) {
this->m_dataProvider->write(address, &value, 1);
}
}
if (this->m_fileBrowser.showFileDialog("Apply IPS32 Patch", imgui_addons::ImGuiFileBrowser::DialogMode::OPEN)) {
auto patchData = hex::readFile(this->m_fileBrowser.selected_path);
auto patch = hex::loadIPS32Patch(patchData);
for (auto &[address, value] : patch) {
this->m_dataProvider->write(address, &value, 1);
}
}
if (this->m_fileBrowser.showFileDialog("Save As", imgui_addons::ImGuiFileBrowser::DialogMode::SAVE)) {
FILE *file = fopen(this->m_fileBrowser.selected_path.c_str(), "wb");
if (file != nullptr) {
std::vector<u8> buffer(0xFF'FFFF, 0x00);
size_t bufferSize = buffer.size();
fseek(file, 0, SEEK_SET);
for (u64 offset = 0; offset < this->m_dataProvider->getActualSize(); offset += bufferSize) {
if (bufferSize > this->m_dataProvider->getActualSize() - offset)
bufferSize = this->m_dataProvider->getActualSize() - offset;
this->m_dataProvider->read(offset, buffer.data(), bufferSize);
fwrite(buffer.data(), 1, bufferSize, file);
}
fclose(file);
}
}
}
void ViewHexEditor::createMenu() {
if (ImGui::BeginMenu("File")) {
if (ImGui::MenuItem("Open File...", "CTRL + O")) {
this->getWindowOpenState() = true;
View::doLater([]{ ImGui::OpenPopup("Open File"); });
}
if (ImGui::MenuItem("Save", "CTRL + S", false, this->m_dataProvider != nullptr && this->m_dataProvider->isWritable())) {
for (const auto &[address, value] : this->m_dataProvider->getPatches())
this->m_dataProvider->writeRaw(address, &value, sizeof(u8));
}
if (ImGui::MenuItem("Save As...", "CTRL + SHIFT + S", false, this->m_dataProvider != nullptr && this->m_dataProvider->isWritable())) {
View::doLater([]{ ImGui::OpenPopup("Save As"); });
}
ImGui::Separator();
if (ImGui::MenuItem("Open Project", "")) {
this->getWindowOpenState() = true;
View::doLater([]{ ImGui::OpenPopup("Open Project"); });
}
if (ImGui::MenuItem("Save Project", "", false, this->m_dataProvider != nullptr && this->m_dataProvider->isWritable())) {
View::postEvent(Events::ProjectFileStore);
if (ProjectFile::getProjectFilePath() == "")
View::doLater([] { ImGui::OpenPopup("Save Project"); });
else
ProjectFile::store();
}
ImGui::Separator();
if (ImGui::BeginMenu("Import...")) {
if (ImGui::MenuItem("Base64 File")) {
this->getWindowOpenState() = true;
View::doLater([]{ ImGui::OpenPopup("Open Base64 File"); });
}
ImGui::Separator();
if (ImGui::MenuItem("IPS Patch")) {
this->getWindowOpenState() = true;
View::doLater([]{ ImGui::OpenPopup("Apply IPS Patch"); });
}
if (ImGui::MenuItem("IPS32 Patch")) {
this->getWindowOpenState() = true;
View::doLater([]{ ImGui::OpenPopup("Apply IPS32 Patch"); });
}
if (ImGui::MenuItem("File with Loader Script")) {
this->getWindowOpenState() = true;
this->m_loaderScriptFilePath.clear();
this->m_loaderScriptScriptPath.clear();
View::doLater([]{ ImGui::OpenPopup("Load File with Loader Script"); });
}
ImGui::EndMenu();
}
if (ImGui::BeginMenu("Export...", this->m_dataProvider != nullptr && this->m_dataProvider->isWritable())) {
if (ImGui::MenuItem("IPS Patch")) {
Patches patches = this->m_dataProvider->getPatches();
if (!patches.contains(0x00454F45) && patches.contains(0x00454F46)) {
u8 value = 0;
this->m_dataProvider->read(0x00454F45, &value, sizeof(u8));
patches[0x00454F45] = value;
}
this->m_dataToSave = generateIPSPatch(patches);
View::doLater([]{ ImGui::OpenPopup("Export File"); });
}
if (ImGui::MenuItem("IPS32 Patch")) {
Patches patches = this->m_dataProvider->getPatches();
if (!patches.contains(0x00454F45) && patches.contains(0x45454F46)) {
u8 value = 0;
this->m_dataProvider->read(0x45454F45, &value, sizeof(u8));
patches[0x45454F45] = value;
}
this->m_dataToSave = generateIPS32Patch(patches);
View::doLater([]{ ImGui::OpenPopup("Export File"); });
}
ImGui::EndMenu();
}
ImGui::Separator();
if (ImGui::MenuItem("Search", "CTRL + F")) {
this->getWindowOpenState() = true;
View::doLater([]{ ImGui::OpenPopup("Search"); });
}
if (ImGui::MenuItem("Goto", "CTRL + G")) {
this->getWindowOpenState() = true;
View::doLater([]{ ImGui::OpenPopup("Goto"); });
}
ImGui::EndMenu();
}
if (ImGui::BeginMenu("Edit")) {
if (ImGui::BeginMenu("Copy as...", this->m_memoryEditor.DataPreviewAddr != -1 && this->m_memoryEditor.DataPreviewAddrEnd != -1)) {
if (ImGui::MenuItem("Bytes", "CTRL + ALT + C"))
this->copyBytes();
if (ImGui::MenuItem("Hex String", "CTRL + SHIFT + C"))
this->copyString();
ImGui::Separator();
if (ImGui::MenuItem("C Array"))
this->copyLanguageArray(Language::C);
if (ImGui::MenuItem("C++ Array"))
this->copyLanguageArray(Language::Cpp);
if (ImGui::MenuItem("C# Array"))
this->copyLanguageArray(Language::CSharp);
if (ImGui::MenuItem("Rust Array"))
this->copyLanguageArray(Language::Rust);
if (ImGui::MenuItem("Python Array"))
this->copyLanguageArray(Language::Python);
if (ImGui::MenuItem("Java Array"))
this->copyLanguageArray(Language::Java);
if (ImGui::MenuItem("JavaScript Array"))
this->copyLanguageArray(Language::JavaScript);
ImGui::Separator();
if (ImGui::MenuItem("Editor View"))
this->copyHexView();
if (ImGui::MenuItem("HTML"))
this->copyHexViewHTML();
ImGui::EndMenu();
}
if (ImGui::MenuItem("Create bookmark", nullptr, false, this->m_memoryEditor.DataPreviewAddr != -1 && this->m_memoryEditor.DataPreviewAddrEnd != -1)) {
size_t start = std::min(this->m_memoryEditor.DataPreviewAddr, this->m_memoryEditor.DataPreviewAddrEnd);
size_t end = std::max(this->m_memoryEditor.DataPreviewAddr, this->m_memoryEditor.DataPreviewAddrEnd);
Bookmark bookmark = { start, end - start + 1, { }, { } };
View::postEvent(Events::AddBookmark, &bookmark);
}
ImGui::EndMenu();
}
}
bool ViewHexEditor::handleShortcut(int key, int mods) {
if (mods == GLFW_MOD_CONTROL && key == GLFW_KEY_S) {
for (const auto &[address, value] : this->m_dataProvider->getPatches())
this->m_dataProvider->writeRaw(address, &value, sizeof(u8));
return true;
} else if (mods == (GLFW_MOD_CONTROL | GLFW_MOD_SHIFT) && key == GLFW_KEY_S) {
ImGui::OpenPopup("Save As");
return true;
} else if (mods == GLFW_MOD_CONTROL && key == GLFW_KEY_F) {
ImGui::OpenPopup("Search");
return true;
} else if (mods == GLFW_MOD_CONTROL && key == GLFW_KEY_G) {
ImGui::OpenPopup("Goto");
return true;
} else if (mods == GLFW_MOD_CONTROL && key == GLFW_KEY_O) {
ImGui::OpenPopup("Open File");
return true;
} else if (mods == (GLFW_MOD_CONTROL | GLFW_MOD_ALT) && key == GLFW_KEY_C) {
this->copyBytes();
return true;
} else if (mods == (GLFW_MOD_CONTROL | GLFW_MOD_SHIFT) && key == GLFW_KEY_C) {
this->copyString();
return true;
}
return false;
}
void ViewHexEditor::openFile(std::string path) {
if (this->m_dataProvider != nullptr)
delete this->m_dataProvider;
this->m_dataProvider = new prv::FileProvider(path);
this->m_memoryEditor.ReadOnly = !this->m_dataProvider->isWritable();
if (this->m_dataProvider->isAvailable())
ProjectFile::setFilePath(path);
this->getWindowOpenState() = true;
View::postEvent(Events::FileLoaded);
View::postEvent(Events::DataChanged);
ProjectFile::markDirty();
}
bool ViewHexEditor::saveToFile(std::string path, const std::vector<u8>& data) {
FILE *file = fopen(path.c_str(), "wb");
if (file == nullptr)
return false;
fwrite(data.data(), 1, data.size(), file);
fclose(file);
return true;
}
bool ViewHexEditor::loadFromFile(std::string path, std::vector<u8>& data) {
FILE *file = fopen(path.c_str(), "rb");
if (file == nullptr)
return false;
fseek(file, 0, SEEK_END);
size_t size = ftello64(file);
rewind(file);
data.resize(size);
fread(data.data(), 1, data.size(), file);
fclose(file);
return true;
}
void ViewHexEditor::copyBytes() {
@@ -114,7 +525,7 @@ namespace hex {
size_t copySize = (end - start) + 1;
std::string buffer;
std::string buffer(copySize, 0x00);
buffer.reserve(copySize + 1);
this->m_dataProvider->read(start, buffer.data(), copySize);
@@ -329,92 +740,6 @@ R"(
ImGui::SetClipboardText(str.c_str());
}
void ViewHexEditor::createMenu() {
if (ImGui::BeginMenu("File")) {
if (ImGui::MenuItem("Open File...", "CTRL + O")) {
View::doLater([]{ ImGui::OpenPopup("Open File"); });
}
ImGui::Separator();
if (ImGui::MenuItem("Search", "CTRL + F")) {
View::doLater([]{ ImGui::OpenPopup("Search"); });
}
if (ImGui::MenuItem("Goto", "CTRL + G")) {
View::doLater([]{ ImGui::OpenPopup("Goto"); });
}
ImGui::EndMenu();
}
if (ImGui::BeginMenu("Edit")) {
if (ImGui::BeginMenu("Copy as...", this->m_memoryEditor.DataPreviewAddr != -1 && this->m_memoryEditor.DataPreviewAddrEnd != -1)) {
if (ImGui::MenuItem("Bytes", "CTRL + ALT + C"))
this->copyBytes();
if (ImGui::MenuItem("Hex String", "CTRL + SHIFT + C"))
this->copyString();
ImGui::Separator();
if (ImGui::MenuItem("C Array"))
this->copyLanguageArray(Language::C);
if (ImGui::MenuItem("C++ Array"))
this->copyLanguageArray(Language::Cpp);
if (ImGui::MenuItem("C# Array"))
this->copyLanguageArray(Language::CSharp);
if (ImGui::MenuItem("Rust Array"))
this->copyLanguageArray(Language::Rust);
if (ImGui::MenuItem("Python Array"))
this->copyLanguageArray(Language::Python);
if (ImGui::MenuItem("Java Array"))
this->copyLanguageArray(Language::Java);
if (ImGui::MenuItem("JavaScript Array"))
this->copyLanguageArray(Language::JavaScript);
ImGui::Separator();
if (ImGui::MenuItem("Editor View"))
this->copyHexView();
if (ImGui::MenuItem("HTML"))
this->copyHexViewHTML();
ImGui::EndMenu();
}
ImGui::EndMenu();
}
if (ImGui::BeginMenu("View")) {
ImGui::MenuItem("Hex View", "", &this->m_memoryEditor.Open);
ImGui::EndMenu();
}
}
bool ViewHexEditor::handleShortcut(int key, int mods) {
if (mods == GLFW_MOD_CONTROL && key == GLFW_KEY_F) {
ImGui::OpenPopup("Search");
return true;
} else if (mods == GLFW_MOD_CONTROL && key == GLFW_KEY_G) {
ImGui::OpenPopup("Goto");
return true;
} else if (mods == GLFW_MOD_CONTROL && key == GLFW_KEY_O) {
ImGui::OpenPopup("Open File");
return true;
} else if (mods == (GLFW_MOD_CONTROL | GLFW_MOD_ALT) && key == GLFW_KEY_C) {
this->copyBytes();
return true;
} else if (mods == (GLFW_MOD_CONTROL | GLFW_MOD_SHIFT) && key == GLFW_KEY_C) {
this->copyString();
return true;
}
return false;
}
static std::vector<std::pair<u64, u64>> findString(prv::Provider* &provider, std::string string) {
std::vector<std::pair<u64, u64>> results;
@@ -423,7 +748,7 @@ R"(
std::vector<u8> buffer(1024, 0x00);
size_t dataSize = provider->getSize();
for (u64 offset = 0; offset < dataSize; offset += 1024) {
size_t usedBufferSize = std::min(buffer.size(), dataSize - offset);
size_t usedBufferSize = std::min(u64(buffer.size()), dataSize - offset);
provider->read(offset, buffer.data(), usedBufferSize);
for (u64 i = 0; i < usedBufferSize; i++) {
@@ -461,7 +786,7 @@ R"(
std::vector<u8> buffer(1024, 0x00);
size_t dataSize = provider->getSize();
for (u64 offset = 0; offset < dataSize; offset += 1024) {
size_t usedBufferSize = std::min(buffer.size(), dataSize - offset);
size_t usedBufferSize = std::min(u64(buffer.size()), dataSize - offset);
provider->read(offset, buffer.data(), usedBufferSize);
for (u64 i = 0; i < usedBufferSize; i++) {
@@ -578,8 +903,8 @@ R"(
if (ImGui::BeginTabItem("Begin")) {
ImGui::InputScalar("##nolabel", ImGuiDataType_U64, &this->m_gotoAddress, nullptr, nullptr, "%llx", ImGuiInputTextFlags_CharsHexadecimal);
if (this->m_gotoAddress >= this->m_dataProvider->getSize())
this->m_gotoAddress = this->m_dataProvider->getSize() - 1;
if (this->m_gotoAddress >= this->m_dataProvider->getActualSize())
this->m_gotoAddress = this->m_dataProvider->getActualSize() - 1;
newOffset = this->m_gotoAddress;
@@ -596,9 +921,9 @@ R"(
s64 currHighlightStart = std::min(this->m_memoryEditor.DataPreviewAddr, this->m_memoryEditor.DataPreviewAddrEnd);
newOffset = this->m_gotoAddress + currHighlightStart;
if (newOffset >= this->m_dataProvider->getSize()) {
newOffset = this->m_dataProvider->getSize() - 1;
this->m_gotoAddress = (this->m_dataProvider->getSize() - 1) - currHighlightStart;
if (newOffset >= this->m_dataProvider->getActualSize()) {
newOffset = this->m_dataProvider->getActualSize() - 1;
this->m_gotoAddress = (this->m_dataProvider->getActualSize() - 1) - currHighlightStart;
} else if (newOffset < 0) {
newOffset = 0;
this->m_gotoAddress = -currHighlightStart;
@@ -609,15 +934,16 @@ R"(
if (ImGui::BeginTabItem("End")) {
ImGui::InputScalar("##nolabel", ImGuiDataType_U64, &this->m_gotoAddress, nullptr, nullptr, "%llx", ImGuiInputTextFlags_CharsHexadecimal);
if (this->m_gotoAddress >= this->m_dataProvider->getSize())
this->m_gotoAddress = this->m_dataProvider->getSize() - 1;
if (this->m_gotoAddress >= this->m_dataProvider->getActualSize())
this->m_gotoAddress = this->m_dataProvider->getActualSize() - 1;
newOffset = (this->m_dataProvider->getSize() - 1) - this->m_gotoAddress;
newOffset = (this->m_dataProvider->getActualSize() - 1) - this->m_gotoAddress;
ImGui::EndTabItem();
}
if (ImGui::Button("Goto")) {
this->m_dataProvider->setCurrentPage(std::floor(newOffset / double(prv::Provider::PageSize)));
this->m_memoryEditor.GotoAddr = newOffset;
this->m_memoryEditor.DataPreviewAddr = newOffset;
this->m_memoryEditor.DataPreviewAddrEnd = newOffset;

View File

@@ -2,7 +2,7 @@
#include "providers/provider.hpp"
#include "utils.hpp"
#include "helpers/utils.hpp"
#include <cstring>
#include <cmath>
@@ -12,18 +12,20 @@
#include <magic.h>
#if defined(__EMX__) || defined (WIN32)
#define MAGIC_PATH_SEPARATOR ";"
#else
#define MAGIC_PATH_SEPARATOR ":"
#endif
namespace hex {
ViewInformation::ViewInformation(prv::Provider* &dataProvider) : View(), m_dataProvider(dataProvider) {
ViewInformation::ViewInformation(prv::Provider* &dataProvider)
: View("Information"), m_dataProvider(dataProvider) {
View::subscribeEvent(Events::DataChanged, [this](const void*) {
this->m_shouldInvalidate = true;
this->m_dataValid = false;
this->m_highestBlockEntropy = 0;
this->m_blockEntropy.clear();
this->m_averageEntropy = 0;
this->m_blockSize = 0;
this->m_valueCounts.fill(0x00);
this->m_mimeType = "";
this->m_fileDescription = "";
this->m_analyzedRegion = { 0, 0 };
});
}
@@ -45,15 +47,14 @@ namespace hex {
}
void ViewInformation::createView() {
if (!this->m_windowOpen)
return;
if (ImGui::Begin("Data Information", &this->m_windowOpen)) {
if (ImGui::Begin("Data Information", &this->getWindowOpenState(), ImGuiWindowFlags_NoCollapse)) {
ImGui::BeginChild("##scrolling", ImVec2(0, 0), false, ImGuiWindowFlags_NoMove | ImGuiWindowFlags_NoNav);
if (this->m_dataProvider != nullptr && this->m_dataProvider->isReadable()) {
if (this->m_shouldInvalidate) {
this->m_analyzedRegion = { this->m_dataProvider->getBaseAddress(), this->m_dataProvider->getBaseAddress() + this->m_dataProvider->getSize() };
{
this->m_blockSize = std::ceil(this->m_dataProvider->getSize() / 2048.0F);
std::vector<u8> buffer(this->m_blockSize, 0x00);
@@ -62,7 +63,7 @@ namespace hex {
for (u64 i = 0; i < this->m_dataProvider->getSize(); i += this->m_blockSize) {
std::array<float, 256> blockValueCounts = { 0 };
this->m_dataProvider->read(i, buffer.data(), std::min(size_t(this->m_blockSize), this->m_dataProvider->getSize() - i));
this->m_dataProvider->read(i, buffer.data(), std::min(u64(this->m_blockSize), this->m_dataProvider->getSize() - i));
for (size_t j = 0; j < this->m_blockSize; j++) {
blockValueCounts[buffer[j]]++;
@@ -95,12 +96,10 @@ namespace hex {
{
magic_t cookie = magic_open(MAGIC_NONE);
if (magic_load(cookie, magicFiles.c_str()) == -1)
goto skip_description;
this->m_fileDescription = magic_buffer(cookie, buffer.data(), buffer.size());
skip_description:
if (magic_load(cookie, magicFiles.c_str()) != -1)
this->m_fileDescription = magic_buffer(cookie, buffer.data(), buffer.size());
else
this->m_fileDescription = "";
magic_close(cookie);
}
@@ -108,12 +107,10 @@ namespace hex {
{
magic_t cookie = magic_open(MAGIC_MIME);
if (magic_load(cookie, magicFiles.c_str()) == -1)
goto skip_mime;
this->m_mimeType = magic_buffer(cookie, buffer.data(), buffer.size());
skip_mime:
if (magic_load(cookie, magicFiles.c_str()) != -1)
this->m_mimeType = magic_buffer(cookie, buffer.data(), buffer.size());
else
this->m_mimeType = "";
magic_close(cookie);
}
@@ -122,53 +119,67 @@ namespace hex {
this->m_shouldInvalidate = false;
this->m_dataValid = true;
}
}
ImGui::NewLine();
for (auto &[name, value] : this->m_dataProvider->getDataInformation()) {
ImGui::LabelText(name.c_str(), "%s", value.c_str());
}
if (ImGui::Button("Analyze current page"))
this->m_shouldInvalidate = true;
ImGui::NewLine();
ImGui::Separator();
ImGui::NewLine();
if (!this->m_fileDescription.empty()) {
ImGui::TextUnformatted("Description:");
ImGui::TextWrapped("%s", this->m_fileDescription.c_str());
if (this->m_dataValid) {
for (auto &[name, value] : this->m_dataProvider->getDataInformation()) {
ImGui::LabelText(name.c_str(), "%s", value.c_str());
}
ImGui::LabelText("Analyzed region", "0x%llx - 0x%llx", this->m_analyzedRegion.first, this->m_analyzedRegion.second);
ImGui::NewLine();
}
if (!this->m_mimeType.empty()) {
ImGui::TextUnformatted("MIME Type:");
ImGui::TextWrapped("%s", this->m_mimeType.c_str());
ImGui::Separator();
ImGui::NewLine();
}
ImGui::Separator();
ImGui::NewLine();
if (!this->m_fileDescription.empty()) {
ImGui::TextUnformatted("Description:");
ImGui::TextWrapped("%s", this->m_fileDescription.c_str());
ImGui::NewLine();
}
ImGui::Text("Byte Distribution");
ImGui::PlotHistogram("##nolabel", this->m_valueCounts.data(), 256, 0, nullptr, FLT_MAX, FLT_MAX, ImVec2(0, 100));
if (!this->m_mimeType.empty()) {
ImGui::TextUnformatted("MIME Type:");
ImGui::TextWrapped("%s", this->m_mimeType.c_str());
ImGui::NewLine();
}
ImGui::NewLine();
ImGui::Separator();
ImGui::NewLine();
ImGui::Text("Entropy");
ImGui::PlotLines("##nolabel", this->m_blockEntropy.data(), this->m_blockEntropy.size(), 0, nullptr, FLT_MAX, FLT_MAX, ImVec2(0, 100));
ImGui::NewLine();
ImGui::LabelText("Block size", "2048 blocks à %lu bytes", this->m_blockSize);
ImGui::LabelText("Average entropy", "%.8f", this->m_averageEntropy);
ImGui::LabelText("Highest entropy block", "%.8f", this->m_highestBlockEntropy);
if (this->m_averageEntropy > 0.83 && this->m_highestBlockEntropy > 0.9) {
ImGui::Separator();
ImGui::NewLine();
ImGui::TextColored(ImVec4(0.92F, 0.25F, 0.2F, 1.0F), "This data is most likely encrypted or compressed!");
ImGui::Text("Byte Distribution");
ImGui::PlotHistogram("##nolabel", this->m_valueCounts.data(), 256, 0, nullptr, FLT_MAX, FLT_MAX,ImVec2(0, 100));
ImGui::NewLine();
ImGui::Separator();
ImGui::NewLine();
ImGui::Text("Entropy");
ImGui::PlotLines("##nolabel", this->m_blockEntropy.data(), this->m_blockEntropy.size(), 0, nullptr, FLT_MAX, FLT_MAX, ImVec2(0, 100));
ImGui::NewLine();
ImGui::LabelText("Block size", "2048 blocks of %lu bytes", this->m_blockSize);
ImGui::LabelText("Average entropy", "%.8f", this->m_averageEntropy);
ImGui::LabelText("Highest entropy block", "%.8f", this->m_highestBlockEntropy);
if (this->m_averageEntropy > 0.83 && this->m_highestBlockEntropy > 0.9) {
ImGui::NewLine();
ImGui::TextColored(ImVec4(0.92F, 0.25F, 0.2F, 1.0F),"This data is most likely encrypted or compressed!");
}
}
}
@@ -178,10 +189,7 @@ namespace hex {
}
void ViewInformation::createMenu() {
if (ImGui::BeginMenu("View")) {
ImGui::MenuItem("Data Information View", "", &this->m_windowOpen);
ImGui::EndMenu();
}
}
}

View File

@@ -0,0 +1,91 @@
#include "views/view_patches.hpp"
#include "providers/provider.hpp"
#include "helpers/utils.hpp"
#include "helpers/project_file_handler.hpp"
#include <string>
using namespace std::literals::string_literals;
namespace hex {
ViewPatches::ViewPatches(prv::Provider* &dataProvider) : View("Patches"), m_dataProvider(dataProvider) {
View::subscribeEvent(Events::ProjectFileStore, [this](const void*) {
if (this->m_dataProvider != nullptr)
ProjectFile::setPatches(this->m_dataProvider->getPatches());
});
View::subscribeEvent(Events::ProjectFileLoad, [this](const void*) {
if (this->m_dataProvider != nullptr)
this->m_dataProvider->getPatches() = ProjectFile::getPatches();
});
}
ViewPatches::~ViewPatches() {
View::unsubscribeEvent(Events::ProjectFileStore);
View::unsubscribeEvent(Events::ProjectFileLoad);
}
void ViewPatches::createView() {
if (ImGui::Begin("Patches", &this->getWindowOpenState(), ImGuiWindowFlags_NoCollapse)) {
if (this->m_dataProvider != nullptr && this->m_dataProvider->isReadable()) {
if (ImGui::BeginTable("##patchesTable", 3, ImGuiTableFlags_Borders | ImGuiTableFlags_Resizable | ImGuiTableFlags_Sortable |
ImGuiTableFlags_Reorderable | ImGuiTableFlags_RowBg | ImGuiTableFlags_ScrollY)) {
ImGui::TableSetupScrollFreeze(0, 1);
ImGui::TableSetupColumn("Offset");
ImGui::TableSetupColumn("Previous Value");
ImGui::TableSetupColumn("Patched Value");
ImGui::TableHeadersRow();
auto& patches = this->m_dataProvider->getPatches();
u32 index = 0;
for (const auto &[address, patch] : patches) {
ImGui::TableNextRow();
ImGui::TableNextColumn();
if (ImGui::Selectable(("##patchLine" + std::to_string(index)).c_str(), false, ImGuiSelectableFlags_SpanAllColumns)) {
Region selectRegion = { address, 1 };
View::postEvent(Events::SelectionChangeRequest, &selectRegion);
}
if (ImGui::IsMouseReleased(1) && ImGui::IsItemHovered()) {
ImGui::OpenPopup("PatchContextMenu");
this->m_selectedPatch = address;
}
ImGui::SameLine();
ImGui::Text("0x%08lX", address);
ImGui::TableNextColumn();
u8 previousValue = 0x00;
this->m_dataProvider->readRaw(address, &previousValue, sizeof(u8));
ImGui::Text("0x%02X", previousValue);
ImGui::TableNextColumn();
ImGui::Text("0x%02X", patch);
index += 1;
}
if (ImGui::BeginPopup("PatchContextMenu")) {
if (ImGui::MenuItem("Remove")) {
patches.erase(this->m_selectedPatch);
ProjectFile::markDirty();
}
ImGui::EndPopup();
}
ImGui::EndTable();
}
}
}
ImGui::End();
}
void ViewPatches::createMenu() {
}
}

View File

@@ -5,7 +5,11 @@
#include "lang/lexer.hpp"
#include "lang/validator.hpp"
#include "lang/evaluator.hpp"
#include "utils.hpp"
#include "helpers/project_file_handler.hpp"
#include "helpers/utils.hpp"
#include <magic.h>
namespace hex {
@@ -14,7 +18,7 @@ namespace hex {
static TextEditor::LanguageDefinition langDef;
if (!initialized) {
static const char* const keywords[] = {
"using", "struct", "union", "enum", "bitfield"
"using", "struct", "union", "enum", "bitfield", "be", "le"
};
for (auto& k : keywords)
langDef.mKeywords.insert(k);
@@ -46,6 +50,8 @@ namespace hex {
paletteIndex = TextEditor::PaletteIndex::Identifier;
else if (TokenizeCStyleNumber(inBegin, inEnd, outBegin, outEnd))
paletteIndex = TextEditor::PaletteIndex::Number;
else if (TokenizeCStyleCharacterLiteral(inBegin, inEnd, outBegin, outEnd))
paletteIndex = TextEditor::PaletteIndex::CharLiteral;
return paletteIndex != TextEditor::PaletteIndex::Max;
};
@@ -67,14 +73,98 @@ namespace hex {
ViewPattern::ViewPattern(prv::Provider* &dataProvider, std::vector<lang::PatternData*> &patternData)
: View(), m_dataProvider(dataProvider), m_patternData(patternData) {
: View("Pattern"), m_dataProvider(dataProvider), m_patternData(patternData) {
this->m_textEditor.SetLanguageDefinition(PatternLanguage());
this->m_textEditor.SetShowWhitespaces(false);
View::subscribeEvent(Events::ProjectFileStore, [this](const void*) {
ProjectFile::setPattern(this->m_textEditor.GetText());
});
View::subscribeEvent(Events::ProjectFileLoad, [this](const void*) {
this->m_textEditor.SetText(ProjectFile::getPattern());
this->parsePattern(this->m_textEditor.GetText().data());
});
View::subscribeEvent(Events::AppendPatternLanguageCode, [this](const void *userData) {
const char *code = static_cast<const char*>(userData);
this->m_textEditor.InsertText("\n");
this->m_textEditor.InsertText(code);
});
View::subscribeEvent(Events::FileLoaded, [this](const void* userData) {
if (this->m_textEditor.GetText().find_first_not_of(" \f\n\r\t\v") != std::string::npos)
return;
lang::Preprocessor preprocessor;
std::string magicFiles;
std::error_code error;
for (const auto &entry : std::filesystem::directory_iterator("magic", error)) {
if (entry.is_regular_file() && entry.path().extension() == ".mgc")
magicFiles += entry.path().string() + MAGIC_PATH_SEPARATOR;
}
if (error)
return;
std::vector<u8> buffer(std::min(this->m_dataProvider->getSize(), size_t(0xFFFF)), 0x00);
this->m_dataProvider->read(0, buffer.data(), buffer.size());
std::string mimeType;
magic_t cookie = magic_open(MAGIC_MIME_TYPE);
if (magic_load(cookie, magicFiles.c_str()) != -1)
mimeType = magic_buffer(cookie, buffer.data(), buffer.size());
magic_close(cookie);
bool foundCorrectType = false;
preprocessor.addPragmaHandler("MIME", [&mimeType, &foundCorrectType](std::string value) {
if (value == mimeType) {
foundCorrectType = true;
return true;
}
return !std::all_of(value.begin(), value.end(), isspace) && !value.ends_with('\n') && !value.ends_with('\r');
});
preprocessor.addDefaultPragmaHandlers();
std::error_code errorCode;
for (auto &entry : std::filesystem::directory_iterator("patterns", errorCode)) {
if (!entry.is_regular_file())
continue;
FILE *file = fopen(entry.path().string().c_str(), "r");
if (file == nullptr)
continue;
fseek(file, 0, SEEK_END);
size_t size = ftell(file);
rewind(file);
std::vector<char> buffer( size + 1, 0x00);
fread(buffer.data(), 1, size, file);
fclose(file);
preprocessor.preprocess(buffer.data());
if (foundCorrectType) {
this->m_possiblePatternFile = entry.path();
View::doLater([] { ImGui::OpenPopup("Accept Pattern"); });
ImGui::SetNextWindowSize(ImVec2(200, 100));
break;
}
}
});
}
ViewPattern::~ViewPattern() {
View::unsubscribeEvent(Events::ProjectFileStore);
View::unsubscribeEvent(Events::ProjectFileLoad);
}
void ViewPattern::createMenu() {
@@ -84,18 +174,10 @@ namespace hex {
}
ImGui::EndMenu();
}
if (ImGui::BeginMenu("View")) {
ImGui::MenuItem("Pattern View", "", &this->m_windowOpen);
ImGui::EndMenu();
}
}
void ViewPattern::createView() {
if (!this->m_windowOpen)
return;
if (ImGui::Begin("Pattern", &this->m_windowOpen, ImGuiWindowFlags_None)) {
if (ImGui::Begin("Pattern", &this->getWindowOpenState(), ImGuiWindowFlags_None | ImGuiWindowFlags_NoCollapse)) {
if (this->m_dataProvider != nullptr && this->m_dataProvider->isAvailable()) {
this->m_textEditor.Render("Pattern");
@@ -107,32 +189,54 @@ namespace hex {
ImGui::End();
if (this->m_fileBrowser.showFileDialog("Open Hex Pattern", imgui_addons::ImGuiFileBrowser::DialogMode::OPEN, ImVec2(0, 0), ".hexpat")) {
this->loadPatternFile(this->m_fileBrowser.selected_path);
}
FILE *file = fopen(this->m_fileBrowser.selected_path.c_str(), "rb");
if (ImGui::BeginPopupModal("Accept Pattern", nullptr, ImGuiWindowFlags_NoResize)) {
ImGui::TextUnformatted("A pattern compatible with this data type has been found:");
ImGui::Text("%ls", this->m_possiblePatternFile.filename().c_str());
ImGui::NewLine();
ImGui::Text("Do you want to load it?");
ImGui::NewLine();
if (file != nullptr) {
char *buffer;
fseek(file, 0, SEEK_END);
size_t size = ftell(file);
rewind(file);
buffer = new char[size + 1];
fread(buffer, size, 1, file);
buffer[size] = 0x00;
fclose(file);
this->parsePattern(buffer);
this->m_textEditor.SetText(buffer);
delete[] buffer;
if (ImGui::Button("Yes", ImVec2(40, 20))) {
this->loadPatternFile(this->m_possiblePatternFile.string());
ImGui::CloseCurrentPopup();
}
ImGui::SameLine();
if (ImGui::Button("No", ImVec2(40, 20))) {
ImGui::CloseCurrentPopup();
}
ImGui::EndPopup();
}
}
void ViewPattern::loadPatternFile(std::string path) {
FILE *file = fopen(path.c_str(), "rb");
if (file != nullptr) {
char *buffer;
fseek(file, 0, SEEK_END);
size_t size = ftell(file);
rewind(file);
buffer = new char[size + 1];
fread(buffer, size, 1, file);
buffer[size] = 0x00;
fclose(file);
this->parsePattern(buffer);
this->m_textEditor.SetText(buffer);
delete[] buffer;
}
}
void ViewPattern::clearPatternData() {
for (auto &data : this->m_patternData)
delete data;
@@ -153,38 +257,61 @@ namespace hex {
}
void ViewPattern::parsePattern(char *buffer) {
hex::lang::Preprocessor preprocessor;
hex::lang::Lexer lexer;
hex::lang::Parser parser;
hex::lang::Validator validator;
hex::lang::Evaluator evaluator;
this->clearPatternData();
this->m_textEditor.SetErrorMarkers({ });
this->postEvent(Events::PatternChanged);
auto [preprocessingResult, preprocesedCode] = preprocessor.preprocess(buffer);
if (preprocessingResult.failed())
return;
hex::lang::Preprocessor preprocessor;
std::endian defaultDataEndianess = std::endian::native;
auto [lexResult, tokens] = lexer.lex(preprocesedCode);
if (lexResult.failed()) {
preprocessor.addPragmaHandler("endian", [&defaultDataEndianess](std::string value) {
if (value == "big") {
defaultDataEndianess = std::endian::big;
return true;
} else if (value == "little") {
defaultDataEndianess = std::endian::little;
return true;
} else if (value == "native") {
defaultDataEndianess = std::endian::native;
return true;
} else
return false;
});
preprocessor.addDefaultPragmaHandlers();
auto [preprocessingResult, preprocesedCode] = preprocessor.preprocess(buffer);
if (preprocessingResult.failed()) {
this->m_textEditor.SetErrorMarkers({ preprocessor.getError() });
return;
}
hex::lang::Lexer lexer;
auto [lexResult, tokens] = lexer.lex(preprocesedCode);
if (lexResult.failed()) {
this->m_textEditor.SetErrorMarkers({ lexer.getError() });
return;
}
hex::lang::Parser parser;
auto [parseResult, ast] = parser.parse(tokens);
if (parseResult.failed()) {
this->m_textEditor.SetErrorMarkers({ parser.getError() });
return;
}
hex::ScopeExit deleteAst([&ast]{ for(auto &node : ast) delete node; });
hex::lang::Validator validator;
auto validatorResult = validator.validate(ast);
if (!validatorResult) {
this->m_textEditor.SetErrorMarkers({ validator.getError() });
return;
}
hex::lang::Evaluator evaluator(this->m_dataProvider, defaultDataEndianess);
auto [evaluateResult, patternData] = evaluator.evaluate(ast);
if (evaluateResult.failed()) {
this->m_textEditor.SetErrorMarkers({ evaluator.getError() });
return;
}
this->m_patternData = patternData;

View File

@@ -6,7 +6,7 @@
namespace hex {
ViewPatternData::ViewPatternData(prv::Provider* &dataProvider, std::vector<lang::PatternData*> &patternData)
: View(), m_dataProvider(dataProvider), m_patternData(patternData) {
: View("Pattern Data"), m_dataProvider(dataProvider), m_patternData(patternData) {
this->subscribeEvent(Events::PatternChanged, [this](auto data) {
this->m_sortedPatternData.clear();
@@ -18,9 +18,10 @@ namespace hex {
}
static bool beginPatternDataTable(prv::Provider* &provider, const std::vector<lang::PatternData*> &patterns, std::vector<lang::PatternData*> &sortedPatterns) {
if (ImGui::BeginTable("##patterndatatable", 6, ImGuiTableFlags_Borders | ImGuiTableFlags_Resizable | ImGuiTableFlags_Sortable | ImGuiTableFlags_Reorderable | ImGuiTableFlags_RowBg)) {
ImGui::TableSetupColumn("Color", 0, -1, ImGui::GetID("color"));
if (ImGui::BeginTable("##patterndatatable", 6, ImGuiTableFlags_Borders | ImGuiTableFlags_Resizable | ImGuiTableFlags_Sortable | ImGuiTableFlags_Reorderable | ImGuiTableFlags_RowBg | ImGuiTableFlags_ScrollY)) {
ImGui::TableSetupScrollFreeze(0, 1);
ImGui::TableSetupColumn("Name", 0, -1, ImGui::GetID("name"));
ImGui::TableSetupColumn("Color", 0, -1, ImGui::GetID("color"));
ImGui::TableSetupColumn("Offset", 0, -1, ImGui::GetID("offset"));
ImGui::TableSetupColumn("Size", 0, -1, ImGui::GetID("size"));
ImGui::TableSetupColumn("Type", 0, -1, ImGui::GetID("type"));
@@ -48,25 +49,15 @@ namespace hex {
}
void ViewPatternData::createView() {
if (!this->m_windowOpen)
return;
if (ImGui::Begin("Pattern Data", &this->m_windowOpen)) {
ImGui::BeginChild("##scrolling", ImVec2(0, 0), false, ImGuiWindowFlags_NoMove | ImGuiWindowFlags_NoNav);
if (ImGui::Begin("Pattern Data", &this->getWindowOpenState(), ImGuiWindowFlags_NoCollapse)) {
if (this->m_dataProvider != nullptr && this->m_dataProvider->isReadable()) {
if (beginPatternDataTable(this->m_dataProvider, this->m_patternData, this->m_sortedPatternData)) {
if (this->m_sortedPatternData.size() > 0) {
ImGui::TableHeadersRow();
u32 rowCount = 0;
for (auto &patternData : this->m_sortedPatternData) {
for (auto &patternData : this->m_sortedPatternData)
patternData->createEntry(this->m_dataProvider);
ImGui::TableSetBgColor(ImGuiTableBgTarget_RowBg0,
((rowCount % 2) == 0) ? 0xFF101010 : 0xFF303030);
rowCount++;
}
}
@@ -74,17 +65,12 @@ namespace hex {
}
}
ImGui::EndChild();
}
ImGui::End();
}
void ViewPatternData::createMenu() {
if (ImGui::BeginMenu("View")) {
ImGui::MenuItem("Data View", "", &this->m_windowOpen);
ImGui::EndMenu();
}
}
}

View File

@@ -1,14 +1,19 @@
#include "views/view_strings.hpp"
#include "providers/provider.hpp"
#include "helpers/utils.hpp"
#include <cstring>
#include <llvm/Demangle/Demangle.h>
using namespace std::literals::string_literals;
namespace hex {
ViewStrings::ViewStrings(prv::Provider* &dataProvider) : View(), m_dataProvider(dataProvider) {
ViewStrings::ViewStrings(prv::Provider* &dataProvider) : View("Strings"), m_dataProvider(dataProvider) {
View::subscribeEvent(Events::DataChanged, [this](const void*){
this->m_shouldInvalidate = true;
this->m_foundStrings.clear();
});
this->m_filter = new char[0xFFFF];
@@ -20,22 +25,40 @@ namespace hex {
delete[] this->m_filter;
}
void ViewStrings::createView() {
if (!this->m_windowOpen)
return;
void ViewStrings::createStringContextMenu(const FoundString &foundString) {
if (ImGui::TableGetHoveredColumn() == 2 && ImGui::IsMouseReleased(1) && ImGui::IsItemHovered()) {
ImGui::OpenPopup("StringContextMenu");
this->m_selectedString = foundString.string;
}
if (ImGui::BeginPopup("StringContextMenu")) {
if (ImGui::MenuItem("Copy string")) {
ImGui::SetClipboardText(this->m_selectedString.c_str());
}
ImGui::Separator();
if (ImGui::MenuItem("Demangle")) {
this->m_demangledName = llvm::demangle(this->m_selectedString);
if (!this->m_demangledName.empty())
View::doLater([]{ ImGui::OpenPopup("Demangled Name"); });
}
ImGui::EndPopup();
}
}
void ViewStrings::createView() {
if (this->m_shouldInvalidate) {
this->m_shouldInvalidate = false;
this->m_foundStrings.clear();
std::vector<u8> buffer(1024, 0x00);
u32 foundCharacters = 0;
for (u64 offset = 0; offset < this->m_dataProvider->getSize(); offset += buffer.size()) {
this->m_dataProvider->read(offset, buffer.data(), buffer.size());
size_t readSize = std::min(u64(buffer.size()), this->m_dataProvider->getSize() - offset);
this->m_dataProvider->read(offset, buffer.data(), readSize);
for (u32 i = 0; i < buffer.size(); i++) {
for (u32 i = 0; i < readSize; i++) {
if (buffer[i] >= 0x20 && buffer[i] <= 0x7E)
foundCharacters++;
else {
@@ -58,21 +81,22 @@ namespace hex {
}
if (ImGui::Begin("Strings", &this->m_windowOpen)) {
if (ImGui::Begin("Strings", &this->getWindowOpenState(), ImGuiWindowFlags_NoCollapse)) {
if (this->m_dataProvider != nullptr && this->m_dataProvider->isReadable()) {
if (ImGui::InputInt("Minimum length", &this->m_minimumLength, 1, 0))
this->m_shouldInvalidate = true;
ImGui::InputText("Filter", this->m_filter, 0xFFFF);
if (ImGui::Button("Extract"))
this->m_shouldInvalidate = true;
ImGui::Separator();
ImGui::NewLine();
ImGui::BeginChild("##scrolling", ImVec2(0, 0), false, ImGuiWindowFlags_NoMove | ImGuiWindowFlags_NoNav);
if (ImGui::BeginTable("##strings", 3,
ImGuiTableFlags_Borders | ImGuiTableFlags_Resizable | ImGuiTableFlags_Sortable |
ImGuiTableFlags_Reorderable | ImGuiTableFlags_RowBg)) {
ImGuiTableFlags_Reorderable | ImGuiTableFlags_RowBg | ImGuiTableFlags_ScrollY)) {
ImGui::TableSetupScrollFreeze(0, 1);
ImGui::TableSetupColumn("Offset", 0, -1, ImGui::GetID("offset"));
ImGui::TableSetupColumn("Size", 0, -1, ImGui::GetID("size"));
ImGui::TableSetupColumn("String", 0, -1, ImGui::GetID("string"));
@@ -111,7 +135,6 @@ namespace hex {
clipper.Begin(this->m_foundStrings.size());
while (clipper.Step()) {
u32 rowCount = clipper.DisplayStart;
for (u64 i = clipper.DisplayStart; i < clipper.DisplayEnd; i++) {
auto &foundString = this->m_foundStrings[i];
@@ -119,34 +142,47 @@ namespace hex {
foundString.string.find(this->m_filter) == std::string::npos)
continue;
ImGui::TableNextRow(ImGuiTableRowFlags_Headers);
ImGui::TableNextRow();
ImGui::TableNextColumn();
if (ImGui::Selectable(("##StringLine"s + std::to_string(i)).c_str(), false, ImGuiSelectableFlags_SpanAllColumns)) {
Region selectRegion = { foundString.offset, foundString.size };
View::postEvent(Events::SelectionChangeRequest, &selectRegion);
}
ImGui::PushID(i + 1);
createStringContextMenu(foundString);
ImGui::PopID();
ImGui::SameLine();
ImGui::Text("0x%08lx : 0x%08lx", foundString.offset, foundString.offset + foundString.size);
ImGui::TableNextColumn();
ImGui::Text("0x%04lx", foundString.size);
ImGui::TableNextColumn();
ImGui::Text("%s", foundString.string.c_str());
ImGui::TableSetBgColor(ImGuiTableBgTarget_RowBg0,
((rowCount % 2) == 0) ? 0xFF101010 : 0xFF303030);
rowCount++;
}
}
clipper.End();
ImGui::EndTable();
}
ImGui::EndChild();
}
}
ImGui::End();
}
void ViewStrings::createMenu() {
if (ImGui::BeginMenu("View")) {
ImGui::MenuItem("Strings View", "", &this->m_windowOpen);
ImGui::EndMenu();
if (ImGui::BeginPopup("Demangled Name")) {
if (ImGui::BeginChild("##scrolling", ImVec2(500, 150))) {
ImGui::Text("Demangled Name");
ImGui::Separator();
ImGui::TextWrapped("%s", this->m_demangledName.c_str());
ImGui::EndChild();
ImGui::NewLine();
if (ImGui::Button("Copy"))
ImGui::SetClipboardText(this->m_demangledName.c_str());
}
ImGui::EndPopup();
}
}
void ViewStrings::createMenu() {
}
}

View File

@@ -1,17 +1,19 @@
#include "views/view_tools.hpp"
#include <cxxabi.h>
#include <cstring>
#include <regex>
#include <optional>
#include "providers/provider.hpp"
#include "helpers/utils.hpp"
#include <llvm/Demangle/Demangle.h>
namespace hex {
ViewTools::ViewTools() {
ViewTools::ViewTools(hex::prv::Provider* &provider) : View("Tools"), m_dataProvider(provider) {
this->m_mangledBuffer = new char[0xF'FFFF];
this->m_demangledName = static_cast<char*>(malloc(8));
std::memset(this->m_mangledBuffer, 0x00, 0xF'FFFF);
strcpy(this->m_demangledName, "< ??? >");
this->m_regexInput = new char[0xF'FFFF];
this->m_regexPattern = new char[0xF'FFFF];
@@ -19,74 +21,65 @@ namespace hex {
std::memset(this->m_regexInput, 0x00, 0xF'FFFF);
std::memset(this->m_regexPattern, 0x00, 0xF'FFFF);
std::memset(this->m_replacePattern, 0x00, 0xF'FFFF);
this->m_mathInput = new char[0xFFFF];
std::memset(this->m_mathInput, 0x00, 0xFFFF);
this->m_mathEvaluator.registerStandardVariables();
this->m_mathEvaluator.registerStandardFunctions();
this->m_mathEvaluator.setFunction("clear", [this](auto args) -> std::optional<long double> {
this->m_mathHistory.clear();
this->m_lastMathError.clear();
this->m_mathEvaluator.getVariables().clear();
this->m_mathEvaluator.registerStandardVariables();
std::memset(this->m_mathInput, 0x00, 0xFFFF);
return { };
}, 0, 0);
this->m_mathEvaluator.setFunction("read", [this](auto args) -> std::optional<long double> {
u8 value = 0;
if (this->m_dataProvider == nullptr || !this->m_dataProvider->isReadable() || args[0] >= this->m_dataProvider->getActualSize())
return { };
this->m_dataProvider->read(args[0], &value, sizeof(u8));
return value;
}, 1, 1);
this->m_mathEvaluator.setFunction("write", [this](auto args) -> std::optional<long double> {
if (this->m_dataProvider == nullptr || !this->m_dataProvider->isWritable() || args[0] >= this->m_dataProvider->getActualSize())
return { };
if (args[1] > 0xFF)
return { };
u8 value = args[1];
this->m_dataProvider->write(args[0], &value, sizeof(u8));
return { };
}, 2, 2);
}
ViewTools::~ViewTools() {
delete[] this->m_mangledBuffer;
free(this->m_demangledName);
delete[] this->m_regexInput;
delete[] this->m_regexPattern;
}
delete[] this->m_replacePattern;
static std::string toASCIITableString(char c) {
switch (c) {
case 0: return "NUL";
case 1: return "SOH";
case 2: return "STX";
case 3: return "ETX";
case 4: return "EOT";
case 5: return "ENQ";
case 6: return "ACK";
case 7: return "BEL";
case 8: return "BS";
case 9: return "TAB";
case 10: return "LF";
case 11: return "VT";
case 12: return "FF";
case 13: return "CR";
case 14: return "SO";
case 15: return "SI";
case 16: return "DLE";
case 17: return "DC1";
case 18: return "DC2";
case 19: return "DC3";
case 20: return "DC4";
case 21: return "NAK";
case 22: return "SYN";
case 23: return "ETB";
case 24: return "CAN";
case 25: return "EM";
case 26: return "SUB";
case 27: return "ESC";
case 28: return "FS";
case 29: return "GS";
case 30: return "RS";
case 31: return "US";
case 32: return "Space";
case 127: return "DEL";
default: return std::string() + c;
}
delete[] this->m_mathInput;
}
void ViewTools::drawDemangler() {
if (ImGui::CollapsingHeader("Itanium demangler")) {
if (ImGui::CollapsingHeader("Itanium/MSVC demangler")) {
if (ImGui::InputText("Mangled name", this->m_mangledBuffer, 0xF'FFFF)) {
size_t length = 0;
int status = 0;
if (this->m_demangledName != nullptr)
free(this->m_demangledName);
this->m_demangledName = abi::__cxa_demangle(this->m_mangledBuffer, nullptr, &length, &status);
if (status != 0) {
this->m_demangledName = static_cast<char*>(malloc(8));
strcpy(this->m_demangledName, "< ??? >");
}
this->m_demangledName = llvm::demangle(this->m_mangledBuffer);
}
ImGui::InputText("Demangled name", this->m_demangledName, strlen(this->m_demangledName), ImGuiInputTextFlags_ReadOnly);
ImGui::InputText("Demangled name", this->m_demangledName.data(), this->m_demangledName.size(), ImGuiInputTextFlags_ReadOnly);
ImGui::NewLine();
}
}
@@ -127,7 +120,7 @@ namespace hex {
ImGui::Text("0x%02x", i + 32 * tablePart);
ImGui::TableNextColumn();
ImGui::Text("%s", toASCIITableString(i + 32 * tablePart).c_str());
ImGui::Text("%s", makePrintable(i + 32 * tablePart).c_str());
ImGui::TableSetBgColor(ImGuiTableBgTarget_RowBg0, ((rowCount % 2) == 0) ? 0xFF101010 : 0xFF303030);
@@ -167,21 +160,144 @@ namespace hex {
void ViewTools::drawColorPicker() {
if (ImGui::CollapsingHeader("Color picker")) {
ImGui::SetNextItemWidth(300.0F);
ImGui::ColorPicker4("Color Picker", this->m_pickedColor.data(),
ImGuiColorEditFlags_Uint8 | ImGuiColorEditFlags_AlphaBar | ImGuiColorEditFlags_DisplayRGB | ImGuiColorEditFlags_DisplayHex);
ImGui::NewLine();
}
}
void ViewTools::createView() {
if (!this->m_windowOpen)
return;
void ViewTools::drawMathEvaluator() {
if (ImGui::CollapsingHeader("Calculator")) {
if (ImGui::InputText("Input", this->m_mathInput, 0xFFFF, ImGuiInputTextFlags_EnterReturnsTrue | ImGuiInputTextFlags_AutoSelectAll)) {
ImGui::SetKeyboardFocusHere();
std::optional<long double> result;
if (ImGui::Begin("Tools", &this->m_windowOpen)) {
try {
result = this->m_mathEvaluator.evaluate(this->m_mathInput);
} catch (std::invalid_argument &e) {
this->m_lastMathError = e.what();
}
if (result.has_value()) {
this->m_mathHistory.push_back(result.value());
std::memset(this->m_mathInput, 0x00, 0xFFFF);
this->m_lastMathError.clear();
}
}
if (!this->m_lastMathError.empty())
ImGui::TextColored(ImColor(0xA00040FF), "Last Error: %s", this->m_lastMathError.c_str());
else
ImGui::NewLine();
enum class MathDisplayType { Standard, Scientific, Programmer } mathDisplayType;
if (ImGui::BeginTabBar("##mathFormatTabBar")) {
if (ImGui::BeginTabItem("Standard")) {
mathDisplayType = MathDisplayType::Standard;
ImGui::EndTabItem();
}
if (ImGui::BeginTabItem("Scientific")) {
mathDisplayType = MathDisplayType::Scientific;
ImGui::EndTabItem();
}
if (ImGui::BeginTabItem("Programmer")) {
mathDisplayType = MathDisplayType::Programmer;
ImGui::EndTabItem();
}
ImGui::EndTabBar();
}
if (ImGui::BeginTable("##mathWrapper", 2)) {
ImGui::TableSetupColumn("##results");
ImGui::TableSetupColumn("##variables", ImGuiTableColumnFlags_WidthStretch, 0.7);
ImGui::TableNextRow();
ImGui::TableNextColumn();
if (ImGui::BeginTable("##mathHistory", 1, ImGuiTableFlags_ScrollY | ImGuiTableFlags_Borders | ImGuiTableFlags_RowBg, ImVec2(0, 400))) {
ImGui::TableSetupScrollFreeze(0, 1);
ImGui::TableSetupColumn("History");
ImGuiListClipper clipper;
clipper.Begin(this->m_mathHistory.size());
ImGui::TableHeadersRow();
while (clipper.Step()) {
for (u64 i = clipper.DisplayStart; i < clipper.DisplayEnd; i++) {
if (i == 0)
ImGui::PushStyleColor(ImGuiCol_Text, ImU32(ImColor(0xA5, 0x45, 0x45)));
ImGui::TableNextRow();
ImGui::TableNextColumn();
switch (mathDisplayType) {
case MathDisplayType::Standard:
ImGui::Text("%.3Lf", this->m_mathHistory[(this->m_mathHistory.size() - 1) - i]);
break;
case MathDisplayType::Scientific:
ImGui::Text("%.6Le", this->m_mathHistory[(this->m_mathHistory.size() - 1) - i]);
break;
case MathDisplayType::Programmer:
ImGui::Text("0x%llX (%llu)",
u64(this->m_mathHistory[(this->m_mathHistory.size() - 1) - i]),
u64(this->m_mathHistory[(this->m_mathHistory.size() - 1) - i]));
break;
}
if (i == 0)
ImGui::PopStyleColor();
}
}
clipper.End();
ImGui::EndTable();
}
ImGui::TableNextColumn();
if (ImGui::BeginTable("##mathVariables", 2, ImGuiTableFlags_ScrollY | ImGuiTableFlags_Borders | ImGuiTableFlags_RowBg, ImVec2(0, 400))) {
ImGui::TableSetupScrollFreeze(0, 1);
ImGui::TableSetupColumn("Name");
ImGui::TableSetupColumn("Value");
ImGui::TableHeadersRow();
for (const auto &[name, value] : this->m_mathEvaluator.getVariables()) {
ImGui::TableNextRow();
ImGui::TableNextColumn();
ImGui::TextUnformatted(name.c_str());
ImGui::TableNextColumn();
switch (mathDisplayType) {
case MathDisplayType::Standard:
ImGui::Text("%.3Lf", value);
break;
case MathDisplayType::Scientific:
ImGui::Text("%.6Le", value);
break;
case MathDisplayType::Programmer:
ImGui::Text("0x%llX (%llu)", u64(value), u64(value));
break;
}
}
ImGui::EndTable();
}
ImGui::EndTable();
}
}
}
void ViewTools::createView() {
if (ImGui::Begin("Tools", &this->getWindowOpenState(), ImGuiWindowFlags_NoCollapse)) {
this->drawDemangler();
this->drawASCIITable();
this->drawRegexReplacer();
this->drawMathEvaluator();
this->drawColorPicker();
}
@@ -189,10 +305,7 @@ namespace hex {
}
void ViewTools::createMenu() {
if (ImGui::BeginMenu("View")) {
ImGui::MenuItem("Tools View", "", &this->m_windowOpen);
ImGui::EndMenu();
}
}
}

View File

@@ -12,42 +12,51 @@
namespace hex {
namespace {
constexpr auto MenuBarItems = { "File", "Edit", "View", "Help" };
void *ImHexSettingsHandler_ReadOpenFn(ImGuiContext *ctx, ImGuiSettingsHandler *, const char *) {
return ctx; // Unused, but the return value has to be non-null
}
void ImHexSettingsHandler_ReadLine(ImGuiContext*, ImGuiSettingsHandler *handler, void *, const char* line) {
auto *window = reinterpret_cast<Window *>(handler->UserData);
float scale;
if (sscanf(line, "Scale=%f", &scale) == 1) { window->m_globalScale = scale; }
else if (sscanf(line, "FontScale=%f", &scale) == 1) { window->m_fontScale = scale; }
}
void ImHexSettingsHandler_ApplyAll(ImGuiContext *ctx, ImGuiSettingsHandler *handler) {
auto *window = reinterpret_cast<Window *>(handler->UserData);
auto &style = ImGui::GetStyle();
auto &io = ImGui::GetIO();
if (window->m_globalScale != 0.0f)
style.ScaleAllSizes(window->m_globalScale);
if (window->m_fontScale != 0.0f)
io.FontGlobalScale = window->m_fontScale;
}
void ImHexSettingsHandler_WriteAll(ImGuiContext* ctx, ImGuiSettingsHandler *handler, ImGuiTextBuffer *buf) {
auto *window = reinterpret_cast<Window *>(handler->UserData);
buf->reserve(buf->size() + 0x20); // Ballpark reserve
buf->appendf("[%s][General]\n", handler->TypeName);
buf->appendf("Scale=%.1f\n", window->m_globalScale);
buf->appendf("FontScale=%.1f\n", window->m_fontScale);
buf->append("\n");
void *ImHexSettingsHandler_ReadOpenFn(ImGuiContext *ctx, ImGuiSettingsHandler *, const char *) {
return ctx; // Unused, but the return value has to be non-null
}
void ImHexSettingsHandler_ReadLine(ImGuiContext*, ImGuiSettingsHandler *handler, void *, const char* line) {
auto *window = reinterpret_cast<Window *>(handler->UserData);
float scale;
if (sscanf(line, "Scale=%f", &scale) == 1) { window->m_globalScale = scale; }
else if (sscanf(line, "FontScale=%f", &scale) == 1) { window->m_fontScale = scale; }
else {
for (auto &view : window->m_views) {
std::string format = view->getName() + "=%d";
sscanf(line, format.c_str(), &view->getWindowOpenState());
}
}
}
void ImHexSettingsHandler_ApplyAll(ImGuiContext *ctx, ImGuiSettingsHandler *handler) {
auto *window = reinterpret_cast<Window *>(handler->UserData);
auto &style = ImGui::GetStyle();
auto &io = ImGui::GetIO();
if (window->m_globalScale != 0.0f)
style.ScaleAllSizes(window->m_globalScale);
if (window->m_fontScale != 0.0f)
io.FontGlobalScale = window->m_fontScale;
}
void ImHexSettingsHandler_WriteAll(ImGuiContext* ctx, ImGuiSettingsHandler *handler, ImGuiTextBuffer *buf) {
auto *window = reinterpret_cast<Window *>(handler->UserData);
buf->reserve(buf->size() + 0x20); // Ballpark reserve
buf->appendf("[%s][General]\n", handler->TypeName);
buf->appendf("Scale=%.1f\n", window->m_globalScale);
buf->appendf("FontScale=%.1f\n", window->m_fontScale);
for (auto &view : window->m_views) {
buf->appendf("%s=%d\n", view->getName().c_str(), view->getWindowOpenState());
}
buf->append("\n");
}
Window::Window() {
@@ -72,9 +81,20 @@ namespace hex {
View::getDeferedCalls().clear();
for (auto &view : this->m_views) {
if (!view->getWindowOpenState())
continue;
ImGui::SetNextWindowSizeConstraints(ImVec2(480, 720), ImVec2(FLT_MAX, FLT_MAX));
view->createView();
}
View::drawCommonInterfaces();
#ifdef DEBUG
if (this->m_demoWindowOpen)
ImGui::ShowDemoWindow(&this->m_demoWindowOpen);
#endif
this->frameEnd();
}
}
@@ -87,64 +107,90 @@ namespace hex {
ImGui_ImplGlfw_NewFrame();
ImGui::NewFrame();
ImGuiWindowFlags windowFlags = ImGuiWindowFlags_MenuBar | ImGuiWindowFlags_NoDocking;
ImGuiViewport* viewport = ImGui::GetMainViewport();
ImGui::SetNextWindowPos(viewport->GetWorkPos());
ImGui::SetNextWindowSize(viewport->GetWorkSize());
ImGui::SetNextWindowViewport(viewport->ID);
ImGui::PushStyleVar(ImGuiStyleVar_WindowRounding, 0.0f);
ImGui::PushStyleVar(ImGuiStyleVar_WindowBorderSize, 0.0f);
windowFlags |= ImGuiWindowFlags_NoTitleBar | ImGuiWindowFlags_NoCollapse | ImGuiWindowFlags_NoMove | ImGuiWindowFlags_NoResize;
windowFlags |= ImGuiWindowFlags_NoBringToFrontOnFocus | ImGuiWindowFlags_NoNavFocus;
ImGui::Begin("DockSpace", nullptr, windowFlags);
ImGui::PopStyleVar(2);
ImGui::DockSpace(ImGui::GetID("MainDock"), ImVec2(0.0f, 0.0f), ImGuiDockNodeFlags_None);
ImGuiWindowFlags windowFlags = ImGuiWindowFlags_MenuBar | ImGuiWindowFlags_NoDocking
| ImGuiWindowFlags_NoTitleBar | ImGuiWindowFlags_NoCollapse
| ImGuiWindowFlags_NoMove | ImGuiWindowFlags_NoResize
| ImGuiWindowFlags_NoNavFocus | ImGuiWindowFlags_NoBringToFrontOnFocus;
ImGui::BeginMenuBar();
if (ImGui::Begin("DockSpace", nullptr, windowFlags)) {
ImGui::PopStyleVar(2);
ImGui::DockSpace(ImGui::GetID("MainDock"), ImVec2(0.0f, 0.0f), ImGuiDockNodeFlags_None);
for (auto &view : this->m_views)
view->createMenu();
if (ImGui::BeginMenuBar()) {
if (ImGui::BeginMenu("View")) {
ImGui::MenuItem("Display FPS", "", &this->m_fpsVisible);
ImGui::EndMenu();
}
for (auto menu : MenuBarItems)
if (ImGui::BeginMenu(menu)) ImGui::EndMenu();
if (this->m_fpsVisible) {
char buffer[0x20];
snprintf(buffer, 0x20, "%.1f FPS", ImGui::GetIO().Framerate);
if (ImGui::BeginMenu("View")) {
for (auto &view : this->m_views) {
if (view->hasViewMenuItemEntry())
ImGui::MenuItem((view->getName() + " View").c_str(), "", &view->getWindowOpenState());
}
ImGui::EndMenu();
}
ImGui::SameLine(ImGui::GetWindowWidth() - ImGui::GetFontSize() * strlen(buffer) + 20);
ImGui::TextUnformatted(buffer);
}
for (auto &view : this->m_views) {
view->createMenu();
}
if (ImGui::BeginMenu("View")) {
ImGui::Separator();
ImGui::MenuItem("Display FPS", "", &this->m_fpsVisible);
#ifdef DEBUG
ImGui::MenuItem("Demo View", "", &this->m_demoWindowOpen);
#endif
ImGui::EndMenu();
}
ImGui::EndMenuBar();
if (this->m_fpsVisible) {
char buffer[0x20];
snprintf(buffer, 0x20, "%.1f FPS", ImGui::GetIO().Framerate);
ImGui::End();
ImGui::SameLine(ImGui::GetWindowWidth() - ImGui::GetFontSize() * strlen(buffer) + 20);
ImGui::TextUnformatted(buffer);
}
if (auto &[key, mods] = Window::s_currShortcut; key != -1) {
for (auto &view : this->m_views) {
if (view->handleShortcut(key, mods))
break;
ImGui::EndMenuBar();
}
if (auto &[key, mods] = Window::s_currShortcut; key != -1) {
for (auto &view : this->m_views) {
if (view->getWindowOpenState()) {
if (view->handleShortcut(key, mods))
break;
}
}
Window::s_currShortcut = { -1, -1 };
}
Window::s_currShortcut = { -1, -1 };
}
ImGui::End();
}
void Window::frameEnd() {
ImGui::Render();
int display_w, display_h;
glfwGetFramebufferSize(this->m_window, &display_w, &display_h);
glViewport(0, 0, display_w, display_h);
int displayWidth, displayHeight;
glfwGetFramebufferSize(this->m_window, &displayWidth, &displayHeight);
glViewport(0, 0, displayWidth, displayHeight);
glClearColor(0.45f, 0.55f, 0.60f, 1.00f);
glClear(GL_COLOR_BUFFER_BIT);
ImGui_ImplOpenGL3_RenderDrawData(ImGui::GetDrawData());
GLFWwindow* backup_current_context = glfwGetCurrentContext();
ImGui::UpdatePlatformWindows();
ImGui::RenderPlatformWindowsDefault();
glfwMakeContextCurrent(backup_current_context);
glfwSwapBuffers(this->m_window);
}
@@ -156,16 +202,18 @@ namespace hex {
if (!glfwInit())
throw std::runtime_error("Failed to initialize GLFW!");
#ifdef __APPLE__
glfwWindowHint(GLFW_OPENGL_FORWARD_COMPAT, GL_TRUE);
#endif
#ifdef __APPLE__
glfwWindowHint(GLFW_OPENGL_FORWARD_COMPAT, GL_TRUE);
#endif
glfwWindowHint(GLFW_CONTEXT_VERSION_MAJOR, 3);
glfwWindowHint(GLFW_CONTEXT_VERSION_MINOR, 2);
glfwWindowHint(GLFW_OPENGL_PROFILE, GLFW_OPENGL_CORE_PROFILE);
this->m_window = glfwCreateWindow(1280, 720, "ImHex", nullptr, nullptr);
if (this->m_window == nullptr)
throw std::runtime_error("Failed to create window!");
@@ -184,7 +232,12 @@ namespace hex {
View::postEvent(Events::FileDropped, paths[0]);
});
glfwSetWindowSizeLimits(this->m_window, 720, 480, GLFW_DONT_CARE, GLFW_DONT_CARE);
glfwSetWindowCloseCallback(this->m_window, [](GLFWwindow *window) {
View::postEvent(Events::WindowClosing, window);
});
glfwSetWindowSizeLimits(this->m_window, 720, 480, GLFW_DONT_CARE, GLFW_DONT_CARE);
if (gladLoadGL() == 0)
throw std::runtime_error("Failed to initialize OpenGL loader!");
@@ -194,7 +247,13 @@ namespace hex {
IMGUI_CHECKVERSION();
auto *ctx = ImGui::CreateContext();
ImGuiIO& io = ImGui::GetIO();
io.ConfigFlags |= ImGuiConfigFlags_DockingEnable;
ImGuiStyle& style = ImGui::GetStyle();
io.ConfigFlags |= ImGuiConfigFlags_DockingEnable | ImGuiConfigFlags_ViewportsEnable;
io.ConfigViewportsNoTaskBarIcon = true;
style.WindowMenuButtonPosition = ImGuiDir_None;
style.IndentSpacing = 10.0F;
// Install custom settings handler
ImGuiSettingsHandler handler;