mirror of
https://github.com/WerWolv/ImHex.git
synced 2026-04-01 21:17:44 -05:00
Compare commits
83 Commits
| Author | SHA1 | Date | |
|---|---|---|---|
|
|
7a9d7b59e8 | ||
|
|
4720cf9fbe | ||
|
|
68f93c5e3d | ||
|
|
7b8330f8f8 | ||
|
|
63696b9f5f | ||
|
|
727b3c6b10 | ||
|
|
05cb3831a2 | ||
|
|
5b51375404 | ||
|
|
398e845e8d | ||
|
|
45c29888b4 | ||
|
|
82c5bd528c | ||
|
|
45d0b614c2 | ||
|
|
4725ff6d5f | ||
|
|
7158d4d4ad | ||
|
|
502d90b117 | ||
|
|
f0c95b3f29 | ||
|
|
d3dccace37 | ||
|
|
a30453616b | ||
|
|
168ba2ff9f | ||
|
|
2ca9a8fc79 | ||
|
|
6456a68805 | ||
|
|
e82b607fa1 | ||
|
|
7168c9ed59 | ||
|
|
d7af6934b6 | ||
|
|
b4a5a936a0 | ||
|
|
6129e0d696 | ||
|
|
a0c8424800 | ||
|
|
dc839e1ad1 | ||
|
|
59155256a9 | ||
|
|
29b7995e7f | ||
|
|
ae9ec8851c | ||
|
|
7c2df736df | ||
|
|
192e7d5060 | ||
|
|
6aed140ecf | ||
|
|
76d56c9ed4 | ||
|
|
17096055f8 | ||
|
|
c6134bc038 | ||
|
|
16f885f469 | ||
|
|
00072d0216 | ||
|
|
4878f70e58 | ||
|
|
9e8523470d | ||
|
|
7316be0bc2 | ||
|
|
8a4b663890 | ||
|
|
3276ad9979 | ||
|
|
0890043bf4 | ||
|
|
c90ef343c1 | ||
|
|
0f6e276113 | ||
|
|
1395c95831 | ||
|
|
33b70a550f | ||
|
|
985e924e9d | ||
|
|
3827919a32 | ||
|
|
d55bea7c46 | ||
|
|
2efe326fdb | ||
|
|
d43bd23e1a | ||
|
|
015ec12215 | ||
|
|
fde9dc7961 | ||
|
|
ed572ececf | ||
|
|
fd2b79bea9 | ||
|
|
acc10930c2 | ||
|
|
f17d6c2359 | ||
|
|
e21211f3f6 | ||
|
|
58deaa6b29 | ||
|
|
fcd88b4b3b | ||
|
|
3bd987ff2c | ||
|
|
45bcdc8c46 | ||
|
|
0d0b2d6962 | ||
|
|
fd6a41d219 | ||
|
|
b052429a73 | ||
|
|
1996f401e0 | ||
|
|
3b3f2226f1 | ||
|
|
5c0f6a1e50 | ||
|
|
b7438f6ab8 | ||
|
|
84f80b3e06 | ||
|
|
c281372b8d | ||
|
|
788a3cfc5e | ||
|
|
01eeb53af0 | ||
|
|
7cf69128ea | ||
|
|
c977baecc4 | ||
|
|
2ab2f5e675 | ||
|
|
6a38f1e9f3 | ||
|
|
f0eba69c4a | ||
|
|
cea366e135 | ||
|
|
d752c7434f |
5
.github/FUNDING.yml
vendored
Normal file
5
.github/FUNDING.yml
vendored
Normal 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
44
.github/workflows/build_linux.yml
vendored
Normal 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
40
.github/workflows/build_win.yml
vendored
Normal 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
8
.idea/modules.xml
generated
@@ -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>
|
|
||||||
@@ -1,26 +1,57 @@
|
|||||||
cmake_minimum_required(VERSION 3.16)
|
cmake_minimum_required(VERSION 3.16)
|
||||||
project(HexEditor)
|
project(HexEditor)
|
||||||
|
|
||||||
|
SET(CMAKE_FIND_PACKAGE_SORT_ORDER NATURAL)
|
||||||
|
SET(CMAKE_FIND_PACKAGE_SORT_DIRECTION DEC)
|
||||||
|
|
||||||
set(CMAKE_CXX_STANDARD 20)
|
set(CMAKE_CXX_STANDARD 20)
|
||||||
|
|
||||||
|
|
||||||
find_package(PkgConfig REQUIRED)
|
find_package(PkgConfig REQUIRED)
|
||||||
pkg_search_module(GLFW REQUIRED glfw3)
|
pkg_search_module(GLFW REQUIRED glfw3)
|
||||||
pkg_search_module(GLM REQUIRED glm)
|
pkg_search_module(GLM REQUIRED glm)
|
||||||
|
pkg_search_module(CRYPTO REQUIRED libcrypto)
|
||||||
pkg_search_module(CAPSTONE REQUIRED capstone)
|
pkg_search_module(CAPSTONE REQUIRED capstone)
|
||||||
find_package(OpenGL REQUIRED)
|
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} ${CAPSTONE_INCLUDE_DIRS} libs/ImGui/include libs/glad/include)
|
if(Python_VERSION LESS 3)
|
||||||
SET(CMAKE_CXX_FLAGS "${CMAKE_C_FLAGS} -DIMGUI_IMPL_OPENGL_LOADER_GLAD")
|
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)
|
if (WIN32)
|
||||||
SET(CMAKE_CXX_FLAGS "${CMAKE_CXX_FLAGS} -static-libstdc++ -static-libgcc -static")
|
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)
|
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
|
add_executable(ImHex
|
||||||
source/main.cpp
|
source/main.cpp
|
||||||
source/window.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/preprocessor.cpp
|
||||||
source/lang/lexer.cpp
|
source/lang/lexer.cpp
|
||||||
@@ -28,7 +59,7 @@ add_executable(ImHex
|
|||||||
source/lang/validator.cpp
|
source/lang/validator.cpp
|
||||||
source/lang/evaluator.cpp
|
source/lang/evaluator.cpp
|
||||||
|
|
||||||
source/provider/file_provider.cpp
|
source/providers/file_provider.cpp
|
||||||
|
|
||||||
source/views/view_hexeditor.cpp
|
source/views/view_hexeditor.cpp
|
||||||
source/views/view_pattern.cpp
|
source/views/view_pattern.cpp
|
||||||
@@ -40,6 +71,8 @@ add_executable(ImHex
|
|||||||
source/views/view_strings.cpp
|
source/views/view_strings.cpp
|
||||||
source/views/view_data_inspector.cpp
|
source/views/view_data_inspector.cpp
|
||||||
source/views/view_disassembler.cpp
|
source/views/view_disassembler.cpp
|
||||||
|
source/views/view_bookmarks.cpp
|
||||||
|
source/views/view_patches.cpp
|
||||||
|
|
||||||
libs/glad/source/glad.c
|
libs/glad/source/glad.c
|
||||||
|
|
||||||
@@ -55,10 +88,12 @@ add_executable(ImHex
|
|||||||
resource.rc
|
resource.rc
|
||||||
)
|
)
|
||||||
|
|
||||||
|
target_link_directories(ImHex PRIVATE ${LLVM_LIBRARY_DIR})
|
||||||
|
|
||||||
if (WIN32)
|
if (WIN32)
|
||||||
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)
|
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)
|
endif (WIN32)
|
||||||
|
|
||||||
if (UNIX)
|
if (UNIX)
|
||||||
target_link_libraries(ImHex libglfw.so libmagic.so libcrypto.so libdl.so libcapstone.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)
|
endif (UNIX)
|
||||||
339
LICENSE
Normal file
339
LICENSE
Normal 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
125
README.md
@@ -1,7 +1,128 @@
|
|||||||
# ImHex
|
# 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
|
## Screenshots
|
||||||
|
|
||||||

|

|
||||||
|

|
||||||
|
|
||||||
|
## 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
|
||||||
|
|
||||||
|
|||||||
@@ -5,6 +5,7 @@
|
|||||||
#include <array>
|
#include <array>
|
||||||
#include <optional>
|
#include <optional>
|
||||||
#include <string>
|
#include <string>
|
||||||
|
#include <vector>
|
||||||
|
|
||||||
namespace hex {
|
namespace hex {
|
||||||
|
|
||||||
@@ -21,4 +22,6 @@ namespace hex {
|
|||||||
std::array<u32, 12> sha384(prv::Provider* &data, u64 offset, size_t size);
|
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::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);
|
||||||
}
|
}
|
||||||
54
include/helpers/disassembler.hpp
Normal file
54
include/helpers/disassembler.hpp
Normal 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;
|
||||||
|
}
|
||||||
|
};
|
||||||
|
}
|
||||||
@@ -6,10 +6,20 @@
|
|||||||
namespace hex {
|
namespace hex {
|
||||||
|
|
||||||
enum class Events {
|
enum class Events {
|
||||||
|
FileLoaded,
|
||||||
DataChanged,
|
DataChanged,
|
||||||
PatternChanged,
|
PatternChanged,
|
||||||
FileDropped,
|
FileDropped,
|
||||||
ByteSelected
|
WindowClosing,
|
||||||
|
RegionSelected,
|
||||||
|
|
||||||
|
SelectionChangeRequest,
|
||||||
|
|
||||||
|
AddBookmark,
|
||||||
|
AppendPatternLanguageCode,
|
||||||
|
|
||||||
|
ProjectFileStore,
|
||||||
|
ProjectFileLoad
|
||||||
};
|
};
|
||||||
|
|
||||||
struct EventHandler {
|
struct EventHandler {
|
||||||
33
include/helpers/loader_script_handler.hpp
Normal file
33
include/helpers/loader_script_handler.hpp
Normal 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);
|
||||||
|
};
|
||||||
|
|
||||||
|
}
|
||||||
88
include/helpers/math_evaluator.hpp
Normal file
88
include/helpers/math_evaluator.hpp
Normal 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;
|
||||||
|
};
|
||||||
|
|
||||||
|
}
|
||||||
17
include/helpers/patches.hpp
Normal file
17
include/helpers/patches.hpp
Normal 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);
|
||||||
|
}
|
||||||
46
include/helpers/project_file_handler.hpp
Normal file
46
include/helpers/project_file_handler.hpp
Normal 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;
|
||||||
|
};
|
||||||
|
|
||||||
|
}
|
||||||
@@ -11,6 +11,7 @@
|
|||||||
|
|
||||||
#ifdef __MINGW32__
|
#ifdef __MINGW32__
|
||||||
#include <winsock.h>
|
#include <winsock.h>
|
||||||
|
|
||||||
#else
|
#else
|
||||||
#include <arpa/inet.h>
|
#include <arpa/inet.h>
|
||||||
#endif
|
#endif
|
||||||
@@ -21,7 +22,7 @@ namespace hex {
|
|||||||
|
|
||||||
template<typename ... Args>
|
template<typename ... Args>
|
||||||
inline std::string format(const std::string &format, Args ... args) {
|
inline std::string format(const std::string &format, Args ... args) {
|
||||||
size_t size = snprintf( nullptr, 0, format.c_str(), args ... );
|
ssize_t size = snprintf( nullptr, 0, format.c_str(), args ... );
|
||||||
|
|
||||||
if( size <= 0 )
|
if( size <= 0 )
|
||||||
return "";
|
return "";
|
||||||
@@ -32,59 +33,34 @@ namespace hex {
|
|||||||
return std::string(buffer.data(), buffer.data() + size);
|
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) {
|
[[nodiscard]] constexpr inline u64 signExtend(u64 value, u8 currWidth, u8 targetWidth) {
|
||||||
u64 mask = 1LLU << (currWidth - 1);
|
u64 mask = 1LLU << (currWidth - 1);
|
||||||
return (((value ^ mask) - mask) << (64 - targetWidth)) >> (64 - targetWidth);
|
return (((value ^ mask) - mask) << (64 - targetWidth)) >> (64 - targetWidth);
|
||||||
}
|
}
|
||||||
|
|
||||||
constexpr inline bool isUnsigned(const lang::Token::TypeToken::Type type) {
|
[[nodiscard]] constexpr inline bool isUnsigned(const lang::Token::TypeToken::Type type) {
|
||||||
return (static_cast<u32>(type) & 0x0F) == 0x00;
|
return (static_cast<u32>(type) & 0x0F) == 0x00;
|
||||||
}
|
}
|
||||||
|
|
||||||
constexpr inline bool isSigned(const lang::Token::TypeToken::Type type) {
|
[[nodiscard]] constexpr inline bool isSigned(const lang::Token::TypeToken::Type type) {
|
||||||
return (static_cast<u32>(type) & 0x0F) == 0x01;
|
return (static_cast<u32>(type) & 0x0F) == 0x01;
|
||||||
}
|
}
|
||||||
|
|
||||||
constexpr inline bool isFloatingPoint(const lang::Token::TypeToken::Type type) {
|
[[nodiscard]] constexpr inline bool isFloatingPoint(const lang::Token::TypeToken::Type type) {
|
||||||
return (static_cast<u32>(type) & 0x0F) == 0x02;
|
return (static_cast<u32>(type) & 0x0F) == 0x02;
|
||||||
}
|
}
|
||||||
|
|
||||||
constexpr inline u32 getTypeSize(const lang::Token::TypeToken::Type type) {
|
[[nodiscard]] constexpr inline u32 getTypeSize(const lang::Token::TypeToken::Type type) {
|
||||||
return static_cast<u32>(type) >> 4;
|
return static_cast<u32>(type) >> 4;
|
||||||
}
|
}
|
||||||
|
|
||||||
inline std::string toByteString(u64 bytes) {
|
std::string toByteString(u64 bytes);
|
||||||
double value = bytes;
|
std::string makePrintable(char c);
|
||||||
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;
|
|
||||||
}
|
|
||||||
|
|
||||||
template<typename T>
|
template<typename T>
|
||||||
struct always_false : std::false_type {};
|
struct always_false : std::false_type {};
|
||||||
@@ -123,6 +99,7 @@ namespace hex {
|
|||||||
throw std::invalid_argument("Invalid value size!");
|
throw std::invalid_argument("Invalid value size!");
|
||||||
}
|
}
|
||||||
|
|
||||||
|
std::vector<u8> readFile(std::string_view path);
|
||||||
|
|
||||||
class ScopeExit {
|
class ScopeExit {
|
||||||
public:
|
public:
|
||||||
@@ -136,4 +113,16 @@ namespace hex {
|
|||||||
private:
|
private:
|
||||||
std::function<void()> m_func;
|
std::function<void()> m_func;
|
||||||
};
|
};
|
||||||
|
|
||||||
|
struct Region {
|
||||||
|
u64 address;
|
||||||
|
size_t size;
|
||||||
|
};
|
||||||
|
|
||||||
|
struct Bookmark {
|
||||||
|
Region region;
|
||||||
|
|
||||||
|
std::vector<char> name;
|
||||||
|
std::vector<char> comment;
|
||||||
|
};
|
||||||
}
|
}
|
||||||
@@ -18,6 +18,9 @@ using s128 = __int128_t;
|
|||||||
#include "lang/result.hpp"
|
#include "lang/result.hpp"
|
||||||
#include "lang/results.hpp"
|
#include "lang/results.hpp"
|
||||||
|
|
||||||
|
extern int mainArgc;
|
||||||
|
extern char **mainArgv;
|
||||||
|
|
||||||
#if defined(__EMX__) || defined (WIN32)
|
#if defined(__EMX__) || defined (WIN32)
|
||||||
#define MAGIC_PATH_SEPARATOR ";"
|
#define MAGIC_PATH_SEPARATOR ";"
|
||||||
#else
|
#else
|
||||||
|
|||||||
@@ -2,6 +2,7 @@
|
|||||||
|
|
||||||
#include "token.hpp"
|
#include "token.hpp"
|
||||||
|
|
||||||
|
#include <bit>
|
||||||
#include <optional>
|
#include <optional>
|
||||||
#include <unordered_map>
|
#include <unordered_map>
|
||||||
#include <vector>
|
#include <vector>
|
||||||
@@ -20,19 +21,21 @@ namespace hex::lang {
|
|||||||
Scope,
|
Scope,
|
||||||
};
|
};
|
||||||
|
|
||||||
explicit ASTNode(Type type) : m_type(type) {}
|
explicit ASTNode(Type type, u32 lineNumber) : m_type(type), m_lineNumber(lineNumber) {}
|
||||||
virtual ~ASTNode() = default;
|
virtual ~ASTNode() = default;
|
||||||
|
|
||||||
Type getType() { return this->m_type; }
|
Type getType() { return this->m_type; }
|
||||||
|
u32 getLineNumber() { return this->m_lineNumber; }
|
||||||
|
|
||||||
private:
|
private:
|
||||||
Type m_type;
|
Type m_type;
|
||||||
|
u32 m_lineNumber;
|
||||||
};
|
};
|
||||||
|
|
||||||
class ASTNodeVariableDecl : public ASTNode {
|
class ASTNodeVariableDecl : public ASTNode {
|
||||||
public:
|
public:
|
||||||
explicit ASTNodeVariableDecl(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 = { })
|
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), m_type(type), m_name(name), m_customTypeName(customTypeName), m_offset(offset), m_arraySize(arraySize), m_arraySizeVariable(arraySizeVariable), m_pointerSize(pointerSize) { }
|
: 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 Token::TypeToken::Type& getVariableType() const { return this->m_type; }
|
||||||
const std::string& getCustomVariableTypeName() const { return this->m_customTypeName; }
|
const std::string& getCustomVariableTypeName() const { return this->m_customTypeName; }
|
||||||
@@ -41,6 +44,7 @@ namespace hex::lang {
|
|||||||
size_t getArraySize() const { return this->m_arraySize; }
|
size_t getArraySize() const { return this->m_arraySize; }
|
||||||
std::optional<std::string> getArraySizeVariable() const { return this->m_arraySizeVariable; }
|
std::optional<std::string> getArraySizeVariable() const { return this->m_arraySizeVariable; }
|
||||||
std::optional<u8> getPointerSize() const { return this->m_pointerSize; }
|
std::optional<u8> getPointerSize() const { return this->m_pointerSize; }
|
||||||
|
std::optional<std::endian> getEndianess() const { return this->m_endianess; }
|
||||||
|
|
||||||
private:
|
private:
|
||||||
Token::TypeToken::Type m_type;
|
Token::TypeToken::Type m_type;
|
||||||
@@ -49,11 +53,12 @@ namespace hex::lang {
|
|||||||
size_t m_arraySize;
|
size_t m_arraySize;
|
||||||
std::optional<std::string> m_arraySizeVariable;
|
std::optional<std::string> m_arraySizeVariable;
|
||||||
std::optional<u8> m_pointerSize;
|
std::optional<u8> m_pointerSize;
|
||||||
|
std::optional<std::endian> m_endianess = { };
|
||||||
};
|
};
|
||||||
|
|
||||||
class ASTNodeScope : public ASTNode {
|
class ASTNodeScope : public ASTNode {
|
||||||
public:
|
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; }
|
std::vector<ASTNode*> &getNodes() { return this->m_nodes; }
|
||||||
private:
|
private:
|
||||||
@@ -62,8 +67,8 @@ namespace hex::lang {
|
|||||||
|
|
||||||
class ASTNodeStruct : public ASTNode {
|
class ASTNodeStruct : public ASTNode {
|
||||||
public:
|
public:
|
||||||
explicit ASTNodeStruct(std::string name, std::vector<ASTNode*> nodes)
|
explicit ASTNodeStruct(u32 lineNumber, std::string name, std::vector<ASTNode*> nodes)
|
||||||
: ASTNode(Type::Struct), m_name(name), m_nodes(nodes) { }
|
: ASTNode(Type::Struct, lineNumber), m_name(name), m_nodes(nodes) { }
|
||||||
|
|
||||||
const std::string& getName() const { return this->m_name; }
|
const std::string& getName() const { return this->m_name; }
|
||||||
std::vector<ASTNode*> &getNodes() { return this->m_nodes; }
|
std::vector<ASTNode*> &getNodes() { return this->m_nodes; }
|
||||||
@@ -74,8 +79,8 @@ namespace hex::lang {
|
|||||||
|
|
||||||
class ASTNodeUnion : public ASTNode {
|
class ASTNodeUnion : public ASTNode {
|
||||||
public:
|
public:
|
||||||
explicit ASTNodeUnion(std::string name, std::vector<ASTNode*> nodes)
|
explicit ASTNodeUnion(u32 lineNumber, std::string name, std::vector<ASTNode*> nodes)
|
||||||
: ASTNode(Type::Union), m_name(name), m_nodes(nodes) { }
|
: ASTNode(Type::Union, lineNumber), m_name(name), m_nodes(nodes) { }
|
||||||
|
|
||||||
const std::string& getName() const { return this->m_name; }
|
const std::string& getName() const { return this->m_name; }
|
||||||
std::vector<ASTNode*> &getNodes() { return this->m_nodes; }
|
std::vector<ASTNode*> &getNodes() { return this->m_nodes; }
|
||||||
@@ -86,8 +91,8 @@ namespace hex::lang {
|
|||||||
|
|
||||||
class ASTNodeBitField : public ASTNode {
|
class ASTNodeBitField : public ASTNode {
|
||||||
public:
|
public:
|
||||||
explicit ASTNodeBitField(std::string name, std::vector<std::pair<std::string, size_t>> fields)
|
explicit ASTNodeBitField(u32 lineNumber, std::string name, std::vector<std::pair<std::string, size_t>> fields)
|
||||||
: ASTNode(Type::Bitfield), m_name(name), m_fields(fields) { }
|
: ASTNode(Type::Bitfield, lineNumber), m_name(name), m_fields(fields) { }
|
||||||
|
|
||||||
const std::string& getName() const { return this->m_name; }
|
const std::string& getName() const { return this->m_name; }
|
||||||
std::vector<std::pair<std::string, size_t>> &getFields() { return this->m_fields; }
|
std::vector<std::pair<std::string, size_t>> &getFields() { return this->m_fields; }
|
||||||
@@ -98,8 +103,8 @@ namespace hex::lang {
|
|||||||
|
|
||||||
class ASTNodeTypeDecl : public ASTNode {
|
class ASTNodeTypeDecl : public ASTNode {
|
||||||
public:
|
public:
|
||||||
explicit ASTNodeTypeDecl(const Token::TypeToken::Type &type, const std::string &name, const std::string& customTypeName = "")
|
explicit ASTNodeTypeDecl(u32 lineNumber, 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) { }
|
: ASTNode(Type::TypeDecl, lineNumber), m_type(type), m_name(name), m_customTypeName(customTypeName) { }
|
||||||
|
|
||||||
const std::string& getTypeName() const { return this->m_name; };
|
const std::string& getTypeName() const { return this->m_name; };
|
||||||
|
|
||||||
@@ -112,8 +117,8 @@ namespace hex::lang {
|
|||||||
|
|
||||||
class ASTNodeEnum : public ASTNode {
|
class ASTNodeEnum : public ASTNode {
|
||||||
public:
|
public:
|
||||||
explicit ASTNodeEnum(const Token::TypeToken::Type &type, const std::string &name)
|
explicit ASTNodeEnum(u32 lineNumber, const Token::TypeToken::Type &type, const std::string &name)
|
||||||
: ASTNode(Type::Enum), m_type(type), m_name(name) { }
|
: ASTNode(Type::Enum, lineNumber), m_type(type), m_name(name) { }
|
||||||
|
|
||||||
const std::string& getName() const { return this->m_name; };
|
const std::string& getName() const { return this->m_name; };
|
||||||
|
|
||||||
|
|||||||
@@ -15,15 +15,18 @@ namespace hex::lang {
|
|||||||
|
|
||||||
class Evaluator {
|
class Evaluator {
|
||||||
public:
|
public:
|
||||||
Evaluator(prv::Provider* &provider, std::endian dataEndianess);
|
Evaluator(prv::Provider* &provider, std::endian defaultDataEndianess);
|
||||||
|
|
||||||
std::pair<Result, std::vector<PatternData*>> evaluate(const std::vector<ASTNode*>& ast);
|
std::pair<Result, std::vector<PatternData*>> evaluate(const std::vector<ASTNode*>& ast);
|
||||||
|
|
||||||
|
const std::pair<u32, std::string>& getError() { return this->m_error; }
|
||||||
|
|
||||||
private:
|
private:
|
||||||
std::unordered_map<std::string, ASTNode*> m_types;
|
std::unordered_map<std::string, ASTNode*> m_types;
|
||||||
prv::Provider* &m_provider;
|
prv::Provider* &m_provider;
|
||||||
std::endian m_dataEndianess;
|
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> createStructPattern(ASTNodeVariableDecl *varDeclNode, u64 offset);
|
||||||
std::pair<PatternData*, size_t> createUnionPattern(ASTNodeVariableDecl *varDeclNode, u64 offset);
|
std::pair<PatternData*, size_t> createUnionPattern(ASTNodeVariableDecl *varDeclNode, u64 offset);
|
||||||
|
|||||||
@@ -14,6 +14,11 @@ namespace hex::lang {
|
|||||||
Lexer();
|
Lexer();
|
||||||
|
|
||||||
std::pair<Result, std::vector<Token>> lex(const std::string& code);
|
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;
|
||||||
};
|
};
|
||||||
|
|
||||||
}
|
}
|
||||||
@@ -12,8 +12,38 @@ namespace hex::lang {
|
|||||||
public:
|
public:
|
||||||
Parser();
|
Parser();
|
||||||
|
|
||||||
|
using TokenIter = std::vector<Token>::const_iterator;
|
||||||
|
|
||||||
std::pair<Result, std::vector<ASTNode*>> parse(const std::vector<Token> &tokens);
|
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);
|
||||||
};
|
};
|
||||||
|
|
||||||
}
|
}
|
||||||
@@ -7,12 +7,14 @@
|
|||||||
#include "imgui_memory_editor.h"
|
#include "imgui_memory_editor.h"
|
||||||
|
|
||||||
#include "providers/provider.hpp"
|
#include "providers/provider.hpp"
|
||||||
#include "utils.hpp"
|
#include "helpers/utils.hpp"
|
||||||
|
|
||||||
#include <random>
|
#include <random>
|
||||||
|
|
||||||
namespace hex::lang {
|
namespace hex::lang {
|
||||||
|
|
||||||
|
using namespace ::std::literals::string_literals;
|
||||||
|
|
||||||
namespace {
|
namespace {
|
||||||
|
|
||||||
std::string makeDisplayable(u8 *data, size_t size) {
|
std::string makeDisplayable(u8 *data, size_t size) {
|
||||||
@@ -33,8 +35,8 @@ namespace hex::lang {
|
|||||||
public:
|
public:
|
||||||
enum class Type { Padding, Unsigned, Signed, Float, Character, String, Struct, Union, Array, Enum };
|
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)
|
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_color(color), m_name(name) {
|
: 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 };
|
constexpr u32 Palette[] = { 0x50b4771f, 0x500e7fff, 0x502ca02c, 0x502827d6, 0x50bd6794, 0x504b568c, 0x50c277e3, 0x507f7f7f, 0x5022bdbc, 0x50cfbe17 };
|
||||||
|
|
||||||
if (color != 0)
|
if (color != 0)
|
||||||
@@ -57,6 +59,9 @@ namespace hex::lang {
|
|||||||
[[nodiscard]] u32 getColor() const { return this->m_color; }
|
[[nodiscard]] u32 getColor() const { return this->m_color; }
|
||||||
void setColor(u32 color) { this->m_color = 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 void createEntry(prv::Provider* &provider) = 0;
|
||||||
virtual std::string getTypeName() = 0;
|
virtual std::string getTypeName() = 0;
|
||||||
|
|
||||||
@@ -95,10 +100,10 @@ namespace hex::lang {
|
|||||||
provider->read(left->getOffset(), leftBuffer.data(), left->getSize());
|
provider->read(left->getOffset(), leftBuffer.data(), left->getSize());
|
||||||
provider->read(right->getOffset(), rightBuffer.data(), right->getSize());
|
provider->read(right->getOffset(), rightBuffer.data(), right->getSize());
|
||||||
|
|
||||||
if (PatternData::s_endianess != std::endian::native) {
|
if (left->m_endianess != std::endian::native)
|
||||||
std::reverse(leftBuffer.begin(), leftBuffer.end());
|
std::reverse(leftBuffer.begin(), leftBuffer.end());
|
||||||
|
if (right->m_endianess != std::endian::native)
|
||||||
std::reverse(rightBuffer.begin(), rightBuffer.end());
|
std::reverse(rightBuffer.begin(), rightBuffer.end());
|
||||||
}
|
|
||||||
|
|
||||||
if (sortSpecs->Specs->SortDirection == ImGuiSortDirection_Ascending)
|
if (sortSpecs->Specs->SortDirection == ImGuiSortDirection_Ascending)
|
||||||
return leftBuffer > rightBuffer;
|
return leftBuffer > rightBuffer;
|
||||||
@@ -122,28 +127,32 @@ namespace hex::lang {
|
|||||||
}
|
}
|
||||||
|
|
||||||
static void resetPalette() { PatternData::s_paletteOffset = 0; }
|
static void resetPalette() { PatternData::s_paletteOffset = 0; }
|
||||||
static void setEndianess(std::endian endianess) { PatternData::s_endianess = endianess; }
|
|
||||||
|
|
||||||
protected:
|
protected:
|
||||||
void createDefaultEntry(std::string value) {
|
void createDefaultEntry(std::string value) {
|
||||||
ImGui::TableNextRow();
|
ImGui::TableNextRow();
|
||||||
ImGui::TreeNodeEx(this->getName().c_str(), ImGuiTreeNodeFlags_Leaf | ImGuiTreeNodeFlags_NoTreePushOnOpen | ImGuiTreeNodeFlags_SpanFullWidth);
|
ImGui::TreeNodeEx(this->getName().c_str(), ImGuiTreeNodeFlags_Leaf | ImGuiTreeNodeFlags_NoTreePushOnOpen | ImGuiTreeNodeFlags_SpanFullWidth | ImGuiTreeNodeFlags_AllowItemOverlap);
|
||||||
ImGui::TableNextColumn();
|
|
||||||
ImGui::ColorButton("color", ImColor(this->getColor()), ImGuiColorEditFlags_NoTooltip);
|
|
||||||
ImGui::TableNextColumn();
|
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::Text("%s", this->getName().c_str());
|
||||||
ImGui::TableNextColumn();
|
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::Text("0x%08lx : 0x%08lx", this->getOffset(), this->getOffset() + this->getSize() - 1);
|
||||||
ImGui::TableNextColumn();
|
ImGui::TableNextColumn();
|
||||||
ImGui::Text("0x%04lx", this->getSize());
|
ImGui::Text("0x%04lx", this->getSize());
|
||||||
ImGui::TableNextColumn();
|
ImGui::TableNextColumn();
|
||||||
ImGui::Text("%s", this->getTypeName().c_str());
|
ImGui::TextColored(ImColor(0xFF9BC64D), "%s", this->getTypeName().c_str());
|
||||||
ImGui::TableNextColumn();
|
ImGui::TableNextColumn();
|
||||||
ImGui::Text("%s", value.c_str());
|
ImGui::Text("%s", value.c_str());
|
||||||
}
|
}
|
||||||
|
|
||||||
protected:
|
protected:
|
||||||
static inline std::endian s_endianess = std::endian::native;
|
std::endian m_endianess = std::endian::native;
|
||||||
|
|
||||||
private:
|
private:
|
||||||
Type m_type;
|
Type m_type;
|
||||||
@@ -159,7 +168,7 @@ namespace hex::lang {
|
|||||||
|
|
||||||
class PatternDataPadding : public PatternData {
|
class PatternDataPadding : public PatternData {
|
||||||
public:
|
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 {
|
void createEntry(prv::Provider* &provider) override {
|
||||||
}
|
}
|
||||||
@@ -171,27 +180,27 @@ namespace hex::lang {
|
|||||||
|
|
||||||
class PatternDataPointer : public PatternData {
|
class PatternDataPointer : public PatternData {
|
||||||
public:
|
public:
|
||||||
PatternDataPointer(u64 offset, size_t size, const std::string &name, PatternData *pointedAt, u32 color = 0)
|
PatternDataPointer(u64 offset, size_t size, const std::string &name, PatternData *pointedAt, std::endian endianess, u32 color = 0)
|
||||||
: PatternData(Type::Unsigned, offset, size, name, color), m_pointedAt(pointedAt) {
|
: PatternData(Type::Unsigned, offset, size, name, endianess, color), m_pointedAt(pointedAt) {
|
||||||
this->m_pointedAt->setName("*" + this->m_pointedAt->getName());
|
this->m_pointedAt->setName("*" + this->m_pointedAt->getName());
|
||||||
}
|
}
|
||||||
|
|
||||||
void createEntry(prv::Provider* &provider) override {
|
void createEntry(prv::Provider* &provider) override {
|
||||||
u64 data = 0;
|
u64 data = 0;
|
||||||
provider->read(this->getOffset(), &data, this->getSize());
|
provider->read(this->getOffset(), &data, this->getSize());
|
||||||
data = hex::changeEndianess(data, this->getSize(), PatternData::s_endianess);
|
data = hex::changeEndianess(data, this->getSize(), this->m_endianess);
|
||||||
|
|
||||||
ImGui::TableNextRow(ImGuiTableRowFlags_Headers);
|
ImGui::TableNextRow();
|
||||||
ImGui::TableNextColumn();
|
|
||||||
ImGui::ColorButton("color", ImColor(this->getColor()), ImGuiColorEditFlags_NoTooltip);
|
|
||||||
ImGui::TableNextColumn();
|
ImGui::TableNextColumn();
|
||||||
bool open = ImGui::TreeNodeEx(this->getName().c_str(), ImGuiTreeNodeFlags_SpanFullWidth);
|
bool open = ImGui::TreeNodeEx(this->getName().c_str(), ImGuiTreeNodeFlags_SpanFullWidth);
|
||||||
ImGui::TableNextColumn();
|
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::Text("0x%08lx : 0x%08lx", this->getOffset(), this->getOffset() + this->getSize() - 1);
|
||||||
ImGui::TableNextColumn();
|
ImGui::TableNextColumn();
|
||||||
ImGui::Text("0x%04lx", this->getSize());
|
ImGui::Text("0x%04lx", this->getSize());
|
||||||
ImGui::TableNextColumn();
|
ImGui::TableNextColumn();
|
||||||
ImGui::Text("%s", this->getTypeName().c_str());
|
ImGui::TextColored(ImColor(0xFF9BC64D), "%s", this->getTypeName().c_str());
|
||||||
ImGui::TableNextColumn();
|
ImGui::TableNextColumn();
|
||||||
ImGui::Text("*(0x%0*llx)", this->getSize() * 2, data);
|
ImGui::Text("*(0x%0*llx)", this->getSize() * 2, data);
|
||||||
|
|
||||||
@@ -221,12 +230,13 @@ namespace hex::lang {
|
|||||||
|
|
||||||
class PatternDataUnsigned : public PatternData {
|
class PatternDataUnsigned : public PatternData {
|
||||||
public:
|
public:
|
||||||
PatternDataUnsigned(u64 offset, size_t size, const std::string &name, u32 color = 0) : PatternData(Type::Unsigned, offset, size, name, color) { }
|
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 {
|
void createEntry(prv::Provider* &provider) override {
|
||||||
u64 data = 0;
|
u64 data = 0;
|
||||||
provider->read(this->getOffset(), &data, this->getSize());
|
provider->read(this->getOffset(), &data, this->getSize());
|
||||||
data = hex::changeEndianess(data, this->getSize(), PatternData::s_endianess);
|
data = hex::changeEndianess(data, this->getSize(), this->m_endianess);
|
||||||
|
|
||||||
this->createDefaultEntry(hex::format("%lu (0x%0*lx)", data, this->getSize() * 2, data));
|
this->createDefaultEntry(hex::format("%lu (0x%0*lx)", data, this->getSize() * 2, data));
|
||||||
}
|
}
|
||||||
@@ -245,12 +255,13 @@ namespace hex::lang {
|
|||||||
|
|
||||||
class PatternDataSigned : public PatternData {
|
class PatternDataSigned : public PatternData {
|
||||||
public:
|
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 {
|
void createEntry(prv::Provider* &provider) override {
|
||||||
u64 data = 0;
|
u64 data = 0;
|
||||||
provider->read(this->getOffset(), &data, this->getSize());
|
provider->read(this->getOffset(), &data, this->getSize());
|
||||||
data = hex::changeEndianess(data, this->getSize(), PatternData::s_endianess);
|
data = hex::changeEndianess(data, this->getSize(), this->m_endianess);
|
||||||
|
|
||||||
s64 signedData = signedData = hex::signExtend(data, this->getSize(), 64);
|
s64 signedData = signedData = hex::signExtend(data, this->getSize(), 64);
|
||||||
|
|
||||||
@@ -271,20 +282,21 @@ namespace hex::lang {
|
|||||||
|
|
||||||
class PatternDataFloat : public PatternData {
|
class PatternDataFloat : public PatternData {
|
||||||
public:
|
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 {
|
void createEntry(prv::Provider* &provider) override {
|
||||||
double formatData = 0;
|
double formatData = 0;
|
||||||
if (this->getSize() == 4) {
|
if (this->getSize() == 4) {
|
||||||
float data = 0;
|
float data = 0;
|
||||||
provider->read(this->getOffset(), &data, 4);
|
provider->read(this->getOffset(), &data, 4);
|
||||||
data = hex::changeEndianess(data, 4, PatternData::s_endianess);
|
data = hex::changeEndianess(data, 4, this->m_endianess);
|
||||||
|
|
||||||
formatData = data;
|
formatData = data;
|
||||||
} else if (this->getSize() == 8) {
|
} else if (this->getSize() == 8) {
|
||||||
double data = 0;
|
double data = 0;
|
||||||
provider->read(this->getOffset(), &data, 8);
|
provider->read(this->getOffset(), &data, 8);
|
||||||
data = hex::changeEndianess(data, 8, PatternData::s_endianess);
|
data = hex::changeEndianess(data, 8, this->m_endianess);
|
||||||
|
|
||||||
formatData = data;
|
formatData = data;
|
||||||
}
|
}
|
||||||
@@ -303,7 +315,8 @@ namespace hex::lang {
|
|||||||
|
|
||||||
class PatternDataCharacter : public PatternData {
|
class PatternDataCharacter : public PatternData {
|
||||||
public:
|
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 {
|
void createEntry(prv::Provider* &provider) override {
|
||||||
char character;
|
char character;
|
||||||
@@ -319,7 +332,8 @@ namespace hex::lang {
|
|||||||
|
|
||||||
class PatternDataString : public PatternData {
|
class PatternDataString : public PatternData {
|
||||||
public:
|
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 {
|
void createEntry(prv::Provider* &provider) override {
|
||||||
std::vector<u8> buffer(this->getSize() + 1, 0x00);
|
std::vector<u8> buffer(this->getSize() + 1, 0x00);
|
||||||
@@ -336,21 +350,29 @@ namespace hex::lang {
|
|||||||
|
|
||||||
class PatternDataArray : public PatternData {
|
class PatternDataArray : public PatternData {
|
||||||
public:
|
public:
|
||||||
PatternDataArray(u64 offset, size_t size, const std::string &name, const std::vector<PatternData*> & entries, u32 color = 0)
|
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, color), m_entries(entries) { }
|
: PatternData(Type::Array, offset, size, name, endianess, color), m_entries(entries) { }
|
||||||
|
|
||||||
void createEntry(prv::Provider* &provider) override {
|
void createEntry(prv::Provider* &provider) override {
|
||||||
ImGui::TableNextRow(ImGuiTableRowFlags_Headers);
|
ImGui::TableNextRow();
|
||||||
ImGui::TableNextColumn();
|
|
||||||
ImGui::ColorButton("color", ImColor(this->getColor()), ImGuiColorEditFlags_NoTooltip | ImGuiColorEditFlags_AlphaPreview);
|
|
||||||
ImGui::TableNextColumn();
|
ImGui::TableNextColumn();
|
||||||
bool open = ImGui::TreeNodeEx(this->getName().c_str(), ImGuiTreeNodeFlags_SpanFullWidth);
|
bool open = ImGui::TreeNodeEx(this->getName().c_str(), ImGuiTreeNodeFlags_SpanFullWidth);
|
||||||
ImGui::TableNextColumn();
|
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::Text("0x%08lx : 0x%08lx", this->getOffset(), this->getOffset() + this->getSize() - 1);
|
||||||
ImGui::TableNextColumn();
|
ImGui::TableNextColumn();
|
||||||
ImGui::Text("0x%04lx", this->getSize());
|
ImGui::Text("0x%04lx", this->getSize());
|
||||||
ImGui::TableNextColumn();
|
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::TableNextColumn();
|
||||||
ImGui::Text("%s", "{ ... }");
|
ImGui::Text("%s", "{ ... }");
|
||||||
|
|
||||||
@@ -381,21 +403,20 @@ namespace hex::lang {
|
|||||||
|
|
||||||
class PatternDataStruct : public PatternData {
|
class PatternDataStruct : public PatternData {
|
||||||
public:
|
public:
|
||||||
PatternDataStruct(u64 offset, size_t size, const std::string &name, const std::string &structName, const std::vector<PatternData*> & members, u32 color = 0)
|
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, color), m_structName(structName), m_members(members), m_sortedMembers(members) { }
|
: PatternData(Type::Struct, offset, size, name, endianess, color), m_structName(structName), m_members(members), m_sortedMembers(members) { }
|
||||||
|
|
||||||
void createEntry(prv::Provider* &provider) override {
|
void createEntry(prv::Provider* &provider) override {
|
||||||
ImGui::TableNextRow(ImGuiTableRowFlags_Headers);
|
ImGui::TableNextRow();
|
||||||
ImGui::TableNextColumn();
|
|
||||||
ImGui::ColorButton("color", ImColor(this->getColor()), ImGuiColorEditFlags_NoTooltip | ImGuiColorEditFlags_AlphaPreview);
|
|
||||||
ImGui::TableNextColumn();
|
ImGui::TableNextColumn();
|
||||||
bool open = ImGui::TreeNodeEx(this->getName().c_str(), ImGuiTreeNodeFlags_SpanFullWidth);
|
bool open = ImGui::TreeNodeEx(this->getName().c_str(), ImGuiTreeNodeFlags_SpanFullWidth);
|
||||||
ImGui::TableNextColumn();
|
ImGui::TableNextColumn();
|
||||||
|
ImGui::TableNextColumn();
|
||||||
ImGui::Text("0x%08lx : 0x%08lx", this->getOffset(), this->getOffset() + this->getSize() - 1);
|
ImGui::Text("0x%08lx : 0x%08lx", this->getOffset(), this->getOffset() + this->getSize() - 1);
|
||||||
ImGui::TableNextColumn();
|
ImGui::TableNextColumn();
|
||||||
ImGui::Text("0x%04lx", this->getSize());
|
ImGui::Text("0x%04lx", this->getSize());
|
||||||
ImGui::TableNextColumn();
|
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::TableNextColumn();
|
||||||
ImGui::Text("%s", "{ ... }");
|
ImGui::Text("%s", "{ ... }");
|
||||||
|
|
||||||
@@ -440,21 +461,21 @@ namespace hex::lang {
|
|||||||
|
|
||||||
class PatternDataUnion : public PatternData {
|
class PatternDataUnion : public PatternData {
|
||||||
public:
|
public:
|
||||||
PatternDataUnion(u64 offset, size_t size, const std::string &name, const std::string &unionName, const std::vector<PatternData*> & members, u32 color = 0)
|
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, color), m_unionName(unionName), m_members(members), m_sortedMembers(members) { }
|
: PatternData(Type::Union, offset, size, name, endianess, color), m_unionName(unionName), m_members(members), m_sortedMembers(members) { }
|
||||||
|
|
||||||
void createEntry(prv::Provider* &provider) override {
|
void createEntry(prv::Provider* &provider) override {
|
||||||
ImGui::TableNextRow(ImGuiTableRowFlags_Headers);
|
ImGui::TableNextRow();
|
||||||
ImGui::TableNextColumn();
|
|
||||||
ImGui::ColorButton("color", ImColor(this->getColor()), ImGuiColorEditFlags_NoTooltip | ImGuiColorEditFlags_AlphaPreview);
|
|
||||||
ImGui::TableNextColumn();
|
ImGui::TableNextColumn();
|
||||||
bool open = ImGui::TreeNodeEx(this->getName().c_str(), ImGuiTreeNodeFlags_SpanFullWidth);
|
bool open = ImGui::TreeNodeEx(this->getName().c_str(), ImGuiTreeNodeFlags_SpanFullWidth);
|
||||||
ImGui::TableNextColumn();
|
ImGui::TableNextColumn();
|
||||||
|
ImGui::TableNextColumn();
|
||||||
ImGui::Text("0x%08lx : 0x%08lx", this->getOffset(), this->getOffset() + this->getSize() - 1);
|
ImGui::Text("0x%08lx : 0x%08lx", this->getOffset(), this->getOffset() + this->getSize() - 1);
|
||||||
ImGui::TableNextColumn();
|
ImGui::TableNextColumn();
|
||||||
ImGui::Text("0x%04lx", this->getSize());
|
ImGui::Text("0x%04lx", this->getSize());
|
||||||
ImGui::TableNextColumn();
|
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::TableNextColumn();
|
||||||
ImGui::Text("%s", "{ ... }");
|
ImGui::Text("%s", "{ ... }");
|
||||||
|
|
||||||
@@ -499,13 +520,13 @@ namespace hex::lang {
|
|||||||
|
|
||||||
class PatternDataEnum : public PatternData {
|
class PatternDataEnum : public PatternData {
|
||||||
public:
|
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)
|
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, color), m_enumName(enumName), m_enumValues(enumValues) { }
|
: PatternData(Type::Enum, offset, size, name, endianess, color), m_enumName(enumName), m_enumValues(enumValues) { }
|
||||||
|
|
||||||
void createEntry(prv::Provider* &provider) override {
|
void createEntry(prv::Provider* &provider) override {
|
||||||
u64 value = 0;
|
u64 value = 0;
|
||||||
provider->read(this->getOffset(), &value, this->getSize());
|
provider->read(this->getOffset(), &value, this->getSize());
|
||||||
value = hex::changeEndianess(value, this->getSize(), PatternData::s_endianess);
|
value = hex::changeEndianess(value, this->getSize(), this->m_endianess);
|
||||||
|
|
||||||
std::string valueString = this->m_enumName + "::";
|
std::string valueString = this->m_enumName + "::";
|
||||||
|
|
||||||
@@ -521,7 +542,25 @@ namespace hex::lang {
|
|||||||
if (!foundValue)
|
if (!foundValue)
|
||||||
valueString += "???";
|
valueString += "???";
|
||||||
|
|
||||||
this->createDefaultEntry(hex::format("%s (0x%0*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 {
|
std::string getTypeName() override {
|
||||||
@@ -535,25 +574,24 @@ namespace hex::lang {
|
|||||||
|
|
||||||
class PatternDataBitfield : public PatternData {
|
class PatternDataBitfield : public PatternData {
|
||||||
public:
|
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)
|
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, color), m_bitfieldName(bitfieldName), m_fields(fields) { }
|
: PatternData(Type::Enum, offset, size, name, endianess, color), m_bitfieldName(bitfieldName), m_fields(fields) { }
|
||||||
|
|
||||||
void createEntry(prv::Provider* &provider) override {
|
void createEntry(prv::Provider* &provider) override {
|
||||||
u64 value = 0;
|
u64 value = 0;
|
||||||
provider->read(this->getOffset(), &value, this->getSize());
|
provider->read(this->getOffset(), &value, this->getSize());
|
||||||
value = hex::changeEndianess(value, this->getSize(), PatternData::s_endianess);
|
value = hex::changeEndianess(value, this->getSize(), this->m_endianess);
|
||||||
|
|
||||||
ImGui::TableNextRow(ImGuiTableRowFlags_Headers);
|
ImGui::TableNextRow();
|
||||||
ImGui::TableNextColumn();
|
|
||||||
ImGui::ColorButton("color", ImColor(0x00FFFFFF), ImGuiColorEditFlags_NoTooltip | ImGuiColorEditFlags_AlphaPreview);
|
|
||||||
ImGui::TableNextColumn();
|
ImGui::TableNextColumn();
|
||||||
bool open = ImGui::TreeNodeEx(this->getName().c_str(), ImGuiTreeNodeFlags_SpanFullWidth);
|
bool open = ImGui::TreeNodeEx(this->getName().c_str(), ImGuiTreeNodeFlags_SpanFullWidth);
|
||||||
ImGui::TableNextColumn();
|
ImGui::TableNextColumn();
|
||||||
|
ImGui::TableNextColumn();
|
||||||
ImGui::Text("0x%08lx : 0x%08lx", this->getOffset(), this->getOffset() + this->getSize() - 1);
|
ImGui::Text("0x%08lx : 0x%08lx", this->getOffset(), this->getOffset() + this->getSize() - 1);
|
||||||
ImGui::TableNextColumn();
|
ImGui::TableNextColumn();
|
||||||
ImGui::Text("0x%04lx", this->getSize());
|
ImGui::Text("0x%04lx", this->getSize());
|
||||||
ImGui::TableNextColumn();
|
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::TableNextColumn();
|
||||||
ImGui::Text("{ %llx }", value);
|
ImGui::Text("{ %llx }", value);
|
||||||
|
|
||||||
@@ -563,10 +601,10 @@ namespace hex::lang {
|
|||||||
ImGui::TableNextRow();
|
ImGui::TableNextRow();
|
||||||
ImGui::TreeNodeEx(this->getName().c_str(), ImGuiTreeNodeFlags_Leaf | ImGuiTreeNodeFlags_NoTreePushOnOpen | ImGuiTreeNodeFlags_SpanFullWidth);
|
ImGui::TreeNodeEx(this->getName().c_str(), ImGuiTreeNodeFlags_Leaf | ImGuiTreeNodeFlags_NoTreePushOnOpen | ImGuiTreeNodeFlags_SpanFullWidth);
|
||||||
ImGui::TableNextColumn();
|
ImGui::TableNextColumn();
|
||||||
ImGui::ColorButton("color", ImColor(this->getColor()), ImGuiColorEditFlags_NoTooltip);
|
|
||||||
ImGui::TableNextColumn();
|
|
||||||
ImGui::Text("%s", entryName.c_str());
|
ImGui::Text("%s", entryName.c_str());
|
||||||
ImGui::TableNextColumn();
|
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::Text("0x%08lx : 0x%08lx", this->getOffset() + (bitOffset >> 3), this->getOffset() + ((bitOffset + entrySize) >> 3) - 1);
|
||||||
ImGui::TableNextColumn();
|
ImGui::TableNextColumn();
|
||||||
if (entrySize == 1)
|
if (entrySize == 1)
|
||||||
|
|||||||
@@ -19,13 +19,17 @@ namespace hex::lang {
|
|||||||
std::pair<Result, std::string> preprocess(const std::string& code, bool initialRun = 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 addPragmaHandler(std::string pragmaType, std::function<bool(std::string)> function);
|
||||||
void addDefaultPragramHandlers();
|
void addDefaultPragmaHandlers();
|
||||||
|
|
||||||
|
const std::pair<u32, std::string>& getError() { return this->m_error; }
|
||||||
|
|
||||||
private:
|
private:
|
||||||
std::unordered_map<std::string, std::function<bool(std::string)>> m_pragmaHandlers;
|
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_defines;
|
||||||
std::set<std::pair<std::string, std::string>> m_pragmas;
|
std::set<std::pair<std::string, std::string>> m_pragmas;
|
||||||
|
|
||||||
|
std::pair<u32, std::string> m_error;
|
||||||
};
|
};
|
||||||
|
|
||||||
}
|
}
|
||||||
@@ -28,7 +28,9 @@ namespace hex::lang {
|
|||||||
Union,
|
Union,
|
||||||
Using,
|
Using,
|
||||||
Enum,
|
Enum,
|
||||||
Bitfield
|
Bitfield,
|
||||||
|
LittleEndian,
|
||||||
|
BigEndian
|
||||||
} keyword;
|
} keyword;
|
||||||
} keywordToken;
|
} keywordToken;
|
||||||
struct IdentifierToken {
|
struct IdentifierToken {
|
||||||
@@ -63,5 +65,7 @@ namespace hex::lang {
|
|||||||
Padding = 0x1F
|
Padding = 0x1F
|
||||||
} type;
|
} type;
|
||||||
} typeToken;
|
} typeToken;
|
||||||
|
|
||||||
|
u32 lineNumber;
|
||||||
};
|
};
|
||||||
}
|
}
|
||||||
@@ -15,6 +15,11 @@ namespace hex::lang {
|
|||||||
Validator();
|
Validator();
|
||||||
|
|
||||||
bool validate(const std::vector<ASTNode*>& ast);
|
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;
|
||||||
};
|
};
|
||||||
|
|
||||||
}
|
}
|
||||||
@@ -18,7 +18,10 @@ namespace hex::prv {
|
|||||||
bool isWritable() override;
|
bool isWritable() override;
|
||||||
|
|
||||||
void read(u64 offset, void *buffer, size_t size) override;
|
void read(u64 offset, void *buffer, size_t size) override;
|
||||||
void write(u64 offset, void *buffer, size_t size) 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;
|
size_t getActualSize() override;
|
||||||
|
|
||||||
std::vector<std::pair<std::string, std::string>> getDataInformation() override;
|
std::vector<std::pair<std::string, std::string>> getDataInformation() override;
|
||||||
|
|||||||
@@ -3,6 +3,9 @@
|
|||||||
#include <hex.hpp>
|
#include <hex.hpp>
|
||||||
|
|
||||||
#include <cmath>
|
#include <cmath>
|
||||||
|
#include <concepts>
|
||||||
|
#include <map>
|
||||||
|
#include <optional>
|
||||||
#include <string>
|
#include <string>
|
||||||
#include <vector>
|
#include <vector>
|
||||||
|
|
||||||
@@ -12,17 +15,29 @@ namespace hex::prv {
|
|||||||
public:
|
public:
|
||||||
constexpr static size_t PageSize = 0x1000'0000;
|
constexpr static size_t PageSize = 0x1000'0000;
|
||||||
|
|
||||||
Provider() = default;
|
Provider() {
|
||||||
|
this->m_patches.emplace_back();
|
||||||
|
}
|
||||||
|
|
||||||
virtual ~Provider() = default;
|
virtual ~Provider() = default;
|
||||||
|
|
||||||
virtual bool isAvailable() = 0;
|
virtual bool isAvailable() = 0;
|
||||||
virtual bool isReadable() = 0;
|
virtual bool isReadable() = 0;
|
||||||
virtual bool isWritable() = 0;
|
virtual bool isWritable() = 0;
|
||||||
|
|
||||||
virtual void read(u64 offset, void *buffer, size_t size) = 0;
|
virtual void read(u64 offset, void *buffer, size_t size) { this->readRaw(offset, buffer, size); }
|
||||||
virtual void write(u64 offset, void *buffer, size_t size) = 0;
|
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;
|
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 getPageCount() { return std::ceil(this->getActualSize() / double(PageSize)); }
|
||||||
u32 getCurrentPage() const { return this->m_currPage; }
|
u32 getCurrentPage() const { return this->m_currPage; }
|
||||||
void setCurrentPage(u32 page) { if (page < getPageCount()) this->m_currPage = page; }
|
void setCurrentPage(u32 page) { if (page < getPageCount()) this->m_currPage = page; }
|
||||||
@@ -35,10 +50,21 @@ namespace hex::prv {
|
|||||||
return std::min(this->getActualSize() - PageSize * this->m_currPage, PageSize);
|
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;
|
virtual std::vector<std::pair<std::string, std::string>> getDataInformation() = 0;
|
||||||
|
|
||||||
protected:
|
protected:
|
||||||
u32 m_currPage = 0;
|
u32 m_currPage = 0;
|
||||||
|
|
||||||
|
std::vector<std::map<u64, u8>> m_patches;
|
||||||
};
|
};
|
||||||
|
|
||||||
}
|
}
|
||||||
@@ -4,9 +4,10 @@
|
|||||||
|
|
||||||
#include "imgui.h"
|
#include "imgui.h"
|
||||||
|
|
||||||
#include "event.hpp"
|
#include "helpers/event.hpp"
|
||||||
|
|
||||||
#include <functional>
|
#include <functional>
|
||||||
|
#include <string>
|
||||||
#include <vector>
|
#include <vector>
|
||||||
|
|
||||||
|
|
||||||
@@ -14,7 +15,7 @@ namespace hex {
|
|||||||
|
|
||||||
class View {
|
class View {
|
||||||
public:
|
public:
|
||||||
View() { }
|
View(std::string viewName) : m_viewName(viewName) { }
|
||||||
virtual ~View() { }
|
virtual ~View() { }
|
||||||
|
|
||||||
virtual void createView() = 0;
|
virtual void createView() = 0;
|
||||||
@@ -29,6 +30,38 @@ namespace hex {
|
|||||||
View::s_eventManager.post(eventType, userData);
|
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:
|
protected:
|
||||||
void subscribeEvent(Events eventType, std::function<void(const void*)> callback) {
|
void subscribeEvent(Events eventType, std::function<void(const void*)> callback) {
|
||||||
View::s_eventManager.subscribe(eventType, this, callback);
|
View::s_eventManager.subscribe(eventType, this, callback);
|
||||||
@@ -42,9 +75,16 @@ namespace hex {
|
|||||||
View::s_deferedCalls.push_back(function);
|
View::s_deferedCalls.push_back(function);
|
||||||
}
|
}
|
||||||
|
|
||||||
|
|
||||||
|
|
||||||
private:
|
private:
|
||||||
|
std::string m_viewName;
|
||||||
|
bool m_windowOpen = false;
|
||||||
|
|
||||||
static inline EventManager s_eventManager;
|
static inline EventManager s_eventManager;
|
||||||
static inline std::vector<std::function<void()>> s_deferedCalls;
|
static inline std::vector<std::function<void()>> s_deferedCalls;
|
||||||
|
|
||||||
|
static inline std::string s_errorMessage;
|
||||||
};
|
};
|
||||||
|
|
||||||
}
|
}
|
||||||
28
include/views/view_bookmarks.hpp
Normal file
28
include/views/view_bookmarks.hpp
Normal 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;
|
||||||
|
};
|
||||||
|
|
||||||
|
}
|
||||||
@@ -51,7 +51,6 @@ namespace hex {
|
|||||||
|
|
||||||
private:
|
private:
|
||||||
prv::Provider* &m_dataProvider;
|
prv::Provider* &m_dataProvider;
|
||||||
bool m_windowOpen = true;
|
|
||||||
bool m_shouldInvalidate = true;
|
bool m_shouldInvalidate = true;
|
||||||
|
|
||||||
std::endian m_endianess = std::endian::native;
|
std::endian m_endianess = std::endian::native;
|
||||||
|
|||||||
@@ -1,5 +1,6 @@
|
|||||||
#pragma once
|
#pragma once
|
||||||
|
|
||||||
|
#include "helpers/disassembler.hpp"
|
||||||
#include "views/view.hpp"
|
#include "views/view.hpp"
|
||||||
|
|
||||||
#include <capstone/capstone.h>
|
#include <capstone/capstone.h>
|
||||||
@@ -15,8 +16,10 @@ namespace hex {
|
|||||||
struct Disassembly {
|
struct Disassembly {
|
||||||
u64 address;
|
u64 address;
|
||||||
u64 offset;
|
u64 offset;
|
||||||
|
size_t size;
|
||||||
std::string bytes;
|
std::string bytes;
|
||||||
std::string opcodeString;
|
std::string mnemonic;
|
||||||
|
std::string operators;
|
||||||
};
|
};
|
||||||
|
|
||||||
class ViewDisassembler : public View {
|
class ViewDisassembler : public View {
|
||||||
@@ -29,15 +32,13 @@ namespace hex {
|
|||||||
|
|
||||||
private:
|
private:
|
||||||
prv::Provider* &m_dataProvider;
|
prv::Provider* &m_dataProvider;
|
||||||
bool m_windowOpen = true;
|
|
||||||
|
|
||||||
bool m_shouldInvalidate = false;
|
bool m_shouldInvalidate = false;
|
||||||
|
|
||||||
u64 m_baseAddress = 0;
|
u64 m_baseAddress = 0;
|
||||||
u64 m_codeOffset = 0;
|
u64 m_codeRegion[2] = { 0 };
|
||||||
u64 m_codeSize = 0;
|
bool m_shouldMatchSelection = false;
|
||||||
|
|
||||||
cs_arch m_architecture = CS_ARCH_ARM;
|
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);
|
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;
|
bool m_littleEndianMode = true, m_micoMode = false, m_sparcV9Mode = false;
|
||||||
|
|
||||||
|
|||||||
@@ -18,11 +18,12 @@ namespace hex {
|
|||||||
|
|
||||||
private:
|
private:
|
||||||
prv::Provider* &m_dataProvider;
|
prv::Provider* &m_dataProvider;
|
||||||
bool m_windowOpen = true;
|
|
||||||
|
|
||||||
bool m_shouldInvalidate = true;
|
bool m_shouldInvalidate = true;
|
||||||
int m_currHashFunction = 0;
|
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" };
|
static constexpr const char* HashFunctionNames[] = { "CRC16", "CRC32", "MD4", "MD5", "SHA-1", "SHA-224", "SHA-256", "SHA-384", "SHA-512" };
|
||||||
};
|
};
|
||||||
|
|
||||||
|
|||||||
@@ -22,12 +22,16 @@ namespace hex {
|
|||||||
void createView() override;
|
void createView() override;
|
||||||
void createMenu() override;
|
void createMenu() override;
|
||||||
|
|
||||||
|
bool hasViewMenuItemEntry() override { return false; }
|
||||||
|
|
||||||
private:
|
private:
|
||||||
bool m_aboutWindowOpen = false;
|
bool m_aboutWindowOpen = false;
|
||||||
bool m_patternHelpWindowOpen = false;
|
bool m_patternHelpWindowOpen = false;
|
||||||
|
bool m_mathHelpWindowOpen = false;
|
||||||
|
|
||||||
void drawAboutPopup();
|
void drawAboutPopup();
|
||||||
void drawPatternHelpPopup();
|
void drawPatternHelpPopup();
|
||||||
|
void drawMathEvaluatorHelp();
|
||||||
};
|
};
|
||||||
|
|
||||||
}
|
}
|
||||||
@@ -1,6 +1,6 @@
|
|||||||
#pragma once
|
#pragma once
|
||||||
|
|
||||||
#include "utils.hpp"
|
#include "helpers/utils.hpp"
|
||||||
#include "views/view.hpp"
|
#include "views/view.hpp"
|
||||||
|
|
||||||
#include "imgui_memory_editor.h"
|
#include "imgui_memory_editor.h"
|
||||||
@@ -45,11 +45,17 @@ namespace hex {
|
|||||||
|
|
||||||
s64 m_gotoAddress = 0;
|
s64 m_gotoAddress = 0;
|
||||||
|
|
||||||
|
std::vector<u8> m_dataToSave;
|
||||||
|
|
||||||
|
std::string m_loaderScriptScriptPath;
|
||||||
|
std::string m_loaderScriptFilePath;
|
||||||
|
|
||||||
void drawSearchPopup();
|
void drawSearchPopup();
|
||||||
void drawGotoPopup();
|
void drawGotoPopup();
|
||||||
|
|
||||||
void openFile(std::string path);
|
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 };
|
enum class Language { C, Cpp, CSharp, Rust, Python, Java, JavaScript };
|
||||||
void copyBytes();
|
void copyBytes();
|
||||||
|
|||||||
@@ -21,7 +21,6 @@ namespace hex {
|
|||||||
|
|
||||||
private:
|
private:
|
||||||
prv::Provider* &m_dataProvider;
|
prv::Provider* &m_dataProvider;
|
||||||
bool m_windowOpen = true;
|
|
||||||
|
|
||||||
bool m_dataValid = false;
|
bool m_dataValid = false;
|
||||||
u32 m_blockSize = 0;
|
u32 m_blockSize = 0;
|
||||||
|
|||||||
29
include/views/view_patches.hpp
Normal file
29
include/views/view_patches.hpp
Normal 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;
|
||||||
|
};
|
||||||
|
|
||||||
|
}
|
||||||
@@ -28,7 +28,6 @@ namespace hex {
|
|||||||
std::vector<lang::PatternData*> &m_patternData;
|
std::vector<lang::PatternData*> &m_patternData;
|
||||||
prv::Provider* &m_dataProvider;
|
prv::Provider* &m_dataProvider;
|
||||||
std::filesystem::path m_possiblePatternFile;
|
std::filesystem::path m_possiblePatternFile;
|
||||||
bool m_windowOpen = true;
|
|
||||||
|
|
||||||
TextEditor m_textEditor;
|
TextEditor m_textEditor;
|
||||||
imgui_addons::ImGuiFileBrowser m_fileBrowser;
|
imgui_addons::ImGuiFileBrowser m_fileBrowser;
|
||||||
|
|||||||
@@ -23,10 +23,10 @@ namespace hex {
|
|||||||
void createMenu() override;
|
void createMenu() override;
|
||||||
|
|
||||||
private:
|
private:
|
||||||
|
|
||||||
prv::Provider* &m_dataProvider;
|
prv::Provider* &m_dataProvider;
|
||||||
std::vector<lang::PatternData*> &m_patternData;
|
std::vector<lang::PatternData*> &m_patternData;
|
||||||
std::vector<lang::PatternData*> m_sortedPatternData;
|
std::vector<lang::PatternData*> m_sortedPatternData;
|
||||||
bool m_windowOpen = true;
|
|
||||||
};
|
};
|
||||||
|
|
||||||
}
|
}
|
||||||
@@ -25,12 +25,16 @@ namespace hex {
|
|||||||
|
|
||||||
private:
|
private:
|
||||||
prv::Provider* &m_dataProvider;
|
prv::Provider* &m_dataProvider;
|
||||||
bool m_windowOpen = true;
|
|
||||||
bool m_shouldInvalidate = false;
|
bool m_shouldInvalidate = false;
|
||||||
|
|
||||||
std::vector<FoundString> m_foundStrings;
|
std::vector<FoundString> m_foundStrings;
|
||||||
int m_minimumLength = 5;
|
int m_minimumLength = 5;
|
||||||
char *m_filter;
|
char *m_filter;
|
||||||
|
|
||||||
|
std::string m_selectedString;
|
||||||
|
std::string m_demangledName;
|
||||||
|
|
||||||
|
void createStringContextMenu(const FoundString &foundString);
|
||||||
};
|
};
|
||||||
|
|
||||||
}
|
}
|
||||||
@@ -4,6 +4,7 @@
|
|||||||
|
|
||||||
#include "imgui.h"
|
#include "imgui.h"
|
||||||
#include "views/view.hpp"
|
#include "views/view.hpp"
|
||||||
|
#include "helpers/math_evaluator.hpp"
|
||||||
|
|
||||||
#include <array>
|
#include <array>
|
||||||
#include <string>
|
#include <string>
|
||||||
@@ -14,17 +15,17 @@ namespace hex {
|
|||||||
|
|
||||||
class ViewTools : public View {
|
class ViewTools : public View {
|
||||||
public:
|
public:
|
||||||
ViewTools();
|
ViewTools(hex::prv::Provider* &provider);
|
||||||
~ViewTools() override;
|
~ViewTools() override;
|
||||||
|
|
||||||
void createView() override;
|
void createView() override;
|
||||||
void createMenu() override;
|
void createMenu() override;
|
||||||
|
|
||||||
private:
|
private:
|
||||||
bool m_windowOpen = true;
|
hex::prv::Provider* &m_dataProvider;
|
||||||
|
|
||||||
char *m_mangledBuffer = nullptr;
|
char *m_mangledBuffer = nullptr;
|
||||||
char *m_demangledName = nullptr;
|
std::string m_demangledName;
|
||||||
|
|
||||||
bool m_asciiTableShowOctal = false;
|
bool m_asciiTableShowOctal = false;
|
||||||
|
|
||||||
@@ -35,10 +36,16 @@ namespace hex {
|
|||||||
|
|
||||||
std::array<float, 4> m_pickedColor;
|
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 drawDemangler();
|
||||||
void drawASCIITable();
|
void drawASCIITable();
|
||||||
void drawRegexReplacer();
|
void drawRegexReplacer();
|
||||||
void drawColorPicker();
|
void drawColorPicker();
|
||||||
|
void drawMathEvaluator();
|
||||||
};
|
};
|
||||||
|
|
||||||
}
|
}
|
||||||
@@ -7,6 +7,7 @@
|
|||||||
#include "views/view.hpp"
|
#include "views/view.hpp"
|
||||||
|
|
||||||
struct GLFWwindow;
|
struct GLFWwindow;
|
||||||
|
struct ImGuiSettingsHandler;
|
||||||
|
|
||||||
namespace hex {
|
namespace hex {
|
||||||
|
|
||||||
@@ -24,8 +25,10 @@ namespace hex {
|
|||||||
return static_cast<T*>(this->m_views.back());
|
return static_cast<T*>(this->m_views.back());
|
||||||
}
|
}
|
||||||
|
|
||||||
public:
|
friend void *ImHexSettingsHandler_ReadOpenFn(ImGuiContext *ctx, ImGuiSettingsHandler *, const char *);
|
||||||
float m_globalScale = 1.0f, m_fontScale = 1.0f;
|
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:
|
private:
|
||||||
void frameBegin();
|
void frameBegin();
|
||||||
@@ -38,7 +41,10 @@ namespace hex {
|
|||||||
|
|
||||||
GLFWwindow* m_window;
|
GLFWwindow* m_window;
|
||||||
std::vector<View*> m_views;
|
std::vector<View*> m_views;
|
||||||
|
|
||||||
|
float m_globalScale = 1.0f, m_fontScale = 1.0f;
|
||||||
bool m_fpsVisible = false;
|
bool m_fpsVisible = false;
|
||||||
|
bool m_demoWindowOpen = false;
|
||||||
|
|
||||||
static inline std::tuple<int, int> s_currShortcut = { -1, -1 };
|
static inline std::tuple<int, int> s_currShortcut = { -1, -1 };
|
||||||
};
|
};
|
||||||
|
|||||||
@@ -46,6 +46,7 @@
|
|||||||
|
|
||||||
#include <stdio.h> // sprintf, scanf
|
#include <stdio.h> // sprintf, scanf
|
||||||
#include <stdint.h> // uint8_t, etc.
|
#include <stdint.h> // uint8_t, etc.
|
||||||
|
#include "helpers/utils.hpp"
|
||||||
|
|
||||||
#include "views/view.hpp"
|
#include "views/view.hpp"
|
||||||
|
|
||||||
@@ -75,7 +76,6 @@ struct MemoryEditor
|
|||||||
};
|
};
|
||||||
|
|
||||||
// Settings
|
// Settings
|
||||||
bool Open; // = true // set to false when DrawWindow() was closed. ignore if not using DrawWindow().
|
|
||||||
bool ReadOnly; // = false // disable any editing.
|
bool ReadOnly; // = false // disable any editing.
|
||||||
int Cols; // = 16 // number of columns to display.
|
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.
|
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()
|
MemoryEditor()
|
||||||
{
|
{
|
||||||
// Settings
|
// Settings
|
||||||
Open = true;
|
|
||||||
ReadOnly = false;
|
ReadOnly = false;
|
||||||
Cols = 16;
|
Cols = 16;
|
||||||
OptShowOptions = true;
|
OptShowOptions = true;
|
||||||
@@ -181,13 +180,12 @@ struct MemoryEditor
|
|||||||
}
|
}
|
||||||
|
|
||||||
// Standalone Memory Editor window
|
// 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;
|
Sizes s;
|
||||||
CalcSizes(s, mem_size, base_display_addr);
|
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);
|
DrawContents(mem_data, mem_size, base_display_addr);
|
||||||
if (ContentsWidthChanged)
|
if (ContentsWidthChanged)
|
||||||
@@ -229,7 +227,7 @@ struct MemoryEditor
|
|||||||
footer_height += height_separator + ImGui::GetFrameHeightWithSpacing() * 1;
|
footer_height += height_separator + ImGui::GetFrameHeightWithSpacing() * 1;
|
||||||
|
|
||||||
ImGui::BeginChild("offset", ImVec2(0, s.LineHeight), false, ImGuiWindowFlags_NoMove | ImGuiWindowFlags_NoNav);
|
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++) {
|
for (int i = 0; i < Cols; i++) {
|
||||||
float byte_pos_x = s.PosHexStart + s.HexCellWidth * i;
|
float byte_pos_x = s.PosHexStart + s.HexCellWidth * i;
|
||||||
if (OptMidColsCount > 0)
|
if (OptMidColsCount > 0)
|
||||||
@@ -264,7 +262,9 @@ struct MemoryEditor
|
|||||||
DataPreviewAddrEnd = (size_t)-1;
|
DataPreviewAddrEnd = (size_t)-1;
|
||||||
|
|
||||||
size_t data_editing_addr_backup = DataEditingAddr;
|
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_editing_addr_next = (size_t)-1;
|
||||||
|
size_t data_preview_addr_next = (size_t)-1;
|
||||||
if (DataEditingAddr != (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)
|
// 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_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_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 (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))
|
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;
|
DataPreviewAddr = addr;
|
||||||
DataPreviewAddrEnd = 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))) {
|
if (!ReadOnly && ImGui::IsItemHovered() && ((ImGui::IsMouseClicked(0) && ImGui::GetIO().KeyShift) || ImGui::IsMouseDragging(0))) {
|
||||||
DataPreviewAddrEnd = addr;
|
DataPreviewAddrEnd = addr;
|
||||||
|
|
||||||
size_t dataPreviewStart = std::min(DataPreviewAddr, DataPreviewAddrEnd);
|
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)
|
else if (data_editing_addr_next != (size_t)-1)
|
||||||
{
|
{
|
||||||
DataEditingAddr = DataPreviewAddr = data_editing_addr_next;
|
DataEditingAddr = DataPreviewAddr = DataPreviewAddrEnd = data_editing_addr_next;
|
||||||
}
|
}
|
||||||
|
|
||||||
if (OptShowOptions)
|
if (OptShowOptions)
|
||||||
|
|||||||
2
python_libs/lib/imhex.py
Normal file
2
python_libs/lib/imhex.py
Normal file
@@ -0,0 +1,2 @@
|
|||||||
|
from _imhex import *
|
||||||
|
import imhex_python.types as types
|
||||||
BIN
python_libs/lib/imhex_python/__pycache__/types.cpython-38.pyc
Normal file
BIN
python_libs/lib/imhex_python/__pycache__/types.cpython-38.pyc
Normal file
Binary file not shown.
44
python_libs/lib/imhex_python/types.py
Normal file
44
python_libs/lib/imhex_python/types.py
Normal 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
|
||||||
@@ -1,4 +1,4 @@
|
|||||||
#include "crypto.hpp"
|
#include "helpers/crypto.hpp"
|
||||||
|
|
||||||
#include "providers/provider.hpp"
|
#include "providers/provider.hpp"
|
||||||
|
|
||||||
@@ -6,6 +6,8 @@
|
|||||||
#include <openssl/md5.h>
|
#include <openssl/md5.h>
|
||||||
#include <openssl/sha.h>
|
#include <openssl/sha.h>
|
||||||
|
|
||||||
|
#include <openssl/evp.h>
|
||||||
|
|
||||||
#include <array>
|
#include <array>
|
||||||
#include <span>
|
#include <span>
|
||||||
|
|
||||||
@@ -39,7 +41,7 @@ namespace hex {
|
|||||||
std::array<u8, 512> buffer = { 0 };
|
std::array<u8, 512> buffer = { 0 };
|
||||||
|
|
||||||
for (u64 bufferOffset = 0; offset < size; offset += buffer.size()) {
|
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);
|
data->read(offset + bufferOffset, buffer.data(), readSize);
|
||||||
|
|
||||||
for (size_t i = 0; i < readSize; i++) {
|
for (size_t i = 0; i < readSize; i++) {
|
||||||
@@ -72,7 +74,7 @@ namespace hex {
|
|||||||
std::array<u8, 512> buffer = { 0 };
|
std::array<u8, 512> buffer = { 0 };
|
||||||
|
|
||||||
for (u64 bufferOffset = 0; offset < size; offset += buffer.size()) {
|
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);
|
data->read(offset + bufferOffset, buffer.data(), readSize);
|
||||||
|
|
||||||
for (size_t i = 0; i < readSize; i++) {
|
for (size_t i = 0; i < readSize; i++) {
|
||||||
@@ -92,7 +94,7 @@ namespace hex {
|
|||||||
|
|
||||||
std::array<u8, 512> buffer = { 0 };
|
std::array<u8, 512> buffer = { 0 };
|
||||||
for (u64 bufferOffset = 0; bufferOffset < size; bufferOffset += buffer.size()) {
|
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);
|
data->read(offset + bufferOffset, buffer.data(), readSize);
|
||||||
MD4_Update(&ctx, buffer.data(), readSize);
|
MD4_Update(&ctx, buffer.data(), readSize);
|
||||||
}
|
}
|
||||||
@@ -111,7 +113,7 @@ namespace hex {
|
|||||||
|
|
||||||
std::array<u8, 512> buffer = { 0 };
|
std::array<u8, 512> buffer = { 0 };
|
||||||
for (u64 bufferOffset = 0; bufferOffset < size; bufferOffset += buffer.size()) {
|
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);
|
data->read(offset + bufferOffset, buffer.data(), readSize);
|
||||||
MD5_Update(&ctx, buffer.data(), readSize);
|
MD5_Update(&ctx, buffer.data(), readSize);
|
||||||
}
|
}
|
||||||
@@ -129,7 +131,7 @@ namespace hex {
|
|||||||
SHA1_Init(&ctx);
|
SHA1_Init(&ctx);
|
||||||
std::array<u8, 512> buffer = { 0 };
|
std::array<u8, 512> buffer = { 0 };
|
||||||
for (u64 bufferOffset = 0; bufferOffset < size; bufferOffset += buffer.size()) {
|
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);
|
data->read(offset + bufferOffset, buffer.data(), readSize);
|
||||||
SHA1_Update(&ctx, buffer.data(), readSize);
|
SHA1_Update(&ctx, buffer.data(), readSize);
|
||||||
}
|
}
|
||||||
@@ -148,7 +150,7 @@ namespace hex {
|
|||||||
|
|
||||||
std::array<u8, 512> buffer = { 0 };
|
std::array<u8, 512> buffer = { 0 };
|
||||||
for (u64 bufferOffset = 0; bufferOffset < size; bufferOffset += buffer.size()) {
|
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);
|
data->read(offset + bufferOffset, buffer.data(), readSize);
|
||||||
SHA224_Update(&ctx, buffer.data(), readSize);
|
SHA224_Update(&ctx, buffer.data(), readSize);
|
||||||
}
|
}
|
||||||
@@ -167,7 +169,7 @@ namespace hex {
|
|||||||
|
|
||||||
std::array<u8, 512> buffer = { 0 };
|
std::array<u8, 512> buffer = { 0 };
|
||||||
for (u64 bufferOffset = 0; bufferOffset < size; bufferOffset += buffer.size()) {
|
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);
|
data->read(offset + bufferOffset, buffer.data(), readSize);
|
||||||
SHA256_Update(&ctx, buffer.data(), readSize);
|
SHA256_Update(&ctx, buffer.data(), readSize);
|
||||||
}
|
}
|
||||||
@@ -186,7 +188,7 @@ namespace hex {
|
|||||||
|
|
||||||
std::array<u8, 512> buffer = { 0 };
|
std::array<u8, 512> buffer = { 0 };
|
||||||
for (u64 bufferOffset = 0; bufferOffset < size; bufferOffset += buffer.size()) {
|
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);
|
data->read(offset + bufferOffset, buffer.data(), readSize);
|
||||||
SHA384_Update(&ctx, buffer.data(), readSize);
|
SHA384_Update(&ctx, buffer.data(), readSize);
|
||||||
}
|
}
|
||||||
@@ -205,7 +207,7 @@ namespace hex {
|
|||||||
|
|
||||||
std::array<u8, 512> buffer = { 0 };
|
std::array<u8, 512> buffer = { 0 };
|
||||||
for (u64 bufferOffset = 0; bufferOffset < size; bufferOffset += buffer.size()) {
|
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);
|
data->read(offset + bufferOffset, buffer.data(), readSize);
|
||||||
SHA512_Update(&ctx, buffer.data(), readSize);
|
SHA512_Update(&ctx, buffer.data(), readSize);
|
||||||
}
|
}
|
||||||
@@ -215,4 +217,24 @@ namespace hex {
|
|||||||
return result;
|
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;
|
||||||
|
}
|
||||||
|
|
||||||
}
|
}
|
||||||
228
source/helpers/loader_script_handler.cpp
Normal file
228
source/helpers/loader_script_handler.cpp
Normal 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;
|
||||||
|
}
|
||||||
|
|
||||||
|
}
|
||||||
390
source/helpers/math_evaluator.cpp
Normal file
390
source/helpers/math_evaluator.cpp
Normal 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
218
source/helpers/patches.cpp
Normal 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 { };
|
||||||
|
}
|
||||||
|
|
||||||
|
}
|
||||||
85
source/helpers/project_file_handler.cpp
Normal file
85
source/helpers/project_file_handler.cpp
Normal 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
93
source/helpers/utils.cpp
Normal 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;
|
||||||
|
}
|
||||||
|
|
||||||
|
}
|
||||||
@@ -5,8 +5,8 @@
|
|||||||
|
|
||||||
namespace hex::lang {
|
namespace hex::lang {
|
||||||
|
|
||||||
Evaluator::Evaluator(prv::Provider* &provider, std::endian dataEndianess) : m_provider(provider), m_dataEndianess(dataEndianess) {
|
Evaluator::Evaluator(prv::Provider* &provider, std::endian defaultDataEndianess) : m_provider(provider), m_defaultDataEndianess(defaultDataEndianess) {
|
||||||
PatternData::setEndianess(dataEndianess);
|
|
||||||
}
|
}
|
||||||
|
|
||||||
std::pair<PatternData*, size_t> Evaluator::createStructPattern(ASTNodeVariableDecl *varDeclNode, u64 offset) {
|
std::pair<PatternData*, size_t> Evaluator::createStructPattern(ASTNodeVariableDecl *varDeclNode, u64 offset) {
|
||||||
@@ -14,8 +14,10 @@ namespace hex::lang {
|
|||||||
|
|
||||||
auto structNode = static_cast<ASTNodeStruct*>(this->m_types[varDeclNode->getCustomVariableTypeName()]);
|
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 };
|
return { nullptr, 0 };
|
||||||
|
}
|
||||||
|
|
||||||
size_t structSize = 0;
|
size_t structSize = 0;
|
||||||
for (const auto &node : structNode->getNodes()) {
|
for (const auto &node : structNode->getNodes()) {
|
||||||
@@ -26,7 +28,7 @@ namespace hex::lang {
|
|||||||
if (member->getPointerSize().has_value()) {
|
if (member->getPointerSize().has_value()) {
|
||||||
this->m_provider->read(offset + structSize, &memberOffset, member->getPointerSize().value());
|
this->m_provider->read(offset + structSize, &memberOffset, member->getPointerSize().value());
|
||||||
|
|
||||||
memberOffset = hex::changeEndianess(memberOffset, member->getPointerSize().value(), this->m_dataEndianess);
|
memberOffset = hex::changeEndianess(memberOffset, member->getPointerSize().value(), member->getEndianess().value_or(varDeclNode->getEndianess().value_or(this->m_defaultDataEndianess)));
|
||||||
}
|
}
|
||||||
else
|
else
|
||||||
memberOffset = offset + structSize;
|
memberOffset = offset + structSize;
|
||||||
@@ -56,16 +58,18 @@ namespace hex::lang {
|
|||||||
u64 value = 0;
|
u64 value = 0;
|
||||||
this->m_provider->read(prevMember->getOffset(), &value, prevMember->getSize());
|
this->m_provider->read(prevMember->getOffset(), &value, prevMember->getSize());
|
||||||
|
|
||||||
value = hex::changeEndianess(value, prevMember->getSize(), this->m_dataEndianess);
|
value = hex::changeEndianess(value, prevMember->getSize(), prevMember->getEndianess());
|
||||||
|
|
||||||
arraySize = value;
|
arraySize = value;
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
if (!arraySize.has_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 };
|
return { nullptr, 0 };
|
||||||
|
}
|
||||||
|
|
||||||
ASTNodeVariableDecl *processedMember = new ASTNodeVariableDecl(member->getVariableType(), member->getVariableName(), member->getCustomVariableTypeName(), member->getOffset(), arraySize.value());
|
ASTNodeVariableDecl *processedMember = new ASTNodeVariableDecl(member->getLineNumber(), member->getVariableType(), member->getVariableName(), member->getCustomVariableTypeName(), member->getOffset(), arraySize.value());
|
||||||
|
|
||||||
std::tie(pattern, memberSize) = this->createArrayPattern(processedMember, memberOffset);
|
std::tie(pattern, memberSize) = this->createArrayPattern(processedMember, memberOffset);
|
||||||
}
|
}
|
||||||
@@ -79,8 +83,10 @@ namespace hex::lang {
|
|||||||
if (pattern == nullptr)
|
if (pattern == nullptr)
|
||||||
return { nullptr, 0 };
|
return { nullptr, 0 };
|
||||||
|
|
||||||
|
pattern->setEndianess(member->getEndianess().value_or(varDeclNode->getEndianess().value_or(this->m_defaultDataEndianess)));
|
||||||
|
|
||||||
if (member->getPointerSize().has_value()) {
|
if (member->getPointerSize().has_value()) {
|
||||||
members.push_back(new PatternDataPointer(offset + structSize, member->getPointerSize().value(), member->getVariableName(), pattern));
|
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();
|
structSize += member->getPointerSize().value();
|
||||||
}
|
}
|
||||||
else {
|
else {
|
||||||
@@ -89,7 +95,7 @@ namespace hex::lang {
|
|||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
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) {
|
std::pair<PatternData*, size_t> Evaluator::createUnionPattern(ASTNodeVariableDecl *varDeclNode, u64 offset) {
|
||||||
@@ -97,8 +103,10 @@ namespace hex::lang {
|
|||||||
|
|
||||||
auto unionNode = static_cast<ASTNodeUnion*>(this->m_types[varDeclNode->getCustomVariableTypeName()]);
|
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 };
|
return { nullptr, 0 };
|
||||||
|
}
|
||||||
|
|
||||||
size_t unionSize = 0;
|
size_t unionSize = 0;
|
||||||
for (const auto &node : unionNode->getNodes()) {
|
for (const auto &node : unionNode->getNodes()) {
|
||||||
@@ -109,7 +117,7 @@ namespace hex::lang {
|
|||||||
if (member->getPointerSize().has_value()) {
|
if (member->getPointerSize().has_value()) {
|
||||||
this->m_provider->read(offset + unionSize, &memberOffset, member->getPointerSize().value());
|
this->m_provider->read(offset + unionSize, &memberOffset, member->getPointerSize().value());
|
||||||
|
|
||||||
memberOffset = hex::changeEndianess(memberOffset, member->getPointerSize().value(), this->m_dataEndianess);
|
memberOffset = hex::changeEndianess(memberOffset, member->getPointerSize().value(), member->getEndianess().value_or(this->m_defaultDataEndianess));
|
||||||
}
|
}
|
||||||
else
|
else
|
||||||
memberOffset = offset;
|
memberOffset = offset;
|
||||||
@@ -142,16 +150,23 @@ namespace hex::lang {
|
|||||||
u64 value = 0;
|
u64 value = 0;
|
||||||
this->m_provider->read(prevMember->getOffset(), &value, prevMember->getSize());
|
this->m_provider->read(prevMember->getOffset(), &value, prevMember->getSize());
|
||||||
|
|
||||||
value = hex::changeEndianess(value, prevMember->getSize(), this->m_dataEndianess);
|
value = hex::changeEndianess(value, prevMember->getSize(), prevMember->getEndianess());
|
||||||
|
|
||||||
arraySize = value;
|
arraySize = value;
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
if (!arraySize.has_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 };
|
return { nullptr, 0 };
|
||||||
|
}
|
||||||
|
|
||||||
ASTNodeVariableDecl *processedMember = new ASTNodeVariableDecl(member->getVariableType(), member->getVariableName(), member->getCustomVariableTypeName(), member->getOffset(), arraySize.value());
|
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);
|
std::tie(pattern, memberSize) = this->createArrayPattern(processedMember, memberOffset);
|
||||||
}
|
}
|
||||||
@@ -165,8 +180,10 @@ namespace hex::lang {
|
|||||||
if (pattern == nullptr)
|
if (pattern == nullptr)
|
||||||
return { nullptr, 0 };
|
return { nullptr, 0 };
|
||||||
|
|
||||||
|
pattern->setEndianess(member->getEndianess().value_or(varDeclNode->getEndianess().value_or(this->m_defaultDataEndianess)));
|
||||||
|
|
||||||
if (member->getPointerSize().has_value()) {
|
if (member->getPointerSize().has_value()) {
|
||||||
members.push_back(new PatternDataPointer(offset, member->getPointerSize().value(), member->getVariableName(), pattern));
|
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);
|
unionSize = std::max(size_t(member->getPointerSize().value()), unionSize);
|
||||||
}
|
}
|
||||||
else {
|
else {
|
||||||
@@ -175,28 +192,30 @@ namespace hex::lang {
|
|||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
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::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()]);
|
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 };
|
return { nullptr, 0 };
|
||||||
|
}
|
||||||
|
|
||||||
size_t size = getTypeSize(enumType->getUnderlyingType());
|
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) {
|
std::pair<PatternData*, size_t> Evaluator::createBitfieldPattern(ASTNodeVariableDecl *varDeclNode, u64 offset) {
|
||||||
|
|
||||||
auto *bitfieldType = static_cast<ASTNodeBitField*>(this->m_types[varDeclNode->getCustomVariableTypeName()]);
|
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 };
|
return { nullptr, 0 };
|
||||||
|
}
|
||||||
|
|
||||||
size_t size = 0;
|
size_t size = 0;
|
||||||
for (auto &[fieldName, fieldSize] : bitfieldType->getFields())
|
for (auto &[fieldName, fieldSize] : bitfieldType->getFields())
|
||||||
@@ -204,18 +223,16 @@ namespace hex::lang {
|
|||||||
|
|
||||||
size = std::bit_ceil(size) / 8;
|
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::pair<PatternData*, size_t> Evaluator::createArrayPattern(ASTNodeVariableDecl *varDeclNode, u64 offset) {
|
||||||
std::vector<PatternData*> entries;
|
std::vector<PatternData*> entries;
|
||||||
|
|
||||||
auto arraySizeVariable = varDeclNode->getArraySizeVariable();
|
|
||||||
|
|
||||||
size_t arrayOffset = 0;
|
size_t arrayOffset = 0;
|
||||||
std::optional<u32> arrayColor;
|
std::optional<u32> arrayColor;
|
||||||
for (u32 i = 0; i < varDeclNode->getArraySize(); i++) {
|
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) {
|
if (varDeclNode->getVariableType() == Token::TypeToken::Type::Padding) {
|
||||||
@@ -223,8 +240,12 @@ namespace hex::lang {
|
|||||||
} else if (varDeclNode->getVariableType() != Token::TypeToken::Type::CustomType) {
|
} 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 };
|
return { nullptr, 0 };
|
||||||
|
}
|
||||||
|
|
||||||
|
pattern->setEndianess(varDeclNode->getEndianess().value_or(varDeclNode->getEndianess().value_or(this->m_defaultDataEndianess)));
|
||||||
|
|
||||||
if (!arrayColor.has_value())
|
if (!arrayColor.has_value())
|
||||||
arrayColor = pattern->getColor();
|
arrayColor = pattern->getColor();
|
||||||
@@ -236,8 +257,12 @@ namespace hex::lang {
|
|||||||
} else {
|
} else {
|
||||||
const auto &[pattern, size] = this->createCustomTypePattern(nonArrayVarDeclNode, offset + arrayOffset);
|
const auto &[pattern, size] = this->createCustomTypePattern(nonArrayVarDeclNode, offset + arrayOffset);
|
||||||
|
|
||||||
if (pattern == nullptr)
|
if (pattern == nullptr) {
|
||||||
|
delete nonArrayVarDeclNode;
|
||||||
return { nullptr, 0 };
|
return { nullptr, 0 };
|
||||||
|
}
|
||||||
|
|
||||||
|
pattern->setEndianess(varDeclNode->getEndianess().value_or(varDeclNode->getEndianess().value_or(this->m_defaultDataEndianess)));
|
||||||
|
|
||||||
if (!arrayColor.has_value())
|
if (!arrayColor.has_value())
|
||||||
arrayColor = pattern->getColor();
|
arrayColor = pattern->getColor();
|
||||||
@@ -251,20 +276,22 @@ namespace hex::lang {
|
|||||||
delete nonArrayVarDeclNode;
|
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) {
|
std::pair<PatternData*, size_t> Evaluator::createStringPattern(ASTNodeVariableDecl *varDeclNode, u64 offset) {
|
||||||
size_t arraySize = varDeclNode->getArraySize();
|
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) {
|
std::pair<PatternData*, size_t> Evaluator::createCustomTypePattern(ASTNodeVariableDecl *varDeclNode, u64 offset) {
|
||||||
auto &currType = this->m_types[varDeclNode->getCustomVariableTypeName()];
|
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 };
|
return { nullptr, 0 };
|
||||||
|
}
|
||||||
|
|
||||||
switch (currType->getType()) {
|
switch (currType->getType()) {
|
||||||
case ASTNode::Type::Struct:
|
case ASTNode::Type::Struct:
|
||||||
@@ -286,8 +313,10 @@ namespace hex::lang {
|
|||||||
auto type = varDeclNode->getVariableType();
|
auto type = varDeclNode->getVariableType();
|
||||||
if (type == Token::TypeToken::Type::CustomType) {
|
if (type == Token::TypeToken::Type::CustomType) {
|
||||||
const auto &currType = static_cast<ASTNodeTypeDecl*>(this->m_types[varDeclNode->getCustomVariableTypeName()]);
|
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 };
|
return { nullptr, 0 };
|
||||||
|
}
|
||||||
|
|
||||||
type = currType->getAssignedType();
|
type = currType->getAssignedType();
|
||||||
}
|
}
|
||||||
@@ -297,21 +326,21 @@ namespace hex::lang {
|
|||||||
|
|
||||||
if (isSigned(type)) {
|
if (isSigned(type)) {
|
||||||
if (typeSize == 1 && arraySize == 1)
|
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)
|
else if (arraySize > 1)
|
||||||
return createArrayPattern(varDeclNode, offset);
|
return createArrayPattern(varDeclNode, offset);
|
||||||
else
|
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())) {
|
} else if (isUnsigned(varDeclNode->getVariableType())) {
|
||||||
if (arraySize > 1)
|
if (arraySize > 1)
|
||||||
return createArrayPattern(varDeclNode, offset);
|
return createArrayPattern(varDeclNode, offset);
|
||||||
else
|
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())) {
|
} else if (isFloatingPoint(varDeclNode->getVariableType())) {
|
||||||
if (arraySize > 1)
|
if (arraySize > 1)
|
||||||
return createArrayPattern(varDeclNode, offset);
|
return createArrayPattern(varDeclNode, offset);
|
||||||
else
|
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 };
|
return { nullptr, 0 };
|
||||||
|
|||||||
@@ -69,6 +69,8 @@ namespace hex::lang {
|
|||||||
std::vector<Token> tokens;
|
std::vector<Token> tokens;
|
||||||
u32 offset = 0;
|
u32 offset = 0;
|
||||||
|
|
||||||
|
u32 lineNumber = 1;
|
||||||
|
|
||||||
while (offset < code.length()) {
|
while (offset < code.length()) {
|
||||||
|
|
||||||
// Handle comments
|
// Handle comments
|
||||||
@@ -85,6 +87,8 @@ namespace hex::lang {
|
|||||||
} else if (offset < code.length() && code[offset] == '*') {
|
} else if (offset < code.length() && code[offset] == '*') {
|
||||||
offset++;
|
offset++;
|
||||||
while (offset < (code.length() - 1)) {
|
while (offset < (code.length() - 1)) {
|
||||||
|
if (code[offset] == '\n') lineNumber++;
|
||||||
|
|
||||||
if (code[offset] == '*' && code[offset + 1] == '/')
|
if (code[offset] == '*' && code[offset + 1] == '/')
|
||||||
break;
|
break;
|
||||||
offset++;
|
offset++;
|
||||||
@@ -100,102 +104,153 @@ namespace hex::lang {
|
|||||||
break;
|
break;
|
||||||
|
|
||||||
if (std::isblank(c) || std::isspace(c)) {
|
if (std::isblank(c) || std::isspace(c)) {
|
||||||
|
if (code[offset] == '\n') lineNumber++;
|
||||||
offset += 1;
|
offset += 1;
|
||||||
} else if (c == ';') {
|
} else if (c == ';') {
|
||||||
tokens.push_back({ .type = Token::Type::EndOfExpression });
|
tokens.push_back({ .type = Token::Type::EndOfExpression, .lineNumber = lineNumber });
|
||||||
offset += 1;
|
offset += 1;
|
||||||
} else if (c == '{') {
|
} else if (c == '{') {
|
||||||
tokens.push_back({ .type = Token::Type::ScopeOpen });
|
tokens.push_back({ .type = Token::Type::ScopeOpen, .lineNumber = lineNumber });
|
||||||
offset += 1;
|
offset += 1;
|
||||||
} else if (c == '}') {
|
} else if (c == '}') {
|
||||||
tokens.push_back({ .type = Token::Type::ScopeClose });
|
tokens.push_back({ .type = Token::Type::ScopeClose, .lineNumber = lineNumber });
|
||||||
offset += 1;
|
offset += 1;
|
||||||
} else if (c == '[') {
|
} else if (c == '[') {
|
||||||
tokens.push_back({ .type = Token::Type::ArrayOpen });
|
tokens.push_back({ .type = Token::Type::ArrayOpen, .lineNumber = lineNumber });
|
||||||
offset += 1;
|
offset += 1;
|
||||||
} else if (c == ']') {
|
} else if (c == ']') {
|
||||||
tokens.push_back({.type = Token::Type::ArrayClose});
|
tokens.push_back({.type = Token::Type::ArrayClose, .lineNumber = lineNumber });
|
||||||
offset += 1;
|
offset += 1;
|
||||||
} else if (c == ',') {
|
} else if (c == ',') {
|
||||||
tokens.push_back({ .type = Token::Type::Separator });
|
tokens.push_back({ .type = Token::Type::Separator, .lineNumber = lineNumber });
|
||||||
offset += 1;
|
offset += 1;
|
||||||
} else if (c == '@') {
|
} 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;
|
offset += 1;
|
||||||
} else if (c == '=') {
|
} 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;
|
offset += 1;
|
||||||
} else if (c == ':') {
|
} 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;
|
offset += 1;
|
||||||
} else if (c == '*') {
|
} else if (c == '*') {
|
||||||
tokens.push_back({ .type = Token::Type::Operator, .operatorToken = { .op = Token::OperatorToken::Operator::Star } });
|
tokens.push_back({ .type = Token::Type::Operator, .operatorToken = { .op = Token::OperatorToken::Operator::Star }, .lineNumber = lineNumber });
|
||||||
offset += 1;
|
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)) {
|
} else if (std::isalpha(c)) {
|
||||||
std::string identifier = matchTillInvalid(&code[offset], [](char c) -> bool { return std::isalnum(c) || c == '_'; });
|
std::string identifier = matchTillInvalid(&code[offset], [](char c) -> bool { return std::isalnum(c) || c == '_'; });
|
||||||
|
|
||||||
// Check for reserved keywords
|
// Check for reserved keywords
|
||||||
|
|
||||||
if (identifier == "struct")
|
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")
|
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")
|
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")
|
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")
|
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")
|
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")
|
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")
|
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")
|
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")
|
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")
|
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")
|
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")
|
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")
|
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")
|
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")
|
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")
|
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")
|
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
|
// If it's not a keyword and a builtin type, it has to be an identifier
|
||||||
|
|
||||||
else
|
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();
|
offset += identifier.length();
|
||||||
} else if (std::isdigit(c)) {
|
} else if (std::isdigit(c)) {
|
||||||
char *end = nullptr;
|
char *end = nullptr;
|
||||||
std::strtoull(&code[offset], &end, 0);
|
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, {}};
|
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]);
|
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 };
|
return { ResultSuccess, tokens };
|
||||||
}
|
}
|
||||||
|
|||||||
@@ -1,6 +1,6 @@
|
|||||||
#include "lang/parser.hpp"
|
#include "lang/parser.hpp"
|
||||||
|
|
||||||
#include "utils.hpp"
|
#include "helpers/utils.hpp"
|
||||||
|
|
||||||
#include <optional>
|
#include <optional>
|
||||||
|
|
||||||
@@ -10,11 +10,7 @@ namespace hex::lang {
|
|||||||
|
|
||||||
}
|
}
|
||||||
|
|
||||||
using TokenIter = std::vector<Token>::const_iterator;
|
bool Parser::tryConsume(TokenIter &curr, std::initializer_list<Token::Type> tokenTypes) {
|
||||||
|
|
||||||
std::vector<ASTNode*> parseTillToken(TokenIter &curr, Token::Type endTokenType);
|
|
||||||
|
|
||||||
bool tryConsume(TokenIter &curr, std::initializer_list<Token::Type> tokenTypes) {
|
|
||||||
std::vector<Token>::const_iterator originalPosition = curr;
|
std::vector<Token>::const_iterator originalPosition = curr;
|
||||||
|
|
||||||
for (const auto& type : tokenTypes) {
|
for (const auto& type : tokenTypes) {
|
||||||
@@ -29,137 +25,356 @@ namespace hex::lang {
|
|||||||
}
|
}
|
||||||
|
|
||||||
|
|
||||||
ASTNode* parseBuiltinVariableDecl(TokenIter &curr) {
|
ASTNode* Parser::parseBuiltinVariableDecl(TokenIter &curr, bool hasEndianDef) {
|
||||||
return new ASTNodeVariableDecl(curr[-3].typeToken.type, curr[-2].identifierToken.identifier);
|
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) {
|
ASTNode* Parser::parseCustomTypeVariableDecl(TokenIter &curr, bool hasEndianDef) {
|
||||||
return new ASTNodeVariableDecl(Token::TypeToken::Type::CustomType, curr[-2].identifierToken.identifier, curr[-3].identifierToken.identifier);
|
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* parseBuiltinPointerVariableDecl(TokenIter &curr) {
|
ASTNode* Parser::parseBuiltinPointerVariableDecl(TokenIter &curr, bool hasEndianDef) {
|
||||||
auto pointerType = curr[-2].typeToken.type;
|
auto pointerType = curr[-2].typeToken.type;
|
||||||
|
|
||||||
if (!isUnsigned(pointerType) || curr[-5].operatorToken.op != Token::OperatorToken::Operator::Star || curr[-3].operatorToken.op != Token::OperatorToken::Operator::Inherit)
|
if (!isUnsigned(pointerType)) {
|
||||||
|
this->m_error = { curr->lineNumber, "Pointer size needs to be a unsigned type" };
|
||||||
return nullptr;
|
return nullptr;
|
||||||
|
}
|
||||||
|
|
||||||
return new ASTNodeVariableDecl(curr[-6].typeToken.type, curr[-4].identifierToken.identifier, "", { }, 1, { }, getTypeSize(pointerType));
|
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* parseCustomTypePointerVariableDecl(TokenIter &curr) {
|
ASTNode* Parser::parseCustomTypePointerVariableDecl(TokenIter &curr, bool hasEndianDef) {
|
||||||
auto pointerType = curr[-2].typeToken.type;
|
auto pointerType = curr[-2].typeToken.type;
|
||||||
|
|
||||||
if (!isUnsigned(pointerType) || curr[-5].operatorToken.op != Token::OperatorToken::Operator::Star || curr[-3].operatorToken.op != Token::OperatorToken::Operator::Inherit)
|
if (!isUnsigned(pointerType)) {
|
||||||
|
this->m_error = { curr->lineNumber, "Pointer size needs to be a unsigned type" };
|
||||||
return nullptr;
|
return nullptr;
|
||||||
|
}
|
||||||
|
|
||||||
return new ASTNodeVariableDecl(Token::TypeToken::Type::CustomType, curr[-4].identifierToken.identifier, curr[-6].identifierToken.identifier, { }, 1, { }, getTypeSize(pointerType));
|
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* parseBuiltinArrayDecl(TokenIter &curr) {
|
ASTNode* Parser::parseBuiltinArrayDecl(TokenIter &curr, bool hasEndianDef) {
|
||||||
return new ASTNodeVariableDecl(curr[-6].typeToken.type, curr[-5].identifierToken.identifier, "", { }, curr[-3].integerToken.integer);
|
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* parseCustomTypeArrayDecl(TokenIter &curr) {
|
ASTNode* Parser::parseCustomTypeArrayDecl(TokenIter &curr, bool hasEndianDef) {
|
||||||
return new ASTNodeVariableDecl(Token::TypeToken::Type::CustomType, curr[-5].identifierToken.identifier, curr[-6].identifierToken.identifier, { }, curr[-3].integerToken.integer);
|
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* parseBuiltinVariableArrayDecl(TokenIter &curr) {
|
ASTNode* Parser::parseBuiltinVariableArrayDecl(TokenIter &curr, bool hasEndianDef) {
|
||||||
return new ASTNodeVariableDecl(curr[-6].typeToken.type, curr[-5].identifierToken.identifier, "", { }, 0, curr[-3].identifierToken.identifier);
|
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* parseCustomTypeVariableArrayDecl(TokenIter &curr) {
|
ASTNode* Parser::parseCustomTypeVariableArrayDecl(TokenIter &curr, bool hasEndianDef) {
|
||||||
return new ASTNodeVariableDecl(Token::TypeToken::Type::CustomType, curr[-5].identifierToken.identifier, curr[-6].identifierToken.identifier, { }, 0, curr[-3].identifierToken.identifier);
|
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* parsePaddingDecl(TokenIter &curr) {
|
ASTNode* Parser::parsePaddingDecl(TokenIter &curr) {
|
||||||
return new ASTNodeVariableDecl(curr[-5].typeToken.type, "", "", { }, curr[-3].integerToken.integer);
|
return new ASTNodeVariableDecl(curr[-5].lineNumber, curr[-5].typeToken.type, "", "", { }, curr[-3].integerToken.integer);
|
||||||
}
|
}
|
||||||
|
|
||||||
ASTNode* parseFreeBuiltinVariableDecl(TokenIter &curr) {
|
ASTNode* Parser::parseFreeBuiltinVariableDecl(TokenIter &curr, bool hasEndianDef) {
|
||||||
return new ASTNodeVariableDecl(curr[-5].typeToken.type, curr[-4].identifierToken.identifier, "", curr[-2].integerToken.integer);
|
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* parseFreeCustomTypeVariableDecl(TokenIter &curr) {
|
ASTNode* Parser::parseFreeCustomTypeVariableDecl(TokenIter &curr, bool hasEndianDef) {
|
||||||
return new ASTNodeVariableDecl(Token::TypeToken::Type::CustomType, curr[-4].identifierToken.identifier, curr[-5].identifierToken.identifier, curr[-2].integerToken.integer);
|
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* parseStruct(TokenIter &curr) {
|
ASTNode* Parser::parseStruct(TokenIter &curr) {
|
||||||
const std::string &structName = curr[-2].identifierToken.identifier;
|
const std::string &structName = curr[-2].identifierToken.identifier;
|
||||||
std::vector<ASTNode*> nodes;
|
std::vector<ASTNode*> nodes;
|
||||||
|
|
||||||
|
u32 startLineNumber = curr[-3].lineNumber;
|
||||||
|
|
||||||
while (!tryConsume(curr, {Token::Type::ScopeClose})) {
|
while (!tryConsume(curr, {Token::Type::ScopeClose})) {
|
||||||
if (tryConsume(curr, {Token::Type::Type, Token::Type::Identifier, Token::Type::EndOfExpression}))
|
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}))
|
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}))
|
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}))
|
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}))
|
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));
|
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}))
|
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));
|
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})) {
|
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) {
|
if (curr[-5].typeToken.type != Token::TypeToken::Type::Padding) {
|
||||||
for(auto &node : nodes) delete node;
|
for(auto &node : nodes) delete node;
|
||||||
|
|
||||||
|
this->m_error = { curr[-5].lineNumber, "No member name provided" };
|
||||||
return nullptr;
|
return nullptr;
|
||||||
}
|
}
|
||||||
nodes.push_back(parsePaddingDecl(curr));
|
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}))
|
} 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));
|
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}))
|
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));
|
nodes.push_back(parseCustomTypePointerVariableDecl(curr, false));
|
||||||
else break;
|
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;
|
||||||
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
if (!tryConsume(curr, {Token::Type::EndOfExpression})) {
|
if (!tryConsume(curr, {Token::Type::EndOfExpression})) {
|
||||||
|
this->m_error = { curr->lineNumber, "Expected ';' after struct definition" };
|
||||||
for(auto &node : nodes) delete node;
|
for(auto &node : nodes) delete node;
|
||||||
return nullptr;
|
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;
|
const std::string &unionName = curr[-2].identifierToken.identifier;
|
||||||
std::vector<ASTNode*> nodes;
|
std::vector<ASTNode*> nodes;
|
||||||
|
|
||||||
|
u32 startLineNumber = curr[-3].lineNumber;
|
||||||
|
|
||||||
while (!tryConsume(curr, {Token::Type::ScopeClose})) {
|
while (!tryConsume(curr, {Token::Type::ScopeClose})) {
|
||||||
if (tryConsume(curr, {Token::Type::Type, Token::Type::Identifier, Token::Type::EndOfExpression}))
|
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}))
|
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}))
|
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}))
|
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::Operator, Token::Type::Identifier, Token::Type::Operator, Token::Type::Type, Token::Type::EndOfExpression}))
|
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));
|
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}))
|
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));
|
nodes.push_back(parseCustomTypePointerVariableDecl(curr, false));
|
||||||
else break;
|
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})) {
|
if (!tryConsume(curr, {Token::Type::EndOfExpression})) {
|
||||||
for(auto &node : nodes) delete node;
|
for(auto &node : nodes) delete node;
|
||||||
|
this->m_error = { curr[-1].lineNumber, "Expected ';' after union definition" };
|
||||||
return nullptr;
|
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 std::string &enumName = curr[-4].identifierToken.identifier;
|
||||||
const Token::TypeToken::Type underlyingType = curr[-2].typeToken.type;
|
const Token::TypeToken::Type underlyingType = curr[-2].typeToken.type;
|
||||||
|
|
||||||
if (curr[-3].operatorToken.op != Token::OperatorToken::Operator::Inherit)
|
u32 startLineNumber = curr[-5].lineNumber;
|
||||||
return nullptr;
|
|
||||||
|
|
||||||
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;
|
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})) {
|
while (!tryConsume(curr, {Token::Type::ScopeClose})) {
|
||||||
if (tryConsume(curr, { Token::Type::Identifier, Token::Type::Separator }) || tryConsume(curr, { Token::Type::Identifier, Token::Type::ScopeClose })) {
|
if (tryConsume(curr, { Token::Type::Identifier, Token::Type::Separator }) || tryConsume(curr, { Token::Type::Identifier, Token::Type::ScopeClose })) {
|
||||||
@@ -183,67 +398,83 @@ namespace hex::lang {
|
|||||||
}
|
}
|
||||||
else {
|
else {
|
||||||
delete enumNode;
|
delete enumNode;
|
||||||
|
this->m_error = { curr->lineNumber, "Expected constant identifier" };
|
||||||
return nullptr;
|
return nullptr;
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
if (!tryConsume(curr, {Token::Type::EndOfExpression})) {
|
if (!tryConsume(curr, {Token::Type::EndOfExpression})) {
|
||||||
delete enumNode;
|
delete enumNode;
|
||||||
|
this->m_error = { curr[-1].lineNumber, "Expected ';' after enum definition" };
|
||||||
return nullptr;
|
return nullptr;
|
||||||
}
|
}
|
||||||
|
|
||||||
return enumNode;
|
return enumNode;
|
||||||
}
|
}
|
||||||
|
|
||||||
ASTNode *parseBitField(TokenIter &curr) {
|
ASTNode* Parser::parseBitField(TokenIter &curr) {
|
||||||
const std::string &bitfieldName = curr[-2].identifierToken.identifier;
|
const std::string &bitfieldName = curr[-2].identifierToken.identifier;
|
||||||
std::vector<std::pair<std::string, size_t>> fields;
|
std::vector<std::pair<std::string, size_t>> fields;
|
||||||
|
|
||||||
|
u32 startLineNumber = curr[-3].lineNumber;
|
||||||
|
|
||||||
while (!tryConsume(curr, {Token::Type::ScopeClose})) {
|
while (!tryConsume(curr, {Token::Type::ScopeClose})) {
|
||||||
if (tryConsume(curr, {Token::Type::Identifier, Token::Type::Operator, Token::Type::Integer, Token::Type::EndOfExpression})) {
|
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;
|
return nullptr;
|
||||||
|
}
|
||||||
|
|
||||||
fields.emplace_back(curr[-4].identifierToken.identifier, curr[-2].integerToken.integer);
|
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 nullptr;
|
||||||
|
}
|
||||||
|
|
||||||
return new ASTNodeBitField(bitfieldName, fields);
|
return new ASTNodeBitField(startLineNumber, bitfieldName, fields);
|
||||||
}
|
}
|
||||||
|
|
||||||
ASTNode *parseScope(TokenIter &curr) {
|
ASTNode* Parser::parseScope(TokenIter &curr) {
|
||||||
return new ASTNodeScope(parseTillToken(curr, Token::Type::ScopeClose));
|
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 keyword = curr[-5].keywordToken;
|
||||||
auto name = curr[-4].identifierToken;
|
auto name = curr[-4].identifierToken;
|
||||||
auto op = curr[-3].operatorToken;
|
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 { };
|
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 { };
|
return { };
|
||||||
|
}
|
||||||
|
|
||||||
if (curr[-2].type == Token::Type::Type) {
|
if (curr[-2].type == Token::Type::Type) {
|
||||||
auto type = curr[-2].typeToken;
|
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) {
|
} else if (curr[-2].type == Token::Type::Identifier) {
|
||||||
auto customType = curr[-2].identifierToken;
|
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 { };
|
return { };
|
||||||
}
|
}
|
||||||
|
|
||||||
std::optional<std::vector<ASTNode*>> parseStatement(TokenIter &curr) {
|
std::optional<std::vector<ASTNode*>> Parser::parseStatement(TokenIter &curr) {
|
||||||
std::vector<ASTNode*> program;
|
std::vector<ASTNode*> program;
|
||||||
|
|
||||||
// Struct
|
// Struct
|
||||||
@@ -324,17 +555,57 @@ namespace hex::lang {
|
|||||||
program.push_back(usingDecl.value());
|
program.push_back(usingDecl.value());
|
||||||
|
|
||||||
return program;
|
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})) {
|
} 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);
|
program.push_back(variableDecl);
|
||||||
|
|
||||||
return program;
|
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})) {
|
} 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);
|
program.push_back(variableDecl);
|
||||||
|
|
||||||
@@ -342,11 +613,12 @@ namespace hex::lang {
|
|||||||
}
|
}
|
||||||
else {
|
else {
|
||||||
for(auto &node : program) delete node;
|
for(auto &node : program) delete node;
|
||||||
|
this->m_error = { curr->lineNumber, "Invalid sequence" };
|
||||||
return { };
|
return { };
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
std::vector<ASTNode*> parseTillToken(TokenIter &curr, Token::Type endTokenType) {
|
std::vector<ASTNode*> Parser::parseTillToken(TokenIter &curr, Token::Type endTokenType) {
|
||||||
std::vector<ASTNode*> program;
|
std::vector<ASTNode*> program;
|
||||||
|
|
||||||
while (curr->type != endTokenType) {
|
while (curr->type != endTokenType) {
|
||||||
|
|||||||
@@ -8,6 +8,7 @@ namespace hex::lang {
|
|||||||
|
|
||||||
std::pair<Result, std::string> Preprocessor::preprocess(const std::string& code, bool initialRun) {
|
std::pair<Result, std::string> Preprocessor::preprocess(const std::string& code, bool initialRun) {
|
||||||
u32 offset = 0;
|
u32 offset = 0;
|
||||||
|
u32 lineNumber = 1;
|
||||||
|
|
||||||
if (initialRun) {
|
if (initialRun) {
|
||||||
this->m_defines.clear();
|
this->m_defines.clear();
|
||||||
@@ -136,6 +137,9 @@ namespace hex::lang {
|
|||||||
return { ResultPreprocessingError, "" };
|
return { ResultPreprocessingError, "" };
|
||||||
}
|
}
|
||||||
|
|
||||||
|
if (code[offset] == '\n')
|
||||||
|
lineNumber++;
|
||||||
|
|
||||||
output += code[offset];
|
output += code[offset];
|
||||||
offset += 1;
|
offset += 1;
|
||||||
}
|
}
|
||||||
@@ -168,7 +172,7 @@ namespace hex::lang {
|
|||||||
this->m_pragmaHandlers.emplace(pragmaType, function);
|
this->m_pragmaHandlers.emplace(pragmaType, function);
|
||||||
}
|
}
|
||||||
|
|
||||||
void Preprocessor::addDefaultPragramHandlers() {
|
void Preprocessor::addDefaultPragmaHandlers() {
|
||||||
this->addPragmaHandler("MIME", [](std::string value) {
|
this->addPragmaHandler("MIME", [](std::string value) {
|
||||||
return !std::all_of(value.begin(), value.end(), isspace) && !value.ends_with('\n') && !value.ends_with('\r');
|
return !std::all_of(value.begin(), value.end(), isspace) && !value.ends_with('\n') && !value.ends_with('\r');
|
||||||
});
|
});
|
||||||
|
|||||||
@@ -3,6 +3,8 @@
|
|||||||
#include <unordered_set>
|
#include <unordered_set>
|
||||||
#include <string>
|
#include <string>
|
||||||
|
|
||||||
|
#include "helpers/utils.hpp"
|
||||||
|
|
||||||
namespace hex::lang {
|
namespace hex::lang {
|
||||||
|
|
||||||
Validator::Validator() {
|
Validator::Validator() {
|
||||||
@@ -19,73 +21,96 @@ namespace hex::lang {
|
|||||||
{
|
{
|
||||||
// Check for duplicate variable names
|
// Check for duplicate variable names
|
||||||
auto varDeclNode = static_cast<ASTNodeVariableDecl*>(node);
|
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;
|
return false;
|
||||||
|
}
|
||||||
|
|
||||||
if (varDeclNode->getArraySize() == 0 && !varDeclNode->getArraySizeVariable().has_value() ||
|
if (varDeclNode->getArraySize() == 0 && !varDeclNode->getArraySizeVariable().has_value() ||
|
||||||
varDeclNode->getArraySize() != 0 && varDeclNode->getArraySizeVariable().has_value())
|
varDeclNode->getArraySize() != 0 && varDeclNode->getArraySizeVariable().has_value()) {
|
||||||
|
|
||||||
|
this->m_error = { varDeclNode->getLineNumber(), "Invalid array size" };
|
||||||
return false;
|
return false;
|
||||||
|
}
|
||||||
}
|
}
|
||||||
break;
|
break;
|
||||||
case ASTNode::Type::TypeDecl:
|
case ASTNode::Type::TypeDecl:
|
||||||
{
|
{
|
||||||
// Check for duplicate type names
|
// Check for duplicate type names
|
||||||
auto typeDeclNode = static_cast<ASTNodeTypeDecl*>(node);
|
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;
|
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;
|
return false;
|
||||||
|
}
|
||||||
}
|
}
|
||||||
break;
|
break;
|
||||||
case ASTNode::Type::Struct:
|
case ASTNode::Type::Struct:
|
||||||
{
|
{
|
||||||
// Check for duplicate type name
|
// Check for duplicate type name
|
||||||
auto structNode = static_cast<ASTNodeStruct*>(node);
|
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;
|
return false;
|
||||||
|
}
|
||||||
|
|
||||||
// Check for duplicate member names
|
// Check for duplicate member names
|
||||||
std::unordered_set<std::string> memberNames;
|
std::unordered_set<std::string> memberNames;
|
||||||
for (const auto &member : structNode->getNodes())
|
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;
|
return false;
|
||||||
|
}
|
||||||
}
|
}
|
||||||
break;
|
break;
|
||||||
case ASTNode::Type::Enum:
|
case ASTNode::Type::Enum:
|
||||||
{
|
{
|
||||||
// Check for duplicate type name
|
// Check for duplicate type name
|
||||||
auto enumNode = static_cast<ASTNodeEnum*>(node);
|
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;
|
return false;
|
||||||
|
}
|
||||||
|
|
||||||
// Check for duplicate constant names
|
// Check for duplicate constant names
|
||||||
std::unordered_set<std::string> constantNames;
|
std::unordered_set<std::string> constantNames;
|
||||||
for (const auto &[value, name] : enumNode->getValues())
|
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;
|
return false;
|
||||||
|
}
|
||||||
}
|
}
|
||||||
break;
|
break;
|
||||||
case ASTNode::Type::Bitfield:
|
case ASTNode::Type::Bitfield:
|
||||||
{
|
{
|
||||||
// Check for duplicate type name
|
// Check for duplicate type name
|
||||||
auto bitfieldNode = static_cast<ASTNodeBitField*>(node);
|
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;
|
return false;
|
||||||
|
}
|
||||||
|
|
||||||
size_t bitfieldSize = 0;
|
size_t bitfieldSize = 0;
|
||||||
|
|
||||||
// Check for duplicate constant names
|
// Check for duplicate constant names
|
||||||
std::unordered_set<std::string> flagNames;
|
std::unordered_set<std::string> flagNames;
|
||||||
for (const auto &[name, size] : bitfieldNode->getFields()) {
|
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;
|
return false;
|
||||||
|
}
|
||||||
|
|
||||||
bitfieldSize += size;
|
bitfieldSize += size;
|
||||||
}
|
}
|
||||||
|
|
||||||
if (bitfieldSize > 64)
|
if (bitfieldSize > 64) {
|
||||||
|
this->m_error = { bitfieldNode->getLineNumber(), "Bitfield exceeds maximum size of 64 bits" };
|
||||||
return false;
|
return false;
|
||||||
|
}
|
||||||
}
|
}
|
||||||
break;
|
break;
|
||||||
}
|
}
|
||||||
|
|||||||
@@ -11,17 +11,26 @@
|
|||||||
#include "views/view_strings.hpp"
|
#include "views/view_strings.hpp"
|
||||||
#include "views/view_data_inspector.hpp"
|
#include "views/view_data_inspector.hpp"
|
||||||
#include "views/view_disassembler.hpp"
|
#include "views/view_disassembler.hpp"
|
||||||
|
#include "views/view_bookmarks.hpp"
|
||||||
|
#include "views/view_patches.hpp"
|
||||||
|
|
||||||
#include "providers/provider.hpp"
|
#include "providers/provider.hpp"
|
||||||
|
|
||||||
#include <vector>
|
#include <vector>
|
||||||
|
|
||||||
int main() {
|
int mainArgc;
|
||||||
|
char **mainArgv;
|
||||||
|
|
||||||
|
int main(int argc, char **argv) {
|
||||||
|
mainArgc = argc;
|
||||||
|
mainArgv = argv;
|
||||||
|
|
||||||
hex::Window window;
|
hex::Window window;
|
||||||
|
|
||||||
// Shared Data
|
// Shared Data
|
||||||
std::vector<hex::lang::PatternData*> patternData;
|
std::vector<hex::lang::PatternData*> patternData;
|
||||||
hex::prv::Provider *dataProvider = nullptr;
|
hex::prv::Provider *dataProvider = nullptr;
|
||||||
|
|
||||||
// Create views
|
// Create views
|
||||||
window.addView<hex::ViewHexEditor>(dataProvider, patternData);
|
window.addView<hex::ViewHexEditor>(dataProvider, patternData);
|
||||||
window.addView<hex::ViewPattern>(dataProvider, patternData);
|
window.addView<hex::ViewPattern>(dataProvider, patternData);
|
||||||
@@ -31,9 +40,14 @@ int main() {
|
|||||||
window.addView<hex::ViewInformation>(dataProvider);
|
window.addView<hex::ViewInformation>(dataProvider);
|
||||||
window.addView<hex::ViewStrings>(dataProvider);
|
window.addView<hex::ViewStrings>(dataProvider);
|
||||||
window.addView<hex::ViewDisassembler>(dataProvider);
|
window.addView<hex::ViewDisassembler>(dataProvider);
|
||||||
window.addView<hex::ViewTools>();
|
window.addView<hex::ViewBookmarks>(dataProvider);
|
||||||
|
window.addView<hex::ViewPatches>(dataProvider);
|
||||||
|
window.addView<hex::ViewTools>(dataProvider);
|
||||||
window.addView<hex::ViewHelp>();
|
window.addView<hex::ViewHelp>();
|
||||||
|
|
||||||
|
if (argc > 1)
|
||||||
|
hex::View::postEvent(hex::Events::FileDropped, argv[1]);
|
||||||
|
|
||||||
window.loop();
|
window.loop();
|
||||||
|
|
||||||
return 0;
|
return 0;
|
||||||
|
|||||||
@@ -6,22 +6,34 @@
|
|||||||
#include <sys/stat.h>
|
#include <sys/stat.h>
|
||||||
#include <time.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 {
|
namespace hex::prv {
|
||||||
|
|
||||||
FileProvider::FileProvider(std::string_view path) : Provider(), m_path(path) {
|
FileProvider::FileProvider(std::string_view path) : Provider(), m_path(path) {
|
||||||
this->m_fileStatsValid = stat(path.data(), &this->m_fileStats) == 0;
|
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_readable = true;
|
||||||
this->m_writable = true;
|
this->m_writable = true;
|
||||||
|
|
||||||
if (this->m_file == nullptr) {
|
if (this->m_file == nullptr) {
|
||||||
this->m_file = fopen(path.data(), "rb");
|
this->m_file = fopen64(path.data(), "rb");
|
||||||
this->m_writable = false;
|
this->m_writable = false;
|
||||||
}
|
}
|
||||||
|
|
||||||
|
if (this->m_file != nullptr)
|
||||||
|
ProjectFile::setFilePath(path);
|
||||||
}
|
}
|
||||||
|
|
||||||
FileProvider::~FileProvider() {
|
FileProvider::~FileProvider() {
|
||||||
@@ -48,17 +60,40 @@ namespace hex::prv {
|
|||||||
return;
|
return;
|
||||||
|
|
||||||
fseeko64(this->m_file, this->getCurrentPage() * PageSize + offset, SEEK_SET);
|
fseeko64(this->m_file, this->getCurrentPage() * PageSize + offset, SEEK_SET);
|
||||||
fread(buffer, 1, size, this->m_file);
|
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, void *buffer, size_t size) {
|
void FileProvider::write(u64 offset, const void *buffer, size_t size) {
|
||||||
if (buffer == nullptr || size == 0)
|
if (buffer == nullptr || size == 0)
|
||||||
return;
|
return;
|
||||||
|
|
||||||
fseeko64(this->m_file, this->getCurrentPage() * PageSize + offset, SEEK_SET);
|
this->m_patches.push_back(this->m_patches.back());
|
||||||
fwrite(buffer, 1, size, this->m_file);
|
|
||||||
|
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::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::getActualSize() {
|
size_t FileProvider::getActualSize() {
|
||||||
fseeko64(this->m_file, 0, SEEK_END);
|
fseeko64(this->m_file, 0, SEEK_END);
|
||||||
return ftello64(this->m_file);
|
return ftello64(this->m_file);
|
||||||
@@ -1,8 +0,0 @@
|
|||||||
#include "utils.hpp"
|
|
||||||
|
|
||||||
#include <codecvt>
|
|
||||||
#include <locale>
|
|
||||||
|
|
||||||
namespace hex {
|
|
||||||
|
|
||||||
}
|
|
||||||
114
source/views/view_bookmarks.cpp
Normal file
114
source/views/view_bookmarks.cpp
Normal 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, ®ion);
|
||||||
|
|
||||||
|
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() {
|
||||||
|
|
||||||
|
}
|
||||||
|
|
||||||
|
}
|
||||||
@@ -1,7 +1,7 @@
|
|||||||
#include "views/view_data_inspector.hpp"
|
#include "views/view_data_inspector.hpp"
|
||||||
|
|
||||||
#include "providers/provider.hpp"
|
#include "providers/provider.hpp"
|
||||||
#include "utils.hpp"
|
#include "helpers/utils.hpp"
|
||||||
|
|
||||||
#include <cstring>
|
#include <cstring>
|
||||||
|
|
||||||
@@ -9,26 +9,23 @@ extern int ImTextCharFromUtf8(unsigned int* out_char, const char* in_text, const
|
|||||||
|
|
||||||
namespace hex {
|
namespace hex {
|
||||||
|
|
||||||
ViewDataInspector::ViewDataInspector(prv::Provider* &dataProvider) : View(), m_dataProvider(dataProvider) {
|
ViewDataInspector::ViewDataInspector(prv::Provider* &dataProvider) : View("Data Inspector"), m_dataProvider(dataProvider) {
|
||||||
View::subscribeEvent(Events::ByteSelected, [this](const void* userData){
|
View::subscribeEvent(Events::RegionSelected, [this](const void* userData){
|
||||||
size_t offset = *static_cast<const size_t*>(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));
|
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;
|
this->m_shouldInvalidate = true;
|
||||||
});
|
});
|
||||||
}
|
}
|
||||||
|
|
||||||
ViewDataInspector::~ViewDataInspector() {
|
ViewDataInspector::~ViewDataInspector() {
|
||||||
View::unsubscribeEvent(Events::ByteSelected);
|
View::unsubscribeEvent(Events::RegionSelected);
|
||||||
}
|
}
|
||||||
|
|
||||||
void ViewDataInspector::createView() {
|
void ViewDataInspector::createView() {
|
||||||
if (!this->m_windowOpen)
|
|
||||||
return;
|
|
||||||
|
|
||||||
if (this->m_shouldInvalidate) {
|
if (this->m_shouldInvalidate) {
|
||||||
this->m_shouldInvalidate = false;
|
this->m_shouldInvalidate = false;
|
||||||
|
|
||||||
@@ -50,8 +47,8 @@ namespace hex {
|
|||||||
this->m_cachedData.emplace_back("uint64_t", hex::format("%llu", hex::changeEndianess(this->m_previewData.unsigned64, 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("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", hex::changeEndianess(this->m_previewData.ansiChar, this->m_endianess)));
|
this->m_cachedData.emplace_back("ASCII Character", hex::format("'%s'", makePrintable(this->m_previewData.ansiChar).c_str()));
|
||||||
this->m_cachedData.emplace_back("Wide Character / char16_t", hex::format("%lc", hex::changeEndianess(this->m_previewData.wideChar, this->m_endianess)));
|
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 buffer[5] = { 0 };
|
||||||
char codepointString[5] = { 0 };
|
char codepointString[5] = { 0 };
|
||||||
@@ -72,7 +69,7 @@ namespace hex {
|
|||||||
auto endianAdjustedTime = hex::changeEndianess(this->m_previewData.time32, this->m_endianess);
|
auto endianAdjustedTime = hex::changeEndianess(this->m_previewData.time32, this->m_endianess);
|
||||||
std::tm * ptm = _localtime32(&endianAdjustedTime);
|
std::tm * ptm = _localtime32(&endianAdjustedTime);
|
||||||
char buffer[32];
|
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);
|
this->m_cachedData.emplace_back("__time32_t", buffer);
|
||||||
else
|
else
|
||||||
this->m_cachedData.emplace_back("__time32_t", "Invalid");
|
this->m_cachedData.emplace_back("__time32_t", "Invalid");
|
||||||
@@ -82,7 +79,7 @@ namespace hex {
|
|||||||
auto endianAdjustedTime = hex::changeEndianess(this->m_previewData.time64, this->m_endianess);
|
auto endianAdjustedTime = hex::changeEndianess(this->m_previewData.time64, this->m_endianess);
|
||||||
std::tm * ptm = _localtime64(&endianAdjustedTime);
|
std::tm * ptm = _localtime64(&endianAdjustedTime);
|
||||||
char buffer[64];
|
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);
|
this->m_cachedData.emplace_back("__time64_t", buffer);
|
||||||
else
|
else
|
||||||
this->m_cachedData.emplace_back("__time64_t", "Invalid");
|
this->m_cachedData.emplace_back("__time64_t", "Invalid");
|
||||||
@@ -92,14 +89,15 @@ namespace hex {
|
|||||||
auto endianAdjustedTime = hex::changeEndianess(this->m_previewData.time, this->m_endianess);
|
auto endianAdjustedTime = hex::changeEndianess(this->m_previewData.time, this->m_endianess);
|
||||||
std::tm * ptm = localtime(&endianAdjustedTime);
|
std::tm * ptm = localtime(&endianAdjustedTime);
|
||||||
char buffer[64];
|
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);
|
this->m_cachedData.emplace_back("time_t", buffer);
|
||||||
else
|
else
|
||||||
this->m_cachedData.emplace_back("time_t", "Invalid");
|
this->m_cachedData.emplace_back("time_t", "Invalid");
|
||||||
}
|
}
|
||||||
#endif
|
#endif
|
||||||
|
|
||||||
this->m_cachedData.emplace_back("GUID", hex::format("{%08lX-%04hX-%04hX-%02hhX%02hhX-%02hhX%02hhX%02hhX%02hhX%02hhX%02hhX}",
|
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.data1, this->m_endianess),
|
||||||
hex::changeEndianess(this->m_previewData.guid.data2, this->m_endianess),
|
hex::changeEndianess(this->m_previewData.guid.data2, this->m_endianess),
|
||||||
hex::changeEndianess(this->m_previewData.guid.data3, this->m_endianess),
|
hex::changeEndianess(this->m_previewData.guid.data3, this->m_endianess),
|
||||||
@@ -108,7 +106,7 @@ namespace hex {
|
|||||||
}
|
}
|
||||||
|
|
||||||
|
|
||||||
if (ImGui::Begin("Data Inspector", &this->m_windowOpen)) {
|
if (ImGui::Begin("Data Inspector", &this->getWindowOpenState(), ImGuiWindowFlags_NoCollapse)) {
|
||||||
if (this->m_dataProvider != nullptr && this->m_dataProvider->isReadable()) {
|
if (this->m_dataProvider != nullptr && this->m_dataProvider->isReadable()) {
|
||||||
if (ImGui::BeginChild("##scrolling", ImVec2(0, ImGui::GetWindowHeight() - 60))) {
|
if (ImGui::BeginChild("##scrolling", ImVec2(0, ImGui::GetWindowHeight() - 60))) {
|
||||||
if (ImGui::BeginTable("##datainspector", 2, ImGuiTableFlags_Borders | ImGuiTableFlags_Resizable | ImGuiTableFlags_RowBg | ImGuiTableFlags_NoBordersInBody)) {
|
if (ImGui::BeginTable("##datainspector", 2, ImGuiTableFlags_Borders | ImGuiTableFlags_Resizable | ImGuiTableFlags_RowBg | ImGuiTableFlags_NoBordersInBody)) {
|
||||||
@@ -155,10 +153,7 @@ namespace hex {
|
|||||||
}
|
}
|
||||||
|
|
||||||
void ViewDataInspector::createMenu() {
|
void ViewDataInspector::createMenu() {
|
||||||
if (ImGui::BeginMenu("View")) {
|
|
||||||
ImGui::MenuItem("Data Preview View", "", &this->m_windowOpen);
|
|
||||||
ImGui::EndMenu();
|
|
||||||
}
|
|
||||||
}
|
}
|
||||||
|
|
||||||
}
|
}
|
||||||
@@ -1,7 +1,7 @@
|
|||||||
#include "views/view_disassembler.hpp"
|
#include "views/view_disassembler.hpp"
|
||||||
|
|
||||||
#include "providers/provider.hpp"
|
#include "providers/provider.hpp"
|
||||||
#include "utils.hpp"
|
#include "helpers/utils.hpp"
|
||||||
|
|
||||||
#include <cstring>
|
#include <cstring>
|
||||||
|
|
||||||
@@ -9,20 +9,27 @@ using namespace std::literals::string_literals;
|
|||||||
|
|
||||||
namespace hex {
|
namespace hex {
|
||||||
|
|
||||||
ViewDisassembler::ViewDisassembler(prv::Provider* &dataProvider) : View(), m_dataProvider(dataProvider) {
|
ViewDisassembler::ViewDisassembler(prv::Provider* &dataProvider) : View("Disassembler"), m_dataProvider(dataProvider) {
|
||||||
View::subscribeEvent(Events::DataChanged, [this](const void*){
|
View::subscribeEvent(Events::DataChanged, [this](const void*){
|
||||||
this->m_shouldInvalidate = true;
|
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() {
|
ViewDisassembler::~ViewDisassembler() {
|
||||||
View::unsubscribeEvent(Events::DataChanged);
|
View::unsubscribeEvent(Events::DataChanged);
|
||||||
|
View::unsubscribeEvent(Events::RegionSelected);
|
||||||
}
|
}
|
||||||
|
|
||||||
void ViewDisassembler::createView() {
|
void ViewDisassembler::createView() {
|
||||||
if (!this->m_windowOpen)
|
|
||||||
return;
|
|
||||||
|
|
||||||
if (this->m_shouldInvalidate) {
|
if (this->m_shouldInvalidate) {
|
||||||
this->m_disassembly.clear();
|
this->m_disassembly.clear();
|
||||||
|
|
||||||
@@ -42,14 +49,14 @@ namespace hex {
|
|||||||
if (this->m_sparcV9Mode)
|
if (this->m_sparcV9Mode)
|
||||||
mode = cs_mode(mode | CS_MODE_V9);
|
mode = cs_mode(mode | CS_MODE_V9);
|
||||||
|
|
||||||
if (cs_open(this->m_architecture, mode, &capstoneHandle) == CS_ERR_OK) {
|
if (cs_open(Disassembler::toCapstoneArchictecture(this->m_architecture), mode, &capstoneHandle) == CS_ERR_OK) {
|
||||||
|
|
||||||
std::vector<u8> buffer(2048, 0x00);
|
std::vector<u8> buffer(2048, 0x00);
|
||||||
for (u64 address = 0; address < this->m_codeSize; address += 2048) {
|
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_codeSize - address);
|
size_t bufferSize = std::min(u64(2048), (this->m_codeRegion[1] - this->m_codeRegion[0] + 1) - address);
|
||||||
this->m_dataProvider->read(this->m_codeOffset + address, buffer.data(), bufferSize);
|
this->m_dataProvider->read(this->m_codeRegion[0] + address, buffer.data(), bufferSize);
|
||||||
|
|
||||||
size_t instructionCount = cs_disasm(capstoneHandle, buffer.data(), buffer.size(), this->m_baseAddress + address, 0, &instructions);
|
size_t instructionCount = cs_disasm(capstoneHandle, buffer.data(), bufferSize, this->m_baseAddress + address, 0, &instructions);
|
||||||
|
|
||||||
if (instructionCount == 0)
|
if (instructionCount == 0)
|
||||||
break;
|
break;
|
||||||
@@ -58,8 +65,10 @@ namespace hex {
|
|||||||
for (u32 instr = 0; instr < instructionCount; instr++) {
|
for (u32 instr = 0; instr < instructionCount; instr++) {
|
||||||
Disassembly disassembly = { 0 };
|
Disassembly disassembly = { 0 };
|
||||||
disassembly.address = instructions[instr].address;
|
disassembly.address = instructions[instr].address;
|
||||||
disassembly.offset = this->m_codeOffset + address + usedBytes;
|
disassembly.offset = this->m_codeRegion[0] + address + usedBytes;
|
||||||
disassembly.opcodeString = instructions[instr].mnemonic + " "s + instructions[instr].op_str;
|
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++)
|
for (u8 i = 0; i < instructions[instr].size; i++)
|
||||||
disassembly.bytes += hex::format("%02X ", instructions[instr].bytes[i]);
|
disassembly.bytes += hex::format("%02X ", instructions[instr].bytes[i]);
|
||||||
@@ -83,23 +92,21 @@ namespace hex {
|
|||||||
}
|
}
|
||||||
|
|
||||||
|
|
||||||
if (ImGui::Begin("Disassembler", &this->m_windowOpen)) {
|
if (ImGui::Begin("Disassembler", &this->getWindowOpenState(), ImGuiWindowFlags_NoCollapse)) {
|
||||||
|
|
||||||
if (this->m_dataProvider != nullptr && this->m_dataProvider->isReadable()) {
|
if (this->m_dataProvider != nullptr && this->m_dataProvider->isReadable()) {
|
||||||
constexpr static const char * const ArchitectureNames[] = { "ARM32", "ARM64", "MIPS", "x86", "PowerPC", "Sparc", "SystemZ", "XCore", "68K", "TMS320C64x", "680X", "Ethereum" };
|
ImGui::TextUnformatted("Position");
|
||||||
|
|
||||||
ImGui::InputScalar("Base address", ImGuiDataType_U64, &this->m_baseAddress, nullptr, nullptr, "%llx", ImGuiInputTextFlags_CharsHexadecimal);
|
|
||||||
|
|
||||||
ImGui::NewLine();
|
|
||||||
|
|
||||||
ImGui::InputScalar("Code start offset", ImGuiDataType_U64, &this->m_codeOffset, nullptr, nullptr, "%llx", ImGuiInputTextFlags_CharsHexadecimal);
|
|
||||||
ImGui::InputScalar("Code size", ImGuiDataType_U64, &this->m_codeSize, nullptr, nullptr, "%llx", ImGuiInputTextFlags_CharsHexadecimal);
|
|
||||||
|
|
||||||
ImGui::NewLine();
|
|
||||||
ImGui::Separator();
|
ImGui::Separator();
|
||||||
ImGui::NewLine();
|
|
||||||
|
|
||||||
ImGui::Combo("Architecture", reinterpret_cast<int*>(&this->m_architecture), ArchitectureNames, 12);
|
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::BeginChild("modes", ImVec2(0, 100), true)) {
|
||||||
@@ -113,13 +120,16 @@ namespace hex {
|
|||||||
ImGui::NewLine();
|
ImGui::NewLine();
|
||||||
|
|
||||||
switch (this->m_architecture) {
|
switch (this->m_architecture) {
|
||||||
case CS_ARCH_ARM:
|
case Architecture::ARM:
|
||||||
this->m_modeBasicMIPS = cs_mode(0);
|
this->m_modeBasicMIPS = cs_mode(0);
|
||||||
this->m_modeBasicX86 = cs_mode(0);
|
this->m_modeBasicX86 = cs_mode(0);
|
||||||
this->m_modeBasicPPC = cs_mode(0);
|
this->m_modeBasicPPC = cs_mode(0);
|
||||||
this->m_micoMode = false;
|
this->m_micoMode = false;
|
||||||
this->m_sparcV9Mode = 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))
|
if (ImGui::RadioButton("ARM mode", this->m_modeBasicARM == CS_MODE_ARM))
|
||||||
this->m_modeBasicARM = CS_MODE_ARM;
|
this->m_modeBasicARM = CS_MODE_ARM;
|
||||||
ImGui::SameLine();
|
ImGui::SameLine();
|
||||||
@@ -135,13 +145,16 @@ namespace hex {
|
|||||||
if (ImGui::RadioButton("ARMv8 mode", (this->m_modeExtraARM & (CS_MODE_MCLASS | CS_MODE_V8)) == CS_MODE_V8))
|
if (ImGui::RadioButton("ARMv8 mode", (this->m_modeExtraARM & (CS_MODE_MCLASS | CS_MODE_V8)) == CS_MODE_V8))
|
||||||
this->m_modeExtraARM = CS_MODE_V8;
|
this->m_modeExtraARM = CS_MODE_V8;
|
||||||
break;
|
break;
|
||||||
case CS_ARCH_MIPS:
|
case Architecture::MIPS:
|
||||||
this->m_modeBasicARM = cs_mode(0);
|
this->m_modeBasicARM = cs_mode(0);
|
||||||
this->m_modeExtraARM = cs_mode(0);
|
this->m_modeExtraARM = cs_mode(0);
|
||||||
this->m_modeBasicX86 = cs_mode(0);
|
this->m_modeBasicX86 = cs_mode(0);
|
||||||
this->m_modeBasicPPC = cs_mode(0);
|
this->m_modeBasicPPC = cs_mode(0);
|
||||||
this->m_sparcV9Mode = false;
|
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))
|
if (ImGui::RadioButton("MIPS32 mode", this->m_modeBasicMIPS == CS_MODE_MIPS32))
|
||||||
this->m_modeBasicMIPS = CS_MODE_MIPS32;
|
this->m_modeBasicMIPS = CS_MODE_MIPS32;
|
||||||
ImGui::SameLine();
|
ImGui::SameLine();
|
||||||
@@ -153,7 +166,7 @@ namespace hex {
|
|||||||
|
|
||||||
ImGui::Checkbox("Micro Mode", &this->m_micoMode);
|
ImGui::Checkbox("Micro Mode", &this->m_micoMode);
|
||||||
break;
|
break;
|
||||||
case CS_ARCH_X86:
|
case Architecture::X86:
|
||||||
this->m_modeBasicARM = cs_mode(0);
|
this->m_modeBasicARM = cs_mode(0);
|
||||||
this->m_modeExtraARM = cs_mode(0);
|
this->m_modeExtraARM = cs_mode(0);
|
||||||
this->m_modeBasicMIPS = cs_mode(0);
|
this->m_modeBasicMIPS = cs_mode(0);
|
||||||
@@ -161,6 +174,9 @@ namespace hex {
|
|||||||
this->m_micoMode = false;
|
this->m_micoMode = false;
|
||||||
this->m_sparcV9Mode = 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))
|
if (ImGui::RadioButton("16-bit mode", this->m_modeBasicX86 == CS_MODE_16))
|
||||||
this->m_modeBasicX86 = CS_MODE_16;
|
this->m_modeBasicX86 = CS_MODE_16;
|
||||||
ImGui::SameLine();
|
ImGui::SameLine();
|
||||||
@@ -170,7 +186,7 @@ namespace hex {
|
|||||||
if (ImGui::RadioButton("64-bit mode", this->m_modeBasicX86 == CS_MODE_64))
|
if (ImGui::RadioButton("64-bit mode", this->m_modeBasicX86 == CS_MODE_64))
|
||||||
this->m_modeBasicX86 = CS_MODE_64;
|
this->m_modeBasicX86 = CS_MODE_64;
|
||||||
break;
|
break;
|
||||||
case CS_ARCH_PPC:
|
case Architecture::PPC:
|
||||||
this->m_modeBasicARM = cs_mode(0);
|
this->m_modeBasicARM = cs_mode(0);
|
||||||
this->m_modeExtraARM = cs_mode(0);
|
this->m_modeExtraARM = cs_mode(0);
|
||||||
this->m_modeBasicMIPS = cs_mode(0);
|
this->m_modeBasicMIPS = cs_mode(0);
|
||||||
@@ -178,13 +194,16 @@ namespace hex {
|
|||||||
this->m_micoMode = false;
|
this->m_micoMode = false;
|
||||||
this->m_sparcV9Mode = 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))
|
if (ImGui::RadioButton("32-bit mode", this->m_modeBasicPPC == CS_MODE_32))
|
||||||
this->m_modeBasicPPC = CS_MODE_32;
|
this->m_modeBasicPPC = CS_MODE_32;
|
||||||
ImGui::SameLine();
|
ImGui::SameLine();
|
||||||
if (ImGui::RadioButton("64-bit mode", this->m_modeBasicPPC == CS_MODE_64))
|
if (ImGui::RadioButton("64-bit mode", this->m_modeBasicPPC == CS_MODE_64))
|
||||||
this->m_modeBasicPPC = CS_MODE_64;
|
this->m_modeBasicPPC = CS_MODE_64;
|
||||||
break;
|
break;
|
||||||
case CS_ARCH_SPARC:
|
case Architecture::SPARC:
|
||||||
this->m_modeBasicARM = cs_mode(0);
|
this->m_modeBasicARM = cs_mode(0);
|
||||||
this->m_modeExtraARM = cs_mode(0);
|
this->m_modeExtraARM = cs_mode(0);
|
||||||
this->m_modeBasicMIPS = cs_mode(0);
|
this->m_modeBasicMIPS = cs_mode(0);
|
||||||
@@ -194,13 +213,13 @@ namespace hex {
|
|||||||
|
|
||||||
ImGui::Checkbox("Sparc V9 mode", &this->m_sparcV9Mode);
|
ImGui::Checkbox("Sparc V9 mode", &this->m_sparcV9Mode);
|
||||||
break;
|
break;
|
||||||
case CS_ARCH_ARM64:
|
case Architecture::ARM64:
|
||||||
case CS_ARCH_SYSZ:
|
case Architecture::SYSZ:
|
||||||
case CS_ARCH_XCORE:
|
case Architecture::XCORE:
|
||||||
case CS_ARCH_M68K:
|
case Architecture::M68K:
|
||||||
case CS_ARCH_TMS320C64X:
|
case Architecture::TMS320C64X:
|
||||||
case CS_ARCH_M680X:
|
case Architecture::M680X:
|
||||||
case CS_ARCH_EVM:
|
case Architecture::EVM:
|
||||||
this->m_modeBasicARM = cs_mode(0);
|
this->m_modeBasicARM = cs_mode(0);
|
||||||
this->m_modeExtraARM = cs_mode(0);
|
this->m_modeExtraARM = cs_mode(0);
|
||||||
this->m_modeBasicMIPS = cs_mode(0);
|
this->m_modeBasicMIPS = cs_mode(0);
|
||||||
@@ -211,58 +230,60 @@ namespace hex {
|
|||||||
break;
|
break;
|
||||||
}
|
}
|
||||||
|
|
||||||
ImGui::EndChild();
|
|
||||||
}
|
|
||||||
|
|
||||||
ImGui::NewLine();
|
|
||||||
if (ImGui::Button("Disassemble"))
|
|
||||||
this->m_shouldInvalidate = true;
|
|
||||||
ImGui::NewLine();
|
|
||||||
ImGui::Separator();
|
|
||||||
ImGui::NewLine();
|
|
||||||
|
|
||||||
if (ImGui::BeginChild("##scrolling")) {
|
|
||||||
if (ImGui::BeginTable("##disassembly", 4, ImGuiTableFlags_Borders | ImGuiTableFlags_Resizable | ImGuiTableFlags_RowBg | ImGuiTableFlags_NoBordersInBody)) {
|
|
||||||
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();
|
|
||||||
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::TextUnformatted(this->m_disassembly[i].opcodeString.c_str());
|
|
||||||
}
|
|
||||||
}
|
|
||||||
|
|
||||||
clipper.End();
|
|
||||||
|
|
||||||
ImGui::EndTable();
|
|
||||||
}
|
|
||||||
}
|
}
|
||||||
ImGui::EndChild();
|
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();
|
ImGui::End();
|
||||||
}
|
}
|
||||||
|
|
||||||
void ViewDisassembler::createMenu() {
|
void ViewDisassembler::createMenu() {
|
||||||
if (ImGui::BeginMenu("View")) {
|
|
||||||
ImGui::MenuItem("Disassembler View", "", &this->m_windowOpen);
|
|
||||||
ImGui::EndMenu();
|
|
||||||
}
|
|
||||||
}
|
}
|
||||||
|
|
||||||
}
|
}
|
||||||
@@ -2,22 +2,33 @@
|
|||||||
|
|
||||||
#include "providers/provider.hpp"
|
#include "providers/provider.hpp"
|
||||||
|
|
||||||
#include "crypto.hpp"
|
#include "helpers/crypto.hpp"
|
||||||
|
|
||||||
#include <vector>
|
#include <vector>
|
||||||
|
|
||||||
#include "utils.hpp"
|
#include "helpers/utils.hpp"
|
||||||
|
|
||||||
namespace hex {
|
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*){
|
View::subscribeEvent(Events::DataChanged, [this](const void*){
|
||||||
this->m_shouldInvalidate = true;
|
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() {
|
ViewHashes::~ViewHashes() {
|
||||||
View::unsubscribeEvent(Events::DataChanged);
|
View::unsubscribeEvent(Events::DataChanged);
|
||||||
|
View::unsubscribeEvent(Events::RegionSelected);
|
||||||
}
|
}
|
||||||
|
|
||||||
|
|
||||||
@@ -27,42 +38,38 @@ namespace hex {
|
|||||||
}
|
}
|
||||||
|
|
||||||
void ViewHashes::createView() {
|
void ViewHashes::createView() {
|
||||||
if (!this->m_windowOpen)
|
if (ImGui::Begin("Hashing", &this->getWindowOpenState(), ImGuiWindowFlags_NoCollapse)) {
|
||||||
return;
|
|
||||||
|
|
||||||
if (ImGui::Begin("Hashing", &this->m_windowOpen)) {
|
|
||||||
ImGui::BeginChild("##scrolling", ImVec2(0, 0), false, ImGuiWindowFlags_NoMove | ImGuiWindowFlags_NoNav);
|
ImGui::BeginChild("##scrolling", ImVec2(0, 0), false, ImGuiWindowFlags_NoMove | ImGuiWindowFlags_NoNav);
|
||||||
ImGui::NewLine();
|
|
||||||
|
|
||||||
if (this->m_dataProvider != nullptr && this->m_dataProvider->isAvailable()) {
|
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 *)))
|
if (ImGui::Combo("Hash Function", &this->m_currHashFunction, HashFunctionNames,sizeof(HashFunctionNames) / sizeof(const char *)))
|
||||||
this->m_shouldInvalidate = true;
|
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();
|
size_t dataSize = this->m_dataProvider->getSize();
|
||||||
if (this->m_hashEnd >= dataSize)
|
if (this->m_hashRegion[1] >= dataSize)
|
||||||
this->m_hashEnd = dataSize - 1;
|
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) {
|
switch (this->m_currHashFunction) {
|
||||||
case 0: // CRC16
|
case 0: // CRC16
|
||||||
{
|
{
|
||||||
int polynomial = 0, init = 0;
|
static int polynomial = 0, init = 0;
|
||||||
|
|
||||||
ImGui::InputInt("Initial Value", &init, 0, 0, ImGuiInputTextFlags_CharsHexadecimal);
|
ImGui::InputInt("Initial Value", &init, 0, 0, ImGuiInputTextFlags_CharsHexadecimal);
|
||||||
if (ImGui::IsItemEdited()) this->m_shouldInvalidate = true;
|
if (ImGui::IsItemEdited()) this->m_shouldInvalidate = true;
|
||||||
@@ -70,23 +77,24 @@ namespace hex {
|
|||||||
ImGui::InputInt("Polynomial", &polynomial, 0, 0, ImGuiInputTextFlags_CharsHexadecimal);
|
ImGui::InputInt("Polynomial", &polynomial, 0, 0, ImGuiInputTextFlags_CharsHexadecimal);
|
||||||
if (ImGui::IsItemEdited()) this->m_shouldInvalidate = true;
|
if (ImGui::IsItemEdited()) this->m_shouldInvalidate = true;
|
||||||
|
|
||||||
ImGui::NewLine();
|
|
||||||
ImGui::Separator();
|
|
||||||
ImGui::NewLine();
|
ImGui::NewLine();
|
||||||
|
|
||||||
static u16 result = 0;
|
static u16 result = 0;
|
||||||
|
|
||||||
if (this->m_shouldInvalidate)
|
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];
|
char buffer[sizeof(result) * 2 + 1];
|
||||||
snprintf(buffer, sizeof(buffer), "%04X", result);
|
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;
|
break;
|
||||||
case 1: // CRC32
|
case 1: // CRC32
|
||||||
{
|
{
|
||||||
int polynomial = 0, init = 0;
|
static int polynomial = 0, init = 0;
|
||||||
|
|
||||||
ImGui::InputInt("Initial Value", &init, 0, 0, ImGuiInputTextFlags_CharsHexadecimal);
|
ImGui::InputInt("Initial Value", &init, 0, 0, ImGuiInputTextFlags_CharsHexadecimal);
|
||||||
if (ImGui::IsItemEdited()) this->m_shouldInvalidate = true;
|
if (ImGui::IsItemEdited()) this->m_shouldInvalidate = true;
|
||||||
@@ -94,18 +102,19 @@ namespace hex {
|
|||||||
ImGui::InputInt("Polynomial", &polynomial, 0, 0, ImGuiInputTextFlags_CharsHexadecimal);
|
ImGui::InputInt("Polynomial", &polynomial, 0, 0, ImGuiInputTextFlags_CharsHexadecimal);
|
||||||
if (ImGui::IsItemEdited()) this->m_shouldInvalidate = true;
|
if (ImGui::IsItemEdited()) this->m_shouldInvalidate = true;
|
||||||
|
|
||||||
ImGui::NewLine();
|
|
||||||
ImGui::Separator();
|
|
||||||
ImGui::NewLine();
|
ImGui::NewLine();
|
||||||
|
|
||||||
static u32 result = 0;
|
static u32 result = 0;
|
||||||
|
|
||||||
if (this->m_shouldInvalidate)
|
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];
|
char buffer[sizeof(result) * 2 + 1];
|
||||||
snprintf(buffer, sizeof(buffer), "%08X", result);
|
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;
|
break;
|
||||||
case 2: // MD4
|
case 2: // MD4
|
||||||
@@ -113,11 +122,15 @@ namespace hex {
|
|||||||
static std::array<u32, 4> result;
|
static std::array<u32, 4> result;
|
||||||
|
|
||||||
if (this->m_shouldInvalidate)
|
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];
|
char buffer[sizeof(result) * 2 + 1];
|
||||||
formatBigHexInt(result, buffer, sizeof(buffer));
|
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;
|
break;
|
||||||
case 3: // MD5
|
case 3: // MD5
|
||||||
@@ -125,11 +138,15 @@ namespace hex {
|
|||||||
static std::array<u32, 4> result = { 0 };
|
static std::array<u32, 4> result = { 0 };
|
||||||
|
|
||||||
if (this->m_shouldInvalidate)
|
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];
|
char buffer[sizeof(result) * 2 + 1];
|
||||||
formatBigHexInt(result, buffer, sizeof(buffer));
|
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;
|
break;
|
||||||
case 4: // SHA-1
|
case 4: // SHA-1
|
||||||
@@ -137,11 +154,15 @@ namespace hex {
|
|||||||
static std::array<u32, 5> result = { 0 };
|
static std::array<u32, 5> result = { 0 };
|
||||||
|
|
||||||
if (this->m_shouldInvalidate)
|
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];
|
char buffer[sizeof(result) * 2 + 1];
|
||||||
formatBigHexInt(result, buffer, sizeof(buffer));
|
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;
|
break;
|
||||||
case 5: // SHA-224
|
case 5: // SHA-224
|
||||||
@@ -149,11 +170,15 @@ namespace hex {
|
|||||||
static std::array<u32, 7> result = { 0 };
|
static std::array<u32, 7> result = { 0 };
|
||||||
|
|
||||||
if (this->m_shouldInvalidate)
|
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];
|
char buffer[sizeof(result) * 2 + 1];
|
||||||
formatBigHexInt(result, buffer, sizeof(buffer));
|
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;
|
break;
|
||||||
case 6: // SHA-256
|
case 6: // SHA-256
|
||||||
@@ -161,11 +186,15 @@ namespace hex {
|
|||||||
static std::array<u32, 8> result;
|
static std::array<u32, 8> result;
|
||||||
|
|
||||||
if (this->m_shouldInvalidate)
|
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];
|
char buffer[sizeof(result) * 2 + 1];
|
||||||
formatBigHexInt(result, buffer, sizeof(buffer));
|
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;
|
break;
|
||||||
case 7: // SHA-384
|
case 7: // SHA-384
|
||||||
@@ -173,11 +202,15 @@ namespace hex {
|
|||||||
static std::array<u32, 12> result;
|
static std::array<u32, 12> result;
|
||||||
|
|
||||||
if (this->m_shouldInvalidate)
|
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];
|
char buffer[sizeof(result) * 2 + 1];
|
||||||
formatBigHexInt(result, buffer, sizeof(buffer));
|
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;
|
break;
|
||||||
case 8: // SHA-512
|
case 8: // SHA-512
|
||||||
@@ -185,11 +218,15 @@ namespace hex {
|
|||||||
static std::array<u32, 16> result;
|
static std::array<u32, 16> result;
|
||||||
|
|
||||||
if (this->m_shouldInvalidate)
|
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];
|
char buffer[sizeof(result) * 2 + 1];
|
||||||
formatBigHexInt(result, buffer, sizeof(buffer));
|
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;
|
break;
|
||||||
}
|
}
|
||||||
@@ -204,10 +241,7 @@ namespace hex {
|
|||||||
}
|
}
|
||||||
|
|
||||||
void ViewHashes::createMenu() {
|
void ViewHashes::createMenu() {
|
||||||
if (ImGui::BeginMenu("View")) {
|
|
||||||
ImGui::MenuItem("Hash View", "", &this->m_windowOpen);
|
|
||||||
ImGui::EndMenu();
|
|
||||||
}
|
|
||||||
}
|
}
|
||||||
|
|
||||||
}
|
}
|
||||||
@@ -2,14 +2,32 @@
|
|||||||
|
|
||||||
namespace hex {
|
namespace hex {
|
||||||
|
|
||||||
ViewHelp::ViewHelp() {
|
ViewHelp::ViewHelp() : View("Help") {
|
||||||
|
this->getWindowOpenState() = true;
|
||||||
}
|
}
|
||||||
|
|
||||||
ViewHelp::~ViewHelp() {
|
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() {
|
void ViewHelp::drawAboutPopup() {
|
||||||
ImGui::SetNextWindowSizeConstraints(ImVec2(450, 300), ImVec2(450, 300));
|
ImGui::SetNextWindowSizeConstraints(ImVec2(450, 300), ImVec2(450, 300));
|
||||||
if (ImGui::BeginPopupModal("About", &this->m_aboutWindowOpen, ImGuiWindowFlags_NoResize)) {
|
if (ImGui::BeginPopupModal("About", &this->m_aboutWindowOpen, ImGuiWindowFlags_NoResize)) {
|
||||||
@@ -33,10 +51,13 @@ namespace hex {
|
|||||||
ImGui::BulletText("ImGui-Addons by gallickgunner");
|
ImGui::BulletText("ImGui-Addons by gallickgunner");
|
||||||
ImGui::BulletText("ImGuiColorTextEdit by BalazsJako");
|
ImGui::BulletText("ImGuiColorTextEdit by BalazsJako");
|
||||||
ImGui::BulletText("capstone by aquynh");
|
ImGui::BulletText("capstone by aquynh");
|
||||||
|
ImGui::BulletText("JSON for Modern C++ by nlohmann");
|
||||||
ImGui::NewLine();
|
ImGui::NewLine();
|
||||||
ImGui::BulletText("GNU libmagic");
|
ImGui::BulletText("GNU libmagic");
|
||||||
ImGui::BulletText("OpenSSL libcrypto");
|
ImGui::BulletText("OpenSSL libcrypto");
|
||||||
ImGui::BulletText("GLFW3");
|
ImGui::BulletText("GLFW3");
|
||||||
|
ImGui::BulletText("LLVM");
|
||||||
|
ImGui::BulletText("Python 3");
|
||||||
|
|
||||||
ImGui::EndChild();
|
ImGui::EndChild();
|
||||||
ImGui::PopStyleColor();
|
ImGui::PopStyleColor();
|
||||||
@@ -45,53 +66,38 @@ namespace hex {
|
|||||||
}
|
}
|
||||||
|
|
||||||
void ViewHelp::drawPatternHelpPopup() {
|
void ViewHelp::drawPatternHelpPopup() {
|
||||||
ImGui::SetNextWindowSizeConstraints(ImVec2(450, 300), ImVec2(450, 300));
|
if (!this->m_patternHelpWindowOpen) return;
|
||||||
|
|
||||||
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();
|
|
||||||
};
|
|
||||||
|
|
||||||
ImGui::SetNextWindowSizeConstraints(ImVec2(450, 300), ImVec2(2000, 1000));
|
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::Text("ImHex Pattern Language Cheat Sheet");
|
||||||
ImGui::Separator();
|
ImGui::Separator();
|
||||||
ImGui::NewLine();
|
ImGui::NewLine();
|
||||||
|
|
||||||
DrawTitle("Preprocessor directives");
|
drawTitle("Preprocessor directives");
|
||||||
ImGui::TextWrapped(
|
ImGui::TextWrapped(
|
||||||
"Preprocessor directives can be used to alter the code before it's being parsed. Supported are "
|
"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");
|
"#define to replace one string with another and #include to include a separate file");
|
||||||
DrawCodeSegment("preprocessor", 40,
|
drawCodeSegment("preprocessor",
|
||||||
"#define HEADER_OFFSET 0x100\n"
|
"#define HEADER_OFFSET 0x100\n"
|
||||||
"#include <cstdint.hexpat>\n"
|
"#include <cstdint.hexpat>\n"
|
||||||
"#include \"mypattern.hexpat\""
|
"#include \"mypattern.hexpat\""
|
||||||
);
|
);
|
||||||
|
|
||||||
DrawTitle("Pragma directives");
|
drawTitle("Pragma directives");
|
||||||
ImGui::TextWrapped(
|
ImGui::TextWrapped(
|
||||||
"Pragma directives are used to give ImHex additional information about the code being read. Currently "
|
"Pragma directives are used to give ImHex additional information about the code being read. Currently "
|
||||||
"the following directives are supported.");
|
"the following directives are supported.");
|
||||||
DrawCodeSegment("pragma", 30,
|
drawCodeSegment("pragma",
|
||||||
"// Allow this file to be loaded as pattern when a file identified as application/x-executable gets loaded"
|
"// 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"
|
"#pragma MIME application/x-executable\n"
|
||||||
);
|
);
|
||||||
|
|
||||||
DrawTitle("Built-in types");
|
drawTitle("Built-in types");
|
||||||
ImGui::TextWrapped(
|
ImGui::TextWrapped(
|
||||||
"The following built-in types are available for use");
|
"The following built-in types are available for use");
|
||||||
DrawCodeSegment("built-in", 80,
|
drawCodeSegment("built-in",
|
||||||
"u8, s8\n"
|
"u8, s8\n"
|
||||||
"u16, s16\n"
|
"u16, s16\n"
|
||||||
"u32, s32\n"
|
"u32, s32\n"
|
||||||
@@ -100,22 +106,24 @@ namespace hex {
|
|||||||
"float, double"
|
"float, double"
|
||||||
);
|
);
|
||||||
|
|
||||||
DrawTitle("Variables and Arrays");
|
drawTitle("Variables and Arrays");
|
||||||
ImGui::TextWrapped(
|
ImGui::TextWrapped(
|
||||||
"Normal variables as well as arrays are used to highlight and display values. "
|
"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 "
|
"It is possible to create arrays within structs and unions that use the value of a previously "
|
||||||
"declared variable as size.");
|
"declared variable as size. In order to override the native / globally set endianess setting, every "
|
||||||
DrawCodeSegment("vars arrays", 45,
|
"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"
|
"u32 variable;\n"
|
||||||
"s8 string[16];\n"
|
"s8 string[16];\n"
|
||||||
"u8 customSizedArray[variable];"
|
"u8 customSizedArray[variable];\n"
|
||||||
|
"be u32 bigEndianVariable;"
|
||||||
);
|
);
|
||||||
|
|
||||||
DrawTitle("Structs");
|
drawTitle("Structs");
|
||||||
ImGui::TextWrapped(
|
ImGui::TextWrapped(
|
||||||
"To bundle multiple variables together, a struct can be used. To insert padding bytes which won't show "
|
"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.");
|
"up in the pattern data view or be highlighted, use the padding[size] syntax.");
|
||||||
DrawCodeSegment("struct", 85,
|
drawCodeSegment("struct",
|
||||||
"struct Header {\n"
|
"struct Header {\n"
|
||||||
" u32 magic;\n"
|
" u32 magic;\n"
|
||||||
" u8 version;\n"
|
" u8 version;\n"
|
||||||
@@ -124,31 +132,31 @@ namespace hex {
|
|||||||
"};"
|
"};"
|
||||||
);
|
);
|
||||||
|
|
||||||
DrawTitle("Unions");
|
drawTitle("Unions");
|
||||||
ImGui::TextWrapped(
|
ImGui::TextWrapped(
|
||||||
"A union is used to make two or more variables occupy the same region of memory. "
|
"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.");
|
"The union will have the size of the biggest contained variable.");
|
||||||
DrawCodeSegment("union", 55,
|
drawCodeSegment("union",
|
||||||
"union Color {\n"
|
"union Color {\n"
|
||||||
" u32 rgba;\n"
|
" u32 rgba;\n"
|
||||||
" Components components;\n"
|
" Components components;\n"
|
||||||
"};"
|
"};"
|
||||||
);
|
);
|
||||||
|
|
||||||
DrawTitle("Pointers");
|
drawTitle("Pointers");
|
||||||
ImGui::TextWrapped(
|
ImGui::TextWrapped(
|
||||||
"\"Another possible type of member in structs and unions are pointers. They are variables"
|
"\"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. "
|
"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.");
|
"The leading type is treated as the data being pointed to and the trailing type as the size of the pointer.");
|
||||||
DrawCodeSegment("pointer", 55,
|
drawCodeSegment("pointer",
|
||||||
"Data *data : u16;"
|
"Data *data : u16;"
|
||||||
);
|
);
|
||||||
|
|
||||||
DrawTitle("Bitfields");
|
drawTitle("Bitfields");
|
||||||
ImGui::TextWrapped(
|
ImGui::TextWrapped(
|
||||||
"To decode values that are stored in fields that don't follow the typical 8 bit alignment, bitfields can be used. "
|
"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.");
|
"The size of these fields get specified in numbers of bits.");
|
||||||
DrawCodeSegment("bitfield", 70,
|
drawCodeSegment("bitfield",
|
||||||
"bitfield Permission {\n"
|
"bitfield Permission {\n"
|
||||||
" r : 1;\n"
|
" r : 1;\n"
|
||||||
" w : 1;\n"
|
" w : 1;\n"
|
||||||
@@ -156,67 +164,143 @@ namespace hex {
|
|||||||
"};"
|
"};"
|
||||||
);
|
);
|
||||||
|
|
||||||
DrawTitle("Enum");
|
drawTitle("Enum");
|
||||||
ImGui::TextWrapped(
|
ImGui::TextWrapped(
|
||||||
"If a value can only be a few specific values with special meaning, an enum can be used. "
|
"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. "
|
"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 "
|
"Entries can be listed with or without a value. The values start counting at zero and increase by one "
|
||||||
"for every next entry");
|
"for every next entry");
|
||||||
DrawCodeSegment("enum", 70,
|
drawCodeSegment("enum",
|
||||||
"enum OperatingSystem : u8 {\n"
|
"enum OperatingSystem : u8 {\n"
|
||||||
" Windows = 0x10,\n"
|
" Windows = 0x10,\n"
|
||||||
" MacOSX,\n"
|
" MacOSX,\n"
|
||||||
" Linux\n"
|
" Linux = 'L'\n"
|
||||||
"};"
|
"};"
|
||||||
);
|
);
|
||||||
|
|
||||||
DrawTitle("Using declarations");
|
drawTitle("Using declarations");
|
||||||
ImGui::TextWrapped(
|
ImGui::TextWrapped(
|
||||||
"A using declaration can be used to create type aliases for already existing types. This can be "
|
"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.");
|
"a built-in type, a struct, enum or another alias type.");
|
||||||
DrawCodeSegment("using", 15,
|
drawCodeSegment("using",
|
||||||
"using magic_t = u32;"
|
"using magic_t = u32;"
|
||||||
);
|
);
|
||||||
|
|
||||||
DrawTitle("Comments");
|
drawTitle("Comments");
|
||||||
ImGui::TextWrapped(
|
ImGui::TextWrapped(
|
||||||
"To create a comment the C // or /* */ syntax can be used. //-style comments end at the next new line "
|
"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 */.");
|
"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 single line comment\n\n"
|
||||||
"/* This is a\n"
|
"/* This is a\n"
|
||||||
"multiline comment */"
|
"multiline comment */"
|
||||||
);
|
);
|
||||||
|
|
||||||
DrawTitle("Variable placement");
|
drawTitle("Variable placement");
|
||||||
ImGui::TextWrapped(
|
ImGui::TextWrapped(
|
||||||
"In order to highlight bytes and displaying their value in the pattern data window, "
|
"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 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."
|
"a unsigned 32 bit variable named data and places it at offset 0x100."
|
||||||
);
|
);
|
||||||
DrawCodeSegment("var placement", 15, "u32 data @ 0x100;");
|
drawCodeSegment("var placement", "u32 data @ 0x100;");
|
||||||
ImGui::EndPopup();
|
|
||||||
}
|
}
|
||||||
|
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() {
|
void ViewHelp::createView() {
|
||||||
this->drawAboutPopup();
|
this->drawAboutPopup();
|
||||||
this->drawPatternHelpPopup();
|
this->drawPatternHelpPopup();
|
||||||
|
this->drawMathEvaluatorHelp();
|
||||||
}
|
}
|
||||||
|
|
||||||
void ViewHelp::createMenu() {
|
void ViewHelp::createMenu() {
|
||||||
if (ImGui::BeginMenu("Help")) {
|
if (ImGui::BeginMenu("Help")) {
|
||||||
if (ImGui::MenuItem("About", ""))
|
if (ImGui::MenuItem("About", "")) {
|
||||||
View::doLater([this]{
|
View::doLater([] { ImGui::OpenPopup("About"); });
|
||||||
ImGui::OpenPopup("About");
|
this->m_aboutWindowOpen = true;
|
||||||
this->m_aboutWindowOpen = true;
|
}
|
||||||
});
|
|
||||||
ImGui::Separator();
|
ImGui::Separator();
|
||||||
if (ImGui::MenuItem("Cheat Sheet", ""))
|
if (ImGui::MenuItem("Pattern Language Cheat Sheet", "")) {
|
||||||
View::doLater([this]{
|
this->m_patternHelpWindowOpen = true;
|
||||||
ImGui::OpenPopup("Cheat Sheet");
|
}
|
||||||
this->m_patternHelpWindowOpen = true;
|
if (ImGui::MenuItem("Calculator Cheat Sheet", "")) {
|
||||||
});
|
this->m_mathHelpWindowOpen = true;
|
||||||
|
}
|
||||||
ImGui::EndMenu();
|
ImGui::EndMenu();
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|||||||
@@ -5,10 +5,18 @@
|
|||||||
|
|
||||||
#include <GLFW/glfw3.h>
|
#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 {
|
namespace hex {
|
||||||
|
|
||||||
ViewHexEditor::ViewHexEditor(prv::Provider* &dataProvider, std::vector<lang::PatternData*> &patternData)
|
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 {
|
this->m_memoryEditor.ReadFn = [](const ImU8 *data, size_t off) -> ImU8 {
|
||||||
ViewHexEditor *_this = (ViewHexEditor *) data;
|
ViewHexEditor *_this = (ViewHexEditor *) data;
|
||||||
@@ -30,6 +38,7 @@ namespace hex {
|
|||||||
|
|
||||||
_this->m_dataProvider->write(off, &d, sizeof(ImU8));
|
_this->m_dataProvider->write(off, &d, sizeof(ImU8));
|
||||||
_this->postEvent(Events::DataChanged);
|
_this->postEvent(Events::DataChanged);
|
||||||
|
ProjectFile::markDirty();
|
||||||
};
|
};
|
||||||
|
|
||||||
this->m_memoryEditor.HighlightFn = [](const ImU8 *data, size_t off, bool next) -> bool {
|
this->m_memoryEditor.HighlightFn = [](const ImU8 *data, size_t off, bool next) -> bool {
|
||||||
@@ -56,6 +65,33 @@ namespace hex {
|
|||||||
if (filePath != nullptr)
|
if (filePath != nullptr)
|
||||||
this->openFile(filePath);
|
this->openFile(filePath);
|
||||||
});
|
});
|
||||||
|
|
||||||
|
View::subscribeEvent(Events::SelectionChangeRequest, [this](const void *userData) {
|
||||||
|
const Region ®ion = *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, ®ion);
|
||||||
|
});
|
||||||
|
|
||||||
|
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() {
|
ViewHexEditor::~ViewHexEditor() {
|
||||||
@@ -65,12 +101,9 @@ namespace hex {
|
|||||||
}
|
}
|
||||||
|
|
||||||
void ViewHexEditor::createView() {
|
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();
|
size_t dataSize = (this->m_dataProvider == nullptr || !this->m_dataProvider->isReadable()) ? 0x00 : this->m_dataProvider->getSize();
|
||||||
|
|
||||||
this->m_memoryEditor.DrawWindow("Hex Editor", this, dataSize, dataSize == 0 ? 0x00 : this->m_dataProvider->getBaseAddress());
|
this->m_memoryEditor.DrawWindow("Hex Editor", &this->getWindowOpenState(), this, dataSize, dataSize == 0 ? 0x00 : this->m_dataProvider->getBaseAddress());
|
||||||
|
|
||||||
if (dataSize != 0x00) {
|
if (dataSize != 0x00) {
|
||||||
ImGui::Begin("Hex Editor");
|
ImGui::Begin("Hex Editor");
|
||||||
@@ -81,8 +114,8 @@ namespace hex {
|
|||||||
if (ImGui::ArrowButton("prevPage", ImGuiDir_Left)) {
|
if (ImGui::ArrowButton("prevPage", ImGuiDir_Left)) {
|
||||||
this->m_dataProvider->setCurrentPage(this->m_dataProvider->getCurrentPage() - 1);
|
this->m_dataProvider->setCurrentPage(this->m_dataProvider->getCurrentPage() - 1);
|
||||||
|
|
||||||
size_t dataPreviewStart = std::min(this->m_memoryEditor.DataPreviewAddr, this->m_memoryEditor.DataPreviewAddrEnd);
|
Region dataPreview = { std::min(this->m_memoryEditor.DataPreviewAddr, this->m_memoryEditor.DataPreviewAddrEnd), 1 };
|
||||||
View::postEvent(Events::ByteSelected, &dataPreviewStart);
|
View::postEvent(Events::RegionSelected, &dataPreview);
|
||||||
}
|
}
|
||||||
|
|
||||||
ImGui::SameLine();
|
ImGui::SameLine();
|
||||||
@@ -90,8 +123,8 @@ namespace hex {
|
|||||||
if (ImGui::ArrowButton("nextPage", ImGuiDir_Right)) {
|
if (ImGui::ArrowButton("nextPage", ImGuiDir_Right)) {
|
||||||
this->m_dataProvider->setCurrentPage(this->m_dataProvider->getCurrentPage() + 1);
|
this->m_dataProvider->setCurrentPage(this->m_dataProvider->getCurrentPage() + 1);
|
||||||
|
|
||||||
size_t dataPreviewStart = std::min(this->m_memoryEditor.DataPreviewAddr, this->m_memoryEditor.DataPreviewAddrEnd);
|
Region dataPreview = { std::min(this->m_memoryEditor.DataPreviewAddr, this->m_memoryEditor.DataPreviewAddrEnd), 1 };
|
||||||
View::postEvent(Events::ByteSelected, &dataPreviewStart);
|
View::postEvent(Events::RegionSelected, &dataPreview);
|
||||||
}
|
}
|
||||||
|
|
||||||
ImGui::End();
|
ImGui::End();
|
||||||
@@ -100,18 +133,373 @@ namespace hex {
|
|||||||
this->drawGotoPopup();
|
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)) {
|
if (this->m_fileBrowser.showFileDialog("Open File", imgui_addons::ImGuiFileBrowser::DialogMode::OPEN)) {
|
||||||
this->openFile(this->m_fileBrowser.selected_path);
|
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) {
|
void ViewHexEditor::openFile(std::string path) {
|
||||||
if (this->m_dataProvider != nullptr)
|
if (this->m_dataProvider != nullptr)
|
||||||
delete this->m_dataProvider;
|
delete this->m_dataProvider;
|
||||||
|
|
||||||
this->m_dataProvider = new prv::FileProvider(path);
|
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);
|
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() {
|
void ViewHexEditor::copyBytes() {
|
||||||
@@ -137,7 +525,7 @@ namespace hex {
|
|||||||
|
|
||||||
size_t copySize = (end - start) + 1;
|
size_t copySize = (end - start) + 1;
|
||||||
|
|
||||||
std::string buffer;
|
std::string buffer(copySize, 0x00);
|
||||||
buffer.reserve(copySize + 1);
|
buffer.reserve(copySize + 1);
|
||||||
this->m_dataProvider->read(start, buffer.data(), copySize);
|
this->m_dataProvider->read(start, buffer.data(), copySize);
|
||||||
|
|
||||||
@@ -352,92 +740,6 @@ R"(
|
|||||||
ImGui::SetClipboardText(str.c_str());
|
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) {
|
static std::vector<std::pair<u64, u64>> findString(prv::Provider* &provider, std::string string) {
|
||||||
std::vector<std::pair<u64, u64>> results;
|
std::vector<std::pair<u64, u64>> results;
|
||||||
|
|
||||||
@@ -446,7 +748,7 @@ R"(
|
|||||||
std::vector<u8> buffer(1024, 0x00);
|
std::vector<u8> buffer(1024, 0x00);
|
||||||
size_t dataSize = provider->getSize();
|
size_t dataSize = provider->getSize();
|
||||||
for (u64 offset = 0; offset < dataSize; offset += 1024) {
|
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);
|
provider->read(offset, buffer.data(), usedBufferSize);
|
||||||
|
|
||||||
for (u64 i = 0; i < usedBufferSize; i++) {
|
for (u64 i = 0; i < usedBufferSize; i++) {
|
||||||
@@ -484,7 +786,7 @@ R"(
|
|||||||
std::vector<u8> buffer(1024, 0x00);
|
std::vector<u8> buffer(1024, 0x00);
|
||||||
size_t dataSize = provider->getSize();
|
size_t dataSize = provider->getSize();
|
||||||
for (u64 offset = 0; offset < dataSize; offset += 1024) {
|
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);
|
provider->read(offset, buffer.data(), usedBufferSize);
|
||||||
|
|
||||||
for (u64 i = 0; i < usedBufferSize; i++) {
|
for (u64 i = 0; i < usedBufferSize; i++) {
|
||||||
|
|||||||
@@ -2,7 +2,7 @@
|
|||||||
|
|
||||||
#include "providers/provider.hpp"
|
#include "providers/provider.hpp"
|
||||||
|
|
||||||
#include "utils.hpp"
|
#include "helpers/utils.hpp"
|
||||||
|
|
||||||
#include <cstring>
|
#include <cstring>
|
||||||
#include <cmath>
|
#include <cmath>
|
||||||
@@ -15,7 +15,7 @@
|
|||||||
namespace hex {
|
namespace hex {
|
||||||
|
|
||||||
ViewInformation::ViewInformation(prv::Provider* &dataProvider)
|
ViewInformation::ViewInformation(prv::Provider* &dataProvider)
|
||||||
: View(), m_dataProvider(dataProvider) {
|
: View("Information"), m_dataProvider(dataProvider) {
|
||||||
View::subscribeEvent(Events::DataChanged, [this](const void*) {
|
View::subscribeEvent(Events::DataChanged, [this](const void*) {
|
||||||
this->m_dataValid = false;
|
this->m_dataValid = false;
|
||||||
this->m_highestBlockEntropy = 0;
|
this->m_highestBlockEntropy = 0;
|
||||||
@@ -47,10 +47,7 @@ namespace hex {
|
|||||||
}
|
}
|
||||||
|
|
||||||
void ViewInformation::createView() {
|
void ViewInformation::createView() {
|
||||||
if (!this->m_windowOpen)
|
if (ImGui::Begin("Data Information", &this->getWindowOpenState(), ImGuiWindowFlags_NoCollapse)) {
|
||||||
return;
|
|
||||||
|
|
||||||
if (ImGui::Begin("Data Information", &this->m_windowOpen)) {
|
|
||||||
ImGui::BeginChild("##scrolling", ImVec2(0, 0), false, ImGuiWindowFlags_NoMove | ImGuiWindowFlags_NoNav);
|
ImGui::BeginChild("##scrolling", ImVec2(0, 0), false, ImGuiWindowFlags_NoMove | ImGuiWindowFlags_NoNav);
|
||||||
|
|
||||||
if (this->m_dataProvider != nullptr && this->m_dataProvider->isReadable()) {
|
if (this->m_dataProvider != nullptr && this->m_dataProvider->isReadable()) {
|
||||||
@@ -66,7 +63,7 @@ namespace hex {
|
|||||||
|
|
||||||
for (u64 i = 0; i < this->m_dataProvider->getSize(); i += this->m_blockSize) {
|
for (u64 i = 0; i < this->m_dataProvider->getSize(); i += this->m_blockSize) {
|
||||||
std::array<float, 256> blockValueCounts = { 0 };
|
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++) {
|
for (size_t j = 0; j < this->m_blockSize; j++) {
|
||||||
blockValueCounts[buffer[j]]++;
|
blockValueCounts[buffer[j]]++;
|
||||||
@@ -174,7 +171,7 @@ namespace hex {
|
|||||||
|
|
||||||
ImGui::NewLine();
|
ImGui::NewLine();
|
||||||
|
|
||||||
ImGui::LabelText("Block size", "2048 blocks à %lu bytes", this->m_blockSize);
|
ImGui::LabelText("Block size", "2048 blocks of %lu bytes", this->m_blockSize);
|
||||||
ImGui::LabelText("Average entropy", "%.8f", this->m_averageEntropy);
|
ImGui::LabelText("Average entropy", "%.8f", this->m_averageEntropy);
|
||||||
ImGui::LabelText("Highest entropy block", "%.8f", this->m_highestBlockEntropy);
|
ImGui::LabelText("Highest entropy block", "%.8f", this->m_highestBlockEntropy);
|
||||||
|
|
||||||
@@ -192,10 +189,7 @@ namespace hex {
|
|||||||
}
|
}
|
||||||
|
|
||||||
void ViewInformation::createMenu() {
|
void ViewInformation::createMenu() {
|
||||||
if (ImGui::BeginMenu("View")) {
|
|
||||||
ImGui::MenuItem("Data Information View", "", &this->m_windowOpen);
|
|
||||||
ImGui::EndMenu();
|
|
||||||
}
|
|
||||||
}
|
}
|
||||||
|
|
||||||
}
|
}
|
||||||
91
source/views/view_patches.cpp
Normal file
91
source/views/view_patches.cpp
Normal 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() {
|
||||||
|
|
||||||
|
}
|
||||||
|
|
||||||
|
}
|
||||||
@@ -5,7 +5,9 @@
|
|||||||
#include "lang/lexer.hpp"
|
#include "lang/lexer.hpp"
|
||||||
#include "lang/validator.hpp"
|
#include "lang/validator.hpp"
|
||||||
#include "lang/evaluator.hpp"
|
#include "lang/evaluator.hpp"
|
||||||
#include "utils.hpp"
|
|
||||||
|
#include "helpers/project_file_handler.hpp"
|
||||||
|
#include "helpers/utils.hpp"
|
||||||
|
|
||||||
#include <magic.h>
|
#include <magic.h>
|
||||||
|
|
||||||
@@ -16,7 +18,7 @@ namespace hex {
|
|||||||
static TextEditor::LanguageDefinition langDef;
|
static TextEditor::LanguageDefinition langDef;
|
||||||
if (!initialized) {
|
if (!initialized) {
|
||||||
static const char* const keywords[] = {
|
static const char* const keywords[] = {
|
||||||
"using", "struct", "union", "enum", "bitfield"
|
"using", "struct", "union", "enum", "bitfield", "be", "le"
|
||||||
};
|
};
|
||||||
for (auto& k : keywords)
|
for (auto& k : keywords)
|
||||||
langDef.mKeywords.insert(k);
|
langDef.mKeywords.insert(k);
|
||||||
@@ -48,6 +50,8 @@ namespace hex {
|
|||||||
paletteIndex = TextEditor::PaletteIndex::Identifier;
|
paletteIndex = TextEditor::PaletteIndex::Identifier;
|
||||||
else if (TokenizeCStyleNumber(inBegin, inEnd, outBegin, outEnd))
|
else if (TokenizeCStyleNumber(inBegin, inEnd, outBegin, outEnd))
|
||||||
paletteIndex = TextEditor::PaletteIndex::Number;
|
paletteIndex = TextEditor::PaletteIndex::Number;
|
||||||
|
else if (TokenizeCStyleCharacterLiteral(inBegin, inEnd, outBegin, outEnd))
|
||||||
|
paletteIndex = TextEditor::PaletteIndex::CharLiteral;
|
||||||
|
|
||||||
return paletteIndex != TextEditor::PaletteIndex::Max;
|
return paletteIndex != TextEditor::PaletteIndex::Max;
|
||||||
};
|
};
|
||||||
@@ -69,14 +73,32 @@ namespace hex {
|
|||||||
|
|
||||||
|
|
||||||
ViewPattern::ViewPattern(prv::Provider* &dataProvider, std::vector<lang::PatternData*> &patternData)
|
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.SetLanguageDefinition(PatternLanguage());
|
||||||
this->m_textEditor.SetShowWhitespaces(false);
|
this->m_textEditor.SetShowWhitespaces(false);
|
||||||
|
|
||||||
View::subscribeEvent(Events::DataChanged, [this](const void* userData) {
|
View::subscribeEvent(Events::ProjectFileStore, [this](const void*) {
|
||||||
lang::Preprocessor preprocessor;
|
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::string magicFiles;
|
||||||
|
|
||||||
std::error_code error;
|
std::error_code error;
|
||||||
@@ -85,7 +107,10 @@ namespace hex {
|
|||||||
magicFiles += entry.path().string() + MAGIC_PATH_SEPARATOR;
|
magicFiles += entry.path().string() + MAGIC_PATH_SEPARATOR;
|
||||||
}
|
}
|
||||||
|
|
||||||
std::vector<u8> buffer(std::min(this->m_dataProvider->getSize(), size_t(0xFF'FFFF)), 0x00);
|
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());
|
this->m_dataProvider->read(0, buffer.data(), buffer.size());
|
||||||
|
|
||||||
std::string mimeType;
|
std::string mimeType;
|
||||||
@@ -104,10 +129,11 @@ namespace hex {
|
|||||||
}
|
}
|
||||||
return !std::all_of(value.begin(), value.end(), isspace) && !value.ends_with('\n') && !value.ends_with('\r');
|
return !std::all_of(value.begin(), value.end(), isspace) && !value.ends_with('\n') && !value.ends_with('\r');
|
||||||
});
|
});
|
||||||
preprocessor.addDefaultPragramHandlers();
|
preprocessor.addDefaultPragmaHandlers();
|
||||||
|
|
||||||
|
|
||||||
for (auto &entry : std::filesystem::directory_iterator("patterns")) {
|
std::error_code errorCode;
|
||||||
|
for (auto &entry : std::filesystem::directory_iterator("patterns", errorCode)) {
|
||||||
if (!entry.is_regular_file())
|
if (!entry.is_regular_file())
|
||||||
continue;
|
continue;
|
||||||
|
|
||||||
@@ -128,7 +154,7 @@ namespace hex {
|
|||||||
|
|
||||||
if (foundCorrectType) {
|
if (foundCorrectType) {
|
||||||
this->m_possiblePatternFile = entry.path();
|
this->m_possiblePatternFile = entry.path();
|
||||||
ImGui::OpenPopup("Accept Pattern");
|
View::doLater([] { ImGui::OpenPopup("Accept Pattern"); });
|
||||||
ImGui::SetNextWindowSize(ImVec2(200, 100));
|
ImGui::SetNextWindowSize(ImVec2(200, 100));
|
||||||
break;
|
break;
|
||||||
}
|
}
|
||||||
@@ -137,7 +163,8 @@ namespace hex {
|
|||||||
}
|
}
|
||||||
|
|
||||||
ViewPattern::~ViewPattern() {
|
ViewPattern::~ViewPattern() {
|
||||||
|
View::unsubscribeEvent(Events::ProjectFileStore);
|
||||||
|
View::unsubscribeEvent(Events::ProjectFileLoad);
|
||||||
}
|
}
|
||||||
|
|
||||||
void ViewPattern::createMenu() {
|
void ViewPattern::createMenu() {
|
||||||
@@ -147,18 +174,10 @@ namespace hex {
|
|||||||
}
|
}
|
||||||
ImGui::EndMenu();
|
ImGui::EndMenu();
|
||||||
}
|
}
|
||||||
|
|
||||||
if (ImGui::BeginMenu("View")) {
|
|
||||||
ImGui::MenuItem("Pattern View", "", &this->m_windowOpen);
|
|
||||||
ImGui::EndMenu();
|
|
||||||
}
|
|
||||||
}
|
}
|
||||||
|
|
||||||
void ViewPattern::createView() {
|
void ViewPattern::createView() {
|
||||||
if (!this->m_windowOpen)
|
if (ImGui::Begin("Pattern", &this->getWindowOpenState(), ImGuiWindowFlags_None | ImGuiWindowFlags_NoCollapse)) {
|
||||||
return;
|
|
||||||
|
|
||||||
if (ImGui::Begin("Pattern", &this->m_windowOpen, ImGuiWindowFlags_None)) {
|
|
||||||
if (this->m_dataProvider != nullptr && this->m_dataProvider->isAvailable()) {
|
if (this->m_dataProvider != nullptr && this->m_dataProvider->isAvailable()) {
|
||||||
this->m_textEditor.Render("Pattern");
|
this->m_textEditor.Render("Pattern");
|
||||||
|
|
||||||
@@ -239,39 +258,44 @@ namespace hex {
|
|||||||
|
|
||||||
void ViewPattern::parsePattern(char *buffer) {
|
void ViewPattern::parsePattern(char *buffer) {
|
||||||
this->clearPatternData();
|
this->clearPatternData();
|
||||||
|
this->m_textEditor.SetErrorMarkers({ });
|
||||||
this->postEvent(Events::PatternChanged);
|
this->postEvent(Events::PatternChanged);
|
||||||
|
|
||||||
hex::lang::Preprocessor preprocessor;
|
hex::lang::Preprocessor preprocessor;
|
||||||
std::endian dataEndianess = std::endian::native;
|
std::endian defaultDataEndianess = std::endian::native;
|
||||||
|
|
||||||
preprocessor.addPragmaHandler("endian", [&dataEndianess](std::string value) {
|
preprocessor.addPragmaHandler("endian", [&defaultDataEndianess](std::string value) {
|
||||||
if (value == "big") {
|
if (value == "big") {
|
||||||
dataEndianess = std::endian::big;
|
defaultDataEndianess = std::endian::big;
|
||||||
return true;
|
return true;
|
||||||
} else if (value == "little") {
|
} else if (value == "little") {
|
||||||
dataEndianess = std::endian::little;
|
defaultDataEndianess = std::endian::little;
|
||||||
return true;
|
return true;
|
||||||
} else if (value == "native") {
|
} else if (value == "native") {
|
||||||
dataEndianess = std::endian::native;
|
defaultDataEndianess = std::endian::native;
|
||||||
return true;
|
return true;
|
||||||
} else
|
} else
|
||||||
return false;
|
return false;
|
||||||
});
|
});
|
||||||
preprocessor.addDefaultPragramHandlers();
|
preprocessor.addDefaultPragmaHandlers();
|
||||||
|
|
||||||
auto [preprocessingResult, preprocesedCode] = preprocessor.preprocess(buffer);
|
auto [preprocessingResult, preprocesedCode] = preprocessor.preprocess(buffer);
|
||||||
if (preprocessingResult.failed())
|
if (preprocessingResult.failed()) {
|
||||||
|
this->m_textEditor.SetErrorMarkers({ preprocessor.getError() });
|
||||||
return;
|
return;
|
||||||
|
}
|
||||||
|
|
||||||
hex::lang::Lexer lexer;
|
hex::lang::Lexer lexer;
|
||||||
auto [lexResult, tokens] = lexer.lex(preprocesedCode);
|
auto [lexResult, tokens] = lexer.lex(preprocesedCode);
|
||||||
if (lexResult.failed()) {
|
if (lexResult.failed()) {
|
||||||
|
this->m_textEditor.SetErrorMarkers({ lexer.getError() });
|
||||||
return;
|
return;
|
||||||
}
|
}
|
||||||
|
|
||||||
hex::lang::Parser parser;
|
hex::lang::Parser parser;
|
||||||
auto [parseResult, ast] = parser.parse(tokens);
|
auto [parseResult, ast] = parser.parse(tokens);
|
||||||
if (parseResult.failed()) {
|
if (parseResult.failed()) {
|
||||||
|
this->m_textEditor.SetErrorMarkers({ parser.getError() });
|
||||||
return;
|
return;
|
||||||
}
|
}
|
||||||
|
|
||||||
@@ -280,12 +304,14 @@ namespace hex {
|
|||||||
hex::lang::Validator validator;
|
hex::lang::Validator validator;
|
||||||
auto validatorResult = validator.validate(ast);
|
auto validatorResult = validator.validate(ast);
|
||||||
if (!validatorResult) {
|
if (!validatorResult) {
|
||||||
|
this->m_textEditor.SetErrorMarkers({ validator.getError() });
|
||||||
return;
|
return;
|
||||||
}
|
}
|
||||||
|
|
||||||
hex::lang::Evaluator evaluator(this->m_dataProvider, dataEndianess);
|
hex::lang::Evaluator evaluator(this->m_dataProvider, defaultDataEndianess);
|
||||||
auto [evaluateResult, patternData] = evaluator.evaluate(ast);
|
auto [evaluateResult, patternData] = evaluator.evaluate(ast);
|
||||||
if (evaluateResult.failed()) {
|
if (evaluateResult.failed()) {
|
||||||
|
this->m_textEditor.SetErrorMarkers({ evaluator.getError() });
|
||||||
return;
|
return;
|
||||||
}
|
}
|
||||||
this->m_patternData = patternData;
|
this->m_patternData = patternData;
|
||||||
|
|||||||
@@ -6,7 +6,7 @@
|
|||||||
namespace hex {
|
namespace hex {
|
||||||
|
|
||||||
ViewPatternData::ViewPatternData(prv::Provider* &dataProvider, std::vector<lang::PatternData*> &patternData)
|
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->subscribeEvent(Events::PatternChanged, [this](auto data) {
|
||||||
this->m_sortedPatternData.clear();
|
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) {
|
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 | ImGuiTableFlags_NoBordersInBody)) {
|
if (ImGui::BeginTable("##patterndatatable", 6, ImGuiTableFlags_Borders | ImGuiTableFlags_Resizable | ImGuiTableFlags_Sortable | ImGuiTableFlags_Reorderable | ImGuiTableFlags_RowBg | ImGuiTableFlags_ScrollY)) {
|
||||||
ImGui::TableSetupColumn("Color", 0, -1, ImGui::GetID("color"));
|
ImGui::TableSetupScrollFreeze(0, 1);
|
||||||
ImGui::TableSetupColumn("Name", 0, -1, ImGui::GetID("name"));
|
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("Offset", 0, -1, ImGui::GetID("offset"));
|
||||||
ImGui::TableSetupColumn("Size", 0, -1, ImGui::GetID("size"));
|
ImGui::TableSetupColumn("Size", 0, -1, ImGui::GetID("size"));
|
||||||
ImGui::TableSetupColumn("Type", 0, -1, ImGui::GetID("type"));
|
ImGui::TableSetupColumn("Type", 0, -1, ImGui::GetID("type"));
|
||||||
@@ -48,12 +49,7 @@ namespace hex {
|
|||||||
}
|
}
|
||||||
|
|
||||||
void ViewPatternData::createView() {
|
void ViewPatternData::createView() {
|
||||||
if (!this->m_windowOpen)
|
if (ImGui::Begin("Pattern Data", &this->getWindowOpenState(), ImGuiWindowFlags_NoCollapse)) {
|
||||||
return;
|
|
||||||
|
|
||||||
if (ImGui::Begin("Pattern Data", &this->m_windowOpen)) {
|
|
||||||
ImGui::BeginChild("##scrolling", ImVec2(0, 0), false, ImGuiWindowFlags_NoMove | ImGuiWindowFlags_NoNav);
|
|
||||||
|
|
||||||
if (this->m_dataProvider != nullptr && this->m_dataProvider->isReadable()) {
|
if (this->m_dataProvider != nullptr && this->m_dataProvider->isReadable()) {
|
||||||
|
|
||||||
if (beginPatternDataTable(this->m_dataProvider, this->m_patternData, this->m_sortedPatternData)) {
|
if (beginPatternDataTable(this->m_dataProvider, this->m_patternData, this->m_sortedPatternData)) {
|
||||||
@@ -69,17 +65,12 @@ namespace hex {
|
|||||||
}
|
}
|
||||||
|
|
||||||
}
|
}
|
||||||
|
|
||||||
ImGui::EndChild();
|
|
||||||
}
|
}
|
||||||
ImGui::End();
|
ImGui::End();
|
||||||
}
|
}
|
||||||
|
|
||||||
void ViewPatternData::createMenu() {
|
void ViewPatternData::createMenu() {
|
||||||
if (ImGui::BeginMenu("View")) {
|
|
||||||
ImGui::MenuItem("Data View", "", &this->m_windowOpen);
|
|
||||||
ImGui::EndMenu();
|
|
||||||
}
|
|
||||||
}
|
}
|
||||||
|
|
||||||
}
|
}
|
||||||
@@ -1,12 +1,17 @@
|
|||||||
#include "views/view_strings.hpp"
|
#include "views/view_strings.hpp"
|
||||||
|
|
||||||
#include "providers/provider.hpp"
|
#include "providers/provider.hpp"
|
||||||
|
#include "helpers/utils.hpp"
|
||||||
|
|
||||||
#include <cstring>
|
#include <cstring>
|
||||||
|
|
||||||
|
#include <llvm/Demangle/Demangle.h>
|
||||||
|
|
||||||
|
using namespace std::literals::string_literals;
|
||||||
|
|
||||||
namespace hex {
|
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*){
|
View::subscribeEvent(Events::DataChanged, [this](const void*){
|
||||||
this->m_foundStrings.clear();
|
this->m_foundStrings.clear();
|
||||||
});
|
});
|
||||||
@@ -20,22 +25,40 @@ namespace hex {
|
|||||||
delete[] this->m_filter;
|
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) {
|
if (this->m_shouldInvalidate) {
|
||||||
this->m_shouldInvalidate = false;
|
this->m_shouldInvalidate = false;
|
||||||
|
|
||||||
this->m_foundStrings.clear();
|
this->m_foundStrings.clear();
|
||||||
|
|
||||||
|
|
||||||
std::vector<u8> buffer(1024, 0x00);
|
std::vector<u8> buffer(1024, 0x00);
|
||||||
u32 foundCharacters = 0;
|
u32 foundCharacters = 0;
|
||||||
for (u64 offset = 0; offset < this->m_dataProvider->getSize(); offset += buffer.size()) {
|
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)
|
if (buffer[i] >= 0x20 && buffer[i] <= 0x7E)
|
||||||
foundCharacters++;
|
foundCharacters++;
|
||||||
else {
|
else {
|
||||||
@@ -58,7 +81,7 @@ 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 (this->m_dataProvider != nullptr && this->m_dataProvider->isReadable()) {
|
||||||
if (ImGui::InputInt("Minimum length", &this->m_minimumLength, 1, 0))
|
if (ImGui::InputInt("Minimum length", &this->m_minimumLength, 1, 0))
|
||||||
this->m_shouldInvalidate = true;
|
this->m_shouldInvalidate = true;
|
||||||
@@ -70,11 +93,10 @@ namespace hex {
|
|||||||
ImGui::Separator();
|
ImGui::Separator();
|
||||||
ImGui::NewLine();
|
ImGui::NewLine();
|
||||||
|
|
||||||
ImGui::BeginChild("##scrolling", ImVec2(0, 0), false, ImGuiWindowFlags_NoMove | ImGuiWindowFlags_NoNav);
|
|
||||||
|
|
||||||
if (ImGui::BeginTable("##strings", 3,
|
if (ImGui::BeginTable("##strings", 3,
|
||||||
ImGuiTableFlags_Borders | ImGuiTableFlags_Resizable | ImGuiTableFlags_Sortable |
|
ImGuiTableFlags_Borders | ImGuiTableFlags_Resizable | ImGuiTableFlags_Sortable |
|
||||||
ImGuiTableFlags_Reorderable | ImGuiTableFlags_RowBg | ImGuiTableFlags_NoBordersInBody)) {
|
ImGuiTableFlags_Reorderable | ImGuiTableFlags_RowBg | ImGuiTableFlags_ScrollY)) {
|
||||||
|
ImGui::TableSetupScrollFreeze(0, 1);
|
||||||
ImGui::TableSetupColumn("Offset", 0, -1, ImGui::GetID("offset"));
|
ImGui::TableSetupColumn("Offset", 0, -1, ImGui::GetID("offset"));
|
||||||
ImGui::TableSetupColumn("Size", 0, -1, ImGui::GetID("size"));
|
ImGui::TableSetupColumn("Size", 0, -1, ImGui::GetID("size"));
|
||||||
ImGui::TableSetupColumn("String", 0, -1, ImGui::GetID("string"));
|
ImGui::TableSetupColumn("String", 0, -1, ImGui::GetID("string"));
|
||||||
@@ -120,8 +142,16 @@ namespace hex {
|
|||||||
foundString.string.find(this->m_filter) == std::string::npos)
|
foundString.string.find(this->m_filter) == std::string::npos)
|
||||||
continue;
|
continue;
|
||||||
|
|
||||||
ImGui::TableNextRow(ImGuiTableRowFlags_Headers);
|
ImGui::TableNextRow();
|
||||||
ImGui::TableNextColumn();
|
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::Text("0x%08lx : 0x%08lx", foundString.offset, foundString.offset + foundString.size);
|
||||||
ImGui::TableNextColumn();
|
ImGui::TableNextColumn();
|
||||||
ImGui::Text("0x%04lx", foundString.size);
|
ImGui::Text("0x%04lx", foundString.size);
|
||||||
@@ -133,18 +163,26 @@ namespace hex {
|
|||||||
|
|
||||||
ImGui::EndTable();
|
ImGui::EndTable();
|
||||||
}
|
}
|
||||||
|
|
||||||
ImGui::EndChild();
|
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
ImGui::End();
|
ImGui::End();
|
||||||
}
|
|
||||||
|
|
||||||
void ViewStrings::createMenu() {
|
if (ImGui::BeginPopup("Demangled Name")) {
|
||||||
if (ImGui::BeginMenu("View")) {
|
if (ImGui::BeginChild("##scrolling", ImVec2(500, 150))) {
|
||||||
ImGui::MenuItem("Strings View", "", &this->m_windowOpen);
|
ImGui::Text("Demangled Name");
|
||||||
ImGui::EndMenu();
|
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() {
|
||||||
|
|
||||||
|
}
|
||||||
|
|
||||||
}
|
}
|
||||||
@@ -1,17 +1,19 @@
|
|||||||
#include "views/view_tools.hpp"
|
#include "views/view_tools.hpp"
|
||||||
|
|
||||||
#include <cxxabi.h>
|
|
||||||
#include <cstring>
|
#include <cstring>
|
||||||
#include <regex>
|
#include <regex>
|
||||||
|
#include <optional>
|
||||||
|
|
||||||
|
#include "providers/provider.hpp"
|
||||||
|
#include "helpers/utils.hpp"
|
||||||
|
|
||||||
|
#include <llvm/Demangle/Demangle.h>
|
||||||
|
|
||||||
namespace hex {
|
namespace hex {
|
||||||
|
|
||||||
ViewTools::ViewTools() {
|
ViewTools::ViewTools(hex::prv::Provider* &provider) : View("Tools"), m_dataProvider(provider) {
|
||||||
this->m_mangledBuffer = new char[0xF'FFFF];
|
this->m_mangledBuffer = new char[0xF'FFFF];
|
||||||
this->m_demangledName = static_cast<char*>(malloc(8));
|
|
||||||
|
|
||||||
std::memset(this->m_mangledBuffer, 0x00, 0xF'FFFF);
|
std::memset(this->m_mangledBuffer, 0x00, 0xF'FFFF);
|
||||||
strcpy(this->m_demangledName, "< ??? >");
|
|
||||||
|
|
||||||
this->m_regexInput = new char[0xF'FFFF];
|
this->m_regexInput = new char[0xF'FFFF];
|
||||||
this->m_regexPattern = 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_regexInput, 0x00, 0xF'FFFF);
|
||||||
std::memset(this->m_regexPattern, 0x00, 0xF'FFFF);
|
std::memset(this->m_regexPattern, 0x00, 0xF'FFFF);
|
||||||
std::memset(this->m_replacePattern, 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() {
|
ViewTools::~ViewTools() {
|
||||||
delete[] this->m_mangledBuffer;
|
delete[] this->m_mangledBuffer;
|
||||||
free(this->m_demangledName);
|
|
||||||
|
|
||||||
delete[] this->m_regexInput;
|
delete[] this->m_regexInput;
|
||||||
delete[] this->m_regexPattern;
|
delete[] this->m_regexPattern;
|
||||||
}
|
delete[] this->m_replacePattern;
|
||||||
|
|
||||||
static std::string toASCIITableString(char c) {
|
delete[] this->m_mathInput;
|
||||||
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;
|
|
||||||
}
|
|
||||||
}
|
}
|
||||||
|
|
||||||
void ViewTools::drawDemangler() {
|
void ViewTools::drawDemangler() {
|
||||||
if (ImGui::CollapsingHeader("Itanium demangler")) {
|
if (ImGui::CollapsingHeader("Itanium/MSVC demangler")) {
|
||||||
if (ImGui::InputText("Mangled name", this->m_mangledBuffer, 0xF'FFFF)) {
|
if (ImGui::InputText("Mangled name", this->m_mangledBuffer, 0xF'FFFF)) {
|
||||||
size_t length = 0;
|
this->m_demangledName = llvm::demangle(this->m_mangledBuffer);
|
||||||
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, "< ??? >");
|
|
||||||
}
|
|
||||||
}
|
}
|
||||||
|
|
||||||
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();
|
ImGui::NewLine();
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
@@ -127,7 +120,7 @@ namespace hex {
|
|||||||
ImGui::Text("0x%02x", i + 32 * tablePart);
|
ImGui::Text("0x%02x", i + 32 * tablePart);
|
||||||
|
|
||||||
ImGui::TableNextColumn();
|
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);
|
ImGui::TableSetBgColor(ImGuiTableBgTarget_RowBg0, ((rowCount % 2) == 0) ? 0xFF101010 : 0xFF303030);
|
||||||
|
|
||||||
@@ -167,21 +160,144 @@ namespace hex {
|
|||||||
|
|
||||||
void ViewTools::drawColorPicker() {
|
void ViewTools::drawColorPicker() {
|
||||||
if (ImGui::CollapsingHeader("Color picker")) {
|
if (ImGui::CollapsingHeader("Color picker")) {
|
||||||
|
ImGui::SetNextItemWidth(300.0F);
|
||||||
ImGui::ColorPicker4("Color Picker", this->m_pickedColor.data(),
|
ImGui::ColorPicker4("Color Picker", this->m_pickedColor.data(),
|
||||||
ImGuiColorEditFlags_Uint8 | ImGuiColorEditFlags_AlphaBar | ImGuiColorEditFlags_DisplayRGB | ImGuiColorEditFlags_DisplayHex);
|
ImGuiColorEditFlags_Uint8 | ImGuiColorEditFlags_AlphaBar | ImGuiColorEditFlags_DisplayRGB | ImGuiColorEditFlags_DisplayHex);
|
||||||
ImGui::NewLine();
|
ImGui::NewLine();
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
void ViewTools::createView() {
|
void ViewTools::drawMathEvaluator() {
|
||||||
if (!this->m_windowOpen)
|
if (ImGui::CollapsingHeader("Calculator")) {
|
||||||
return;
|
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->drawDemangler();
|
||||||
this->drawASCIITable();
|
this->drawASCIITable();
|
||||||
this->drawRegexReplacer();
|
this->drawRegexReplacer();
|
||||||
|
this->drawMathEvaluator();
|
||||||
this->drawColorPicker();
|
this->drawColorPicker();
|
||||||
|
|
||||||
}
|
}
|
||||||
@@ -189,10 +305,7 @@ namespace hex {
|
|||||||
}
|
}
|
||||||
|
|
||||||
void ViewTools::createMenu() {
|
void ViewTools::createMenu() {
|
||||||
if (ImGui::BeginMenu("View")) {
|
|
||||||
ImGui::MenuItem("Tools View", "", &this->m_windowOpen);
|
|
||||||
ImGui::EndMenu();
|
|
||||||
}
|
|
||||||
}
|
}
|
||||||
|
|
||||||
}
|
}
|
||||||
@@ -12,42 +12,51 @@
|
|||||||
|
|
||||||
namespace hex {
|
namespace hex {
|
||||||
|
|
||||||
namespace {
|
constexpr auto MenuBarItems = { "File", "Edit", "View", "Help" };
|
||||||
|
|
||||||
void *ImHexSettingsHandler_ReadOpenFn(ImGuiContext *ctx, ImGuiSettingsHandler *, const char *) {
|
void *ImHexSettingsHandler_ReadOpenFn(ImGuiContext *ctx, ImGuiSettingsHandler *, const char *) {
|
||||||
return ctx; // Unused, but the return value has to be non-null
|
return ctx; // Unused, but the return value has to be non-null
|
||||||
}
|
}
|
||||||
|
|
||||||
void ImHexSettingsHandler_ReadLine(ImGuiContext*, ImGuiSettingsHandler *handler, void *, const char* line) {
|
void ImHexSettingsHandler_ReadLine(ImGuiContext*, ImGuiSettingsHandler *handler, void *, const char* line) {
|
||||||
auto *window = reinterpret_cast<Window *>(handler->UserData);
|
auto *window = reinterpret_cast<Window *>(handler->UserData);
|
||||||
|
|
||||||
float scale;
|
float scale;
|
||||||
if (sscanf(line, "Scale=%f", &scale) == 1) { window->m_globalScale = 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 if (sscanf(line, "FontScale=%f", &scale) == 1) { window->m_fontScale = scale; }
|
||||||
}
|
else {
|
||||||
|
for (auto &view : window->m_views) {
|
||||||
void ImHexSettingsHandler_ApplyAll(ImGuiContext *ctx, ImGuiSettingsHandler *handler) {
|
std::string format = view->getName() + "=%d";
|
||||||
auto *window = reinterpret_cast<Window *>(handler->UserData);
|
sscanf(line, format.c_str(), &view->getWindowOpenState());
|
||||||
auto &style = ImGui::GetStyle();
|
}
|
||||||
auto &io = ImGui::GetIO();
|
}
|
||||||
|
}
|
||||||
if (window->m_globalScale != 0.0f)
|
|
||||||
style.ScaleAllSizes(window->m_globalScale);
|
void ImHexSettingsHandler_ApplyAll(ImGuiContext *ctx, ImGuiSettingsHandler *handler) {
|
||||||
if (window->m_fontScale != 0.0f)
|
auto *window = reinterpret_cast<Window *>(handler->UserData);
|
||||||
io.FontGlobalScale = window->m_fontScale;
|
auto &style = ImGui::GetStyle();
|
||||||
}
|
auto &io = ImGui::GetIO();
|
||||||
|
|
||||||
void ImHexSettingsHandler_WriteAll(ImGuiContext* ctx, ImGuiSettingsHandler *handler, ImGuiTextBuffer *buf) {
|
if (window->m_globalScale != 0.0f)
|
||||||
auto *window = reinterpret_cast<Window *>(handler->UserData);
|
style.ScaleAllSizes(window->m_globalScale);
|
||||||
|
if (window->m_fontScale != 0.0f)
|
||||||
buf->reserve(buf->size() + 0x20); // Ballpark reserve
|
io.FontGlobalScale = window->m_fontScale;
|
||||||
|
}
|
||||||
buf->appendf("[%s][General]\n", handler->TypeName);
|
|
||||||
buf->appendf("Scale=%.1f\n", window->m_globalScale);
|
void ImHexSettingsHandler_WriteAll(ImGuiContext* ctx, ImGuiSettingsHandler *handler, ImGuiTextBuffer *buf) {
|
||||||
buf->appendf("FontScale=%.1f\n", window->m_fontScale);
|
auto *window = reinterpret_cast<Window *>(handler->UserData);
|
||||||
buf->append("\n");
|
|
||||||
|
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() {
|
Window::Window() {
|
||||||
@@ -72,9 +81,20 @@ namespace hex {
|
|||||||
View::getDeferedCalls().clear();
|
View::getDeferedCalls().clear();
|
||||||
|
|
||||||
for (auto &view : this->m_views) {
|
for (auto &view : this->m_views) {
|
||||||
|
if (!view->getWindowOpenState())
|
||||||
|
continue;
|
||||||
|
|
||||||
|
ImGui::SetNextWindowSizeConstraints(ImVec2(480, 720), ImVec2(FLT_MAX, FLT_MAX));
|
||||||
view->createView();
|
view->createView();
|
||||||
}
|
}
|
||||||
|
|
||||||
|
View::drawCommonInterfaces();
|
||||||
|
|
||||||
|
#ifdef DEBUG
|
||||||
|
if (this->m_demoWindowOpen)
|
||||||
|
ImGui::ShowDemoWindow(&this->m_demoWindowOpen);
|
||||||
|
#endif
|
||||||
|
|
||||||
this->frameEnd();
|
this->frameEnd();
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
@@ -87,64 +107,90 @@ namespace hex {
|
|||||||
ImGui_ImplGlfw_NewFrame();
|
ImGui_ImplGlfw_NewFrame();
|
||||||
ImGui::NewFrame();
|
ImGui::NewFrame();
|
||||||
|
|
||||||
ImGuiWindowFlags windowFlags = ImGuiWindowFlags_MenuBar | ImGuiWindowFlags_NoDocking;
|
|
||||||
ImGuiViewport* viewport = ImGui::GetMainViewport();
|
ImGuiViewport* viewport = ImGui::GetMainViewport();
|
||||||
ImGui::SetNextWindowPos(viewport->GetWorkPos());
|
ImGui::SetNextWindowPos(viewport->GetWorkPos());
|
||||||
ImGui::SetNextWindowSize(viewport->GetWorkSize());
|
ImGui::SetNextWindowSize(viewport->GetWorkSize());
|
||||||
ImGui::SetNextWindowViewport(viewport->ID);
|
ImGui::SetNextWindowViewport(viewport->ID);
|
||||||
ImGui::PushStyleVar(ImGuiStyleVar_WindowRounding, 0.0f);
|
ImGui::PushStyleVar(ImGuiStyleVar_WindowRounding, 0.0f);
|
||||||
ImGui::PushStyleVar(ImGuiStyleVar_WindowBorderSize, 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);
|
ImGuiWindowFlags windowFlags = ImGuiWindowFlags_MenuBar | ImGuiWindowFlags_NoDocking
|
||||||
ImGui::PopStyleVar(2);
|
| ImGuiWindowFlags_NoTitleBar | ImGuiWindowFlags_NoCollapse
|
||||||
ImGui::DockSpace(ImGui::GetID("MainDock"), ImVec2(0.0f, 0.0f), ImGuiDockNodeFlags_None);
|
| 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)
|
if (ImGui::BeginMenuBar()) {
|
||||||
view->createMenu();
|
|
||||||
|
|
||||||
if (ImGui::BeginMenu("View")) {
|
for (auto menu : MenuBarItems)
|
||||||
ImGui::MenuItem("Display FPS", "", &this->m_fpsVisible);
|
if (ImGui::BeginMenu(menu)) ImGui::EndMenu();
|
||||||
ImGui::EndMenu();
|
|
||||||
}
|
|
||||||
|
|
||||||
if (this->m_fpsVisible) {
|
if (ImGui::BeginMenu("View")) {
|
||||||
char buffer[0x20];
|
for (auto &view : this->m_views) {
|
||||||
snprintf(buffer, 0x20, "%.1f FPS", ImGui::GetIO().Framerate);
|
if (view->hasViewMenuItemEntry())
|
||||||
|
ImGui::MenuItem((view->getName() + " View").c_str(), "", &view->getWindowOpenState());
|
||||||
|
}
|
||||||
|
ImGui::EndMenu();
|
||||||
|
}
|
||||||
|
|
||||||
ImGui::SameLine(ImGui::GetWindowWidth() - ImGui::GetFontSize() * strlen(buffer) + 20);
|
for (auto &view : this->m_views) {
|
||||||
ImGui::TextUnformatted(buffer);
|
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) {
|
ImGui::EndMenuBar();
|
||||||
for (auto &view : this->m_views) {
|
}
|
||||||
if (view->handleShortcut(key, mods))
|
|
||||||
break;
|
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() {
|
void Window::frameEnd() {
|
||||||
ImGui::Render();
|
ImGui::Render();
|
||||||
|
|
||||||
int display_w, display_h;
|
int displayWidth, displayHeight;
|
||||||
glfwGetFramebufferSize(this->m_window, &display_w, &display_h);
|
glfwGetFramebufferSize(this->m_window, &displayWidth, &displayHeight);
|
||||||
glViewport(0, 0, display_w, display_h);
|
glViewport(0, 0, displayWidth, displayHeight);
|
||||||
glClearColor(0.45f, 0.55f, 0.60f, 1.00f);
|
glClearColor(0.45f, 0.55f, 0.60f, 1.00f);
|
||||||
glClear(GL_COLOR_BUFFER_BIT);
|
glClear(GL_COLOR_BUFFER_BIT);
|
||||||
ImGui_ImplOpenGL3_RenderDrawData(ImGui::GetDrawData());
|
ImGui_ImplOpenGL3_RenderDrawData(ImGui::GetDrawData());
|
||||||
|
|
||||||
|
GLFWwindow* backup_current_context = glfwGetCurrentContext();
|
||||||
|
ImGui::UpdatePlatformWindows();
|
||||||
|
ImGui::RenderPlatformWindowsDefault();
|
||||||
|
glfwMakeContextCurrent(backup_current_context);
|
||||||
|
|
||||||
glfwSwapBuffers(this->m_window);
|
glfwSwapBuffers(this->m_window);
|
||||||
}
|
}
|
||||||
|
|
||||||
@@ -156,16 +202,18 @@ namespace hex {
|
|||||||
if (!glfwInit())
|
if (!glfwInit())
|
||||||
throw std::runtime_error("Failed to initialize GLFW!");
|
throw std::runtime_error("Failed to initialize GLFW!");
|
||||||
|
|
||||||
#ifdef __APPLE__
|
#ifdef __APPLE__
|
||||||
glfwWindowHint(GLFW_OPENGL_FORWARD_COMPAT, GL_TRUE);
|
glfwWindowHint(GLFW_OPENGL_FORWARD_COMPAT, GL_TRUE);
|
||||||
#endif
|
#endif
|
||||||
|
|
||||||
glfwWindowHint(GLFW_CONTEXT_VERSION_MAJOR, 3);
|
glfwWindowHint(GLFW_CONTEXT_VERSION_MAJOR, 3);
|
||||||
glfwWindowHint(GLFW_CONTEXT_VERSION_MINOR, 2);
|
glfwWindowHint(GLFW_CONTEXT_VERSION_MINOR, 2);
|
||||||
glfwWindowHint(GLFW_OPENGL_PROFILE, GLFW_OPENGL_CORE_PROFILE);
|
glfwWindowHint(GLFW_OPENGL_PROFILE, GLFW_OPENGL_CORE_PROFILE);
|
||||||
|
|
||||||
|
|
||||||
this->m_window = glfwCreateWindow(1280, 720, "ImHex", nullptr, nullptr);
|
this->m_window = glfwCreateWindow(1280, 720, "ImHex", nullptr, nullptr);
|
||||||
|
|
||||||
|
|
||||||
if (this->m_window == nullptr)
|
if (this->m_window == nullptr)
|
||||||
throw std::runtime_error("Failed to create window!");
|
throw std::runtime_error("Failed to create window!");
|
||||||
|
|
||||||
@@ -184,7 +232,12 @@ namespace hex {
|
|||||||
View::postEvent(Events::FileDropped, paths[0]);
|
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)
|
if (gladLoadGL() == 0)
|
||||||
throw std::runtime_error("Failed to initialize OpenGL loader!");
|
throw std::runtime_error("Failed to initialize OpenGL loader!");
|
||||||
@@ -194,7 +247,13 @@ namespace hex {
|
|||||||
IMGUI_CHECKVERSION();
|
IMGUI_CHECKVERSION();
|
||||||
auto *ctx = ImGui::CreateContext();
|
auto *ctx = ImGui::CreateContext();
|
||||||
ImGuiIO& io = ImGui::GetIO();
|
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
|
// Install custom settings handler
|
||||||
ImGuiSettingsHandler handler;
|
ImGuiSettingsHandler handler;
|
||||||
|
|||||||
Reference in New Issue
Block a user