mirror of
https://github.com/WerWolv/ImHex.git
synced 2026-03-31 05:15:55 -05:00
Compare commits
178 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 | ||
|
|
4402120ffc | ||
|
|
b3fffdf530 | ||
|
|
8d6d959e17 | ||
|
|
43f5cc622e | ||
|
|
5f025bcbcc | ||
|
|
8297e22f10 | ||
|
|
989eade5d7 | ||
|
|
cd4de2ff96 | ||
|
|
73d66365e9 | ||
|
|
fbd4e593d2 | ||
|
|
033ef3889a | ||
|
|
0ce1b5d40b | ||
|
|
ed4ed6b433 | ||
|
|
4cd18b8358 | ||
|
|
fb85f272a1 | ||
|
|
28bb28b79c | ||
|
|
e3b6cfd54f | ||
|
|
bf6ed3d540 | ||
|
|
9c0a270d90 | ||
|
|
5112c3aa1e | ||
|
|
57dcf6cc93 | ||
|
|
48296775ae | ||
|
|
e3cb078306 | ||
|
|
2f78a10e4c | ||
|
|
f3e2e35533 | ||
|
|
0d9175dc15 | ||
|
|
302caba403 | ||
|
|
920b32b432 | ||
|
|
81e5c945b4 | ||
|
|
78ea4276ae | ||
|
|
12a36d08e2 | ||
|
|
e4879f7546 | ||
|
|
34b8f481e1 | ||
|
|
f36014194d | ||
|
|
763d1f0e2d | ||
|
|
5c6fb302d9 | ||
|
|
f748b75a19 | ||
|
|
6a815d5ebb | ||
|
|
bfb079cb4f | ||
|
|
89afbd1aef | ||
|
|
ed9922c8a9 | ||
|
|
3fe231cdb0 | ||
|
|
269af11eb4 | ||
|
|
6ed3936424 | ||
|
|
24c8fc6957 | ||
|
|
9965322505 | ||
|
|
9b04373809 | ||
|
|
6fffc589bf | ||
|
|
0889764bcc | ||
|
|
e40bb5c498 | ||
|
|
a255e062be | ||
|
|
02c3821ea7 | ||
|
|
e61dfa0927 | ||
|
|
c8304eb497 | ||
|
|
6e21f703ab | ||
|
|
7550cf394c | ||
|
|
d05805595e | ||
|
|
975c3a9276 | ||
|
|
43d5fe2f4d | ||
|
|
a2fb9306c7 | ||
|
|
4c07983834 | ||
|
|
b28d45df8a | ||
|
|
c863b2f65b | ||
|
|
896cad1fe0 | ||
|
|
8b9b284ae9 | ||
|
|
9ffd393a4a | ||
|
|
559fd28036 | ||
|
|
4452f9754e | ||
|
|
983c1b4a90 | ||
|
|
cd9d0bcf34 | ||
|
|
ec294228ae | ||
|
|
bbfb0556a6 | ||
|
|
0b8b887978 | ||
|
|
9320ffdbbd | ||
|
|
a955f522bd | ||
|
|
2526eda0db | ||
|
|
5b2dc51c07 | ||
|
|
30c0ce8d2c | ||
|
|
c758eb244b | ||
|
|
d9f5a974cb | ||
|
|
658d4ec478 | ||
|
|
72f9da2a67 | ||
|
|
41c70bce44 | ||
|
|
999db12a3a | ||
|
|
7a30072fcb | ||
|
|
0cdacc4b9f | ||
|
|
a6b04e99f6 | ||
|
|
8aa4402f88 | ||
|
|
295b32b890 | ||
|
|
0dcf02f891 | ||
|
|
15b91c1cac | ||
|
|
0bdc442bf0 | ||
|
|
d44ffde2a9 | ||
|
|
867b87415b | ||
|
|
761522a540 |
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
|
||||
6
.gitignore
vendored
6
.gitignore
vendored
@@ -2,3 +2,9 @@
|
||||
cmake-build-debug/
|
||||
|
||||
cmake-build-release/
|
||||
|
||||
build-linux/
|
||||
build/
|
||||
|
||||
*.mgc
|
||||
imgui.ini
|
||||
|
||||
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,48 +1,99 @@
|
||||
cmake_minimum_required(VERSION 3.16)
|
||||
project(HexEditor)
|
||||
|
||||
SET(CMAKE_FIND_PACKAGE_SORT_ORDER NATURAL)
|
||||
SET(CMAKE_FIND_PACKAGE_SORT_DIRECTION DEC)
|
||||
|
||||
set(CMAKE_CXX_STANDARD 20)
|
||||
|
||||
|
||||
find_package(PkgConfig REQUIRED)
|
||||
pkg_search_module(GLFW REQUIRED glfw3)
|
||||
pkg_search_module(GLM REQUIRED glm)
|
||||
pkg_search_module(CRYPTO REQUIRED libcrypto)
|
||||
pkg_search_module(CAPSTONE REQUIRED capstone)
|
||||
find_package(OpenGL REQUIRED)
|
||||
find_package(LLVM REQUIRED CONFIG)
|
||||
find_package(nlohmann_json REQUIRED)
|
||||
find_package(Python COMPONENTS Interpreter Development)
|
||||
|
||||
include_directories(include ${GLFW_INCLUDE_DIRS} libs/ImGui/include libs/glad/include)
|
||||
SET(CMAKE_CXX_FLAGS "${CMAKE_C_FLAGS} -DIMGUI_IMPL_OPENGL_LOADER_GLAD")
|
||||
if(Python_VERSION LESS 3)
|
||||
message(STATUS ${PYTHON_VERSION_MAJOR_MINOR})
|
||||
message(FATAL_ERROR "No valid version of Python 3 was found.")
|
||||
endif()
|
||||
|
||||
llvm_map_components_to_libnames(_llvm_libs demangle)
|
||||
llvm_expand_dependencies(llvm_libs ${_llvm_libs})
|
||||
|
||||
include_directories(include ${GLFW_INCLUDE_DIRS} ${CAPSTONE_INCLUDE_DIRS} ${LLVM_INCLUDE_DIRS} libs/ImGui/include libs/glad/include ${Python_INCLUDE_DIRS})
|
||||
|
||||
# Get Python major and minor
|
||||
string(REPLACE "." ";" PYTHON_VERSION_MAJOR_MINOR ${Python_VERSION})
|
||||
list(REMOVE_AT PYTHON_VERSION_MAJOR_MINOR 2)
|
||||
list(JOIN PYTHON_VERSION_MAJOR_MINOR "." PYTHON_VERSION_MAJOR_MINOR)
|
||||
|
||||
SET(CMAKE_CXX_FLAGS "${CMAKE_C_FLAGS} -DIMGUI_IMPL_OPENGL_LOADER_GLAD -DPYTHON_VERSION_MAJOR_MINOR=\"\\\"${PYTHON_VERSION_MAJOR_MINOR}\"\\\"")
|
||||
|
||||
if (WIN32)
|
||||
SET(CMAKE_CXX_FLAGS "${CMAKE_CXX_FLAGS} -static-libstdc++ -static-libgcc -static")
|
||||
SET(CMAKE_CXX_FLAGS_RELEASE "${CMAKE_CXX_FLAGS_RELEASE} -Wl,-subsystem,windows")
|
||||
endif (WIN32)
|
||||
|
||||
SET(CMAKE_CXX_FLAGS_RELEASE "${CMAKE_CXX_FLAGS_RELEASE} -DRELEASE")
|
||||
SET(CMAKE_CXX_FLAGS_DEBUG "${CMAKE_CXX_FLAGS_DEBUG} -DDEBUG")
|
||||
|
||||
add_executable(ImHex
|
||||
source/main.cpp
|
||||
source/window.cpp
|
||||
source/utils.cpp
|
||||
source/crypto.cpp
|
||||
|
||||
source/parser/lexer.cpp
|
||||
source/parser/parser.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/provider/file_provider.cpp
|
||||
source/lang/preprocessor.cpp
|
||||
source/lang/lexer.cpp
|
||||
source/lang/parser.cpp
|
||||
source/lang/validator.cpp
|
||||
source/lang/evaluator.cpp
|
||||
|
||||
source/providers/file_provider.cpp
|
||||
|
||||
source/views/view_hexeditor.cpp
|
||||
source/views/view_pattern.cpp
|
||||
source/views/view_pattern_data.cpp
|
||||
source/views/view_hashes.cpp
|
||||
source/views/view_information.cpp
|
||||
source/views/view_help.cpp
|
||||
source/views/view_tools.cpp
|
||||
source/views/view_strings.cpp
|
||||
source/views/view_data_inspector.cpp
|
||||
source/views/view_disassembler.cpp
|
||||
source/views/view_bookmarks.cpp
|
||||
source/views/view_patches.cpp
|
||||
|
||||
libs/glad/source/glad.c
|
||||
|
||||
libs/ImGui/source/imgui.cpp
|
||||
libs/ImGui/source/imgui_demo.cpp
|
||||
libs/ImGui/source/imgui_draw.cpp
|
||||
libs/ImGui/source/imgui_widgets.cpp
|
||||
libs/ImGui/source/imgui_demo.cpp
|
||||
libs/ImGui/source/imgui_impl_glfw.cpp
|
||||
libs/ImGui/source/imgui_impl_opengl3.cpp
|
||||
libs/ImGui/source/ImGuiFileBrowser.cpp
|
||||
libs/ImGui/source/TextEditor.cpp
|
||||
|
||||
res.rc
|
||||
resource.rc
|
||||
)
|
||||
|
||||
target_link_directories(ImHex PRIVATE ${LLVM_LIBRARY_DIR})
|
||||
|
||||
if (WIN32)
|
||||
target_link_libraries(ImHex libglfw3.a libgcc.a libstdc++.a libmagic.a libgnurx.a libtre.a libintl.a libiconv.a shlwapi.lib libcrypto.a)
|
||||
target_link_libraries(ImHex libglfw3.a libgcc.a libstdc++.a libmagic.a libgnurx.a libtre.a libintl.a libiconv.a shlwapi.lib libcrypto.a libwinpthread.a libcapstone.a ${llvm_libs} ${Python_LIBRARIES} nlohmann_json::nlohmann_json)
|
||||
endif (WIN32)
|
||||
|
||||
if (UNIX)
|
||||
target_link_libraries(ImHex libglfw.so libmagic.so libcrypto.so libdl.so)
|
||||
target_link_libraries(ImHex libglfw.so libmagic.so libcrypto.so libdl.so libcapstone.so ${llvm_libs} ${Python_LIBRARIES} nlohmann_json::nlohmann_json)
|
||||
endif (UNIX)
|
||||
339
LICENSE
Normal file
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
|
||||
|
||||
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
|
||||
|
||||

|
||||

|
||||

|
||||
|
||||
## 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 <optional>
|
||||
#include <string>
|
||||
#include <vector>
|
||||
|
||||
namespace hex {
|
||||
|
||||
@@ -21,4 +22,6 @@ namespace hex {
|
||||
std::array<u32, 12> sha384(prv::Provider* &data, u64 offset, size_t size);
|
||||
std::array<u32, 16> sha512(prv::Provider* &data, u64 offset, size_t size);
|
||||
|
||||
std::vector<u8> decode64(const std::vector<u8> &input);
|
||||
std::vector<u8> encode64(const std::vector<u8> &input);
|
||||
}
|
||||
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,25 +6,38 @@
|
||||
namespace hex {
|
||||
|
||||
enum class Events {
|
||||
DataChanged
|
||||
FileLoaded,
|
||||
DataChanged,
|
||||
PatternChanged,
|
||||
FileDropped,
|
||||
WindowClosing,
|
||||
RegionSelected,
|
||||
|
||||
SelectionChangeRequest,
|
||||
|
||||
AddBookmark,
|
||||
AppendPatternLanguageCode,
|
||||
|
||||
ProjectFileStore,
|
||||
ProjectFileLoad
|
||||
};
|
||||
|
||||
struct EventHandler {
|
||||
void *sender;
|
||||
Events eventType;
|
||||
std::function<void(void*)> callback;
|
||||
std::function<void(const void*)> callback;
|
||||
};
|
||||
|
||||
class EventManager {
|
||||
public:
|
||||
|
||||
void post(Events eventType, void *userData) {
|
||||
void post(Events eventType, const void *userData) {
|
||||
for (auto &handler : this->m_eventHandlers)
|
||||
if (eventType == handler.eventType)
|
||||
handler.callback(userData);
|
||||
}
|
||||
|
||||
void subscribe(Events eventType, void *sender, std::function<void(void*)> callback) {
|
||||
void subscribe(Events eventType, void *sender, std::function<void(const void*)> callback) {
|
||||
for (auto &handler : this->m_eventHandlers)
|
||||
if (eventType == handler.eventType && sender == handler.sender)
|
||||
return;
|
||||
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;
|
||||
};
|
||||
|
||||
}
|
||||
128
include/helpers/utils.hpp
Normal file
128
include/helpers/utils.hpp
Normal file
@@ -0,0 +1,128 @@
|
||||
#pragma once
|
||||
|
||||
#include <hex.hpp>
|
||||
|
||||
#include <array>
|
||||
#include <functional>
|
||||
#include <memory>
|
||||
#include <optional>
|
||||
#include <string>
|
||||
#include <vector>
|
||||
|
||||
#ifdef __MINGW32__
|
||||
#include <winsock.h>
|
||||
|
||||
#else
|
||||
#include <arpa/inet.h>
|
||||
#endif
|
||||
|
||||
#include "lang/token.hpp"
|
||||
|
||||
namespace hex {
|
||||
|
||||
template<typename ... Args>
|
||||
inline std::string format(const std::string &format, Args ... args) {
|
||||
ssize_t size = snprintf( nullptr, 0, format.c_str(), args ... );
|
||||
|
||||
if( size <= 0 )
|
||||
return "";
|
||||
|
||||
std::vector<char> buffer(size + 1, 0x00);
|
||||
snprintf(buffer.data(), size + 1, format.c_str(), args ...);
|
||||
|
||||
return std::string(buffer.data(), buffer.data() + size);
|
||||
}
|
||||
|
||||
[[nodiscard]] constexpr inline u64 extract(u8 from, u8 to, const u64 &value) {
|
||||
u64 mask = (std::numeric_limits<u64>::max() >> (63 - (from - to))) << to;
|
||||
return (value & mask) >> to;
|
||||
}
|
||||
|
||||
[[nodiscard]] constexpr inline u64 signExtend(u64 value, u8 currWidth, u8 targetWidth) {
|
||||
u64 mask = 1LLU << (currWidth - 1);
|
||||
return (((value ^ mask) - mask) << (64 - targetWidth)) >> (64 - targetWidth);
|
||||
}
|
||||
|
||||
[[nodiscard]] constexpr inline bool isUnsigned(const lang::Token::TypeToken::Type type) {
|
||||
return (static_cast<u32>(type) & 0x0F) == 0x00;
|
||||
}
|
||||
|
||||
[[nodiscard]] constexpr inline bool isSigned(const lang::Token::TypeToken::Type type) {
|
||||
return (static_cast<u32>(type) & 0x0F) == 0x01;
|
||||
}
|
||||
|
||||
[[nodiscard]] constexpr inline bool isFloatingPoint(const lang::Token::TypeToken::Type type) {
|
||||
return (static_cast<u32>(type) & 0x0F) == 0x02;
|
||||
}
|
||||
|
||||
[[nodiscard]] constexpr inline u32 getTypeSize(const lang::Token::TypeToken::Type type) {
|
||||
return static_cast<u32>(type) >> 4;
|
||||
}
|
||||
|
||||
std::string toByteString(u64 bytes);
|
||||
std::string makePrintable(char c);
|
||||
|
||||
template<typename T>
|
||||
struct always_false : std::false_type {};
|
||||
|
||||
template<typename T>
|
||||
constexpr T changeEndianess(T value, std::endian endian) {
|
||||
if (endian == std::endian::native)
|
||||
return value;
|
||||
|
||||
if constexpr (sizeof(T) == 1)
|
||||
return value;
|
||||
else if constexpr (sizeof(T) == 2)
|
||||
return __builtin_bswap16(value);
|
||||
else if constexpr (sizeof(T) == 4)
|
||||
return __builtin_bswap32(value);
|
||||
else if constexpr (sizeof(T) == 8)
|
||||
return __builtin_bswap64(value);
|
||||
else
|
||||
static_assert(always_false<T>::value, "Invalid type provided!");
|
||||
}
|
||||
|
||||
template<typename T>
|
||||
constexpr T changeEndianess(T value, size_t size, std::endian endian) {
|
||||
if (endian == std::endian::native)
|
||||
return value;
|
||||
|
||||
if (size == 1)
|
||||
return value;
|
||||
else if (size == 2)
|
||||
return __builtin_bswap16(value);
|
||||
else if (size == 4)
|
||||
return __builtin_bswap32(value);
|
||||
else if (size == 8)
|
||||
return __builtin_bswap64(value);
|
||||
else
|
||||
throw std::invalid_argument("Invalid value size!");
|
||||
}
|
||||
|
||||
std::vector<u8> readFile(std::string_view path);
|
||||
|
||||
class ScopeExit {
|
||||
public:
|
||||
ScopeExit(std::function<void()> func) : m_func(func) {}
|
||||
~ScopeExit() { if (this->m_func != nullptr) this->m_func(); }
|
||||
|
||||
void release() {
|
||||
this->m_func = nullptr;
|
||||
}
|
||||
|
||||
private:
|
||||
std::function<void()> m_func;
|
||||
};
|
||||
|
||||
struct Region {
|
||||
u64 address;
|
||||
size_t size;
|
||||
};
|
||||
|
||||
struct Bookmark {
|
||||
Region region;
|
||||
|
||||
std::vector<char> name;
|
||||
std::vector<char> comment;
|
||||
};
|
||||
}
|
||||
@@ -15,5 +15,14 @@ using s32 = std::int32_t;
|
||||
using s64 = std::int64_t;
|
||||
using s128 = __int128_t;
|
||||
|
||||
#include "parser/result.hpp"
|
||||
#include "parser/results.hpp"
|
||||
#include "lang/result.hpp"
|
||||
#include "lang/results.hpp"
|
||||
|
||||
extern int mainArgc;
|
||||
extern char **mainArgv;
|
||||
|
||||
#if defined(__EMX__) || defined (WIN32)
|
||||
#define MAGIC_PATH_SEPARATOR ";"
|
||||
#else
|
||||
#define MAGIC_PATH_SEPARATOR ":"
|
||||
#endif
|
||||
133
include/lang/ast_node.hpp
Normal file
133
include/lang/ast_node.hpp
Normal file
@@ -0,0 +1,133 @@
|
||||
#pragma once
|
||||
|
||||
#include "token.hpp"
|
||||
|
||||
#include <bit>
|
||||
#include <optional>
|
||||
#include <unordered_map>
|
||||
#include <vector>
|
||||
|
||||
namespace hex::lang {
|
||||
|
||||
class ASTNode {
|
||||
public:
|
||||
enum class Type {
|
||||
VariableDecl,
|
||||
TypeDecl,
|
||||
Struct,
|
||||
Union,
|
||||
Enum,
|
||||
Bitfield,
|
||||
Scope,
|
||||
};
|
||||
|
||||
explicit ASTNode(Type type, u32 lineNumber) : m_type(type), m_lineNumber(lineNumber) {}
|
||||
virtual ~ASTNode() = default;
|
||||
|
||||
Type getType() { return this->m_type; }
|
||||
u32 getLineNumber() { return this->m_lineNumber; }
|
||||
|
||||
private:
|
||||
Type m_type;
|
||||
u32 m_lineNumber;
|
||||
};
|
||||
|
||||
class ASTNodeVariableDecl : public ASTNode {
|
||||
public:
|
||||
explicit ASTNodeVariableDecl(u32 lineNumber, const Token::TypeToken::Type &type, const std::string &name, const std::string& customTypeName = "", std::optional<u64> offset = { }, size_t arraySize = 1, std::optional<std::string> arraySizeVariable = { }, std::optional<u8> pointerSize = { }, std::optional<std::endian> endianess = { })
|
||||
: ASTNode(Type::VariableDecl, lineNumber), m_type(type), m_name(name), m_customTypeName(customTypeName), m_offset(offset), m_arraySize(arraySize), m_arraySizeVariable(arraySizeVariable), m_pointerSize(pointerSize), m_endianess(endianess) { }
|
||||
|
||||
const Token::TypeToken::Type& getVariableType() const { return this->m_type; }
|
||||
const std::string& getCustomVariableTypeName() const { return this->m_customTypeName; }
|
||||
const std::string& getVariableName() const { return this->m_name; };
|
||||
std::optional<u64> getOffset() const { return this->m_offset; }
|
||||
size_t getArraySize() const { return this->m_arraySize; }
|
||||
std::optional<std::string> getArraySizeVariable() const { return this->m_arraySizeVariable; }
|
||||
std::optional<u8> getPointerSize() const { return this->m_pointerSize; }
|
||||
std::optional<std::endian> getEndianess() const { return this->m_endianess; }
|
||||
|
||||
private:
|
||||
Token::TypeToken::Type m_type;
|
||||
std::string m_name, m_customTypeName;
|
||||
std::optional<u64> m_offset;
|
||||
size_t m_arraySize;
|
||||
std::optional<std::string> m_arraySizeVariable;
|
||||
std::optional<u8> m_pointerSize;
|
||||
std::optional<std::endian> m_endianess = { };
|
||||
};
|
||||
|
||||
class ASTNodeScope : public ASTNode {
|
||||
public:
|
||||
explicit ASTNodeScope(u32 lineNumber, std::vector<ASTNode*> nodes) : ASTNode(Type::Scope, lineNumber), m_nodes(nodes) { }
|
||||
|
||||
std::vector<ASTNode*> &getNodes() { return this->m_nodes; }
|
||||
private:
|
||||
std::vector<ASTNode*> m_nodes;
|
||||
};
|
||||
|
||||
class ASTNodeStruct : public ASTNode {
|
||||
public:
|
||||
explicit ASTNodeStruct(u32 lineNumber, std::string name, std::vector<ASTNode*> nodes)
|
||||
: ASTNode(Type::Struct, lineNumber), m_name(name), m_nodes(nodes) { }
|
||||
|
||||
const std::string& getName() const { return this->m_name; }
|
||||
std::vector<ASTNode*> &getNodes() { return this->m_nodes; }
|
||||
private:
|
||||
std::string m_name;
|
||||
std::vector<ASTNode*> m_nodes;
|
||||
};
|
||||
|
||||
class ASTNodeUnion : public ASTNode {
|
||||
public:
|
||||
explicit ASTNodeUnion(u32 lineNumber, std::string name, std::vector<ASTNode*> nodes)
|
||||
: ASTNode(Type::Union, lineNumber), m_name(name), m_nodes(nodes) { }
|
||||
|
||||
const std::string& getName() const { return this->m_name; }
|
||||
std::vector<ASTNode*> &getNodes() { return this->m_nodes; }
|
||||
private:
|
||||
std::string m_name;
|
||||
std::vector<ASTNode*> m_nodes;
|
||||
};
|
||||
|
||||
class ASTNodeBitField : public ASTNode {
|
||||
public:
|
||||
explicit ASTNodeBitField(u32 lineNumber, std::string name, std::vector<std::pair<std::string, size_t>> fields)
|
||||
: ASTNode(Type::Bitfield, lineNumber), m_name(name), m_fields(fields) { }
|
||||
|
||||
const std::string& getName() const { return this->m_name; }
|
||||
std::vector<std::pair<std::string, size_t>> &getFields() { return this->m_fields; }
|
||||
private:
|
||||
std::string m_name;
|
||||
std::vector<std::pair<std::string, size_t>> m_fields;
|
||||
};
|
||||
|
||||
class ASTNodeTypeDecl : public ASTNode {
|
||||
public:
|
||||
explicit ASTNodeTypeDecl(u32 lineNumber, const Token::TypeToken::Type &type, const std::string &name, const std::string& customTypeName = "")
|
||||
: ASTNode(Type::TypeDecl, lineNumber), m_type(type), m_name(name), m_customTypeName(customTypeName) { }
|
||||
|
||||
const std::string& getTypeName() const { return this->m_name; };
|
||||
|
||||
const Token::TypeToken::Type& getAssignedType() const { return this->m_type; }
|
||||
const std::string& getAssignedCustomTypeName() const { return this->m_customTypeName; }
|
||||
private:
|
||||
Token::TypeToken::Type m_type;
|
||||
std::string m_name, m_customTypeName;
|
||||
};
|
||||
|
||||
class ASTNodeEnum : public ASTNode {
|
||||
public:
|
||||
explicit ASTNodeEnum(u32 lineNumber, const Token::TypeToken::Type &type, const std::string &name)
|
||||
: ASTNode(Type::Enum, lineNumber), m_type(type), m_name(name) { }
|
||||
|
||||
const std::string& getName() const { return this->m_name; };
|
||||
|
||||
const Token::TypeToken::Type& getUnderlyingType() const { return this->m_type; }
|
||||
std::vector<std::pair<u64, std::string>>& getValues() { return this->m_values; }
|
||||
private:
|
||||
Token::TypeToken::Type m_type;
|
||||
std::string m_name;
|
||||
std::vector<std::pair<u64, std::string>> m_values;
|
||||
};
|
||||
|
||||
}
|
||||
42
include/lang/evaluator.hpp
Normal file
42
include/lang/evaluator.hpp
Normal file
@@ -0,0 +1,42 @@
|
||||
#pragma once
|
||||
|
||||
#include <hex.hpp>
|
||||
|
||||
#include "providers/provider.hpp"
|
||||
#include "lang/pattern_data.hpp"
|
||||
#include "ast_node.hpp"
|
||||
|
||||
#include <bit>
|
||||
#include <string>
|
||||
#include <unordered_map>
|
||||
#include <vector>
|
||||
|
||||
namespace hex::lang {
|
||||
|
||||
class Evaluator {
|
||||
public:
|
||||
Evaluator(prv::Provider* &provider, std::endian defaultDataEndianess);
|
||||
|
||||
std::pair<Result, std::vector<PatternData*>> evaluate(const std::vector<ASTNode*>& ast);
|
||||
|
||||
const std::pair<u32, std::string>& getError() { return this->m_error; }
|
||||
|
||||
private:
|
||||
std::unordered_map<std::string, ASTNode*> m_types;
|
||||
prv::Provider* &m_provider;
|
||||
std::endian m_defaultDataEndianess;
|
||||
|
||||
std::pair<u32, std::string> m_error;
|
||||
|
||||
std::pair<PatternData*, size_t> createStructPattern(ASTNodeVariableDecl *varDeclNode, u64 offset);
|
||||
std::pair<PatternData*, size_t> createUnionPattern(ASTNodeVariableDecl *varDeclNode, u64 offset);
|
||||
std::pair<PatternData*, size_t> createEnumPattern(ASTNodeVariableDecl *varDeclNode, u64 offset);
|
||||
std::pair<PatternData*, size_t> createBitfieldPattern(ASTNodeVariableDecl *varDeclNode, u64 offset);
|
||||
std::pair<PatternData*, size_t> createArrayPattern(ASTNodeVariableDecl *varDeclNode, u64 offset);
|
||||
std::pair<PatternData*, size_t> createStringPattern(ASTNodeVariableDecl *varDeclNode, u64 offset);
|
||||
std::pair<PatternData*, size_t> createCustomTypePattern(ASTNodeVariableDecl *varDeclNode, u64 offset);
|
||||
std::pair<PatternData*, size_t> createBuiltInTypePattern(ASTNodeVariableDecl *varDeclNode, u64 offset);
|
||||
|
||||
};
|
||||
|
||||
}
|
||||
@@ -14,6 +14,11 @@ namespace hex::lang {
|
||||
Lexer();
|
||||
|
||||
std::pair<Result, std::vector<Token>> lex(const std::string& code);
|
||||
|
||||
const std::pair<u32, std::string>& getError() { return this->m_error; }
|
||||
|
||||
private:
|
||||
std::pair<u32, std::string> m_error;
|
||||
};
|
||||
|
||||
}
|
||||
49
include/lang/parser.hpp
Normal file
49
include/lang/parser.hpp
Normal file
@@ -0,0 +1,49 @@
|
||||
#pragma once
|
||||
|
||||
#include <hex.hpp>
|
||||
#include "token.hpp"
|
||||
#include "ast_node.hpp"
|
||||
|
||||
#include <vector>
|
||||
|
||||
namespace hex::lang {
|
||||
|
||||
class Parser {
|
||||
public:
|
||||
Parser();
|
||||
|
||||
using TokenIter = std::vector<Token>::const_iterator;
|
||||
|
||||
std::pair<Result, std::vector<ASTNode*>> parse(const std::vector<Token> &tokens);
|
||||
|
||||
const std::pair<u32, std::string>& getError() { return this->m_error; }
|
||||
|
||||
private:
|
||||
std::pair<u32, std::string> m_error;
|
||||
|
||||
|
||||
ASTNode* parseBuiltinVariableDecl(TokenIter &curr, bool hasEndianDef);
|
||||
ASTNode* parseCustomTypeVariableDecl(TokenIter &curr, bool hasEndianDef);
|
||||
ASTNode* parseBuiltinPointerVariableDecl(TokenIter &curr, bool hasEndianDef);
|
||||
ASTNode* parseCustomTypePointerVariableDecl(TokenIter &curr, bool hasEndianDef);
|
||||
ASTNode* parseBuiltinArrayDecl(TokenIter &curr, bool hasEndianDef);
|
||||
ASTNode* parseCustomTypeArrayDecl(TokenIter &curr, bool hasEndianDef);
|
||||
ASTNode* parseBuiltinVariableArrayDecl(TokenIter &curr, bool hasEndianDef);
|
||||
ASTNode* parseCustomTypeVariableArrayDecl(TokenIter &curr, bool hasEndianDef);
|
||||
ASTNode* parsePaddingDecl(TokenIter &curr);
|
||||
ASTNode* parseFreeBuiltinVariableDecl(TokenIter &curr, bool hasEndianDef);
|
||||
ASTNode* parseFreeCustomTypeVariableDecl(TokenIter &curr, bool hasEndianDef);
|
||||
|
||||
ASTNode* parseStruct(TokenIter &curr);
|
||||
ASTNode* parseUnion(TokenIter &curr);
|
||||
ASTNode* parseEnum(TokenIter &curr);
|
||||
ASTNode *parseBitField(TokenIter &curr);
|
||||
ASTNode *parseScope(TokenIter &curr);
|
||||
std::optional<ASTNode*> parseUsingDeclaration(TokenIter &curr);
|
||||
std::optional<std::vector<ASTNode*>> parseStatement(TokenIter &curr);
|
||||
|
||||
std::vector<ASTNode*> parseTillToken(TokenIter &curr, Token::Type endTokenType);
|
||||
bool tryConsume(TokenIter &curr, std::initializer_list<Token::Type> tokenTypes);
|
||||
};
|
||||
|
||||
}
|
||||
636
include/lang/pattern_data.hpp
Normal file
636
include/lang/pattern_data.hpp
Normal file
@@ -0,0 +1,636 @@
|
||||
#pragma once
|
||||
|
||||
#include <hex.hpp>
|
||||
#include <string>
|
||||
|
||||
#include "imgui.h"
|
||||
#include "imgui_memory_editor.h"
|
||||
|
||||
#include "providers/provider.hpp"
|
||||
#include "helpers/utils.hpp"
|
||||
|
||||
#include <random>
|
||||
|
||||
namespace hex::lang {
|
||||
|
||||
using namespace ::std::literals::string_literals;
|
||||
|
||||
namespace {
|
||||
|
||||
std::string makeDisplayable(u8 *data, size_t size) {
|
||||
std::string result;
|
||||
for (u8* c = data; c < (data + size); c++) {
|
||||
if (iscntrl(*c) || *c > 0x7F)
|
||||
result += " ";
|
||||
else
|
||||
result += *c;
|
||||
}
|
||||
|
||||
return result;
|
||||
}
|
||||
|
||||
}
|
||||
|
||||
class PatternData {
|
||||
public:
|
||||
enum class Type { Padding, Unsigned, Signed, Float, Character, String, Struct, Union, Array, Enum };
|
||||
|
||||
PatternData(Type type, u64 offset, size_t size, const std::string &name, std::endian endianess, u32 color = 0)
|
||||
: m_type(type), m_offset(offset), m_size(size), m_name(name), m_endianess(endianess), m_color(color) {
|
||||
constexpr u32 Palette[] = { 0x50b4771f, 0x500e7fff, 0x502ca02c, 0x502827d6, 0x50bd6794, 0x504b568c, 0x50c277e3, 0x507f7f7f, 0x5022bdbc, 0x50cfbe17 };
|
||||
|
||||
if (color != 0)
|
||||
return;
|
||||
|
||||
this->m_color = Palette[PatternData::s_paletteOffset++];
|
||||
|
||||
if (PatternData::s_paletteOffset >= (sizeof(Palette) / sizeof(u32)))
|
||||
PatternData::s_paletteOffset = 0;
|
||||
}
|
||||
virtual ~PatternData() = default;
|
||||
|
||||
[[nodiscard]] Type getPatternType() const { return this->m_type; }
|
||||
[[nodiscard]] u64 getOffset() const { return this->m_offset; }
|
||||
[[nodiscard]] size_t getSize() const { return this->m_size; }
|
||||
|
||||
[[nodiscard]] const std::string& getName() const { return this->m_name; }
|
||||
void setName(std::string name) { this->m_name = name; }
|
||||
|
||||
[[nodiscard]] u32 getColor() const { return this->m_color; }
|
||||
void setColor(u32 color) { this->m_color = color; }
|
||||
|
||||
[[nodiscard]] std::endian getEndianess() const { return this->m_endianess; }
|
||||
void setEndianess(std::endian endianess) { this->m_endianess = endianess; }
|
||||
|
||||
virtual void createEntry(prv::Provider* &provider) = 0;
|
||||
virtual std::string getTypeName() = 0;
|
||||
|
||||
virtual std::optional<u32> highlightBytes(size_t offset) {
|
||||
if (offset >= this->getOffset() && offset < (this->getOffset() + this->getSize()))
|
||||
return this->getColor();
|
||||
else
|
||||
return { };
|
||||
}
|
||||
|
||||
virtual void sort(ImGuiTableSortSpecs *sortSpecs, prv::Provider *provider) { }
|
||||
|
||||
static bool sortPatternDataTable(ImGuiTableSortSpecs *sortSpecs, prv::Provider *provider, lang::PatternData* left, lang::PatternData* right) {
|
||||
if (sortSpecs->Specs->ColumnUserID == ImGui::GetID("name")) {
|
||||
if (sortSpecs->Specs->SortDirection == ImGuiSortDirection_Ascending)
|
||||
return left->getName() > right->getName();
|
||||
else
|
||||
return left->getName() < right->getName();
|
||||
}
|
||||
else if (sortSpecs->Specs->ColumnUserID == ImGui::GetID("offset")) {
|
||||
if (sortSpecs->Specs->SortDirection == ImGuiSortDirection_Ascending)
|
||||
return left->getOffset() > right->getOffset();
|
||||
else
|
||||
return left->getOffset() < right->getOffset();
|
||||
}
|
||||
else if (sortSpecs->Specs->ColumnUserID == ImGui::GetID("size")) {
|
||||
if (sortSpecs->Specs->SortDirection == ImGuiSortDirection_Ascending)
|
||||
return left->getSize() > right->getSize();
|
||||
else
|
||||
return left->getSize() < right->getSize();
|
||||
}
|
||||
else if (sortSpecs->Specs->ColumnUserID == ImGui::GetID("value")) {
|
||||
size_t biggerSize = std::max(left->getSize(), right->getSize());
|
||||
std::vector<u8> leftBuffer(biggerSize, 0x00), rightBuffer(biggerSize, 0x00);
|
||||
|
||||
provider->read(left->getOffset(), leftBuffer.data(), left->getSize());
|
||||
provider->read(right->getOffset(), rightBuffer.data(), right->getSize());
|
||||
|
||||
if (left->m_endianess != std::endian::native)
|
||||
std::reverse(leftBuffer.begin(), leftBuffer.end());
|
||||
if (right->m_endianess != std::endian::native)
|
||||
std::reverse(rightBuffer.begin(), rightBuffer.end());
|
||||
|
||||
if (sortSpecs->Specs->SortDirection == ImGuiSortDirection_Ascending)
|
||||
return leftBuffer > rightBuffer;
|
||||
else
|
||||
return leftBuffer < rightBuffer;
|
||||
}
|
||||
else if (sortSpecs->Specs->ColumnUserID == ImGui::GetID("type")) {
|
||||
if (sortSpecs->Specs->SortDirection == ImGuiSortDirection_Ascending)
|
||||
return left->getTypeName() > right->getTypeName();
|
||||
else
|
||||
return left->getTypeName() < right->getTypeName();
|
||||
}
|
||||
else if (sortSpecs->Specs->ColumnUserID == ImGui::GetID("color")) {
|
||||
if (sortSpecs->Specs->SortDirection == ImGuiSortDirection_Ascending)
|
||||
return left->getColor() > right->getColor();
|
||||
else
|
||||
return left->getColor() < right->getColor();
|
||||
}
|
||||
|
||||
return false;
|
||||
}
|
||||
|
||||
static void resetPalette() { PatternData::s_paletteOffset = 0; }
|
||||
|
||||
protected:
|
||||
void createDefaultEntry(std::string value) {
|
||||
ImGui::TableNextRow();
|
||||
ImGui::TreeNodeEx(this->getName().c_str(), ImGuiTreeNodeFlags_Leaf | ImGuiTreeNodeFlags_NoTreePushOnOpen | ImGuiTreeNodeFlags_SpanFullWidth | ImGuiTreeNodeFlags_AllowItemOverlap);
|
||||
ImGui::TableNextColumn();
|
||||
if (ImGui::Selectable(("##PatternDataLine"s + std::to_string(this->getOffset())).c_str(), false, ImGuiSelectableFlags_SpanAllColumns | ImGuiSelectableFlags_AllowItemOverlap)) {
|
||||
Region selectRegion = { this->getOffset(), this->getSize() };
|
||||
View::postEvent(Events::SelectionChangeRequest, &selectRegion);
|
||||
}
|
||||
ImGui::SameLine();
|
||||
ImGui::Text("%s", this->getName().c_str());
|
||||
ImGui::TableNextColumn();
|
||||
ImGui::ColorButton("color", ImColor(this->getColor()), ImGuiColorEditFlags_NoTooltip, ImVec2(ImGui::GetColumnWidth(), 14));
|
||||
ImGui::TableNextColumn();
|
||||
ImGui::Text("0x%08lx : 0x%08lx", this->getOffset(), this->getOffset() + this->getSize() - 1);
|
||||
ImGui::TableNextColumn();
|
||||
ImGui::Text("0x%04lx", this->getSize());
|
||||
ImGui::TableNextColumn();
|
||||
ImGui::TextColored(ImColor(0xFF9BC64D), "%s", this->getTypeName().c_str());
|
||||
ImGui::TableNextColumn();
|
||||
ImGui::Text("%s", value.c_str());
|
||||
}
|
||||
|
||||
protected:
|
||||
std::endian m_endianess = std::endian::native;
|
||||
|
||||
private:
|
||||
Type m_type;
|
||||
u64 m_offset;
|
||||
size_t m_size;
|
||||
|
||||
u32 m_color;
|
||||
std::string m_name;
|
||||
|
||||
static inline u8 s_paletteOffset = 0;
|
||||
|
||||
};
|
||||
|
||||
class PatternDataPadding : public PatternData {
|
||||
public:
|
||||
PatternDataPadding(u64 offset, size_t size) : PatternData(Type::Padding, offset, size, "", { }, 0x00FFFFFF) { }
|
||||
|
||||
void createEntry(prv::Provider* &provider) override {
|
||||
}
|
||||
|
||||
std::string getTypeName() override {
|
||||
return "";
|
||||
}
|
||||
};
|
||||
|
||||
class PatternDataPointer : public PatternData {
|
||||
public:
|
||||
PatternDataPointer(u64 offset, size_t size, const std::string &name, PatternData *pointedAt, std::endian endianess, u32 color = 0)
|
||||
: PatternData(Type::Unsigned, offset, size, name, endianess, color), m_pointedAt(pointedAt) {
|
||||
this->m_pointedAt->setName("*" + this->m_pointedAt->getName());
|
||||
}
|
||||
|
||||
void createEntry(prv::Provider* &provider) override {
|
||||
u64 data = 0;
|
||||
provider->read(this->getOffset(), &data, this->getSize());
|
||||
data = hex::changeEndianess(data, this->getSize(), this->m_endianess);
|
||||
|
||||
ImGui::TableNextRow();
|
||||
ImGui::TableNextColumn();
|
||||
bool open = ImGui::TreeNodeEx(this->getName().c_str(), ImGuiTreeNodeFlags_SpanFullWidth);
|
||||
ImGui::TableNextColumn();
|
||||
ImGui::ColorButton("color", ImColor(this->getColor()), ImGuiColorEditFlags_NoTooltip, ImVec2(ImGui::GetColumnWidth(), 14));
|
||||
ImGui::TableNextColumn();
|
||||
ImGui::Text("0x%08lx : 0x%08lx", this->getOffset(), this->getOffset() + this->getSize() - 1);
|
||||
ImGui::TableNextColumn();
|
||||
ImGui::Text("0x%04lx", this->getSize());
|
||||
ImGui::TableNextColumn();
|
||||
ImGui::TextColored(ImColor(0xFF9BC64D), "%s", this->getTypeName().c_str());
|
||||
ImGui::TableNextColumn();
|
||||
ImGui::Text("*(0x%0*llx)", this->getSize() * 2, data);
|
||||
|
||||
if (open) {
|
||||
this->m_pointedAt->createEntry(provider);
|
||||
|
||||
ImGui::TreePop();
|
||||
}
|
||||
}
|
||||
|
||||
virtual std::optional<u32> highlightBytes(size_t offset) {
|
||||
if (offset >= this->getOffset() && offset < (this->getOffset() + this->getSize()))
|
||||
return this->getColor();
|
||||
else if (auto color = this->m_pointedAt->highlightBytes(offset); color.has_value())
|
||||
return color.value();
|
||||
else
|
||||
return { };
|
||||
}
|
||||
|
||||
std::string getTypeName() override {
|
||||
return "Pointer";
|
||||
}
|
||||
|
||||
private:
|
||||
PatternData *m_pointedAt;
|
||||
};
|
||||
|
||||
class PatternDataUnsigned : public PatternData {
|
||||
public:
|
||||
PatternDataUnsigned(u64 offset, size_t size, const std::string &name, std::endian endianess, u32 color = 0)
|
||||
: PatternData(Type::Unsigned, offset, size, name, endianess, color) { }
|
||||
|
||||
void createEntry(prv::Provider* &provider) override {
|
||||
u64 data = 0;
|
||||
provider->read(this->getOffset(), &data, this->getSize());
|
||||
data = hex::changeEndianess(data, this->getSize(), this->m_endianess);
|
||||
|
||||
this->createDefaultEntry(hex::format("%lu (0x%0*lx)", data, this->getSize() * 2, data));
|
||||
}
|
||||
|
||||
std::string getTypeName() override {
|
||||
switch (this->getSize()) {
|
||||
case 1: return "u8";
|
||||
case 2: return "u16";
|
||||
case 4: return "u32";
|
||||
case 8: return "u64";
|
||||
case 16: return "u128";
|
||||
default: return "Unsigned data";
|
||||
}
|
||||
}
|
||||
};
|
||||
|
||||
class PatternDataSigned : public PatternData {
|
||||
public:
|
||||
PatternDataSigned(u64 offset, size_t size, const std::string &name, std::endian endianess, u32 color = 0)
|
||||
: PatternData(Type::Signed, offset, size, name, endianess, color) { }
|
||||
|
||||
void createEntry(prv::Provider* &provider) override {
|
||||
u64 data = 0;
|
||||
provider->read(this->getOffset(), &data, this->getSize());
|
||||
data = hex::changeEndianess(data, this->getSize(), this->m_endianess);
|
||||
|
||||
s64 signedData = signedData = hex::signExtend(data, this->getSize(), 64);
|
||||
|
||||
this->createDefaultEntry(hex::format("%ld (0x%0*lx)", signedData, this->getSize() * 2, data));
|
||||
}
|
||||
|
||||
std::string getTypeName() override {
|
||||
switch (this->getSize()) {
|
||||
case 1: return "s8";
|
||||
case 2: return "s16";
|
||||
case 4: return "s32";
|
||||
case 8: return "s64";
|
||||
case 16: return "s128";
|
||||
default: return "Signed data";
|
||||
}
|
||||
}
|
||||
};
|
||||
|
||||
class PatternDataFloat : public PatternData {
|
||||
public:
|
||||
PatternDataFloat(u64 offset, size_t size, const std::string &name, std::endian endianess, u32 color = 0)
|
||||
: PatternData(Type::Float, offset, size, name, endianess, color) { }
|
||||
|
||||
void createEntry(prv::Provider* &provider) override {
|
||||
double formatData = 0;
|
||||
if (this->getSize() == 4) {
|
||||
float data = 0;
|
||||
provider->read(this->getOffset(), &data, 4);
|
||||
data = hex::changeEndianess(data, 4, this->m_endianess);
|
||||
|
||||
formatData = data;
|
||||
} else if (this->getSize() == 8) {
|
||||
double data = 0;
|
||||
provider->read(this->getOffset(), &data, 8);
|
||||
data = hex::changeEndianess(data, 8, this->m_endianess);
|
||||
|
||||
formatData = data;
|
||||
}
|
||||
|
||||
this->createDefaultEntry(hex::format("%f (0x%0*lx)", formatData, this->getSize() * 2, formatData));
|
||||
}
|
||||
|
||||
std::string getTypeName() override {
|
||||
switch (this->getSize()) {
|
||||
case 4: return "float";
|
||||
case 8: return "double";
|
||||
default: return "Floating point data";
|
||||
}
|
||||
}
|
||||
};
|
||||
|
||||
class PatternDataCharacter : public PatternData {
|
||||
public:
|
||||
PatternDataCharacter(u64 offset, size_t size, const std::string &name, std::endian endianess, u32 color = 0)
|
||||
: PatternData(Type::Character, offset, size, name, endianess, color) { }
|
||||
|
||||
void createEntry(prv::Provider* &provider) override {
|
||||
char character;
|
||||
provider->read(this->getOffset(), &character, 1);
|
||||
|
||||
this->createDefaultEntry(hex::format("'%c'", character));
|
||||
}
|
||||
|
||||
std::string getTypeName() override {
|
||||
return "Character";
|
||||
}
|
||||
};
|
||||
|
||||
class PatternDataString : public PatternData {
|
||||
public:
|
||||
PatternDataString(u64 offset, size_t size, const std::string &name, std::endian endianess, u32 color = 0)
|
||||
: PatternData(Type::String, offset, size, name, endianess, color) { }
|
||||
|
||||
void createEntry(prv::Provider* &provider) override {
|
||||
std::vector<u8> buffer(this->getSize() + 1, 0x00);
|
||||
provider->read(this->getOffset(), buffer.data(), this->getSize());
|
||||
buffer[this->getSize()] = '\0';
|
||||
|
||||
this->createDefaultEntry(hex::format("\"%s\"", makeDisplayable(buffer.data(), this->getSize()).c_str()));
|
||||
}
|
||||
|
||||
std::string getTypeName() override {
|
||||
return "String";
|
||||
}
|
||||
};
|
||||
|
||||
class PatternDataArray : public PatternData {
|
||||
public:
|
||||
PatternDataArray(u64 offset, size_t size, const std::string &name, std::endian endianess, const std::vector<PatternData*> & entries, u32 color = 0)
|
||||
: PatternData(Type::Array, offset, size, name, endianess, color), m_entries(entries) { }
|
||||
|
||||
void createEntry(prv::Provider* &provider) override {
|
||||
ImGui::TableNextRow();
|
||||
ImGui::TableNextColumn();
|
||||
bool open = ImGui::TreeNodeEx(this->getName().c_str(), ImGuiTreeNodeFlags_SpanFullWidth);
|
||||
ImGui::TableNextColumn();
|
||||
ImGui::ColorButton("color", ImColor(this->getColor()), ImGuiColorEditFlags_NoTooltip, ImVec2(ImGui::GetColumnWidth(), 14));
|
||||
ImGui::TableNextColumn();
|
||||
ImGui::Text("0x%08lx : 0x%08lx", this->getOffset(), this->getOffset() + this->getSize() - 1);
|
||||
ImGui::TableNextColumn();
|
||||
ImGui::Text("0x%04lx", this->getSize());
|
||||
ImGui::TableNextColumn();
|
||||
ImGui::TextColored(ImColor(0xFF9BC64D), "%s", this->m_entries[0]->getTypeName().c_str());
|
||||
ImGui::SameLine(0, 0);
|
||||
|
||||
ImGui::TextUnformatted("[");
|
||||
ImGui::SameLine(0, 0);
|
||||
ImGui::TextColored(ImColor(0xFF00FF00), "%llu", this->m_entries.size());
|
||||
ImGui::SameLine(0, 0);
|
||||
ImGui::TextUnformatted("]");
|
||||
|
||||
ImGui::TableNextColumn();
|
||||
ImGui::Text("%s", "{ ... }");
|
||||
|
||||
if (open) {
|
||||
for (auto &member : this->m_entries)
|
||||
member->createEntry(provider);
|
||||
|
||||
ImGui::TreePop();
|
||||
}
|
||||
}
|
||||
|
||||
std::optional<u32> highlightBytes(size_t offset) override{
|
||||
for (auto &entry : this->m_entries) {
|
||||
if (auto color = entry->highlightBytes(offset); color.has_value())
|
||||
return color.value();
|
||||
}
|
||||
|
||||
return { };
|
||||
}
|
||||
|
||||
std::string getTypeName() override {
|
||||
return this->m_entries[0]->getTypeName() + "[" + std::to_string(this->m_entries.size()) + "]";
|
||||
}
|
||||
|
||||
private:
|
||||
std::vector<PatternData*> m_entries;
|
||||
};
|
||||
|
||||
class PatternDataStruct : public PatternData {
|
||||
public:
|
||||
PatternDataStruct(u64 offset, size_t size, const std::string &name, std::endian endianess, const std::string &structName, const std::vector<PatternData*> & members, u32 color = 0)
|
||||
: PatternData(Type::Struct, offset, size, name, endianess, color), m_structName(structName), m_members(members), m_sortedMembers(members) { }
|
||||
|
||||
void createEntry(prv::Provider* &provider) override {
|
||||
ImGui::TableNextRow();
|
||||
ImGui::TableNextColumn();
|
||||
bool open = ImGui::TreeNodeEx(this->getName().c_str(), ImGuiTreeNodeFlags_SpanFullWidth);
|
||||
ImGui::TableNextColumn();
|
||||
ImGui::TableNextColumn();
|
||||
ImGui::Text("0x%08lx : 0x%08lx", this->getOffset(), this->getOffset() + this->getSize() - 1);
|
||||
ImGui::TableNextColumn();
|
||||
ImGui::Text("0x%04lx", this->getSize());
|
||||
ImGui::TableNextColumn();
|
||||
ImGui::TextColored(ImColor(0xFFD69C56), "struct"); ImGui::SameLine(); ImGui::Text("%s", this->m_structName.c_str());
|
||||
ImGui::TableNextColumn();
|
||||
ImGui::Text("%s", "{ ... }");
|
||||
|
||||
if (open) {
|
||||
for (auto &member : this->m_sortedMembers)
|
||||
member->createEntry(provider);
|
||||
|
||||
ImGui::TreePop();
|
||||
}
|
||||
|
||||
}
|
||||
|
||||
std::optional<u32> highlightBytes(size_t offset) override{
|
||||
for (auto &member : this->m_members) {
|
||||
if (auto color = member->highlightBytes(offset); color.has_value())
|
||||
return color.value();
|
||||
}
|
||||
|
||||
return { };
|
||||
}
|
||||
|
||||
void sort(ImGuiTableSortSpecs *sortSpecs, prv::Provider *provider) override {
|
||||
this->m_sortedMembers = this->m_members;
|
||||
|
||||
std::sort(this->m_sortedMembers.begin(), this->m_sortedMembers.end(), [&sortSpecs, &provider](PatternData *left, PatternData *right) {
|
||||
return PatternData::sortPatternDataTable(sortSpecs, provider, left, right);
|
||||
});
|
||||
|
||||
for (auto &member : this->m_members)
|
||||
member->sort(sortSpecs, provider);
|
||||
}
|
||||
|
||||
std::string getTypeName() override {
|
||||
return "struct " + this->m_structName;
|
||||
}
|
||||
|
||||
private:
|
||||
std::string m_structName;
|
||||
std::vector<PatternData*> m_members;
|
||||
std::vector<PatternData*> m_sortedMembers;
|
||||
};
|
||||
|
||||
class PatternDataUnion : public PatternData {
|
||||
public:
|
||||
PatternDataUnion(u64 offset, size_t size, const std::string &name, const std::string &unionName, const std::vector<PatternData*> & members, std::endian endianess, u32 color = 0)
|
||||
: PatternData(Type::Union, offset, size, name, endianess, color), m_unionName(unionName), m_members(members), m_sortedMembers(members) { }
|
||||
|
||||
void createEntry(prv::Provider* &provider) override {
|
||||
ImGui::TableNextRow();
|
||||
ImGui::TableNextColumn();
|
||||
bool open = ImGui::TreeNodeEx(this->getName().c_str(), ImGuiTreeNodeFlags_SpanFullWidth);
|
||||
ImGui::TableNextColumn();
|
||||
ImGui::TableNextColumn();
|
||||
ImGui::Text("0x%08lx : 0x%08lx", this->getOffset(), this->getOffset() + this->getSize() - 1);
|
||||
ImGui::TableNextColumn();
|
||||
ImGui::Text("0x%04lx", this->getSize());
|
||||
ImGui::TableNextColumn();
|
||||
ImGui::TextColored(ImColor(0xFFD69C56), "union"); ImGui::SameLine(); ImGui::Text("%s", this->m_unionName.c_str());
|
||||
|
||||
ImGui::TableNextColumn();
|
||||
ImGui::Text("%s", "{ ... }");
|
||||
|
||||
if (open) {
|
||||
for (auto &member : this->m_sortedMembers)
|
||||
member->createEntry(provider);
|
||||
|
||||
ImGui::TreePop();
|
||||
}
|
||||
|
||||
}
|
||||
|
||||
std::optional<u32> highlightBytes(size_t offset) override{
|
||||
for (auto &member : this->m_members) {
|
||||
if (auto color = member->highlightBytes(offset); color.has_value())
|
||||
return color.value();
|
||||
}
|
||||
|
||||
return { };
|
||||
}
|
||||
|
||||
void sort(ImGuiTableSortSpecs *sortSpecs, prv::Provider *provider) override {
|
||||
this->m_sortedMembers = this->m_members;
|
||||
|
||||
std::sort(this->m_sortedMembers.begin(), this->m_sortedMembers.end(), [&sortSpecs, &provider](PatternData *left, PatternData *right) {
|
||||
return PatternData::sortPatternDataTable(sortSpecs, provider, left, right);
|
||||
});
|
||||
|
||||
for (auto &member : this->m_members)
|
||||
member->sort(sortSpecs, provider);
|
||||
}
|
||||
|
||||
std::string getTypeName() override {
|
||||
return "union " + this->m_unionName;
|
||||
}
|
||||
|
||||
private:
|
||||
std::string m_unionName;
|
||||
std::vector<PatternData*> m_members;
|
||||
std::vector<PatternData*> m_sortedMembers;
|
||||
};
|
||||
|
||||
class PatternDataEnum : public PatternData {
|
||||
public:
|
||||
PatternDataEnum(u64 offset, size_t size, const std::string &name, const std::string &enumName, std::vector<std::pair<u64, std::string>> enumValues, std::endian endianess, u32 color = 0)
|
||||
: PatternData(Type::Enum, offset, size, name, endianess, color), m_enumName(enumName), m_enumValues(enumValues) { }
|
||||
|
||||
void createEntry(prv::Provider* &provider) override {
|
||||
u64 value = 0;
|
||||
provider->read(this->getOffset(), &value, this->getSize());
|
||||
value = hex::changeEndianess(value, this->getSize(), this->m_endianess);
|
||||
|
||||
std::string valueString = this->m_enumName + "::";
|
||||
|
||||
bool foundValue = false;
|
||||
for (auto &[entryValue, entryName] : this->m_enumValues) {
|
||||
if (value == entryValue) {
|
||||
valueString += entryName;
|
||||
foundValue = true;
|
||||
break;
|
||||
}
|
||||
}
|
||||
|
||||
if (!foundValue)
|
||||
valueString += "???";
|
||||
|
||||
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 {
|
||||
return "enum " + this->m_enumName;
|
||||
}
|
||||
|
||||
private:
|
||||
std::string m_enumName;
|
||||
std::vector<std::pair<u64, std::string>> m_enumValues;
|
||||
};
|
||||
|
||||
class PatternDataBitfield : public PatternData {
|
||||
public:
|
||||
PatternDataBitfield(u64 offset, size_t size, const std::string &name, const std::string &bitfieldName, std::vector<std::pair<std::string, size_t>> fields, std::endian endianess, u32 color = 0)
|
||||
: PatternData(Type::Enum, offset, size, name, endianess, color), m_bitfieldName(bitfieldName), m_fields(fields) { }
|
||||
|
||||
void createEntry(prv::Provider* &provider) override {
|
||||
u64 value = 0;
|
||||
provider->read(this->getOffset(), &value, this->getSize());
|
||||
value = hex::changeEndianess(value, this->getSize(), this->m_endianess);
|
||||
|
||||
ImGui::TableNextRow();
|
||||
ImGui::TableNextColumn();
|
||||
bool open = ImGui::TreeNodeEx(this->getName().c_str(), ImGuiTreeNodeFlags_SpanFullWidth);
|
||||
ImGui::TableNextColumn();
|
||||
ImGui::TableNextColumn();
|
||||
ImGui::Text("0x%08lx : 0x%08lx", this->getOffset(), this->getOffset() + this->getSize() - 1);
|
||||
ImGui::TableNextColumn();
|
||||
ImGui::Text("0x%04lx", this->getSize());
|
||||
ImGui::TableNextColumn();
|
||||
ImGui::TextColored(ImColor(0xFFD69C56), "bitfield"); ImGui::SameLine(); ImGui::Text("%s", this->m_bitfieldName.c_str());
|
||||
ImGui::TableNextColumn();
|
||||
ImGui::Text("{ %llx }", value);
|
||||
|
||||
if (open) {
|
||||
u16 bitOffset = 0;
|
||||
for (auto &[entryName, entrySize] : this->m_fields) {
|
||||
ImGui::TableNextRow();
|
||||
ImGui::TreeNodeEx(this->getName().c_str(), ImGuiTreeNodeFlags_Leaf | ImGuiTreeNodeFlags_NoTreePushOnOpen | ImGuiTreeNodeFlags_SpanFullWidth);
|
||||
ImGui::TableNextColumn();
|
||||
ImGui::Text("%s", entryName.c_str());
|
||||
ImGui::TableNextColumn();
|
||||
ImGui::ColorButton("color", ImColor(this->getColor()), ImGuiColorEditFlags_NoTooltip, ImVec2(ImGui::GetColumnWidth(), 14));
|
||||
ImGui::TableNextColumn();
|
||||
ImGui::Text("0x%08lx : 0x%08lx", this->getOffset() + (bitOffset >> 3), this->getOffset() + ((bitOffset + entrySize) >> 3) - 1);
|
||||
ImGui::TableNextColumn();
|
||||
if (entrySize == 1)
|
||||
ImGui::Text("%llu bit", entrySize);
|
||||
else
|
||||
ImGui::Text("%llu bits", entrySize);
|
||||
ImGui::TableNextColumn();
|
||||
ImGui::Text("%s", entryName.c_str());
|
||||
ImGui::TableNextColumn();
|
||||
ImGui::Text("%llx", hex::extract((bitOffset + entrySize) - 1, bitOffset, value));
|
||||
bitOffset += entrySize;
|
||||
}
|
||||
|
||||
ImGui::TreePop();
|
||||
}
|
||||
|
||||
}
|
||||
|
||||
std::string getTypeName() override {
|
||||
return "bitfield " + this->m_bitfieldName;
|
||||
}
|
||||
|
||||
private:
|
||||
std::string m_bitfieldName;
|
||||
std::vector<std::pair<std::string, size_t>> m_fields;
|
||||
};
|
||||
|
||||
|
||||
}
|
||||
35
include/lang/preprocessor.hpp
Normal file
35
include/lang/preprocessor.hpp
Normal file
@@ -0,0 +1,35 @@
|
||||
#pragma once
|
||||
|
||||
#include <hex.hpp>
|
||||
|
||||
#include "token.hpp"
|
||||
|
||||
#include <functional>
|
||||
#include <set>
|
||||
#include <string>
|
||||
#include <unordered_map>
|
||||
#include <utility>
|
||||
|
||||
namespace hex::lang {
|
||||
|
||||
class Preprocessor {
|
||||
public:
|
||||
Preprocessor();
|
||||
|
||||
std::pair<Result, std::string> preprocess(const std::string& code, bool initialRun = true);
|
||||
|
||||
void addPragmaHandler(std::string pragmaType, std::function<bool(std::string)> function);
|
||||
void addDefaultPragmaHandlers();
|
||||
|
||||
const std::pair<u32, std::string>& getError() { return this->m_error; }
|
||||
|
||||
private:
|
||||
std::unordered_map<std::string, std::function<bool(std::string)>> m_pragmaHandlers;
|
||||
|
||||
std::set<std::pair<std::string, std::string>> m_defines;
|
||||
std::set<std::pair<std::string, std::string>> m_pragmas;
|
||||
|
||||
std::pair<u32, std::string> m_error;
|
||||
};
|
||||
|
||||
}
|
||||
14
include/lang/results.hpp
Normal file
14
include/lang/results.hpp
Normal file
@@ -0,0 +1,14 @@
|
||||
#pragma once
|
||||
|
||||
#include "result.hpp"
|
||||
|
||||
namespace hex::lang {
|
||||
|
||||
constexpr Result ResultSuccess(0, 0);
|
||||
|
||||
constexpr Result ResultPreprocessingError(1, 1);
|
||||
constexpr Result ResultLexicalError(2, 1);
|
||||
constexpr Result ResultParseError(3, 1);
|
||||
constexpr Result ResultEvaluatorError(4, 1);
|
||||
|
||||
}
|
||||
@@ -25,7 +25,12 @@ namespace hex::lang {
|
||||
struct KeywordToken {
|
||||
enum class Keyword {
|
||||
Struct,
|
||||
Using
|
||||
Union,
|
||||
Using,
|
||||
Enum,
|
||||
Bitfield,
|
||||
LittleEndian,
|
||||
BigEndian
|
||||
} keyword;
|
||||
} keywordToken;
|
||||
struct IdentifierToken {
|
||||
@@ -34,7 +39,9 @@ namespace hex::lang {
|
||||
struct OperatorToken {
|
||||
enum class Operator {
|
||||
AtDeclaration,
|
||||
Assignment
|
||||
Assignment,
|
||||
Inherit,
|
||||
Star
|
||||
} op;
|
||||
} operatorToken;
|
||||
struct IntegerToken {
|
||||
@@ -54,8 +61,11 @@ namespace hex::lang {
|
||||
Signed128Bit = 0x101,
|
||||
Float = 0x42,
|
||||
Double = 0x82,
|
||||
CustomType = 0x00
|
||||
CustomType = 0x00,
|
||||
Padding = 0x1F
|
||||
} type;
|
||||
} typeToken;
|
||||
|
||||
u32 lineNumber;
|
||||
};
|
||||
}
|
||||
25
include/lang/validator.hpp
Normal file
25
include/lang/validator.hpp
Normal file
@@ -0,0 +1,25 @@
|
||||
#pragma once
|
||||
|
||||
#include <hex.hpp>
|
||||
|
||||
#include "token.hpp"
|
||||
#include "ast_node.hpp"
|
||||
|
||||
#include <string>
|
||||
#include <vector>
|
||||
|
||||
namespace hex::lang {
|
||||
|
||||
class Validator {
|
||||
public:
|
||||
Validator();
|
||||
|
||||
bool validate(const std::vector<ASTNode*>& ast);
|
||||
|
||||
const std::pair<u32, std::string>& getError() { return this->m_error; }
|
||||
|
||||
private:
|
||||
std::pair<u32, std::string> m_error;
|
||||
};
|
||||
|
||||
}
|
||||
@@ -1,81 +0,0 @@
|
||||
#pragma once
|
||||
|
||||
#include "token.hpp"
|
||||
|
||||
#include <optional>
|
||||
#include <vector>
|
||||
|
||||
namespace hex::lang {
|
||||
|
||||
class ASTNode {
|
||||
public:
|
||||
enum class Type {
|
||||
VariableDecl,
|
||||
TypeDecl,
|
||||
Struct,
|
||||
Scope,
|
||||
};
|
||||
|
||||
explicit ASTNode(Type type) : m_type(type) {}
|
||||
virtual ~ASTNode() = default;
|
||||
|
||||
Type getType() { return this->m_type; }
|
||||
|
||||
private:
|
||||
Type m_type;
|
||||
};
|
||||
|
||||
class ASTNodeVariableDecl : public ASTNode {
|
||||
public:
|
||||
explicit ASTNodeVariableDecl(const Token::TypeToken::Type &type, const std::string &name, const std::string& customTypeName = "", std::optional<u64> offset = { }, size_t arraySize = 1)
|
||||
: ASTNode(Type::VariableDecl), m_type(type), m_name(name), m_customTypeName(customTypeName), m_offset(offset), m_arraySize(arraySize) { }
|
||||
|
||||
const Token::TypeToken::Type& getVariableType() const { return this->m_type; }
|
||||
const std::string& getCustomVariableTypeName() const { return this->m_customTypeName; }
|
||||
const std::string& getVariableName() const { return this->m_name; };
|
||||
std::optional<u64> getOffset() const { return this->m_offset; }
|
||||
size_t getArraySize() const { return this->m_arraySize; }
|
||||
|
||||
private:
|
||||
Token::TypeToken::Type m_type;
|
||||
std::string m_name, m_customTypeName;
|
||||
std::optional<u64> m_offset;
|
||||
size_t m_arraySize;
|
||||
};
|
||||
|
||||
class ASTNodeScope : public ASTNode {
|
||||
public:
|
||||
explicit ASTNodeScope(std::vector<ASTNode*> nodes) : ASTNode(Type::Scope), m_nodes(nodes) { }
|
||||
|
||||
std::vector<ASTNode*> &getNodes() { return this->m_nodes; }
|
||||
private:
|
||||
std::vector<ASTNode*> m_nodes;
|
||||
};
|
||||
|
||||
class ASTNodeStruct : public ASTNode {
|
||||
public:
|
||||
explicit ASTNodeStruct(std::string name, std::vector<ASTNode*> nodes)
|
||||
: ASTNode(Type::Struct), m_name(name), m_nodes(nodes) { }
|
||||
|
||||
const std::string& getName() const { return this->m_name; }
|
||||
std::vector<ASTNode*> &getNodes() { return this->m_nodes; }
|
||||
private:
|
||||
std::string m_name;
|
||||
std::vector<ASTNode*> m_nodes;
|
||||
};
|
||||
|
||||
class ASTNodeTypeDecl : public ASTNode {
|
||||
public:
|
||||
explicit ASTNodeTypeDecl(const Token::TypeToken::Type &type, const std::string &name, const std::string& customTypeName = "")
|
||||
: ASTNode(Type::TypeDecl), m_type(type), m_name(name), m_customTypeName(customTypeName) { }
|
||||
|
||||
const std::string& getTypeName() const { return this->m_name; };
|
||||
|
||||
const Token::TypeToken::Type& getAssignedType() const { return this->m_type; }
|
||||
const std::string& getAssignedCustomTypeName() const { return this->m_customTypeName; }
|
||||
private:
|
||||
Token::TypeToken::Type m_type;
|
||||
std::string m_name, m_customTypeName;
|
||||
};
|
||||
|
||||
}
|
||||
@@ -1,19 +0,0 @@
|
||||
#pragma once
|
||||
|
||||
#include <hex.hpp>
|
||||
#include "token.hpp"
|
||||
#include "ast_node.hpp"
|
||||
|
||||
#include <vector>
|
||||
|
||||
namespace hex::lang {
|
||||
|
||||
class Parser {
|
||||
public:
|
||||
Parser();
|
||||
|
||||
std::pair<Result, std::vector<ASTNode*>> parse(const std::vector<Token> &tokens);
|
||||
|
||||
};
|
||||
|
||||
}
|
||||
@@ -1,12 +0,0 @@
|
||||
#pragma once
|
||||
|
||||
#include "result.hpp"
|
||||
|
||||
namespace hex::lang {
|
||||
|
||||
constexpr Result ResultSuccess(0, 0);
|
||||
|
||||
constexpr Result ResultLexicalError(1, 1);
|
||||
constexpr Result ResultParseError(2, 1);
|
||||
|
||||
}
|
||||
@@ -4,6 +4,8 @@
|
||||
|
||||
#include <string_view>
|
||||
|
||||
#include <sys/stat.h>
|
||||
|
||||
namespace hex::prv {
|
||||
|
||||
class FileProvider : public Provider {
|
||||
@@ -16,11 +18,20 @@ namespace hex::prv {
|
||||
bool isWritable() override;
|
||||
|
||||
void read(u64 offset, void *buffer, size_t size) override;
|
||||
void write(u64 offset, void *buffer, size_t size) override;
|
||||
size_t getSize() override;
|
||||
void write(u64 offset, const void *buffer, size_t size) override;
|
||||
|
||||
void readRaw(u64 offset, void *buffer, size_t size) override;
|
||||
void writeRaw(u64 offset, const void *buffer, size_t size) override;
|
||||
size_t getActualSize() override;
|
||||
|
||||
std::vector<std::pair<std::string, std::string>> getDataInformation() override;
|
||||
|
||||
private:
|
||||
FILE *m_file;
|
||||
std::string m_path;
|
||||
|
||||
bool m_fileStatsValid = false;
|
||||
struct stat m_fileStats = { 0 };
|
||||
|
||||
bool m_readable, m_writable;
|
||||
};
|
||||
|
||||
@@ -2,20 +2,69 @@
|
||||
|
||||
#include <hex.hpp>
|
||||
|
||||
#include <cmath>
|
||||
#include <concepts>
|
||||
#include <map>
|
||||
#include <optional>
|
||||
#include <string>
|
||||
#include <vector>
|
||||
|
||||
namespace hex::prv {
|
||||
|
||||
class Provider {
|
||||
public:
|
||||
Provider() = default;
|
||||
constexpr static size_t PageSize = 0x1000'0000;
|
||||
|
||||
Provider() {
|
||||
this->m_patches.emplace_back();
|
||||
}
|
||||
|
||||
virtual ~Provider() = default;
|
||||
|
||||
virtual bool isAvailable() = 0;
|
||||
virtual bool isReadable() = 0;
|
||||
virtual bool isWritable() = 0;
|
||||
|
||||
virtual void read(u64 offset, void *buffer, size_t size) = 0;
|
||||
virtual void write(u64 offset, void *buffer, size_t size) = 0;
|
||||
virtual size_t getSize() = 0;
|
||||
virtual void read(u64 offset, void *buffer, size_t size) { this->readRaw(offset, buffer, size); }
|
||||
virtual void write(u64 offset, const void *buffer, size_t size) { this->writeRaw(offset, buffer, size); }
|
||||
|
||||
virtual void readRaw(u64 offset, void *buffer, size_t size) = 0;
|
||||
virtual void writeRaw(u64 offset, const void *buffer, size_t size) = 0;
|
||||
virtual size_t getActualSize() = 0;
|
||||
|
||||
std::map<u64, u8>& getPatches() { return this->m_patches.back(); }
|
||||
void applyPatches() {
|
||||
for (auto &[patchAddress, patch] : this->m_patches.back())
|
||||
this->writeRaw(patchAddress, &patch, 1);
|
||||
}
|
||||
|
||||
u32 getPageCount() { return std::ceil(this->getActualSize() / double(PageSize)); }
|
||||
u32 getCurrentPage() const { return this->m_currPage; }
|
||||
void setCurrentPage(u32 page) { if (page < getPageCount()) this->m_currPage = page; }
|
||||
|
||||
virtual size_t getBaseAddress() {
|
||||
return PageSize * this->m_currPage;
|
||||
}
|
||||
|
||||
virtual size_t getSize() {
|
||||
return std::min(this->getActualSize() - PageSize * this->m_currPage, PageSize);
|
||||
}
|
||||
|
||||
virtual std::optional<u32> getPageOfAddress(u64 address) {
|
||||
u32 page = std::floor(address / double(PageSize));
|
||||
|
||||
if (page >= this->getPageCount())
|
||||
return { };
|
||||
|
||||
return page;
|
||||
}
|
||||
|
||||
virtual std::vector<std::pair<std::string, std::string>> getDataInformation() = 0;
|
||||
|
||||
protected:
|
||||
u32 m_currPage = 0;
|
||||
|
||||
std::vector<std::map<u64, u8>> m_patches;
|
||||
};
|
||||
|
||||
}
|
||||
@@ -1,11 +0,0 @@
|
||||
#pragma once
|
||||
|
||||
#include <hex.hpp>
|
||||
|
||||
#include <array>
|
||||
#include <optional>
|
||||
#include <string>
|
||||
|
||||
namespace hex {
|
||||
|
||||
}
|
||||
@@ -1,20 +0,0 @@
|
||||
#pragma once
|
||||
|
||||
#include <hex.hpp>
|
||||
#include <string>
|
||||
|
||||
namespace hex {
|
||||
|
||||
struct Highlight {
|
||||
Highlight(u64 offset, size_t size, u32 color, std::string name)
|
||||
: offset(offset), size(size), color(color), name(name) {
|
||||
|
||||
}
|
||||
|
||||
u64 offset;
|
||||
size_t size;
|
||||
u32 color;
|
||||
std::string name;
|
||||
};
|
||||
|
||||
}
|
||||
@@ -4,21 +4,66 @@
|
||||
|
||||
#include "imgui.h"
|
||||
|
||||
#include "event.hpp"
|
||||
#include "helpers/event.hpp"
|
||||
|
||||
#include <functional>
|
||||
#include <string>
|
||||
#include <vector>
|
||||
|
||||
|
||||
namespace hex {
|
||||
|
||||
class View {
|
||||
public:
|
||||
View() { }
|
||||
View(std::string viewName) : m_viewName(viewName) { }
|
||||
virtual ~View() { }
|
||||
|
||||
virtual void createView() = 0;
|
||||
virtual void createMenu() { }
|
||||
virtual bool handleShortcut(int key, int mods) { return false; }
|
||||
|
||||
static std::vector<std::function<void()>>& getDeferedCalls() {
|
||||
return View::s_deferedCalls;
|
||||
}
|
||||
|
||||
static void postEvent(Events eventType, const void *userData = nullptr) {
|
||||
View::s_eventManager.post(eventType, userData);
|
||||
}
|
||||
|
||||
static void drawCommonInterfaces() {
|
||||
if (ImGui::BeginPopupModal("Error", nullptr, ImGuiWindowFlags_NoResize)) {
|
||||
ImGui::NewLine();
|
||||
if (ImGui::BeginChild("##scrolling", ImVec2(300, 100))) {
|
||||
ImGui::SetCursorPosX((300 - ImGui::CalcTextSize(View::s_errorMessage.c_str(), nullptr, false).x) / 2.0F);
|
||||
ImGui::TextWrapped("%s", View::s_errorMessage.c_str());
|
||||
ImGui::EndChild();
|
||||
}
|
||||
ImGui::NewLine();
|
||||
ImGui::SetCursorPosX(75);
|
||||
if (ImGui::Button("Okay", ImVec2(150, 20)))
|
||||
ImGui::CloseCurrentPopup();
|
||||
ImGui::EndPopup();
|
||||
}
|
||||
}
|
||||
|
||||
static void showErrorPopup(std::string_view errorMessage) {
|
||||
View::s_errorMessage = errorMessage;
|
||||
|
||||
ImGui::OpenPopup("Error");
|
||||
}
|
||||
|
||||
virtual bool hasViewMenuItemEntry() { return true; }
|
||||
|
||||
bool& getWindowOpenState() {
|
||||
return this->m_windowOpen;
|
||||
}
|
||||
|
||||
const std::string getName() const {
|
||||
return this->m_viewName;
|
||||
}
|
||||
|
||||
protected:
|
||||
void subscribeEvent(Events eventType, std::function<void(void*)> callback) {
|
||||
void subscribeEvent(Events eventType, std::function<void(const void*)> callback) {
|
||||
View::s_eventManager.subscribe(eventType, this, callback);
|
||||
}
|
||||
|
||||
@@ -26,12 +71,20 @@ namespace hex {
|
||||
View::s_eventManager.unsubscribe(eventType, this);
|
||||
}
|
||||
|
||||
void postEvent(Events eventType, void *userData = nullptr) {
|
||||
View::s_eventManager.post(eventType, userData);
|
||||
void doLater(std::function<void()> &&function) {
|
||||
View::s_deferedCalls.push_back(function);
|
||||
}
|
||||
|
||||
|
||||
|
||||
private:
|
||||
std::string m_viewName;
|
||||
bool m_windowOpen = false;
|
||||
|
||||
static inline EventManager s_eventManager;
|
||||
static inline std::vector<std::function<void()>> s_deferedCalls;
|
||||
|
||||
static inline std::string s_errorMessage;
|
||||
};
|
||||
|
||||
}
|
||||
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;
|
||||
};
|
||||
|
||||
}
|
||||
63
include/views/view_data_inspector.hpp
Normal file
63
include/views/view_data_inspector.hpp
Normal file
@@ -0,0 +1,63 @@
|
||||
#pragma once
|
||||
|
||||
#include "views/view.hpp"
|
||||
|
||||
#include <bit>
|
||||
#include <cstdio>
|
||||
#include <ctime>
|
||||
#include <string>
|
||||
|
||||
namespace hex {
|
||||
|
||||
namespace prv { class Provider; }
|
||||
|
||||
struct GUID {
|
||||
u32 data1;
|
||||
u16 data2;
|
||||
u16 data3;
|
||||
u8 data4[8];
|
||||
};
|
||||
|
||||
union PreviewData {
|
||||
u8 unsigned8;
|
||||
s8 signed8;
|
||||
u16 unsigned16;
|
||||
s16 signed16;
|
||||
u32 unsigned32;
|
||||
s32 signed32;
|
||||
u64 unsigned64;
|
||||
s64 signed64;
|
||||
char8_t ansiChar;
|
||||
char16_t wideChar;
|
||||
u8 utf8Char[4];
|
||||
float float32;
|
||||
double float64;
|
||||
#if defined(_WIN64)
|
||||
__time32_t time32;
|
||||
__time64_t time64;
|
||||
#else
|
||||
time_t time;
|
||||
#endif
|
||||
GUID guid;
|
||||
};
|
||||
|
||||
class ViewDataInspector : public View {
|
||||
public:
|
||||
explicit ViewDataInspector(prv::Provider* &dataProvider);
|
||||
~ViewDataInspector() override;
|
||||
|
||||
void createView() override;
|
||||
void createMenu() override;
|
||||
|
||||
private:
|
||||
prv::Provider* &m_dataProvider;
|
||||
bool m_shouldInvalidate = true;
|
||||
|
||||
std::endian m_endianess = std::endian::native;
|
||||
|
||||
PreviewData m_previewData = { 0 };
|
||||
size_t m_validBytes = 0;
|
||||
std::vector<std::pair<std::string, std::string>> m_cachedData;
|
||||
};
|
||||
|
||||
}
|
||||
50
include/views/view_disassembler.hpp
Normal file
50
include/views/view_disassembler.hpp
Normal file
@@ -0,0 +1,50 @@
|
||||
#pragma once
|
||||
|
||||
#include "helpers/disassembler.hpp"
|
||||
#include "views/view.hpp"
|
||||
|
||||
#include <capstone/capstone.h>
|
||||
|
||||
#include <cstdio>
|
||||
#include <string>
|
||||
#include <vector>
|
||||
|
||||
namespace hex {
|
||||
|
||||
namespace prv { class Provider; }
|
||||
|
||||
struct Disassembly {
|
||||
u64 address;
|
||||
u64 offset;
|
||||
size_t size;
|
||||
std::string bytes;
|
||||
std::string mnemonic;
|
||||
std::string operators;
|
||||
};
|
||||
|
||||
class ViewDisassembler : public View {
|
||||
public:
|
||||
explicit ViewDisassembler(prv::Provider* &dataProvider);
|
||||
~ViewDisassembler() override;
|
||||
|
||||
void createView() override;
|
||||
void createMenu() override;
|
||||
|
||||
private:
|
||||
prv::Provider* &m_dataProvider;
|
||||
bool m_shouldInvalidate = false;
|
||||
|
||||
u64 m_baseAddress = 0;
|
||||
u64 m_codeRegion[2] = { 0 };
|
||||
bool m_shouldMatchSelection = false;
|
||||
|
||||
Architecture m_architecture = Architecture::ARM;
|
||||
cs_mode m_modeBasicARM = cs_mode(0), m_modeExtraARM = cs_mode(0), m_modeBasicMIPS = cs_mode(0), m_modeBasicPPC = cs_mode(0), m_modeBasicX86 = cs_mode(0);
|
||||
bool m_littleEndianMode = true, m_micoMode = false, m_sparcV9Mode = false;
|
||||
|
||||
std::vector<Disassembly> m_disassembly;
|
||||
|
||||
|
||||
};
|
||||
|
||||
}
|
||||
@@ -18,11 +18,12 @@ namespace hex {
|
||||
|
||||
private:
|
||||
prv::Provider* &m_dataProvider;
|
||||
bool m_windowOpen = true;
|
||||
|
||||
bool m_shouldInvalidate = true;
|
||||
int m_currHashFunction = 0;
|
||||
int m_hashStart = 0, m_hashEnd = 0;
|
||||
u64 m_hashRegion[2] = { 0 };
|
||||
bool m_shouldMatchSelection = false;
|
||||
|
||||
static constexpr const char* HashFunctionNames[] = { "CRC16", "CRC32", "MD4", "MD5", "SHA-1", "SHA-224", "SHA-256", "SHA-384", "SHA-512" };
|
||||
};
|
||||
|
||||
|
||||
37
include/views/view_help.hpp
Normal file
37
include/views/view_help.hpp
Normal file
@@ -0,0 +1,37 @@
|
||||
#pragma once
|
||||
|
||||
#include <hex.hpp>
|
||||
|
||||
#include "imgui.h"
|
||||
#include "views/view.hpp"
|
||||
#include "lang/pattern_data.hpp"
|
||||
|
||||
#include <vector>
|
||||
#include <tuple>
|
||||
#include <cstdio>
|
||||
|
||||
namespace hex {
|
||||
|
||||
namespace prv { class Provider; }
|
||||
|
||||
class ViewHelp : public View {
|
||||
public:
|
||||
ViewHelp();
|
||||
~ViewHelp() override;
|
||||
|
||||
void createView() override;
|
||||
void createMenu() override;
|
||||
|
||||
bool hasViewMenuItemEntry() override { return false; }
|
||||
|
||||
private:
|
||||
bool m_aboutWindowOpen = false;
|
||||
bool m_patternHelpWindowOpen = false;
|
||||
bool m_mathHelpWindowOpen = false;
|
||||
|
||||
void drawAboutPopup();
|
||||
void drawPatternHelpPopup();
|
||||
void drawMathEvaluatorHelp();
|
||||
};
|
||||
|
||||
}
|
||||
@@ -1,24 +1,26 @@
|
||||
#pragma once
|
||||
|
||||
#include "utils.hpp"
|
||||
#include "helpers/utils.hpp"
|
||||
#include "views/view.hpp"
|
||||
|
||||
#include "imgui_memory_editor.h"
|
||||
#include "imfilebrowser.h"
|
||||
#include "ImGuiFileBrowser.h"
|
||||
|
||||
#include <tuple>
|
||||
#include <random>
|
||||
#include <vector>
|
||||
|
||||
#include "views/highlight.hpp"
|
||||
#include "lang/pattern_data.hpp"
|
||||
|
||||
namespace hex {
|
||||
|
||||
namespace prv { class Provider; }
|
||||
|
||||
using SearchFunction = std::vector<std::pair<u64, u64>> (*)(prv::Provider* &provider, std::string string);
|
||||
|
||||
class ViewHexEditor : public View {
|
||||
public:
|
||||
ViewHexEditor(prv::Provider* &dataProvider, std::vector<Highlight> &highlights);
|
||||
ViewHexEditor(prv::Provider* &dataProvider, std::vector<lang::PatternData*> &patternData);
|
||||
~ViewHexEditor() override;
|
||||
|
||||
void createView() override;
|
||||
@@ -27,21 +29,41 @@ namespace hex {
|
||||
|
||||
private:
|
||||
MemoryEditor m_memoryEditor;
|
||||
|
||||
ImGui::FileBrowser m_fileBrowser;
|
||||
|
||||
imgui_addons::ImGuiFileBrowser m_fileBrowser;
|
||||
|
||||
prv::Provider* &m_dataProvider;
|
||||
std::vector<Highlight> &m_highlights;
|
||||
std::vector<lang::PatternData*> &m_patternData;
|
||||
|
||||
char m_searchStringBuffer[0xFFFF] = { 0 };
|
||||
char m_searchHexBuffer[0xFFFF] = { 0 };
|
||||
SearchFunction m_searchFunction = nullptr;
|
||||
std::vector<std::pair<u64, u64>> *m_lastSearchBuffer;
|
||||
|
||||
char m_searchBuffer[0xFFFF] = { 0 };
|
||||
s64 m_lastSearchIndex = 0;
|
||||
std::vector<std::pair<u64, u64>> m_lastSearch;
|
||||
u64 m_gotoAddress = 0;
|
||||
std::vector<std::pair<u64, u64>> m_lastStringSearch;
|
||||
std::vector<std::pair<u64, u64>> m_lastHexSearch;
|
||||
|
||||
s64 m_gotoAddress = 0;
|
||||
|
||||
std::vector<u8> m_dataToSave;
|
||||
|
||||
std::string m_loaderScriptScriptPath;
|
||||
std::string m_loaderScriptFilePath;
|
||||
|
||||
void drawSearchPopup();
|
||||
void drawGotoPopup();
|
||||
|
||||
void openFile(std::string path);
|
||||
bool saveToFile(std::string path, const std::vector<u8>& data);
|
||||
bool loadFromFile(std::string path, std::vector<u8>& data);
|
||||
|
||||
enum class Language { C, Cpp, CSharp, Rust, Python, Java, JavaScript };
|
||||
void copyBytes();
|
||||
void copyString();
|
||||
void copyLanguageArray(Language language);
|
||||
void copyHexView();
|
||||
void copyHexViewHTML();
|
||||
|
||||
};
|
||||
|
||||
}
|
||||
@@ -21,14 +21,17 @@ namespace hex {
|
||||
|
||||
private:
|
||||
prv::Provider* &m_dataProvider;
|
||||
bool m_windowOpen = true;
|
||||
|
||||
bool m_dataValid = false;
|
||||
u32 m_blockSize = 0;
|
||||
float m_averageEntropy = 0;
|
||||
float m_highestBlockEntropy = 0;
|
||||
std::vector<float> m_blockEntropy;
|
||||
|
||||
std::array<float, 256> m_valueCounts = { 0 };
|
||||
bool m_shouldInvalidate = true;
|
||||
bool m_shouldInvalidate = false;
|
||||
|
||||
std::pair<u64, u64> m_analyzedRegion = { 0, 0 };
|
||||
|
||||
std::string m_fileDescription;
|
||||
std::string m_mimeType;
|
||||
|
||||
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;
|
||||
};
|
||||
|
||||
}
|
||||
@@ -1,41 +1,40 @@
|
||||
#pragma once
|
||||
|
||||
#include "parser/ast_node.hpp"
|
||||
#include "parser/parser.hpp"
|
||||
#include "parser/lexer.hpp"
|
||||
#include "lang/ast_node.hpp"
|
||||
|
||||
#include "views/view.hpp"
|
||||
#include "lang/pattern_data.hpp"
|
||||
|
||||
#include "providers/provider.hpp"
|
||||
|
||||
#include <concepts>
|
||||
#include <cstring>
|
||||
#include <filesystem>
|
||||
|
||||
#include "views/highlight.hpp"
|
||||
|
||||
#include "imfilebrowser.h"
|
||||
#include "ImGuiFileBrowser.h"
|
||||
#include "TextEditor.h"
|
||||
|
||||
namespace hex {
|
||||
|
||||
class ViewPattern : public View {
|
||||
public:
|
||||
explicit ViewPattern(std::vector<Highlight> &highlights);
|
||||
explicit ViewPattern(prv::Provider* &dataProvider, std::vector<lang::PatternData*> &patternData);
|
||||
~ViewPattern() override;
|
||||
|
||||
void createMenu() override;
|
||||
void createView() override;
|
||||
|
||||
private:
|
||||
char *m_buffer;
|
||||
std::vector<lang::PatternData*> &m_patternData;
|
||||
prv::Provider* &m_dataProvider;
|
||||
std::filesystem::path m_possiblePatternFile;
|
||||
|
||||
std::vector<Highlight> &m_highlights;
|
||||
bool m_windowOpen = true;
|
||||
TextEditor m_textEditor;
|
||||
imgui_addons::ImGuiFileBrowser m_fileBrowser;
|
||||
|
||||
ImGui::FileBrowser m_fileBrowser;
|
||||
|
||||
|
||||
void setHighlight(u64 offset, size_t size, std::string name, u32 color = 0);
|
||||
void loadPatternFile(std::string path);
|
||||
void clearPatternData();
|
||||
void parsePattern(char *buffer);
|
||||
|
||||
s32 highlightUsingDecls(std::vector<lang::ASTNode*> &ast, lang::ASTNodeTypeDecl* currTypeDeclNode, lang::ASTNodeVariableDecl* currVarDec, u64 offset);
|
||||
s32 highlightStruct(std::vector<lang::ASTNode*> &ast, lang::ASTNodeStruct* currStructNode, u64 offset);
|
||||
};
|
||||
|
||||
}
|
||||
@@ -4,7 +4,7 @@
|
||||
|
||||
#include "imgui.h"
|
||||
#include "views/view.hpp"
|
||||
#include "views/highlight.hpp"
|
||||
#include "lang/pattern_data.hpp"
|
||||
|
||||
#include <vector>
|
||||
#include <tuple>
|
||||
@@ -16,16 +16,17 @@ namespace hex {
|
||||
|
||||
class ViewPatternData : public View {
|
||||
public:
|
||||
ViewPatternData(prv::Provider* &dataProvider, std::vector<Highlight> &highlights);
|
||||
ViewPatternData(prv::Provider* &dataProvider, std::vector<lang::PatternData*> &patternData);
|
||||
~ViewPatternData() override;
|
||||
|
||||
void createView() override;
|
||||
void createMenu() override;
|
||||
|
||||
private:
|
||||
|
||||
prv::Provider* &m_dataProvider;
|
||||
std::vector<Highlight> &m_highlights;
|
||||
bool m_windowOpen = true;
|
||||
std::vector<lang::PatternData*> &m_patternData;
|
||||
std::vector<lang::PatternData*> m_sortedPatternData;
|
||||
};
|
||||
|
||||
}
|
||||
40
include/views/view_strings.hpp
Normal file
40
include/views/view_strings.hpp
Normal file
@@ -0,0 +1,40 @@
|
||||
#pragma once
|
||||
|
||||
#include "views/view.hpp"
|
||||
|
||||
#include <cstdio>
|
||||
#include <string>
|
||||
|
||||
namespace hex {
|
||||
|
||||
namespace prv { class Provider; }
|
||||
|
||||
struct FoundString {
|
||||
std::string string;
|
||||
u64 offset;
|
||||
size_t size;
|
||||
};
|
||||
|
||||
class ViewStrings : public View {
|
||||
public:
|
||||
explicit ViewStrings(prv::Provider* &dataProvider);
|
||||
~ViewStrings() override;
|
||||
|
||||
void createView() override;
|
||||
void createMenu() override;
|
||||
|
||||
private:
|
||||
prv::Provider* &m_dataProvider;
|
||||
bool m_shouldInvalidate = false;
|
||||
|
||||
std::vector<FoundString> m_foundStrings;
|
||||
int m_minimumLength = 5;
|
||||
char *m_filter;
|
||||
|
||||
std::string m_selectedString;
|
||||
std::string m_demangledName;
|
||||
|
||||
void createStringContextMenu(const FoundString &foundString);
|
||||
};
|
||||
|
||||
}
|
||||
51
include/views/view_tools.hpp
Normal file
51
include/views/view_tools.hpp
Normal file
@@ -0,0 +1,51 @@
|
||||
#pragma once
|
||||
|
||||
#include <hex.hpp>
|
||||
|
||||
#include "imgui.h"
|
||||
#include "views/view.hpp"
|
||||
#include "helpers/math_evaluator.hpp"
|
||||
|
||||
#include <array>
|
||||
#include <string>
|
||||
|
||||
namespace hex {
|
||||
|
||||
namespace prv { class Provider; }
|
||||
|
||||
class ViewTools : public View {
|
||||
public:
|
||||
ViewTools(hex::prv::Provider* &provider);
|
||||
~ViewTools() override;
|
||||
|
||||
void createView() override;
|
||||
void createMenu() override;
|
||||
|
||||
private:
|
||||
hex::prv::Provider* &m_dataProvider;
|
||||
|
||||
char *m_mangledBuffer = nullptr;
|
||||
std::string m_demangledName;
|
||||
|
||||
bool m_asciiTableShowOctal = false;
|
||||
|
||||
char *m_regexInput = nullptr;
|
||||
char *m_regexPattern = nullptr;
|
||||
char *m_replacePattern = nullptr;
|
||||
std::string m_regexOutput;
|
||||
|
||||
std::array<float, 4> m_pickedColor;
|
||||
|
||||
MathEvaluator m_mathEvaluator;
|
||||
std::vector<long double> m_mathHistory;
|
||||
std::string m_lastMathError;
|
||||
char *m_mathInput = nullptr;
|
||||
|
||||
void drawDemangler();
|
||||
void drawASCIITable();
|
||||
void drawRegexReplacer();
|
||||
void drawColorPicker();
|
||||
void drawMathEvaluator();
|
||||
};
|
||||
|
||||
}
|
||||
@@ -7,6 +7,7 @@
|
||||
#include "views/view.hpp"
|
||||
|
||||
struct GLFWwindow;
|
||||
struct ImGuiSettingsHandler;
|
||||
|
||||
namespace hex {
|
||||
|
||||
@@ -24,6 +25,11 @@ namespace hex {
|
||||
return static_cast<T*>(this->m_views.back());
|
||||
}
|
||||
|
||||
friend void *ImHexSettingsHandler_ReadOpenFn(ImGuiContext *ctx, ImGuiSettingsHandler *, const char *);
|
||||
friend void ImHexSettingsHandler_ReadLine(ImGuiContext*, ImGuiSettingsHandler *handler, void *, const char* line);
|
||||
friend void ImHexSettingsHandler_ApplyAll(ImGuiContext *ctx, ImGuiSettingsHandler *handler);
|
||||
friend void ImHexSettingsHandler_WriteAll(ImGuiContext* ctx, ImGuiSettingsHandler *handler, ImGuiTextBuffer *buf);
|
||||
|
||||
private:
|
||||
void frameBegin();
|
||||
void frameEnd();
|
||||
@@ -35,7 +41,10 @@ namespace hex {
|
||||
|
||||
GLFWwindow* m_window;
|
||||
std::vector<View*> m_views;
|
||||
|
||||
float m_globalScale = 1.0f, m_fontScale = 1.0f;
|
||||
bool m_fpsVisible = false;
|
||||
bool m_demoWindowOpen = false;
|
||||
|
||||
static inline std::tuple<int, int> s_currShortcut = { -1, -1 };
|
||||
};
|
||||
|
||||
1160
libs/ImGui/include/Dirent/dirent.h
Normal file
1160
libs/ImGui/include/Dirent/dirent.h
Normal file
File diff suppressed because it is too large
Load Diff
123
libs/ImGui/include/ImGuiFileBrowser.h
Normal file
123
libs/ImGui/include/ImGuiFileBrowser.h
Normal file
@@ -0,0 +1,123 @@
|
||||
#ifndef IMGUIFILEBROWSER_H
|
||||
#define IMGUIFILEBROWSER_H
|
||||
|
||||
#include <imgui.h>
|
||||
#include <string>
|
||||
#include <vector>
|
||||
|
||||
namespace imgui_addons
|
||||
{
|
||||
class ImGuiFileBrowser
|
||||
{
|
||||
public:
|
||||
ImGuiFileBrowser();
|
||||
~ImGuiFileBrowser();
|
||||
|
||||
enum class DialogMode
|
||||
{
|
||||
SELECT, //Select Directory Mode
|
||||
OPEN, //Open File mode
|
||||
SAVE //Save File mode.
|
||||
};
|
||||
|
||||
/* Use this to show an open file dialog. The function takes label for the window,
|
||||
* the size, a DialogMode enum value defining in which mode the dialog should operate and optionally the extensions that are valid for opening.
|
||||
* Note that the select directory mode doesn't need any extensions.
|
||||
*/
|
||||
bool showFileDialog(const std::string& label, const DialogMode mode, const ImVec2& sz_xy = ImVec2(0,0), const std::string& valid_types = "*.*");
|
||||
|
||||
/* Store the opened/saved file name or dir name (incase of selectDirectoryDialog) and the absolute path to the selection
|
||||
* Should only be accessed when above functions return true else may contain garbage.
|
||||
*/
|
||||
std::string selected_fn;
|
||||
std::string selected_path;
|
||||
std::string ext; // Store the saved file extension
|
||||
|
||||
|
||||
private:
|
||||
struct Info
|
||||
{
|
||||
Info(std::string name, bool is_hidden) : name(name), is_hidden(is_hidden)
|
||||
{
|
||||
}
|
||||
std::string name;
|
||||
bool is_hidden;
|
||||
};
|
||||
|
||||
//Enum used as bit flags.
|
||||
enum FilterMode
|
||||
{
|
||||
FilterMode_Files = 0x01,
|
||||
FilterMode_Dirs = 0x02
|
||||
};
|
||||
|
||||
//Helper Functions
|
||||
static std::string wStringToString(const wchar_t* wchar_arr);
|
||||
static bool alphaSortComparator(const Info& a, const Info& b);
|
||||
ImVec2 getButtonSize(std::string button_text);
|
||||
|
||||
/* Helper Functions that render secondary modals
|
||||
* and help in validating file extensions and for filtering, parsing top navigation bar.
|
||||
*/
|
||||
void setValidExtTypes(const std::string& valid_types_string);
|
||||
bool validateFile();
|
||||
void showErrorModal();
|
||||
void showInvalidFileModal();
|
||||
bool showReplaceFileModal();
|
||||
void showHelpMarker(std::string desc);
|
||||
void parsePathTabs(std::string str);
|
||||
void filterFiles(int filter_mode);
|
||||
|
||||
/* Core Functions that render the 4 different regions making up
|
||||
* a simple file dialog
|
||||
*/
|
||||
bool renderNavAndSearchBarRegion();
|
||||
bool renderFileListRegion();
|
||||
bool renderInputTextAndExtRegion();
|
||||
bool renderButtonsAndCheckboxRegion();
|
||||
bool renderInputComboBox();
|
||||
void renderExtBox();
|
||||
|
||||
/* Core Functions that handle navigation and
|
||||
* reading directories/files
|
||||
*/
|
||||
bool readDIR(std::string path);
|
||||
bool onNavigationButtonClick(int idx);
|
||||
bool onDirClick(int idx);
|
||||
|
||||
// Functions that reset state and/or clear file list when reading new directory
|
||||
void clearFileList();
|
||||
void closeDialog();
|
||||
|
||||
#if defined (WIN32) || defined (_WIN32) || defined (__WIN32)
|
||||
bool loadWindowsDrives(); // Helper Function for Windows to load Drive Letters.
|
||||
#endif
|
||||
|
||||
#if defined(unix) || defined(__unix__) || defined(__unix) || defined(__APPLE__)
|
||||
void initCurrentPath(); // Helper function for UNIX based system to load Absolute path using realpath
|
||||
#endif
|
||||
|
||||
ImVec2 min_size, max_size, input_combobox_pos, input_combobox_sz;
|
||||
DialogMode dialog_mode;
|
||||
int filter_mode, col_items_limit, selected_idx, selected_ext_idx;
|
||||
float col_width, ext_box_width;
|
||||
bool show_hidden, show_inputbar_combobox, is_dir, is_appearing, filter_dirty, validate_file, path_input_enabled;
|
||||
char input_fn[256];
|
||||
char temp_dir_input[256];
|
||||
|
||||
std::vector<std::string> valid_exts;
|
||||
std::vector<std::string> current_dirlist;
|
||||
std::vector<Info> subdirs;
|
||||
std::vector<Info> subfiles;
|
||||
std::string current_path, error_msg, error_title, invfile_modal_id, repfile_modal_id;
|
||||
|
||||
ImGuiTextFilter filter;
|
||||
std::string valid_types;
|
||||
std::vector<const Info*> filtered_dirs; // Note: We don't need to call delete. It's just for storing filtered items from subdirs and subfiles so we don't use PassFilter every frame.
|
||||
std::vector<const Info*> filtered_files;
|
||||
std::vector< std::reference_wrapper<std::string> > inputcb_filter_files;
|
||||
};
|
||||
}
|
||||
|
||||
|
||||
#endif // IMGUIFILEBROWSER_H
|
||||
395
libs/ImGui/include/TextEditor.h
Normal file
395
libs/ImGui/include/TextEditor.h
Normal file
@@ -0,0 +1,395 @@
|
||||
#pragma once
|
||||
|
||||
#include <string>
|
||||
#include <vector>
|
||||
#include <array>
|
||||
#include <memory>
|
||||
#include <unordered_set>
|
||||
#include <unordered_map>
|
||||
#include <map>
|
||||
#include <regex>
|
||||
#include "imgui.h"
|
||||
|
||||
class TextEditor
|
||||
{
|
||||
public:
|
||||
enum class PaletteIndex
|
||||
{
|
||||
Default,
|
||||
Keyword,
|
||||
Number,
|
||||
String,
|
||||
CharLiteral,
|
||||
Punctuation,
|
||||
Preprocessor,
|
||||
Identifier,
|
||||
KnownIdentifier,
|
||||
PreprocIdentifier,
|
||||
Comment,
|
||||
MultiLineComment,
|
||||
Background,
|
||||
Cursor,
|
||||
Selection,
|
||||
ErrorMarker,
|
||||
Breakpoint,
|
||||
LineNumber,
|
||||
CurrentLineFill,
|
||||
CurrentLineFillInactive,
|
||||
CurrentLineEdge,
|
||||
Max
|
||||
};
|
||||
|
||||
enum class SelectionMode
|
||||
{
|
||||
Normal,
|
||||
Word,
|
||||
Line
|
||||
};
|
||||
|
||||
struct Breakpoint
|
||||
{
|
||||
int mLine;
|
||||
bool mEnabled;
|
||||
std::string mCondition;
|
||||
|
||||
Breakpoint()
|
||||
: mLine(-1)
|
||||
, mEnabled(false)
|
||||
{}
|
||||
};
|
||||
|
||||
// Represents a character coordinate from the user's point of view,
|
||||
// i. e. consider an uniform grid (assuming fixed-width font) on the
|
||||
// screen as it is rendered, and each cell has its own coordinate, starting from 0.
|
||||
// Tabs are counted as [1..mTabSize] count empty spaces, depending on
|
||||
// how many space is necessary to reach the next tab stop.
|
||||
// For example, coordinate (1, 5) represents the character 'B' in a line "\tABC", when mTabSize = 4,
|
||||
// because it is rendered as " ABC" on the screen.
|
||||
struct Coordinates
|
||||
{
|
||||
int mLine, mColumn;
|
||||
Coordinates() : mLine(0), mColumn(0) {}
|
||||
Coordinates(int aLine, int aColumn) : mLine(aLine), mColumn(aColumn)
|
||||
{
|
||||
assert(aLine >= 0);
|
||||
assert(aColumn >= 0);
|
||||
}
|
||||
static Coordinates Invalid() { static Coordinates invalid(-1, -1); return invalid; }
|
||||
|
||||
bool operator ==(const Coordinates& o) const
|
||||
{
|
||||
return
|
||||
mLine == o.mLine &&
|
||||
mColumn == o.mColumn;
|
||||
}
|
||||
|
||||
bool operator !=(const Coordinates& o) const
|
||||
{
|
||||
return
|
||||
mLine != o.mLine ||
|
||||
mColumn != o.mColumn;
|
||||
}
|
||||
|
||||
bool operator <(const Coordinates& o) const
|
||||
{
|
||||
if (mLine != o.mLine)
|
||||
return mLine < o.mLine;
|
||||
return mColumn < o.mColumn;
|
||||
}
|
||||
|
||||
bool operator >(const Coordinates& o) const
|
||||
{
|
||||
if (mLine != o.mLine)
|
||||
return mLine > o.mLine;
|
||||
return mColumn > o.mColumn;
|
||||
}
|
||||
|
||||
bool operator <=(const Coordinates& o) const
|
||||
{
|
||||
if (mLine != o.mLine)
|
||||
return mLine < o.mLine;
|
||||
return mColumn <= o.mColumn;
|
||||
}
|
||||
|
||||
bool operator >=(const Coordinates& o) const
|
||||
{
|
||||
if (mLine != o.mLine)
|
||||
return mLine > o.mLine;
|
||||
return mColumn >= o.mColumn;
|
||||
}
|
||||
};
|
||||
|
||||
struct Identifier
|
||||
{
|
||||
Coordinates mLocation;
|
||||
std::string mDeclaration;
|
||||
};
|
||||
|
||||
typedef std::string String;
|
||||
typedef std::unordered_map<std::string, Identifier> Identifiers;
|
||||
typedef std::unordered_set<std::string> Keywords;
|
||||
typedef std::map<int, std::string> ErrorMarkers;
|
||||
typedef std::unordered_set<int> Breakpoints;
|
||||
typedef std::array<ImU32, (unsigned)PaletteIndex::Max> Palette;
|
||||
typedef uint8_t Char;
|
||||
|
||||
struct Glyph
|
||||
{
|
||||
Char mChar;
|
||||
PaletteIndex mColorIndex = PaletteIndex::Default;
|
||||
bool mComment : 1;
|
||||
bool mMultiLineComment : 1;
|
||||
bool mPreprocessor : 1;
|
||||
|
||||
Glyph(Char aChar, PaletteIndex aColorIndex) : mChar(aChar), mColorIndex(aColorIndex),
|
||||
mComment(false), mMultiLineComment(false), mPreprocessor(false) {}
|
||||
};
|
||||
|
||||
typedef std::vector<Glyph> Line;
|
||||
typedef std::vector<Line> Lines;
|
||||
|
||||
struct LanguageDefinition
|
||||
{
|
||||
typedef std::pair<std::string, PaletteIndex> TokenRegexString;
|
||||
typedef std::vector<TokenRegexString> TokenRegexStrings;
|
||||
typedef bool(*TokenizeCallback)(const char * in_begin, const char * in_end, const char *& out_begin, const char *& out_end, PaletteIndex & paletteIndex);
|
||||
|
||||
std::string mName;
|
||||
Keywords mKeywords;
|
||||
Identifiers mIdentifiers;
|
||||
Identifiers mPreprocIdentifiers;
|
||||
std::string mCommentStart, mCommentEnd, mSingleLineComment;
|
||||
char mPreprocChar;
|
||||
bool mAutoIndentation;
|
||||
|
||||
TokenizeCallback mTokenize;
|
||||
|
||||
TokenRegexStrings mTokenRegexStrings;
|
||||
|
||||
bool mCaseSensitive;
|
||||
|
||||
LanguageDefinition()
|
||||
: mPreprocChar('#'), mAutoIndentation(true), mTokenize(nullptr), mCaseSensitive(true)
|
||||
{
|
||||
}
|
||||
|
||||
static const LanguageDefinition& CPlusPlus();
|
||||
static const LanguageDefinition& HLSL();
|
||||
static const LanguageDefinition& GLSL();
|
||||
static const LanguageDefinition& C();
|
||||
static const LanguageDefinition& SQL();
|
||||
static const LanguageDefinition& AngelScript();
|
||||
static const LanguageDefinition& Lua();
|
||||
};
|
||||
|
||||
TextEditor();
|
||||
~TextEditor();
|
||||
|
||||
void SetLanguageDefinition(const LanguageDefinition& aLanguageDef);
|
||||
const LanguageDefinition& GetLanguageDefinition() const { return mLanguageDefinition; }
|
||||
|
||||
const Palette& GetPalette() const { return mPaletteBase; }
|
||||
void SetPalette(const Palette& aValue);
|
||||
|
||||
void SetErrorMarkers(const ErrorMarkers& aMarkers) { mErrorMarkers = aMarkers; }
|
||||
void SetBreakpoints(const Breakpoints& aMarkers) { mBreakpoints = aMarkers; }
|
||||
|
||||
void Render(const char* aTitle, const ImVec2& aSize = ImVec2(), bool aBorder = false);
|
||||
void SetText(const std::string& aText);
|
||||
std::string GetText() const;
|
||||
|
||||
void SetTextLines(const std::vector<std::string>& aLines);
|
||||
std::vector<std::string> GetTextLines() const;
|
||||
|
||||
std::string GetSelectedText() const;
|
||||
std::string GetCurrentLineText()const;
|
||||
|
||||
int GetTotalLines() const { return (int)mLines.size(); }
|
||||
bool IsOverwrite() const { return mOverwrite; }
|
||||
|
||||
void SetReadOnly(bool aValue);
|
||||
bool IsReadOnly() const { return mReadOnly; }
|
||||
bool IsTextChanged() const { return mTextChanged; }
|
||||
bool IsCursorPositionChanged() const { return mCursorPositionChanged; }
|
||||
|
||||
bool IsColorizerEnabled() const { return mColorizerEnabled; }
|
||||
void SetColorizerEnable(bool aValue);
|
||||
|
||||
Coordinates GetCursorPosition() const { return GetActualCursorCoordinates(); }
|
||||
void SetCursorPosition(const Coordinates& aPosition);
|
||||
|
||||
inline void SetHandleMouseInputs (bool aValue){ mHandleMouseInputs = aValue;}
|
||||
inline bool IsHandleMouseInputsEnabled() const { return mHandleKeyboardInputs; }
|
||||
|
||||
inline void SetHandleKeyboardInputs (bool aValue){ mHandleKeyboardInputs = aValue;}
|
||||
inline bool IsHandleKeyboardInputsEnabled() const { return mHandleKeyboardInputs; }
|
||||
|
||||
inline void SetImGuiChildIgnored (bool aValue){ mIgnoreImGuiChild = aValue;}
|
||||
inline bool IsImGuiChildIgnored() const { return mIgnoreImGuiChild; }
|
||||
|
||||
inline void SetShowWhitespaces(bool aValue) { mShowWhitespaces = aValue; }
|
||||
inline bool IsShowingWhitespaces() const { return mShowWhitespaces; }
|
||||
|
||||
void SetTabSize(int aValue);
|
||||
inline int GetTabSize() const { return mTabSize; }
|
||||
|
||||
void InsertText(const std::string& aValue);
|
||||
void InsertText(const char* aValue);
|
||||
|
||||
void MoveUp(int aAmount = 1, bool aSelect = false);
|
||||
void MoveDown(int aAmount = 1, bool aSelect = false);
|
||||
void MoveLeft(int aAmount = 1, bool aSelect = false, bool aWordMode = false);
|
||||
void MoveRight(int aAmount = 1, bool aSelect = false, bool aWordMode = false);
|
||||
void MoveTop(bool aSelect = false);
|
||||
void MoveBottom(bool aSelect = false);
|
||||
void MoveHome(bool aSelect = false);
|
||||
void MoveEnd(bool aSelect = false);
|
||||
|
||||
void SetSelectionStart(const Coordinates& aPosition);
|
||||
void SetSelectionEnd(const Coordinates& aPosition);
|
||||
void SetSelection(const Coordinates& aStart, const Coordinates& aEnd, SelectionMode aMode = SelectionMode::Normal);
|
||||
void SelectWordUnderCursor();
|
||||
void SelectAll();
|
||||
bool HasSelection() const;
|
||||
|
||||
void Copy();
|
||||
void Cut();
|
||||
void Paste();
|
||||
void Delete();
|
||||
|
||||
bool CanUndo() const;
|
||||
bool CanRedo() const;
|
||||
void Undo(int aSteps = 1);
|
||||
void Redo(int aSteps = 1);
|
||||
|
||||
static const Palette& GetDarkPalette();
|
||||
static const Palette& GetLightPalette();
|
||||
static const Palette& GetRetroBluePalette();
|
||||
|
||||
private:
|
||||
typedef std::vector<std::pair<std::regex, PaletteIndex>> RegexList;
|
||||
|
||||
struct EditorState
|
||||
{
|
||||
Coordinates mSelectionStart;
|
||||
Coordinates mSelectionEnd;
|
||||
Coordinates mCursorPosition;
|
||||
};
|
||||
|
||||
class UndoRecord
|
||||
{
|
||||
public:
|
||||
UndoRecord() {}
|
||||
~UndoRecord() {}
|
||||
|
||||
UndoRecord(
|
||||
const std::string& aAdded,
|
||||
const TextEditor::Coordinates aAddedStart,
|
||||
const TextEditor::Coordinates aAddedEnd,
|
||||
|
||||
const std::string& aRemoved,
|
||||
const TextEditor::Coordinates aRemovedStart,
|
||||
const TextEditor::Coordinates aRemovedEnd,
|
||||
|
||||
TextEditor::EditorState& aBefore,
|
||||
TextEditor::EditorState& aAfter);
|
||||
|
||||
void Undo(TextEditor* aEditor);
|
||||
void Redo(TextEditor* aEditor);
|
||||
|
||||
std::string mAdded;
|
||||
Coordinates mAddedStart;
|
||||
Coordinates mAddedEnd;
|
||||
|
||||
std::string mRemoved;
|
||||
Coordinates mRemovedStart;
|
||||
Coordinates mRemovedEnd;
|
||||
|
||||
EditorState mBefore;
|
||||
EditorState mAfter;
|
||||
};
|
||||
|
||||
typedef std::vector<UndoRecord> UndoBuffer;
|
||||
|
||||
void ProcessInputs();
|
||||
void Colorize(int aFromLine = 0, int aCount = -1);
|
||||
void ColorizeRange(int aFromLine = 0, int aToLine = 0);
|
||||
void ColorizeInternal();
|
||||
float TextDistanceToLineStart(const Coordinates& aFrom) const;
|
||||
void EnsureCursorVisible();
|
||||
int GetPageSize() const;
|
||||
std::string GetText(const Coordinates& aStart, const Coordinates& aEnd) const;
|
||||
Coordinates GetActualCursorCoordinates() const;
|
||||
Coordinates SanitizeCoordinates(const Coordinates& aValue) const;
|
||||
void Advance(Coordinates& aCoordinates) const;
|
||||
void DeleteRange(const Coordinates& aStart, const Coordinates& aEnd);
|
||||
int InsertTextAt(Coordinates& aWhere, const char* aValue);
|
||||
void AddUndo(UndoRecord& aValue);
|
||||
Coordinates ScreenPosToCoordinates(const ImVec2& aPosition) const;
|
||||
Coordinates FindWordStart(const Coordinates& aFrom) const;
|
||||
Coordinates FindWordEnd(const Coordinates& aFrom) const;
|
||||
Coordinates FindNextWord(const Coordinates& aFrom) const;
|
||||
int GetCharacterIndex(const Coordinates& aCoordinates) const;
|
||||
int GetCharacterColumn(int aLine, int aIndex) const;
|
||||
int GetLineCharacterCount(int aLine) const;
|
||||
int GetLineMaxColumn(int aLine) const;
|
||||
bool IsOnWordBoundary(const Coordinates& aAt) const;
|
||||
void RemoveLine(int aStart, int aEnd);
|
||||
void RemoveLine(int aIndex);
|
||||
Line& InsertLine(int aIndex);
|
||||
void EnterCharacter(ImWchar aChar, bool aShift);
|
||||
void Backspace();
|
||||
void DeleteSelection();
|
||||
std::string GetWordUnderCursor() const;
|
||||
std::string GetWordAt(const Coordinates& aCoords) const;
|
||||
ImU32 GetGlyphColor(const Glyph& aGlyph) const;
|
||||
|
||||
void HandleKeyboardInputs();
|
||||
void HandleMouseInputs();
|
||||
void Render();
|
||||
|
||||
float mLineSpacing;
|
||||
Lines mLines;
|
||||
EditorState mState;
|
||||
UndoBuffer mUndoBuffer;
|
||||
int mUndoIndex;
|
||||
|
||||
int mTabSize;
|
||||
bool mOverwrite;
|
||||
bool mReadOnly;
|
||||
bool mWithinRender;
|
||||
bool mScrollToCursor;
|
||||
bool mScrollToTop;
|
||||
bool mTextChanged;
|
||||
bool mColorizerEnabled;
|
||||
float mTextStart; // position (in pixels) where a code line starts relative to the left of the TextEditor.
|
||||
int mLeftMargin;
|
||||
bool mCursorPositionChanged;
|
||||
int mColorRangeMin, mColorRangeMax;
|
||||
SelectionMode mSelectionMode;
|
||||
bool mHandleKeyboardInputs;
|
||||
bool mHandleMouseInputs;
|
||||
bool mIgnoreImGuiChild;
|
||||
bool mShowWhitespaces;
|
||||
|
||||
Palette mPaletteBase;
|
||||
Palette mPalette;
|
||||
LanguageDefinition mLanguageDefinition;
|
||||
RegexList mRegexList;
|
||||
|
||||
bool mCheckComments;
|
||||
Breakpoints mBreakpoints;
|
||||
ErrorMarkers mErrorMarkers;
|
||||
ImVec2 mCharAdvance;
|
||||
Coordinates mInteractiveStart, mInteractiveEnd;
|
||||
std::string mLineBuffer;
|
||||
uint64_t mStartTime;
|
||||
|
||||
float mLastClick;
|
||||
};
|
||||
|
||||
bool TokenizeCStyleString(const char * in_begin, const char * in_end, const char *& out_begin, const char *& out_end);
|
||||
bool TokenizeCStyleCharacterLiteral(const char * in_begin, const char * in_end, const char *& out_begin, const char *& out_end);
|
||||
bool TokenizeCStyleIdentifier(const char * in_begin, const char * in_end, const char *& out_begin, const char *& out_end);
|
||||
bool TokenizeCStyleNumber(const char * in_begin, const char * in_end, const char *& out_begin, const char *& out_end);
|
||||
bool TokenizeCStylePunctuation(const char * in_begin, const char * in_end, const char *& out_begin, const char *& out_end);
|
||||
@@ -31,7 +31,7 @@
|
||||
// It is very strongly recommended to NOT disable the demo windows during development. Please read comments in imgui_demo.cpp.
|
||||
//#define IMGUI_DISABLE // Disable everything: all headers and source files will be empty.
|
||||
//#define IMGUI_DISABLE_DEMO_WINDOWS // Disable demo windows: ShowDemoWindow()/ShowStyleEditor() will be empty. Not recommended.
|
||||
//#define IMGUI_DISABLE_METRICS_WINDOW // Disable debug/metrics window: ShowMetricsWindow() will be empty.
|
||||
//#define IMGUI_DISABLE_METRICS_WINDOW // Disable metrics/debugger window: ShowMetricsWindow() will be empty.
|
||||
|
||||
//---- Don't implement some functions to reduce linkage requirements.
|
||||
//#define IMGUI_DISABLE_WIN32_DEFAULT_CLIPBOARD_FUNCTIONS // [Win32] Don't implement default clipboard handler. Won't use and link with OpenClipboard/GetClipboardData/CloseClipboard etc.
|
||||
|
||||
@@ -1,721 +0,0 @@
|
||||
/*
|
||||
MIT License
|
||||
|
||||
Copyright (c) 2019-2020 Zhuang Guan
|
||||
|
||||
https://github.com/AirGuanZ
|
||||
|
||||
Permission is hereby granted, free of charge, to any person obtaining a copy
|
||||
of this software and associated documentation files (the "Software"), to deal
|
||||
in the Software without restriction, including without limitation the rights
|
||||
to use, copy, modify, merge, publish, distribute, sublicense, and/or sell
|
||||
copies of the Software, and to permit persons to whom the Software is
|
||||
furnished to do so, subject to the following conditions:
|
||||
|
||||
The above copyright notice and this permission notice shall be included in all
|
||||
copies or substantial portions of the Software.
|
||||
|
||||
THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR
|
||||
IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY,
|
||||
FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE
|
||||
AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER
|
||||
LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM,
|
||||
OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN THE
|
||||
SOFTWARE.
|
||||
*/
|
||||
|
||||
#pragma once
|
||||
|
||||
#include <algorithm>
|
||||
#include <array>
|
||||
#include <cstring>
|
||||
#include <filesystem>
|
||||
#include <memory>
|
||||
#include <set>
|
||||
#include <string>
|
||||
#include <vector>
|
||||
|
||||
#ifndef IMGUI_VERSION
|
||||
# error "include imgui.h before this header"
|
||||
#endif
|
||||
|
||||
using ImGuiFileBrowserFlags = int;
|
||||
|
||||
enum ImGuiFileBrowserFlags_
|
||||
{
|
||||
ImGuiFileBrowserFlags_SelectDirectory = 1 << 0, // select directory instead of regular file
|
||||
ImGuiFileBrowserFlags_EnterNewFilename = 1 << 1, // allow user to enter new filename when selecting regular file
|
||||
ImGuiFileBrowserFlags_NoModal = 1 << 2, // file browsing window is modal by default. specify this to use a popup window
|
||||
ImGuiFileBrowserFlags_NoTitleBar = 1 << 3, // hide window title bar
|
||||
ImGuiFileBrowserFlags_NoStatusBar = 1 << 4, // hide status bar at the bottom of browsing window
|
||||
ImGuiFileBrowserFlags_CloseOnEsc = 1 << 5, // close file browser when pressing 'ESC'
|
||||
ImGuiFileBrowserFlags_CreateNewDir = 1 << 6, // allow user to create new directory
|
||||
ImGuiFileBrowserFlags_MultipleSelection = 1 << 7, // allow user to select multiple files. this will hide ImGuiFileBrowserFlags_EnterNewFilename
|
||||
};
|
||||
|
||||
namespace ImGui
|
||||
{
|
||||
class FileBrowser
|
||||
{
|
||||
public:
|
||||
|
||||
// pwd is set to current working directory by default
|
||||
explicit FileBrowser(ImGuiFileBrowserFlags flags = 0);
|
||||
|
||||
FileBrowser(const FileBrowser ©From);
|
||||
|
||||
FileBrowser &operator=(const FileBrowser ©From);
|
||||
|
||||
// set the window size (in pixels)
|
||||
// default is (700, 450)
|
||||
void SetWindowSize(int width, int height) noexcept;
|
||||
|
||||
// set the window title text
|
||||
void SetTitle(std::string title);
|
||||
|
||||
// open the browsing window
|
||||
void Open();
|
||||
|
||||
// close the browsing window
|
||||
void Close();
|
||||
|
||||
// the browsing window is opened or not
|
||||
bool IsOpened() const noexcept;
|
||||
|
||||
// display the browsing window if opened
|
||||
void Display();
|
||||
|
||||
// returns true when there is a selected filename and the "ok" button was clicked
|
||||
bool HasSelected() const noexcept;
|
||||
|
||||
// set current browsing directory
|
||||
bool SetPwd(const std::filesystem::path &pwd =
|
||||
std::filesystem::current_path());
|
||||
|
||||
// get current browsing directory
|
||||
const std::filesystem::path &GetPwd() const noexcept;
|
||||
|
||||
// returns selected filename. make sense only when HasSelected returns true
|
||||
// when ImGuiFileBrowserFlags_MultipleSelection is enabled, only one of
|
||||
// selected filename will be returned
|
||||
std::filesystem::path GetSelected() const;
|
||||
|
||||
// returns all selected filenames.
|
||||
// when ImGuiFileBrowserFlags_MultipleSelection is enabled, use this
|
||||
// instead of GetSelected
|
||||
std::vector<std::filesystem::path> GetMultiSelected() const;
|
||||
|
||||
// set selected filename to empty
|
||||
void ClearSelected();
|
||||
|
||||
// set file type filters. eg. { ".h", ".cpp", ".hpp", ".cc", ".inl" }
|
||||
void SetTypeFilters(const std::vector<const char*> &typeFilters);
|
||||
|
||||
private:
|
||||
|
||||
template <class Functor>
|
||||
struct ScopeGuard
|
||||
{
|
||||
ScopeGuard(Functor&& t) : func(std::move(t)) { }
|
||||
|
||||
~ScopeGuard()
|
||||
{
|
||||
func();
|
||||
}
|
||||
|
||||
private:
|
||||
|
||||
Functor func;
|
||||
};
|
||||
|
||||
void SetPwdUncatched(const std::filesystem::path &pwd);
|
||||
|
||||
#ifdef _WIN32
|
||||
static std::uint32_t GetDrivesBitMask();
|
||||
#endif
|
||||
|
||||
// for c++17 compatibility
|
||||
|
||||
#if defined(__cpp_lib_char8_t)
|
||||
static std::string u8StrToStr(std::u8string s);
|
||||
#endif
|
||||
static std::string u8StrToStr(std::string s);
|
||||
|
||||
int width_;
|
||||
int height_;
|
||||
ImGuiFileBrowserFlags flags_;
|
||||
|
||||
std::string title_;
|
||||
std::string openLabel_;
|
||||
|
||||
bool openFlag_;
|
||||
bool closeFlag_;
|
||||
bool isOpened_;
|
||||
bool ok_;
|
||||
|
||||
std::string statusStr_;
|
||||
|
||||
std::vector<const char*> typeFilters_;
|
||||
int typeFilterIndex_;
|
||||
|
||||
std::filesystem::path pwd_;
|
||||
std::set<std::filesystem::path> selectedFilenames_;
|
||||
|
||||
struct FileRecord
|
||||
{
|
||||
bool isDir = false;
|
||||
std::filesystem::path name;
|
||||
std::string showName;
|
||||
std::filesystem::path extension;
|
||||
};
|
||||
std::vector<FileRecord> fileRecords_;
|
||||
|
||||
// IMPROVE: truncate when selectedFilename_.length() > inputNameBuf_.size() - 1
|
||||
static constexpr size_t INPUT_NAME_BUF_SIZE = 512;
|
||||
std::unique_ptr<std::array<char, INPUT_NAME_BUF_SIZE>> inputNameBuf_;
|
||||
|
||||
std::string openNewDirLabel_;
|
||||
std::unique_ptr<std::array<char, INPUT_NAME_BUF_SIZE>> newDirNameBuf_;
|
||||
|
||||
#ifdef _WIN32
|
||||
uint32_t drives_;
|
||||
#endif
|
||||
};
|
||||
} // namespace ImGui
|
||||
|
||||
inline ImGui::FileBrowser::FileBrowser(ImGuiFileBrowserFlags flags)
|
||||
: width_(700), height_(450), flags_(flags),
|
||||
openFlag_(false), closeFlag_(false), isOpened_(false), ok_(false),
|
||||
inputNameBuf_(std::make_unique<std::array<char, INPUT_NAME_BUF_SIZE>>())
|
||||
{
|
||||
if(flags_ & ImGuiFileBrowserFlags_CreateNewDir)
|
||||
newDirNameBuf_ = std::make_unique<
|
||||
std::array<char, INPUT_NAME_BUF_SIZE>>();
|
||||
|
||||
inputNameBuf_->front() = '\0';
|
||||
inputNameBuf_->back() = '\0';
|
||||
SetTitle("file browser");
|
||||
SetPwd(std::filesystem::current_path());
|
||||
|
||||
typeFilters_.clear();
|
||||
typeFilterIndex_ = 0;
|
||||
|
||||
#ifdef _WIN32
|
||||
drives_ = GetDrivesBitMask();
|
||||
#endif
|
||||
}
|
||||
|
||||
inline ImGui::FileBrowser::FileBrowser(const FileBrowser ©From)
|
||||
: FileBrowser()
|
||||
{
|
||||
*this = copyFrom;
|
||||
}
|
||||
|
||||
inline ImGui::FileBrowser &ImGui::FileBrowser::operator=(
|
||||
const FileBrowser ©From)
|
||||
{
|
||||
flags_ = copyFrom.flags_;
|
||||
SetTitle(copyFrom.title_);
|
||||
|
||||
openFlag_ = copyFrom.openFlag_;
|
||||
closeFlag_ = copyFrom.closeFlag_;
|
||||
isOpened_ = copyFrom.isOpened_;
|
||||
ok_ = copyFrom.ok_;
|
||||
|
||||
statusStr_ = "";
|
||||
pwd_ = copyFrom.pwd_;
|
||||
selectedFilenames_ = copyFrom.selectedFilenames_;
|
||||
|
||||
fileRecords_ = copyFrom.fileRecords_;
|
||||
|
||||
*inputNameBuf_ = *copyFrom.inputNameBuf_;
|
||||
|
||||
if(flags_ & ImGuiFileBrowserFlags_CreateNewDir)
|
||||
{
|
||||
newDirNameBuf_ = std::make_unique<
|
||||
std::array<char, INPUT_NAME_BUF_SIZE>>();
|
||||
*newDirNameBuf_ = *copyFrom.newDirNameBuf_;
|
||||
}
|
||||
|
||||
return *this;
|
||||
}
|
||||
|
||||
inline void ImGui::FileBrowser::SetWindowSize(int width, int height) noexcept
|
||||
{
|
||||
assert(width > 0 && height > 0);
|
||||
width_ = width;
|
||||
height_ = height;
|
||||
}
|
||||
|
||||
inline void ImGui::FileBrowser::SetTitle(std::string title)
|
||||
{
|
||||
title_ = std::move(title);
|
||||
openLabel_ = title_ + "##filebrowser_" +
|
||||
std::to_string(reinterpret_cast<size_t>(this));
|
||||
openNewDirLabel_ = "new dir##new_dir_" +
|
||||
std::to_string(reinterpret_cast<size_t>(this));
|
||||
}
|
||||
|
||||
inline void ImGui::FileBrowser::Open()
|
||||
{
|
||||
ClearSelected();
|
||||
statusStr_ = std::string();
|
||||
openFlag_ = true;
|
||||
closeFlag_ = false;
|
||||
}
|
||||
|
||||
inline void ImGui::FileBrowser::Close()
|
||||
{
|
||||
ClearSelected();
|
||||
statusStr_ = std::string();
|
||||
closeFlag_ = true;
|
||||
openFlag_ = false;
|
||||
}
|
||||
|
||||
inline bool ImGui::FileBrowser::IsOpened() const noexcept
|
||||
{
|
||||
return isOpened_;
|
||||
}
|
||||
|
||||
inline void ImGui::FileBrowser::Display()
|
||||
{
|
||||
PushID(this);
|
||||
ScopeGuard exitThis([this]
|
||||
{
|
||||
openFlag_ = false;
|
||||
closeFlag_ = false;
|
||||
PopID();
|
||||
});
|
||||
|
||||
if(openFlag_)
|
||||
OpenPopup(openLabel_.c_str());
|
||||
isOpened_ = false;
|
||||
|
||||
// open the popup window
|
||||
|
||||
if(openFlag_ && (flags_ & ImGuiFileBrowserFlags_NoModal))
|
||||
{
|
||||
SetNextWindowSize(
|
||||
ImVec2(static_cast<float>(width_), static_cast<float>(height_)));
|
||||
}
|
||||
else
|
||||
{
|
||||
SetNextWindowSize(
|
||||
ImVec2(static_cast<float>(width_), static_cast<float>(height_)),
|
||||
ImGuiCond_FirstUseEver);
|
||||
}
|
||||
if(flags_ & ImGuiFileBrowserFlags_NoModal)
|
||||
{
|
||||
if(!BeginPopup(openLabel_.c_str()))
|
||||
return;
|
||||
}
|
||||
else if(!BeginPopupModal(openLabel_.c_str(), nullptr,
|
||||
flags_ & ImGuiFileBrowserFlags_NoTitleBar ?
|
||||
ImGuiWindowFlags_NoTitleBar : 0))
|
||||
{
|
||||
return;
|
||||
}
|
||||
isOpened_ = true;
|
||||
ScopeGuard endPopup([] { EndPopup(); });
|
||||
|
||||
// display elements in pwd
|
||||
|
||||
#ifdef _WIN32
|
||||
char currentDrive = static_cast<char>(pwd_.c_str()[0]);
|
||||
char driveStr[] = { currentDrive, ':', '\0' };
|
||||
|
||||
PushItemWidth(4 * GetFontSize());
|
||||
if(BeginCombo("##select_drive", driveStr))
|
||||
{
|
||||
ScopeGuard guard([&] { ImGui::EndCombo(); });
|
||||
for(int i = 0; i < 26; ++i)
|
||||
{
|
||||
if(!(drives_ & (1 << i)))
|
||||
continue;
|
||||
char driveCh = static_cast<char>('A' + i);
|
||||
char selectableStr[] = { driveCh, ':', '\0' };
|
||||
bool selected = currentDrive == driveCh;
|
||||
if(Selectable(selectableStr, selected) && !selected)
|
||||
{
|
||||
char newPwd[] = { driveCh, ':', '\\', '\0' };
|
||||
SetPwd(newPwd);
|
||||
}
|
||||
}
|
||||
}
|
||||
PopItemWidth();
|
||||
|
||||
SameLine();
|
||||
#endif
|
||||
|
||||
int secIdx = 0, newPwdLastSecIdx = -1;
|
||||
for(auto &sec : pwd_)
|
||||
{
|
||||
#ifdef _WIN32
|
||||
if(secIdx == 1)
|
||||
{
|
||||
++secIdx;
|
||||
continue;
|
||||
}
|
||||
#endif
|
||||
PushID(secIdx);
|
||||
if(secIdx > 0)
|
||||
SameLine();
|
||||
if(SmallButton(u8StrToStr(sec.u8string()).c_str()))
|
||||
newPwdLastSecIdx = secIdx;
|
||||
PopID();
|
||||
++secIdx;
|
||||
}
|
||||
|
||||
if(newPwdLastSecIdx >= 0)
|
||||
{
|
||||
int i = 0;
|
||||
std::filesystem::path newPwd;
|
||||
for(auto &sec : pwd_)
|
||||
{
|
||||
if(i++ > newPwdLastSecIdx)
|
||||
break;
|
||||
newPwd /= sec;
|
||||
}
|
||||
#ifdef _WIN32
|
||||
if(newPwdLastSecIdx == 0)
|
||||
newPwd /= "\\";
|
||||
#endif
|
||||
SetPwd(newPwd);
|
||||
}
|
||||
|
||||
SameLine();
|
||||
|
||||
if(SmallButton("*"))
|
||||
SetPwd(pwd_);
|
||||
|
||||
if(newDirNameBuf_)
|
||||
{
|
||||
SameLine();
|
||||
if(SmallButton("+"))
|
||||
{
|
||||
OpenPopup(openNewDirLabel_.c_str());
|
||||
(*newDirNameBuf_)[0] = '\0';
|
||||
}
|
||||
|
||||
if(BeginPopup(openNewDirLabel_.c_str()))
|
||||
{
|
||||
ScopeGuard endNewDirPopup([] { EndPopup(); });
|
||||
|
||||
InputText("name", newDirNameBuf_->data(), newDirNameBuf_->size());
|
||||
SameLine();
|
||||
|
||||
if(Button("ok") && (*newDirNameBuf_)[0] != '\0')
|
||||
{
|
||||
ScopeGuard closeNewDirPopup([] { CloseCurrentPopup(); });
|
||||
if(create_directory(pwd_ / newDirNameBuf_->data()))
|
||||
SetPwd(pwd_);
|
||||
else
|
||||
{
|
||||
statusStr_ = "failed to create " +
|
||||
std::string(newDirNameBuf_->data());
|
||||
}
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
// browse files in a child window
|
||||
|
||||
float reserveHeight = GetFrameHeightWithSpacing();
|
||||
std::filesystem::path newPwd; bool setNewPwd = false;
|
||||
if(!(flags_ & ImGuiFileBrowserFlags_SelectDirectory) &&
|
||||
(flags_ & ImGuiFileBrowserFlags_EnterNewFilename))
|
||||
reserveHeight += GetFrameHeightWithSpacing();
|
||||
{
|
||||
BeginChild("ch", ImVec2(0, -reserveHeight), true,
|
||||
(flags_ & ImGuiFileBrowserFlags_NoModal) ?
|
||||
ImGuiWindowFlags_AlwaysHorizontalScrollbar : 0);
|
||||
ScopeGuard endChild([] { EndChild(); });
|
||||
|
||||
for(auto &rsc : fileRecords_)
|
||||
{
|
||||
if (!rsc.isDir && typeFilters_.size() > 0 &&
|
||||
static_cast<size_t>(typeFilterIndex_) < typeFilters_.size() &&
|
||||
!(rsc.extension == typeFilters_[typeFilterIndex_]))
|
||||
continue;
|
||||
|
||||
if(!rsc.name.empty() && rsc.name.c_str()[0] == '$')
|
||||
continue;
|
||||
|
||||
bool selected = selectedFilenames_.find(rsc.name)
|
||||
!= selectedFilenames_.end();
|
||||
|
||||
if(Selectable(rsc.showName.c_str(), selected,
|
||||
ImGuiSelectableFlags_DontClosePopups))
|
||||
{
|
||||
const bool multiSelect =
|
||||
(flags_ & ImGuiFileBrowserFlags_MultipleSelection) &&
|
||||
IsWindowFocused(ImGuiFocusedFlags_RootAndChildWindows) &&
|
||||
(GetIO().KeyCtrl || GetIO().KeyShift);
|
||||
|
||||
if(selected)
|
||||
{
|
||||
if(!multiSelect)
|
||||
selectedFilenames_.clear();
|
||||
else
|
||||
selectedFilenames_.erase(rsc.name);
|
||||
|
||||
(*inputNameBuf_)[0] = '\0';
|
||||
}
|
||||
else if(rsc.name != "..")
|
||||
{
|
||||
if((rsc.isDir && (flags_ & ImGuiFileBrowserFlags_SelectDirectory)) ||
|
||||
(!rsc.isDir && !(flags_ & ImGuiFileBrowserFlags_SelectDirectory)))
|
||||
{
|
||||
if(!multiSelect)
|
||||
selectedFilenames_.clear();
|
||||
selectedFilenames_.insert(rsc.name);
|
||||
if(!(flags_ & ImGuiFileBrowserFlags_SelectDirectory))
|
||||
{
|
||||
#ifdef _MSC_VER
|
||||
strcpy_s(
|
||||
inputNameBuf_->data(), inputNameBuf_->size(),
|
||||
u8StrToStr(rsc.name.u8string()).c_str());
|
||||
#else
|
||||
std::strncpy(inputNameBuf_->data(),
|
||||
u8StrToStr(rsc.name.u8string()).c_str(),
|
||||
inputNameBuf_->size() - 1);
|
||||
#endif
|
||||
}
|
||||
}
|
||||
}
|
||||
else
|
||||
{
|
||||
if(!multiSelect)
|
||||
selectedFilenames_.clear();
|
||||
}
|
||||
}
|
||||
|
||||
if(IsItemClicked(0) && IsMouseDoubleClicked(0))
|
||||
{
|
||||
if(rsc.isDir)
|
||||
{
|
||||
setNewPwd = true;
|
||||
newPwd = (rsc.name != "..") ? (pwd_ / rsc.name) :
|
||||
pwd_.parent_path();
|
||||
}
|
||||
else if(!(flags_ & ImGuiFileBrowserFlags_SelectDirectory))
|
||||
{
|
||||
selectedFilenames_ = { rsc.name };
|
||||
ok_ = true;
|
||||
CloseCurrentPopup();
|
||||
}
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
if(setNewPwd)
|
||||
SetPwd(newPwd);
|
||||
|
||||
if(!(flags_ & ImGuiFileBrowserFlags_SelectDirectory) &&
|
||||
(flags_ & ImGuiFileBrowserFlags_EnterNewFilename))
|
||||
{
|
||||
PushID(this);
|
||||
ScopeGuard popTextID([] { PopID(); });
|
||||
|
||||
PushItemWidth(-1);
|
||||
if(InputText("", inputNameBuf_->data(), inputNameBuf_->size()) &&
|
||||
inputNameBuf_->at(0) != '\0')
|
||||
{
|
||||
selectedFilenames_ = { inputNameBuf_->data() };
|
||||
}
|
||||
PopItemWidth();
|
||||
}
|
||||
|
||||
if(!(flags_ & ImGuiFileBrowserFlags_SelectDirectory))
|
||||
{
|
||||
if(Button(" ok ") && !selectedFilenames_.empty())
|
||||
{
|
||||
ok_ = true;
|
||||
CloseCurrentPopup();
|
||||
}
|
||||
}
|
||||
else
|
||||
{
|
||||
if(Button(" ok "))
|
||||
{
|
||||
ok_ = true;
|
||||
CloseCurrentPopup();
|
||||
}
|
||||
}
|
||||
|
||||
SameLine();
|
||||
|
||||
int escIdx = GetIO().KeyMap[ImGuiKey_Escape];
|
||||
if(Button("cancel") || closeFlag_ ||
|
||||
((flags_ & ImGuiFileBrowserFlags_CloseOnEsc) &&
|
||||
IsWindowFocused(ImGuiFocusedFlags_RootAndChildWindows) &&
|
||||
escIdx >= 0 && IsKeyPressed(escIdx)))
|
||||
CloseCurrentPopup();
|
||||
|
||||
if(!statusStr_.empty() && !(flags_ & ImGuiFileBrowserFlags_NoStatusBar))
|
||||
{
|
||||
SameLine();
|
||||
Text("%s", statusStr_.c_str());
|
||||
}
|
||||
|
||||
if(!typeFilters_.empty())
|
||||
{
|
||||
SameLine();
|
||||
PushItemWidth(8 * GetFontSize());
|
||||
Combo("##type_filters", &typeFilterIndex_,
|
||||
typeFilters_.data(), int(typeFilters_.size()));
|
||||
PopItemWidth();
|
||||
}
|
||||
}
|
||||
|
||||
inline bool ImGui::FileBrowser::HasSelected() const noexcept
|
||||
{
|
||||
return ok_;
|
||||
}
|
||||
|
||||
inline bool ImGui::FileBrowser::SetPwd(const std::filesystem::path &pwd)
|
||||
{
|
||||
try
|
||||
{
|
||||
SetPwdUncatched(pwd);
|
||||
return true;
|
||||
}
|
||||
catch(const std::exception &err)
|
||||
{
|
||||
statusStr_ = std::string("last error: ") + err.what();
|
||||
}
|
||||
catch(...)
|
||||
{
|
||||
statusStr_ = "last error: unknown";
|
||||
}
|
||||
|
||||
SetPwdUncatched(std::filesystem::current_path());
|
||||
return false;
|
||||
}
|
||||
|
||||
inline const class std::filesystem::path &ImGui::FileBrowser::GetPwd() const noexcept
|
||||
{
|
||||
return pwd_;
|
||||
}
|
||||
|
||||
inline std::filesystem::path ImGui::FileBrowser::GetSelected() const
|
||||
{
|
||||
// when ok_ is true, selectedFilenames_ may be empty if SelectDirectory
|
||||
// is enabled. return pwd in that case.
|
||||
if(selectedFilenames_.empty())
|
||||
return pwd_;
|
||||
return pwd_ / *selectedFilenames_.begin();
|
||||
}
|
||||
|
||||
inline std::vector<std::filesystem::path>
|
||||
ImGui::FileBrowser::GetMultiSelected() const
|
||||
{
|
||||
if(selectedFilenames_.empty())
|
||||
return { pwd_ };
|
||||
std::vector<std::filesystem::path> ret;
|
||||
ret.reserve(selectedFilenames_.size());
|
||||
for(auto &s : selectedFilenames_)
|
||||
ret.push_back(pwd_ / s);
|
||||
return ret;
|
||||
}
|
||||
|
||||
inline void ImGui::FileBrowser::ClearSelected()
|
||||
{
|
||||
selectedFilenames_.clear();
|
||||
(*inputNameBuf_)[0] = '\0';
|
||||
ok_ = false;
|
||||
}
|
||||
|
||||
inline void ImGui::FileBrowser::SetTypeFilters(
|
||||
const std::vector<const char*> &typeFilters)
|
||||
{
|
||||
typeFilters_ = typeFilters;
|
||||
typeFilterIndex_ = 0;
|
||||
}
|
||||
|
||||
inline void ImGui::FileBrowser::SetPwdUncatched(const std::filesystem::path &pwd)
|
||||
{
|
||||
fileRecords_ = { FileRecord{ true, "..", "[D] ..", "" } };
|
||||
|
||||
for(auto &p : std::filesystem::directory_iterator(pwd))
|
||||
{
|
||||
FileRecord rcd;
|
||||
|
||||
if(p.is_regular_file())
|
||||
rcd.isDir = false;
|
||||
else if(p.is_directory())
|
||||
rcd.isDir = true;
|
||||
else
|
||||
continue;
|
||||
|
||||
rcd.name = p.path().filename();
|
||||
if(rcd.name.empty())
|
||||
continue;
|
||||
|
||||
rcd.extension = p.path().filename().extension();
|
||||
|
||||
rcd.showName = (rcd.isDir ? "[D] " : "[F] ") +
|
||||
u8StrToStr(p.path().filename().u8string());
|
||||
fileRecords_.push_back(rcd);
|
||||
}
|
||||
|
||||
std::sort(fileRecords_.begin(), fileRecords_.end(),
|
||||
[](const FileRecord &L, const FileRecord &R)
|
||||
{
|
||||
return (L.isDir ^ R.isDir) ? L.isDir : (L.name < R.name);
|
||||
});
|
||||
|
||||
pwd_ = absolute(pwd);
|
||||
selectedFilenames_.clear();
|
||||
(*inputNameBuf_)[0] = '\0';
|
||||
}
|
||||
|
||||
#if defined(__cpp_lib_char8_t)
|
||||
inline std::string ImGui::FileBrowser::u8StrToStr(std::u8string s)
|
||||
{
|
||||
return std::string(s.begin(), s.end());
|
||||
}
|
||||
#endif
|
||||
|
||||
inline std::string ImGui::FileBrowser::u8StrToStr(std::string s)
|
||||
{
|
||||
return s;
|
||||
}
|
||||
|
||||
#ifdef _WIN32
|
||||
|
||||
#ifndef _INC_WINDOWS
|
||||
|
||||
#ifndef WIN32_LEAN_AND_MEAN
|
||||
|
||||
#define IMGUI_FILEBROWSER_UNDEF_WIN32_LEAN_AND_MEAN
|
||||
#define WIN32_LEAN_AND_MEAN
|
||||
|
||||
#endif // #ifndef WIN32_LEAN_AND_MEAN
|
||||
|
||||
#include <windows.h>
|
||||
|
||||
#ifdef IMGUI_FILEBROWSER_UNDEF_WIN32_LEAN_AND_MEAN
|
||||
#undef IMGUI_FILEBROWSER_UNDEF_WIN32_LEAN_AND_MEAN
|
||||
#undef WIN32_LEAN_AND_MEAN
|
||||
#endif // #ifdef IMGUI_FILEBROWSER_UNDEF_WIN32_LEAN_AND_MEAN
|
||||
|
||||
#endif // #ifdef _INC_WINDOWS
|
||||
|
||||
inline std::uint32_t ImGui::FileBrowser::GetDrivesBitMask()
|
||||
{
|
||||
DWORD mask = GetLogicalDrives();
|
||||
uint32_t ret = 0;
|
||||
for(int i = 0; i < 26; ++i)
|
||||
{
|
||||
if(!(mask & (1 << i)))
|
||||
continue;
|
||||
char rootName[4] = { static_cast<char>('A' + i), ':', '\\', '\0' };
|
||||
UINT type = GetDriveTypeA(rootName);
|
||||
if(type == DRIVE_REMOVABLE || type == DRIVE_FIXED)
|
||||
ret |= (1 << i);
|
||||
}
|
||||
return ret;
|
||||
}
|
||||
|
||||
#endif
|
||||
@@ -34,6 +34,8 @@ Index of this file:
|
||||
// Font API (ImFontConfig, ImFontGlyph, ImFontGlyphRangesBuilder, ImFontAtlasFlags, ImFontAtlas, ImFont)
|
||||
// Platform interface for multi-viewport support (ImGuiPlatformIO, ImGuiPlatformMonitor, ImGuiViewportFlags, ImGuiViewport)
|
||||
|
||||
// FIXME-TABLE: Add ImGuiTableSortSpecsColumn and ImGuiTableSortSpecs in "Misc data structures" section above (we don't do it right now to facilitate merging various branches)
|
||||
|
||||
*/
|
||||
|
||||
#pragma once
|
||||
@@ -61,7 +63,7 @@ Index of this file:
|
||||
// Version
|
||||
// (Integer encoded as XYYZZ for use in #if preprocessor conditionals. Work in progress versions typically starts at XYY99 then bounce up to XYY00, XYY01 etc. when release tagging happens)
|
||||
#define IMGUI_VERSION "1.80 WIP"
|
||||
#define IMGUI_VERSION_NUM 17905
|
||||
#define IMGUI_VERSION_NUM 17906
|
||||
#define IMGUI_CHECKVERSION() ImGui::DebugCheckVersionAndDataLayout(IMGUI_VERSION, sizeof(ImGuiIO), sizeof(ImGuiStyle), sizeof(ImVec2), sizeof(ImVec4), sizeof(ImDrawVert), sizeof(ImDrawIdx))
|
||||
#define IMGUI_HAS_VIEWPORT 1 // Viewport WIP branch
|
||||
#define IMGUI_HAS_DOCK 1 // Docking WIP branch
|
||||
@@ -81,13 +83,6 @@ Index of this file:
|
||||
#include <assert.h>
|
||||
#define IM_ASSERT(_EXPR) assert(_EXPR) // You can override the default assert handler by editing imconfig.h
|
||||
#endif
|
||||
#if !defined(IMGUI_USE_STB_SPRINTF) && (defined(__clang__) || defined(__GNUC__))
|
||||
#define IM_FMTARGS(FMT) __attribute__((format(printf, FMT, FMT+1))) // To apply printf-style warnings to our functions.
|
||||
#define IM_FMTLIST(FMT) __attribute__((format(printf, FMT, 0)))
|
||||
#else
|
||||
#define IM_FMTARGS(FMT)
|
||||
#define IM_FMTLIST(FMT)
|
||||
#endif
|
||||
#define IM_ARRAYSIZE(_ARR) ((int)(sizeof(_ARR) / sizeof(*(_ARR)))) // Size of a static C-style array. Don't use on pointers!
|
||||
#define IM_UNUSED(_VAR) ((void)(_VAR)) // Used to silence "unused variable warnings". Often useful as asserts may be stripped out from final builds.
|
||||
#if (__cplusplus >= 201100)
|
||||
@@ -95,6 +90,16 @@ Index of this file:
|
||||
#else
|
||||
#define IM_OFFSETOF(_TYPE,_MEMBER) ((size_t)&(((_TYPE*)0)->_MEMBER)) // Offset of _MEMBER within _TYPE. Old style macro.
|
||||
#endif
|
||||
#if !defined(IMGUI_USE_STB_SPRINTF) && defined(__clang__)
|
||||
#define IM_FMTARGS(FMT) __attribute__((format(printf, FMT, FMT+1))) // Apply printf-style warnings to our formatting functions.
|
||||
#define IM_FMTLIST(FMT) __attribute__((format(printf, FMT, 0)))
|
||||
#elif !defined(IMGUI_USE_STB_SPRINTF) && defined(__GNUC__) && defined(__MINGW32__)
|
||||
#define IM_FMTARGS(FMT) __attribute__((format(gnu_printf, FMT, FMT+1))) // Apply printf-style warnings to our formatting functions.
|
||||
#define IM_FMTLIST(FMT) __attribute__((format(gnu_printf, FMT, 0)))
|
||||
#else
|
||||
#define IM_FMTARGS(FMT)
|
||||
#define IM_FMTLIST(FMT)
|
||||
#endif
|
||||
|
||||
// Warnings
|
||||
#if defined(__clang__)
|
||||
@@ -138,6 +143,8 @@ struct ImGuiPlatformMonitor; // Multi-viewport support: user-provided bou
|
||||
struct ImGuiSizeCallbackData; // Callback data when using SetNextWindowSizeConstraints() (rare/advanced use)
|
||||
struct ImGuiStorage; // Helper for key->value storage
|
||||
struct ImGuiStyle; // Runtime data for styling/colors
|
||||
struct ImGuiTableSortSpecs; // Sorting specifications for a table (often handling sort specs for a single column, occasionally more)
|
||||
struct ImGuiTableSortSpecsColumn; // Sorting specification for one column of a table
|
||||
struct ImGuiTextBuffer; // Helper to hold and append into a text buffer (~string builder)
|
||||
struct ImGuiTextFilter; // Helper to parse and apply text filters (e.g. "aaaaa[,bbbbb][,ccccc]")
|
||||
struct ImGuiViewport; // Viewport (generally ~1 per window to output to at the OS level. Need per-platform support to use multiple viewports)
|
||||
@@ -155,7 +162,9 @@ typedef int ImGuiKey; // -> enum ImGuiKey_ // Enum: A
|
||||
typedef int ImGuiNavInput; // -> enum ImGuiNavInput_ // Enum: An input identifier for navigation
|
||||
typedef int ImGuiMouseButton; // -> enum ImGuiMouseButton_ // Enum: A mouse button identifier (0=left, 1=right, 2=middle)
|
||||
typedef int ImGuiMouseCursor; // -> enum ImGuiMouseCursor_ // Enum: A mouse cursor identifier
|
||||
typedef int ImGuiSortDirection; // -> enum ImGuiSortDirection_ // Enum: A sorting direction (ascending or descending)
|
||||
typedef int ImGuiStyleVar; // -> enum ImGuiStyleVar_ // Enum: A variable identifier for styling
|
||||
typedef int ImGuiTableBgTarget; // -> enum ImGuiTableBgTarget_ // Enum: A color target for TableSetBgColor()
|
||||
typedef int ImDrawCornerFlags; // -> enum ImDrawCornerFlags_ // Flags: for ImDrawList::AddRect(), AddRectFilled() etc.
|
||||
typedef int ImDrawListFlags; // -> enum ImDrawListFlags_ // Flags: for ImDrawList
|
||||
typedef int ImFontAtlasFlags; // -> enum ImFontAtlasFlags_ // Flags: for ImFontAtlas build
|
||||
@@ -175,6 +184,9 @@ typedef int ImGuiSelectableFlags; // -> enum ImGuiSelectableFlags_ // Flags: f
|
||||
typedef int ImGuiSliderFlags; // -> enum ImGuiSliderFlags_ // Flags: for DragFloat(), DragInt(), SliderFloat(), SliderInt() etc.
|
||||
typedef int ImGuiTabBarFlags; // -> enum ImGuiTabBarFlags_ // Flags: for BeginTabBar()
|
||||
typedef int ImGuiTabItemFlags; // -> enum ImGuiTabItemFlags_ // Flags: for BeginTabItem()
|
||||
typedef int ImGuiTableFlags; // -> enum ImGuiTableFlags_ // Flags: For BeginTable()
|
||||
typedef int ImGuiTableColumnFlags; // -> enum ImGuiTableColumnFlags_// Flags: For TableSetupColumn()
|
||||
typedef int ImGuiTableRowFlags; // -> enum ImGuiTableRowFlags_ // Flags: For TableNextRow()
|
||||
typedef int ImGuiTreeNodeFlags; // -> enum ImGuiTreeNodeFlags_ // Flags: for TreeNode(), TreeNodeEx(), CollapsingHeader()
|
||||
typedef int ImGuiViewportFlags; // -> enum ImGuiViewportFlags_ // Flags: for ImGuiViewport
|
||||
typedef int ImGuiWindowFlags; // -> enum ImGuiWindowFlags_ // Flags: for Begin(), BeginChild()
|
||||
@@ -266,7 +278,7 @@ namespace ImGui
|
||||
// Demo, Debug, Information
|
||||
IMGUI_API void ShowDemoWindow(bool* p_open = NULL); // create Demo window (previously called ShowTestWindow). demonstrate most ImGui features. call this to learn about the library! try to make it always available in your application!
|
||||
IMGUI_API void ShowAboutWindow(bool* p_open = NULL); // create About window. display Dear ImGui version, credits and build/system information.
|
||||
IMGUI_API void ShowMetricsWindow(bool* p_open = NULL); // create Debug/Metrics window. display Dear ImGui internals: draw commands (with individual draw calls and vertices), window list, basic internal state, etc.
|
||||
IMGUI_API void ShowMetricsWindow(bool* p_open = NULL); // create Metrics/Debugger window. display Dear ImGui internals: windows, draw commands, various internal state, etc.
|
||||
IMGUI_API void ShowStyleEditor(ImGuiStyle* ref = NULL); // add style editor block (not a window). you can pass in a reference ImGuiStyle structure to compare to, revert to and save to (else it uses the default style)
|
||||
IMGUI_API bool ShowStyleSelector(const char* label); // add style selector block (not a window), essentially a combo listing the default styles.
|
||||
IMGUI_API void ShowFontSelector(const char* label); // add font selector block (not a window), essentially a combo listing the loaded fonts.
|
||||
@@ -297,7 +309,10 @@ namespace ImGui
|
||||
// - Use child windows to begin into a self-contained independent scrolling/clipping regions within a host window. Child windows can embed their own child.
|
||||
// - For each independent axis of 'size': ==0.0f: use remaining host window size / >0.0f: fixed size / <0.0f: use remaining window size minus abs(size) / Each axis can use a different mode, e.g. ImVec2(0,400).
|
||||
// - BeginChild() returns false to indicate the window is collapsed or fully clipped, so you may early out and omit submitting anything to the window.
|
||||
// Always call a matching EndChild() for each BeginChild() call, regardless of its return value [as with Begin: this is due to legacy reason and inconsistent with most BeginXXX functions apart from the regular Begin() which behaves like BeginChild().]
|
||||
// Always call a matching EndChild() for each BeginChild() call, regardless of its return value.
|
||||
// [Important: due to legacy reason, this is inconsistent with most other functions such as BeginMenu/EndMenu,
|
||||
// BeginPopup/EndPopup, etc. where the EndXXX call should only be called if the corresponding BeginXXX function
|
||||
// returned true. Begin and BeginChild are the only odd ones out. Will be fixed in a future update.]
|
||||
IMGUI_API bool BeginChild(const char* str_id, const ImVec2& size = ImVec2(0, 0), bool border = false, ImGuiWindowFlags flags = 0);
|
||||
IMGUI_API bool BeginChild(ImGuiID id, const ImVec2& size = ImVec2(0, 0), bool border = false, ImGuiWindowFlags flags = 0);
|
||||
IMGUI_API void EndChild();
|
||||
@@ -364,6 +379,10 @@ namespace ImGui
|
||||
IMGUI_API void PushStyleVar(ImGuiStyleVar idx, float val);
|
||||
IMGUI_API void PushStyleVar(ImGuiStyleVar idx, const ImVec2& val);
|
||||
IMGUI_API void PopStyleVar(int count = 1);
|
||||
IMGUI_API void PushAllowKeyboardFocus(bool allow_keyboard_focus); // allow focusing using TAB/Shift-TAB, enabled by default but you can disable it for certain widgets
|
||||
IMGUI_API void PopAllowKeyboardFocus();
|
||||
IMGUI_API void PushButtonRepeat(bool repeat); // in 'repeat' mode, Button*() functions return repeated true in a typematic manner (using io.KeyRepeatDelay/io.KeyRepeatRate setting). Note that you can call IsItemActive() after any Button() to tell if the button is held in the current frame.
|
||||
IMGUI_API void PopButtonRepeat();
|
||||
IMGUI_API const ImVec4& GetStyleColorVec4(ImGuiCol idx); // retrieve style color as stored in ImGuiStyle structure. use to feed back into PushStyleColor(), otherwise use GetColorU32() to get style color with style alpha baked in.
|
||||
IMGUI_API ImFont* GetFont(); // get current font
|
||||
IMGUI_API float GetFontSize(); // get current font size (= height in pixels) of current font with current scale applied
|
||||
@@ -379,10 +398,6 @@ namespace ImGui
|
||||
IMGUI_API float CalcItemWidth(); // width of item given pushed settings and current cursor position. NOT necessarily the width of last item unlike most 'Item' functions.
|
||||
IMGUI_API void PushTextWrapPos(float wrap_local_pos_x = 0.0f); // push word-wrapping position for Text*() commands. < 0.0f: no wrapping; 0.0f: wrap to end of window (or column); > 0.0f: wrap at 'wrap_pos_x' position in window local space
|
||||
IMGUI_API void PopTextWrapPos();
|
||||
IMGUI_API void PushAllowKeyboardFocus(bool allow_keyboard_focus); // allow focusing using TAB/Shift-TAB, enabled by default but you can disable it for certain widgets
|
||||
IMGUI_API void PopAllowKeyboardFocus();
|
||||
IMGUI_API void PushButtonRepeat(bool repeat); // in 'repeat' mode, Button*() functions return repeated true in a typematic manner (using io.KeyRepeatDelay/io.KeyRepeatRate setting). Note that you can call IsItemActive() after any Button() to tell if the button is held in the current frame.
|
||||
IMGUI_API void PopButtonRepeat();
|
||||
|
||||
// Cursor / Layout
|
||||
// - By "cursor" we mean the current output position.
|
||||
@@ -396,8 +411,8 @@ namespace ImGui
|
||||
IMGUI_API void NewLine(); // undo a SameLine() or force a new line when in an horizontal-layout context.
|
||||
IMGUI_API void Spacing(); // add vertical spacing.
|
||||
IMGUI_API void Dummy(const ImVec2& size); // add a dummy item of given size. unlike InvisibleButton(), Dummy() won't take the mouse click or be navigable into.
|
||||
IMGUI_API void Indent(float indent_w = 0.0f); // move content position toward the right, by style.IndentSpacing or indent_w if != 0
|
||||
IMGUI_API void Unindent(float indent_w = 0.0f); // move content position back to the left, by style.IndentSpacing or indent_w if != 0
|
||||
IMGUI_API void Indent(float indent_w = 0.0f); // move content position toward the right, by indent_w, or style.IndentSpacing if indent_w <= 0
|
||||
IMGUI_API void Unindent(float indent_w = 0.0f); // move content position back to the left, by indent_w, or style.IndentSpacing if indent_w <= 0
|
||||
IMGUI_API void BeginGroup(); // lock horizontal starting position
|
||||
IMGUI_API void EndGroup(); // unlock horizontal starting position + capture the whole group bounding box into one "item" (so you can use IsItemHovered() or layout primitives such as SameLine() on whole group, etc.)
|
||||
IMGUI_API ImVec2 GetCursorPos(); // cursor position in window coordinates (relative to window position)
|
||||
@@ -456,6 +471,7 @@ namespace ImGui
|
||||
IMGUI_API void Image(ImTextureID user_texture_id, const ImVec2& size, const ImVec2& uv0 = ImVec2(0, 0), const ImVec2& uv1 = ImVec2(1,1), const ImVec4& tint_col = ImVec4(1,1,1,1), const ImVec4& border_col = ImVec4(0,0,0,0));
|
||||
IMGUI_API bool ImageButton(ImTextureID user_texture_id, const ImVec2& size, const ImVec2& uv0 = ImVec2(0, 0), const ImVec2& uv1 = ImVec2(1,1), int frame_padding = -1, const ImVec4& bg_col = ImVec4(0,0,0,0), const ImVec4& tint_col = ImVec4(1,1,1,1)); // <0 frame_padding uses default frame padding settings. 0 for no padding
|
||||
IMGUI_API bool Checkbox(const char* label, bool* v);
|
||||
IMGUI_API bool CheckboxFlags(const char* label, int* flags, int flags_value);
|
||||
IMGUI_API bool CheckboxFlags(const char* label, unsigned int* flags, unsigned int flags_value);
|
||||
IMGUI_API bool RadioButton(const char* label, bool active); // use with e.g. if (RadioButton("one", my_value==1)) { my_value = 1; }
|
||||
IMGUI_API bool RadioButton(const char* label, int* v, int v_button); // shortcut to handle the above pattern when value is an integer
|
||||
@@ -648,11 +664,66 @@ namespace ImGui
|
||||
// - IsPopupOpen() with ImGuiPopupFlags_AnyPopupId + ImGuiPopupFlags_AnyPopupLevel: return true if any popup is open.
|
||||
IMGUI_API bool IsPopupOpen(const char* str_id, ImGuiPopupFlags flags = 0); // return true if the popup is open.
|
||||
|
||||
// Columns
|
||||
// Tables
|
||||
// [ALPHA API] API may evolve!
|
||||
// - Full-featured replacement for old Columns API.
|
||||
// - See Demo->Tables for details.
|
||||
// - See ImGuiTableFlags_ and ImGuiTableColumnFlags_ enums for a description of available flags.
|
||||
// The typical call flow is:
|
||||
// - 1. Call BeginTable()
|
||||
// - 2. Optionally call TableSetupColumn() to submit column name/flags/defaults
|
||||
// - 3. Optionally call TableSetupScrollFreeze() to request scroll freezing of columns/rows
|
||||
// - 4. Optionally call TableHeadersRow() to submit a header row (names will be pulled from data submitted to TableSetupColumns)
|
||||
// - 5. Populate contents
|
||||
// - In most situations you can use TableNextRow() + TableSetColumnIndex(xx) to start appending into a column.
|
||||
// - If you are using tables as a sort of grid, where every columns is holding the same type of contents,
|
||||
// you may prefer using TableNextColumn() instead of TableNextRow() + TableSetColumnIndex().
|
||||
// TableNextColumn() will automatically wrap-around into the next row if needed.
|
||||
// - IMPORTANT: Comparatively to the old Columns() API, we need to call TableNextColumn() for the first column!
|
||||
// - Both TableSetColumnIndex() and TableNextColumn() return false when the column is not visible, so you can
|
||||
// skip submitting the contents of a cell but only if you know the contents is not going to alter row height.
|
||||
// - Summary of possible call flow:
|
||||
// ----------------------------------------------------------------------------------------------------------
|
||||
// TableNextRow() -> TableSetColumnIndex(0) -> Text("Hello 0") -> TableSetColumnIndex(1) -> Text("Hello 1") // OK
|
||||
// TableNextRow() -> TableNextColumn() Text("Hello 0") -> TableNextColumn() -> Text("Hello 1") // OK
|
||||
// TableNextColumn() Text("Hello 0") -> TableNextColumn() -> Text("Hello 1") // OK: TableNextColumn() automatically gets to next row!
|
||||
// TableNextRow() Text("Hello 0") // Not OK! Missing TableSetColumnIndex() or TableNextColumn()! Text will not appear!
|
||||
// ----------------------------------------------------------------------------------------------------------
|
||||
// - 5. Call EndTable()
|
||||
#define IMGUI_HAS_TABLE 1
|
||||
IMGUI_API bool BeginTable(const char* str_id, int columns_count, ImGuiTableFlags flags = 0, const ImVec2& outer_size = ImVec2(0, 0), float inner_width = 0.0f);
|
||||
IMGUI_API void EndTable(); // only call EndTable() if BeginTable() returns true!
|
||||
IMGUI_API void TableNextRow(ImGuiTableRowFlags row_flags = 0, float min_row_height = 0.0f); // append into the first cell of a new row.
|
||||
IMGUI_API bool TableNextColumn(); // append into the next column (or first column of next row if currently in last column). Return false when column is not visible.
|
||||
IMGUI_API bool TableSetColumnIndex(int column_n); // append into the specified column. Return false when column is not visible.
|
||||
IMGUI_API int TableGetColumnIndex(); // return current column index.
|
||||
// Tables: Headers & Columns declaration
|
||||
// - Use TableSetupColumn() to specify label, resizing policy, default width/weight, id, various other flags etc.
|
||||
// Important: this will not display anything! The name passed to TableSetupColumn() is used by TableHeadersRow() and context-menus.
|
||||
// - Use TableHeadersRow() to create a row and automatically submit a TableHeader() for each column.
|
||||
// Headers are required to perform: reordering, sorting, and opening the context menu (but context menu can also be available in columns body using ImGuiTableFlags_ContextMenuInBody).
|
||||
// - You may manually submit headers using TableNextRow() + TableHeader() calls, but this is only useful in some advanced cases (e.g. adding custom widgets in header row).
|
||||
// - Use TableSetupScrollFreeze() to lock columns (from the right) or rows (from the top) so they stay visible when scrolled.
|
||||
IMGUI_API void TableSetupColumn(const char* label, ImGuiTableColumnFlags flags = 0, float init_width_or_weight = -1.0f, ImU32 user_id = 0);
|
||||
IMGUI_API void TableSetupScrollFreeze(int cols, int rows); // lock columns/rows so they stay visible when scrolled.
|
||||
IMGUI_API void TableHeadersRow(); // submit all headers cells based on data provided to TableSetupColumn() + submit context menu
|
||||
IMGUI_API void TableHeader(const char* label); // submit one header cell manually (rarely used)
|
||||
// Tables: Miscellaneous functions
|
||||
// - Most functions taking 'int column_n' treat the default value of -1 as the same as passing the current column index
|
||||
// - Sorting: call TableGetSortSpecs() to retrieve latest sort specs for the table. Return value will be NULL if no sorting.
|
||||
// When 'SpecsDirty == true' you should sort your data. It will be true when sorting specs have changed since last call, or the first time.
|
||||
// Make sure to set 'SpecsDirty = false' after sorting, else you may wastefully sort your data every frame!
|
||||
// Lifetime: don't hold on this pointer over multiple frames or past any subsequent call to BeginTable().
|
||||
IMGUI_API int TableGetColumnCount(); // return number of columns (value passed to BeginTable)
|
||||
IMGUI_API const char* TableGetColumnName(int column_n = -1); // return "" if column didn't have a name declared by TableSetupColumn(). Pass -1 to use current column.
|
||||
IMGUI_API bool TableGetColumnIsVisible(int column_n = -1); // return true if column is visible. Same value is also returned by TableNextColumn() and TableSetColumnIndex(). Pass -1 to use current column.
|
||||
IMGUI_API bool TableGetColumnIsSorted(int column_n = -1); // return true if column is included in the sort specs. Rarely used, can be useful to tell if a data change should trigger resort. Equivalent to test ImGuiTableSortSpecs's ->ColumnsMask & (1 << column_n). Pass -1 to use current column.
|
||||
IMGUI_API int TableGetHoveredColumn(); // return hovered column. return -1 when table is not hovered. return columns_count if the unused space at the right of visible columns is hovered.
|
||||
IMGUI_API ImGuiTableSortSpecs* TableGetSortSpecs(); // get latest sort specs for the table (NULL if not sorting).
|
||||
IMGUI_API void TableSetBgColor(ImGuiTableBgTarget bg_target, ImU32 color, int column_n = -1); // change the color of a cell, row, or column. See ImGuiTableBgTarget_ flags for details.
|
||||
|
||||
// Legacy Columns API (2020: prefer using Tables!)
|
||||
// - You can also use SameLine(pos_x) to mimic simplified columns.
|
||||
// - The columns API is work-in-progress and rather lacking (columns are arguably the worst part of dear imgui at the moment!)
|
||||
// - There is a maximum of 64 columns.
|
||||
// - Currently working on new 'Tables' api which will replace columns around Q2 2020 (see GitHub #2957).
|
||||
IMGUI_API void Columns(int count = 1, const char* id = NULL, bool border = true);
|
||||
IMGUI_API void NextColumn(); // next column, defaults to current row or next row if the current row is finished
|
||||
IMGUI_API int GetColumnIndex(); // get current column index
|
||||
@@ -1004,6 +1075,122 @@ enum ImGuiTabItemFlags_
|
||||
ImGuiTabItemFlags_Trailing = 1 << 7 // Enforce the tab position to the right of the tab bar (before the scrolling buttons)
|
||||
};
|
||||
|
||||
// Flags for ImGui::BeginTable()
|
||||
// - Important! Sizing policies have particularly complex and subtle side effects, more so than you would expect.
|
||||
// Read comments/demos carefully + experiment with live demos to get acquainted with them.
|
||||
// - The default sizing policy for columns depends on whether the ScrollX flag is set on the table:
|
||||
// When ScrollX is off:
|
||||
// - Table defaults to ImGuiTableFlags_SizingPolicyStretchX -> all Columns defaults to ImGuiTableColumnFlags_WidthStretch.
|
||||
// - Columns sizing policy allowed: Fixed/Auto or Stretch.
|
||||
// - Stretch Columns will share the width available in table.
|
||||
// - Fixed Columns will generally obtain their requested width unless the Table cannot fit them all.
|
||||
// When ScrollX is on:
|
||||
// - Table defaults to ImGuiTableFlags_SizingPolicyFixedX -> all Columns defaults to ImGuiTableColumnFlags_WidthFixed.
|
||||
// - Columns sizing policy allowed: Fixed/Auto mostly! Using Stretch columns OFTEN DOES NOT MAKE SENSE if ScrollX is on, UNLESS you have specified a value for 'inner_width' in BeginTable().
|
||||
// - Fixed Columns can be enlarged as needed. Table will show an horizontal scrollbar if needed.
|
||||
// - Stretch Columns, if any, will calculate their width using inner_width, assuming no scrolling (it really doesn't make sense to do otherwise).
|
||||
// - Mixing up columns with different sizing policy is possible BUT can be tricky and has some side-effects and restrictions.
|
||||
// (their visible order and the scrolling state have subtle but necessary effects on how they can be manually resized).
|
||||
// The typical use of mixing sizing policies is to have ScrollX disabled, one or two Stretch Column and many Fixed Columns.
|
||||
enum ImGuiTableFlags_
|
||||
{
|
||||
// Features
|
||||
ImGuiTableFlags_None = 0,
|
||||
ImGuiTableFlags_Resizable = 1 << 0, // Allow resizing columns.
|
||||
ImGuiTableFlags_Reorderable = 1 << 1, // Allow reordering columns in header row (need calling TableSetupColumn() + TableHeadersRow() to display headers)
|
||||
ImGuiTableFlags_Hideable = 1 << 2, // Allow hiding columns in context menu.
|
||||
ImGuiTableFlags_Sortable = 1 << 3, // Allow sorting on one column (sort_specs_count will always be == 1). Call TableGetSortSpecs() to obtain sort specs.
|
||||
ImGuiTableFlags_MultiSortable = 1 << 4, // Allow sorting on multiple columns by holding Shift (sort_specs_count may be > 1). Call TableGetSortSpecs() to obtain sort specs.
|
||||
ImGuiTableFlags_NoSavedSettings = 1 << 5, // Disable persisting columns order, width and sort settings in the .ini file.
|
||||
ImGuiTableFlags_ContextMenuInBody = 1 << 6, // Right-click on columns body/contents will display table context menu. By default it is available in TableHeadersRow().
|
||||
// Decoration
|
||||
ImGuiTableFlags_RowBg = 1 << 7, // Set each RowBg color with ImGuiCol_TableRowBg or ImGuiCol_TableRowBgAlt (equivalent to calling TableSetBgColor with ImGuiTableBgFlags_RowBg0 on each row manually)
|
||||
ImGuiTableFlags_BordersInnerH = 1 << 8, // Draw horizontal borders between rows.
|
||||
ImGuiTableFlags_BordersOuterH = 1 << 9, // Draw horizontal borders at the top and bottom.
|
||||
ImGuiTableFlags_BordersInnerV = 1 << 10, // Draw vertical borders between columns.
|
||||
ImGuiTableFlags_BordersOuterV = 1 << 11, // Draw vertical borders on the left and right sides.
|
||||
ImGuiTableFlags_BordersH = ImGuiTableFlags_BordersInnerH | ImGuiTableFlags_BordersOuterH, // Draw horizontal borders.
|
||||
ImGuiTableFlags_BordersV = ImGuiTableFlags_BordersInnerV | ImGuiTableFlags_BordersOuterV, // Draw vertical borders.
|
||||
ImGuiTableFlags_BordersInner = ImGuiTableFlags_BordersInnerV | ImGuiTableFlags_BordersInnerH, // Draw inner borders.
|
||||
ImGuiTableFlags_BordersOuter = ImGuiTableFlags_BordersOuterV | ImGuiTableFlags_BordersOuterH, // Draw outer borders.
|
||||
ImGuiTableFlags_Borders = ImGuiTableFlags_BordersInner | ImGuiTableFlags_BordersOuter, // Draw all borders.
|
||||
ImGuiTableFlags_NoBordersInBody = 1 << 12, // Disable vertical borders in columns Body (borders will always appears in Headers).
|
||||
ImGuiTableFlags_NoBordersInBodyUntilResize = 1 << 13, // Disable vertical borders in columns Body until hovered for resize (borders will always appears in Headers).
|
||||
// Sizing
|
||||
ImGuiTableFlags_SizingPolicyFixedX = 1 << 14, // Default if ScrollX is on. Columns will default to use _WidthFixed or _WidthAlwaysAutoResize policy. Read description above for more details.
|
||||
ImGuiTableFlags_SizingPolicyStretchX = 1 << 15, // Default if ScrollX is off. Columns will default to use _WidthStretch policy. Read description above for more details.
|
||||
ImGuiTableFlags_NoHeadersWidth = 1 << 16, // Disable header width contribution to automatic width calculation.
|
||||
ImGuiTableFlags_NoHostExtendY = 1 << 17, // (FIXME-TABLE: Reword as SizingPolicy?) Disable extending past the limit set by outer_size.y, only meaningful when neither of ScrollX|ScrollY are set (data below the limit will be clipped and not visible)
|
||||
ImGuiTableFlags_NoKeepColumnsVisible = 1 << 18, // Disable keeping column always minimally visible when table width gets too small and ScrllX is off.
|
||||
ImGuiTableFlags_PreciseStretchWidths = 1 << 19, // Disable distributing remainder width to stretched columns (width allocation on a 100-wide table with 3 columns: Without this flag: 33,33,34. With this flag: 33,33,33). With larger number of columns, resizing will appear to be less smooth.
|
||||
ImGuiTableFlags_NoClip = 1 << 20, // Disable clipping rectangle for every individual columns (reduce draw command count, items will be able to overflow into other columns). Generally incompatible with TableSetupScrollFreeze().
|
||||
// Padding
|
||||
ImGuiTableFlags_PadOuterX = 1 << 21, // Default if BordersOuterV is on. Enable outer-most padding.
|
||||
ImGuiTableFlags_NoPadOuterX = 1 << 22, // Default if BordersOuterV is off. Disable outer-most padding.
|
||||
ImGuiTableFlags_NoPadInnerX = 1 << 23, // Disable inner padding between columns (double inner padding if BordersOuterV is on, single inner padding if BordersOuterV is off).
|
||||
// Scrolling
|
||||
ImGuiTableFlags_ScrollX = 1 << 24, // Enable horizontal scrolling. Require 'outer_size' parameter of BeginTable() to specify the container size. Changes default sizing policy. Because this create a child window, ScrollY is currently generally recommended when using ScrollX.
|
||||
ImGuiTableFlags_ScrollY = 1 << 25, // Enable vertical scrolling. Require 'outer_size' parameter of BeginTable() to specify the container size.
|
||||
|
||||
// [Internal] Combinations and masks
|
||||
ImGuiTableFlags_SizingPolicyMaskX_ = ImGuiTableFlags_SizingPolicyStretchX | ImGuiTableFlags_SizingPolicyFixedX
|
||||
};
|
||||
|
||||
// Flags for ImGui::TableSetupColumn()
|
||||
// FIXME-TABLE: Rename to ImGuiColumns_*, stick old columns api flags in there under an obsolete api block
|
||||
enum ImGuiTableColumnFlags_
|
||||
{
|
||||
ImGuiTableColumnFlags_None = 0,
|
||||
ImGuiTableColumnFlags_DefaultHide = 1 << 0, // Default as a hidden column.
|
||||
ImGuiTableColumnFlags_DefaultSort = 1 << 1, // Default as a sorting column.
|
||||
ImGuiTableColumnFlags_WidthFixed = 1 << 2, // Column will keep a fixed size, preferable with horizontal scrolling enabled (default if table sizing policy is SizingPolicyFixedX and table is resizable).
|
||||
ImGuiTableColumnFlags_WidthStretch = 1 << 3, // Column will stretch, preferable with horizontal scrolling disabled (default if table sizing policy is SizingPolicyStretchX).
|
||||
ImGuiTableColumnFlags_WidthAlwaysAutoResize = 1 << 4, // Column will keep resizing based on submitted contents (with a one frame delay) == Fixed with auto resize (default if table sizing policy is SizingPolicyFixedX and table is not resizable).
|
||||
ImGuiTableColumnFlags_NoResize = 1 << 5, // Disable manual resizing.
|
||||
ImGuiTableColumnFlags_NoClipX = 1 << 6, // Disable clipping for this column (all NoClipX columns will render in a same draw command).
|
||||
ImGuiTableColumnFlags_NoSort = 1 << 7, // Disable ability to sort on this field (even if ImGuiTableFlags_Sortable is set on the table).
|
||||
ImGuiTableColumnFlags_NoSortAscending = 1 << 8, // Disable ability to sort in the ascending direction.
|
||||
ImGuiTableColumnFlags_NoSortDescending = 1 << 9, // Disable ability to sort in the descending direction.
|
||||
ImGuiTableColumnFlags_NoHide = 1 << 10, // Disable hiding this column.
|
||||
ImGuiTableColumnFlags_NoHeaderWidth = 1 << 11, // Header width don't contribute to automatic column width.
|
||||
ImGuiTableColumnFlags_PreferSortAscending = 1 << 12, // Make the initial sort direction Ascending when first sorting on this column (default).
|
||||
ImGuiTableColumnFlags_PreferSortDescending = 1 << 13, // Make the initial sort direction Descending when first sorting on this column.
|
||||
ImGuiTableColumnFlags_IndentEnable = 1 << 14, // Use current Indent value when entering cell (default for 1st column).
|
||||
ImGuiTableColumnFlags_IndentDisable = 1 << 15, // Ignore current Indent value when entering cell (default for columns after the 1st one). Indentation changes _within_ the cell will still be honored.
|
||||
ImGuiTableColumnFlags_NoReorder = 1 << 16, // Disable reordering this column, this will also prevent other columns from crossing over this column.
|
||||
|
||||
// [Internal] Combinations and masks
|
||||
ImGuiTableColumnFlags_WidthMask_ = ImGuiTableColumnFlags_WidthFixed | ImGuiTableColumnFlags_WidthStretch | ImGuiTableColumnFlags_WidthAlwaysAutoResize,
|
||||
ImGuiTableColumnFlags_IndentMask_ = ImGuiTableColumnFlags_IndentEnable | ImGuiTableColumnFlags_IndentDisable,
|
||||
ImGuiTableColumnFlags_NoDirectResize_ = 1 << 20 // [Internal] Disable user resizing this column directly (it may however we resized indirectly from its left edge)
|
||||
};
|
||||
|
||||
// Flags for ImGui::TableNextRow()
|
||||
enum ImGuiTableRowFlags_
|
||||
{
|
||||
ImGuiTableRowFlags_None = 0,
|
||||
ImGuiTableRowFlags_Headers = 1 << 0 // Identify header row (set default background color + width of its contents accounted different for auto column width)
|
||||
};
|
||||
|
||||
// Enum for ImGui::TableSetBgColor()
|
||||
// Background colors are rendering in 3 layers:
|
||||
// - Layer 0: draw with RowBg0 color if set, otherwise draw with ColumnBg0 if set.
|
||||
// - Layer 1: draw with RowBg1 color if set, otherwise draw with ColumnBg1 if set.
|
||||
// - Layer 2: draw with CellBg color if set.
|
||||
// The purpose of the two row/columns layers is to let you decide if a background color changes should override or blend with the existing color.
|
||||
// When using ImGuiTableFlags_RowBg on the table, each row has the RowBg0 color automatically set for odd/even rows.
|
||||
// If you set the color of RowBg0 target, your color will override the existing RowBg0 color.
|
||||
// If you set the color of RowBg1 or ColumnBg1 target, your color will blend over the RowBg0 color.
|
||||
enum ImGuiTableBgTarget_
|
||||
{
|
||||
ImGuiTableBgTarget_None = 0,
|
||||
//ImGuiTableBgTarget_ColumnBg0 = 1, // FIXME-TABLE: Todo. Set column background color 0 (generally used for background
|
||||
//ImGuiTableBgTarget_ColumnBg1 = 2, // FIXME-TABLE: Todo. Set column background color 1 (generally used for selection marking)
|
||||
ImGuiTableBgTarget_RowBg0 = 3, // Set row background color 0 (generally used for background, automatically set when ImGuiTableFlags_RowBg is used)
|
||||
ImGuiTableBgTarget_RowBg1 = 4, // Set row background color 1 (generally used for selection marking)
|
||||
ImGuiTableBgTarget_CellBg = 5 // Set cell background color (top-most color)
|
||||
};
|
||||
|
||||
// Flags for ImGui::IsWindowFocused()
|
||||
enum ImGuiFocusedFlags_
|
||||
{
|
||||
@@ -1096,6 +1283,14 @@ enum ImGuiDir_
|
||||
ImGuiDir_COUNT
|
||||
};
|
||||
|
||||
// A sorting direction
|
||||
enum ImGuiSortDirection_
|
||||
{
|
||||
ImGuiSortDirection_None = 0,
|
||||
ImGuiSortDirection_Ascending = 1, // Ascending = 0->9, A->Z etc.
|
||||
ImGuiSortDirection_Descending = 2 // Descending = 9->0, Z->A etc.
|
||||
};
|
||||
|
||||
// User fill ImGuiIO.KeyMap[] array with indices into the ImGuiIO.KeysDown[512] array
|
||||
enum ImGuiKey_
|
||||
{
|
||||
@@ -1256,6 +1451,11 @@ enum ImGuiCol_
|
||||
ImGuiCol_PlotLinesHovered,
|
||||
ImGuiCol_PlotHistogram,
|
||||
ImGuiCol_PlotHistogramHovered,
|
||||
ImGuiCol_TableHeaderBg, // Table header background
|
||||
ImGuiCol_TableBorderStrong, // Table outer and header borders (prefer using Alpha=1.0 here)
|
||||
ImGuiCol_TableBorderLight, // Table inner borders (prefer using Alpha=1.0 here)
|
||||
ImGuiCol_TableRowBg, // Table row background (even rows)
|
||||
ImGuiCol_TableRowBgAlt, // Table row background (odd rows)
|
||||
ImGuiCol_TextSelectedBg,
|
||||
ImGuiCol_DragDropTarget,
|
||||
ImGuiCol_NavHighlight, // Gamepad/keyboard: current highlighted item
|
||||
@@ -1296,6 +1496,7 @@ enum ImGuiStyleVar_
|
||||
ImGuiStyleVar_ItemSpacing, // ImVec2 ItemSpacing
|
||||
ImGuiStyleVar_ItemInnerSpacing, // ImVec2 ItemInnerSpacing
|
||||
ImGuiStyleVar_IndentSpacing, // float IndentSpacing
|
||||
ImGuiStyleVar_CellPadding, // ImVec2 CellPadding
|
||||
ImGuiStyleVar_ScrollbarSize, // float ScrollbarSize
|
||||
ImGuiStyleVar_ScrollbarRounding, // float ScrollbarRounding
|
||||
ImGuiStyleVar_GrabMinSize, // float GrabMinSize
|
||||
@@ -1532,6 +1733,7 @@ struct ImGuiStyle
|
||||
float FrameBorderSize; // Thickness of border around frames. Generally set to 0.0f or 1.0f. (Other values are not well tested and more CPU/GPU costly).
|
||||
ImVec2 ItemSpacing; // Horizontal and vertical spacing between widgets/lines.
|
||||
ImVec2 ItemInnerSpacing; // Horizontal and vertical spacing between within elements of a composed widget (e.g. a slider and its label).
|
||||
ImVec2 CellPadding; // Padding within a table cell
|
||||
ImVec2 TouchExtraPadding; // Expand reactive bounding box for touch-based system where touch position is not accurate enough. Unfortunately we don't sort widgets so priority on overlap will always be given to the first widget. So don't grow this too much!
|
||||
float IndentSpacing; // Horizontal indentation when e.g. entering a tree node. Generally == (FontSize + FramePadding.x*2).
|
||||
float ColumnsMinSpacing; // Minimum horizontal spacing between two columns. Preferably > (FramePadding.x + 1).
|
||||
@@ -1611,7 +1813,7 @@ struct ImGuiIO
|
||||
bool ConfigInputTextCursorBlink; // = true // Set to false to disable blinking cursor, for users who consider it distracting. (was called: io.OptCursorBlink prior to 1.63)
|
||||
bool ConfigWindowsResizeFromEdges; // = true // Enable resizing of windows from their edges and from the lower-left corner. This requires (io.BackendFlags & ImGuiBackendFlags_HasMouseCursors) because it needs mouse cursor feedback. (This used to be a per-window ImGuiWindowFlags_ResizeFromAnySide flag)
|
||||
bool ConfigWindowsMoveFromTitleBarOnly; // = false // [BETA] Set to true to only allow moving windows when clicked+dragged from the title bar. Windows without a title bar are not affected.
|
||||
float ConfigWindowsMemoryCompactTimer;// = 60.0f // [BETA] Compact window memory usage when unused. Set to -1.0f to disable.
|
||||
float ConfigMemoryCompactTimer; // = 60.0f // [BETA] Free transient windows/tables memory buffers when unused for given amount of time. Set to -1.0f to disable.
|
||||
|
||||
//------------------------------------------------------------------
|
||||
// Platform Functions
|
||||
@@ -1797,6 +1999,31 @@ struct ImGuiPayload
|
||||
bool IsDelivery() const { return Delivery; }
|
||||
};
|
||||
|
||||
// Sorting specification for one column of a table (sizeof == 8 bytes)
|
||||
struct ImGuiTableSortSpecsColumn
|
||||
{
|
||||
ImGuiID ColumnUserID; // User id of the column (if specified by a TableSetupColumn() call)
|
||||
ImU8 ColumnIndex; // Index of the column
|
||||
ImU8 SortOrder; // Index within parent ImGuiTableSortSpecs (always stored in order starting from 0, tables sorted on a single criteria will always have a 0 here)
|
||||
ImGuiSortDirection SortDirection : 8; // ImGuiSortDirection_Ascending or ImGuiSortDirection_Descending (you can use this or SortSign, whichever is more convenient for your sort function)
|
||||
|
||||
ImGuiTableSortSpecsColumn() { ColumnUserID = 0; ColumnIndex = 0; SortOrder = 0; SortDirection = ImGuiSortDirection_Ascending; }
|
||||
};
|
||||
|
||||
// Sorting specifications for a table (often handling sort specs for a single column, occasionally more)
|
||||
// Obtained by calling TableGetSortSpecs().
|
||||
// When 'SpecsDirty == true' you can sort your data. It will be true with sorting specs have changed since last call, or the first time.
|
||||
// Make sure to set 'SpecsDirty = false' after sorting, else you may wastefully sort your data every frame!
|
||||
struct ImGuiTableSortSpecs
|
||||
{
|
||||
const ImGuiTableSortSpecsColumn* Specs; // Pointer to sort spec array.
|
||||
int SpecsCount; // Sort spec count. Most often 1 unless e.g. ImGuiTableFlags_MultiSortable is enabled.
|
||||
bool SpecsDirty; // Set to true when specs have changed since last time! Use this to sort again, then clear the flag.
|
||||
ImU64 ColumnsMask; // Set to the mask of column indexes included in the Specs array. e.g. (1 << N) when column N is sorted.
|
||||
|
||||
ImGuiTableSortSpecs() { Specs = NULL; SpecsCount = 0; SpecsDirty = false; ColumnsMask = 0x00; }
|
||||
};
|
||||
|
||||
//-----------------------------------------------------------------------------
|
||||
// Obsolete functions (Will be removed! Read 'API BREAKING CHANGES' section in imgui.cpp for details)
|
||||
// Please keep your copy of dear imgui up to date! Occasionally set '#define IMGUI_DISABLE_OBSOLETE_FUNCTIONS' in imconfig.h to stay ahead.
|
||||
@@ -1985,6 +2212,7 @@ struct ImGuiListClipper
|
||||
// [Internal]
|
||||
int ItemsCount;
|
||||
int StepNo;
|
||||
int ItemsFrozen;
|
||||
float ItemsHeight;
|
||||
float StartPosY;
|
||||
|
||||
@@ -2110,22 +2338,31 @@ struct ImDrawVert
|
||||
IMGUI_OVERRIDE_DRAWVERT_STRUCT_LAYOUT;
|
||||
#endif
|
||||
|
||||
// For use by ImDrawListSplitter.
|
||||
// [Internal] For use by ImDrawList
|
||||
struct ImDrawCmdHeader
|
||||
{
|
||||
ImVec4 ClipRect;
|
||||
ImTextureID TextureId;
|
||||
unsigned int VtxOffset;
|
||||
};
|
||||
|
||||
// [Internal] For use by ImDrawListSplitter
|
||||
struct ImDrawChannel
|
||||
{
|
||||
ImVector<ImDrawCmd> _CmdBuffer;
|
||||
ImVector<ImDrawIdx> _IdxBuffer;
|
||||
};
|
||||
|
||||
|
||||
// Split/Merge functions are used to split the draw list into different layers which can be drawn into out of order.
|
||||
// This is used by the Columns api, so items of each column can be batched together in a same draw call.
|
||||
// This is used by the Columns/Tables API, so items of each column can be batched together in a same draw call.
|
||||
struct ImDrawListSplitter
|
||||
{
|
||||
int _Current; // Current channel number (0)
|
||||
int _Count; // Number of active channels (1+)
|
||||
ImVector<ImDrawChannel> _Channels; // Draw channels (not resized down so _Count might be < Channels.Size)
|
||||
|
||||
inline ImDrawListSplitter() { Clear(); }
|
||||
inline ImDrawListSplitter() { memset(this, 0, sizeof(*this)); }
|
||||
inline ~ImDrawListSplitter() { ClearFreeMemory(); }
|
||||
inline void Clear() { _Current = 0; _Count = 1; } // Do not clear Channels[] so our allocations are reused next frame
|
||||
IMGUI_API void ClearFreeMemory();
|
||||
@@ -2176,19 +2413,19 @@ struct ImDrawList
|
||||
ImDrawListFlags Flags; // Flags, you may poke into these to adjust anti-aliasing settings per-primitive.
|
||||
|
||||
// [Internal, used while building lists]
|
||||
unsigned int _VtxCurrentIdx; // [Internal] generally == VtxBuffer.Size unless we are past 64K vertices, in which case this gets reset to 0.
|
||||
const ImDrawListSharedData* _Data; // Pointer to shared draw data (you can use ImGui::GetDrawListSharedData() to get the one from current ImGui context)
|
||||
const char* _OwnerName; // Pointer to owner window's name for debugging
|
||||
unsigned int _VtxCurrentIdx; // [Internal] Generally == VtxBuffer.Size unless we are past 64K vertices, in which case this gets reset to 0.
|
||||
ImDrawVert* _VtxWritePtr; // [Internal] point within VtxBuffer.Data after each add command (to avoid using the ImVector<> operators too much)
|
||||
ImDrawIdx* _IdxWritePtr; // [Internal] point within IdxBuffer.Data after each add command (to avoid using the ImVector<> operators too much)
|
||||
ImVector<ImVec4> _ClipRectStack; // [Internal]
|
||||
ImVector<ImTextureID> _TextureIdStack; // [Internal]
|
||||
ImVector<ImVec2> _Path; // [Internal] current path building
|
||||
ImDrawCmd _CmdHeader; // [Internal] Template of active commands. Fields should match those of CmdBuffer.back().
|
||||
ImDrawCmdHeader _CmdHeader; // [Internal] template of active commands. Fields should match those of CmdBuffer.back().
|
||||
ImDrawListSplitter _Splitter; // [Internal] for channels api (note: prefer using your own persistent instance of ImDrawListSplitter!)
|
||||
|
||||
// If you want to create ImDrawList instances, pass them ImGui::GetDrawListSharedData() or create and use your own ImDrawListSharedData (so you can use ImDrawList without ImGui)
|
||||
ImDrawList(const ImDrawListSharedData* shared_data) { _Data = shared_data; Flags = ImDrawListFlags_None; _VtxCurrentIdx = 0; _VtxWritePtr = NULL; _IdxWritePtr = NULL; _OwnerName = NULL; }
|
||||
ImDrawList(const ImDrawListSharedData* shared_data) { memset(this, 0, sizeof(*this)); _Data = shared_data; }
|
||||
|
||||
~ImDrawList() { _ClearFreeMemory(); }
|
||||
IMGUI_API void PushClipRect(ImVec2 clip_rect_min, ImVec2 clip_rect_max, bool intersect_with_current_clip_rect = false); // Render-level scissoring. This is passed down to your render function but not used for CPU-side coarse clipping. Prefer using higher-level ImGui::PushClipRect() to affect logic (hit-testing and widget culling)
|
||||
|
||||
@@ -23,6 +23,7 @@ Index of this file:
|
||||
// [SECTION] Docking support
|
||||
// [SECTION] Viewport support
|
||||
// [SECTION] Settings support
|
||||
// [SECTION] Metrics, Debug
|
||||
// [SECTION] Generic context hooks
|
||||
// [SECTION] ImGuiContext (main imgui context)
|
||||
// [SECTION] ImGuiWindowTempData, ImGuiWindow
|
||||
@@ -110,9 +111,14 @@ struct ImGuiNextWindowData; // Storage for SetNextWindow** functions
|
||||
struct ImGuiNextItemData; // Storage for SetNextItem** functions
|
||||
struct ImGuiPopupData; // Storage for current popup stack
|
||||
struct ImGuiSettingsHandler; // Storage for one type registered in the .ini file
|
||||
struct ImGuiStackSizes; // Storage of stack sizes for debugging/asserting
|
||||
struct ImGuiStyleMod; // Stacked style modifier, backup of modified data so we can restore it
|
||||
struct ImGuiTabBar; // Storage for a tab bar
|
||||
struct ImGuiTabItem; // Storage for a tab item (within a tab bar)
|
||||
struct ImGuiTable; // Storage for a table
|
||||
struct ImGuiTableColumn; // Storage for one column of a table
|
||||
struct ImGuiTableSettings; // Storage for a table .ini settings
|
||||
struct ImGuiTableColumnsSettings; // Storage for a column .ini settings
|
||||
struct ImGuiWindow; // Storage for one window
|
||||
struct ImGuiWindowTempData; // Temporary storage for one window (that's the data which in theory we could ditch at the end of the frame)
|
||||
struct ImGuiWindowSettings; // Storage for a window .ini settings (we keep one of those even if the actual window wasn't instanced during this session)
|
||||
@@ -254,6 +260,7 @@ namespace ImStb
|
||||
// - Helper: ImRect
|
||||
// - Helper: ImBitArray
|
||||
// - Helper: ImBitVector
|
||||
// - Helper: ImSpan<>, ImSpanAllocator<>
|
||||
// - Helper: ImPool<>
|
||||
// - Helper: ImChunkStream<>
|
||||
//-----------------------------------------------------------------------------
|
||||
@@ -273,6 +280,7 @@ IMGUI_API ImU32 ImAlphaBlendColors(ImU32 col_a, ImU32 col_b);
|
||||
|
||||
// Helpers: Bit manipulation
|
||||
static inline bool ImIsPowerOfTwo(int v) { return v != 0 && (v & (v - 1)) == 0; }
|
||||
static inline bool ImIsPowerOfTwo(ImU64 v) { return v != 0 && (v & (v - 1)) == 0; }
|
||||
static inline int ImUpperPowerOfTwo(int v) { v--; v |= v >> 1; v |= v >> 2; v |= v >> 4; v |= v >> 8; v |= v >> 16; v++; return v; }
|
||||
|
||||
// Helpers: String, Formatting
|
||||
@@ -465,21 +473,35 @@ struct IMGUI_API ImRect
|
||||
};
|
||||
|
||||
// Helper: ImBitArray
|
||||
inline bool ImBitArrayTestBit(const ImU32* arr, int n) { ImU32 mask = (ImU32)1 << (n & 31); return (arr[n >> 5] & mask) != 0; }
|
||||
inline void ImBitArrayClearBit(ImU32* arr, int n) { ImU32 mask = (ImU32)1 << (n & 31); arr[n >> 5] &= ~mask; }
|
||||
inline void ImBitArraySetBit(ImU32* arr, int n) { ImU32 mask = (ImU32)1 << (n & 31); arr[n >> 5] |= mask; }
|
||||
inline void ImBitArraySetBitRange(ImU32* arr, int n, int n2)
|
||||
inline bool ImBitArrayTestBit(const ImU32* arr, int n) { ImU32 mask = (ImU32)1 << (n & 31); return (arr[n >> 5] & mask) != 0; }
|
||||
inline void ImBitArrayClearBit(ImU32* arr, int n) { ImU32 mask = (ImU32)1 << (n & 31); arr[n >> 5] &= ~mask; }
|
||||
inline void ImBitArraySetBit(ImU32* arr, int n) { ImU32 mask = (ImU32)1 << (n & 31); arr[n >> 5] |= mask; }
|
||||
inline void ImBitArraySetBitRange(ImU32* arr, int n, int n2)
|
||||
{
|
||||
while (n <= n2)
|
||||
{
|
||||
int a_mod = (n & 31);
|
||||
int b_mod = ((n2 >= n + 31) ? 31 : (n2 & 31)) + 1;
|
||||
int b_mod = (n2 > (n | 31) ? 31 : (n2 & 31)) + 1;
|
||||
ImU32 mask = (ImU32)(((ImU64)1 << b_mod) - 1) & ~(ImU32)(((ImU64)1 << a_mod) - 1);
|
||||
arr[n >> 5] |= mask;
|
||||
n = (n + 32) & ~31;
|
||||
}
|
||||
}
|
||||
|
||||
// Helper: ImBitArray class (wrapper over ImBitArray functions)
|
||||
// Store 1-bit per value. NOT CLEARED by constructor.
|
||||
template<int BITCOUNT>
|
||||
struct IMGUI_API ImBitArray
|
||||
{
|
||||
ImU32 Storage[(BITCOUNT + 31) >> 5];
|
||||
ImBitArray() { }
|
||||
void ClearBits() { memset(Storage, 0, sizeof(Storage)); }
|
||||
bool TestBit(int n) const { IM_ASSERT(n < BITCOUNT); return ImBitArrayTestBit(Storage, n); }
|
||||
void SetBit(int n) { IM_ASSERT(n < BITCOUNT); ImBitArraySetBit(Storage, n); }
|
||||
void ClearBit(int n) { IM_ASSERT(n < BITCOUNT); ImBitArrayClearBit(Storage, n); }
|
||||
void SetBitRange(int n1, int n2) { ImBitArraySetBitRange(Storage, n1, n2); }
|
||||
};
|
||||
|
||||
// Helper: ImBitVector
|
||||
// Store 1-bit per value.
|
||||
struct IMGUI_API ImBitVector
|
||||
@@ -492,6 +514,54 @@ struct IMGUI_API ImBitVector
|
||||
void ClearBit(int n) { IM_ASSERT(n < (Storage.Size << 5)); ImBitArrayClearBit(Storage.Data, n); }
|
||||
};
|
||||
|
||||
// Helper: ImSpan<>
|
||||
// Pointing to a span of data we don't own.
|
||||
template<typename T>
|
||||
struct ImSpan
|
||||
{
|
||||
T* Data;
|
||||
T* DataEnd;
|
||||
|
||||
// Constructors, destructor
|
||||
inline ImSpan() { Data = DataEnd = NULL; }
|
||||
inline ImSpan(T* data, int size) { Data = data; DataEnd = data + size; }
|
||||
inline ImSpan(T* data, T* data_end) { Data = data; DataEnd = data_end; }
|
||||
|
||||
inline void set(T* data, int size) { Data = data; DataEnd = data + size; }
|
||||
inline void set(T* data, T* data_end) { Data = data; DataEnd = data_end; }
|
||||
inline int size() const { return (int)(ptrdiff_t)(DataEnd - Data); }
|
||||
inline T& operator[](int i) { T* p = Data + i; IM_ASSERT(p >= Data && p < DataEnd); return *p; }
|
||||
inline const T& operator[](int i) const { const T* p = Data + i; IM_ASSERT(p >= Data && p < DataEnd); return *p; }
|
||||
|
||||
inline T* begin() { return Data; }
|
||||
inline const T* begin() const { return Data; }
|
||||
inline T* end() { return DataEnd; }
|
||||
inline const T* end() const { return DataEnd; }
|
||||
|
||||
// Utilities
|
||||
inline int index_from_ptr(const T* it) const { IM_ASSERT(it >= Data && it < DataEnd); const ptrdiff_t off = it - Data; return (int)off; }
|
||||
};
|
||||
|
||||
// Helper: ImSpanAllocator<>
|
||||
// Facilitate storing multiple chunks into a single large block (the "arena")
|
||||
template<int CHUNKS>
|
||||
struct ImSpanAllocator
|
||||
{
|
||||
char* BasePtr;
|
||||
int TotalSize;
|
||||
int CurrSpan;
|
||||
int Offsets[CHUNKS];
|
||||
|
||||
ImSpanAllocator() { memset(this, 0, sizeof(*this)); }
|
||||
inline void ReserveBytes(int n, size_t sz) { IM_ASSERT(n == CurrSpan && n < CHUNKS); IM_UNUSED(n); Offsets[CurrSpan++] = TotalSize; TotalSize += (int)sz; }
|
||||
inline int GetArenaSizeInBytes() { return TotalSize; }
|
||||
inline void SetArenaBasePtr(void* base_ptr) { BasePtr = (char*)base_ptr; }
|
||||
inline void* GetSpanPtrBegin(int n) { IM_ASSERT(n >= 0 && n < CHUNKS && CurrSpan == CHUNKS); return (void*)(BasePtr + Offsets[n]); }
|
||||
inline void* GetSpanPtrEnd(int n) { IM_ASSERT(n >= 0 && n < CHUNKS && CurrSpan == CHUNKS); return (n + 1 < CHUNKS) ? BasePtr + Offsets[n + 1] : (void*)(BasePtr + TotalSize); }
|
||||
template<typename T>
|
||||
inline void GetSpan(int n, ImSpan<T>* span) { span->set((T*)GetSpanPtrBegin(n), (T*)GetSpanPtrEnd(n)); }
|
||||
};
|
||||
|
||||
// Helper: ImPool<>
|
||||
// Basic keyed storage for contiguous instances, slow/amortized insertion, O(1) indexable, O(Log N) queries by ID over a dense/hot buffer,
|
||||
// Honor constructor/destructor. Add/remove invalidate all pointers. Indexes have the same lifetime as the associated object.
|
||||
@@ -538,6 +608,8 @@ struct IMGUI_API ImChunkStream
|
||||
T* end() { return (T*)(void*)(Buf.Data + Buf.Size); }
|
||||
int offset_from_ptr(const T* p) { IM_ASSERT(p >= begin() && p < end()); const ptrdiff_t off = (const char*)p - Buf.Data; return (int)off; }
|
||||
T* ptr_from_offset(int off) { IM_ASSERT(off >= 4 && off < Buf.Size); return (T*)(void*)(Buf.Data + off); }
|
||||
void swap(ImChunkStream<T>& rhs) { rhs.Buf.swap(Buf); }
|
||||
|
||||
};
|
||||
|
||||
//-----------------------------------------------------------------------------
|
||||
@@ -838,6 +910,7 @@ struct ImGuiStyleMod
|
||||
// Stacked storage data for BeginGroup()/EndGroup()
|
||||
struct ImGuiGroupData
|
||||
{
|
||||
ImGuiID WindowID;
|
||||
ImVec2 BackupCursorPos;
|
||||
ImVec2 BackupCursorMaxPos;
|
||||
ImVec1 BackupIndent;
|
||||
@@ -856,7 +929,7 @@ struct IMGUI_API ImGuiMenuColumns
|
||||
float Width, NextWidth;
|
||||
float Pos[3], NextWidths[3];
|
||||
|
||||
ImGuiMenuColumns();
|
||||
ImGuiMenuColumns() { memset(this, 0, sizeof(*this)); }
|
||||
void Update(int count, float spacing, bool clear);
|
||||
float DeclColumns(float w0, float w1, float w2);
|
||||
float CalcExtraSpace(float avail_w) const;
|
||||
@@ -909,7 +982,7 @@ struct ImGuiPopupData
|
||||
ImVec2 OpenPopupPos; // Set on OpenPopup(), preferred popup position (typically == OpenMousePos when using mouse)
|
||||
ImVec2 OpenMousePos; // Set on OpenPopup(), copy of mouse position at the time of opening popup
|
||||
|
||||
ImGuiPopupData() { PopupId = 0; Window = SourceWindow = NULL; OpenFrameCount = -1; OpenParentId = 0; }
|
||||
ImGuiPopupData() { memset(this, 0, sizeof(*this)); OpenFrameCount = -1; }
|
||||
};
|
||||
|
||||
struct ImGuiNavMoveResult
|
||||
@@ -1026,7 +1099,7 @@ struct ImGuiColumnData
|
||||
ImGuiColumnsFlags Flags; // Not exposed
|
||||
ImRect ClipRect;
|
||||
|
||||
ImGuiColumnData() { OffsetNorm = OffsetNormBeforeResize = 0.0f; Flags = ImGuiColumnsFlags_None; }
|
||||
ImGuiColumnData() { memset(this, 0, sizeof(*this)); }
|
||||
};
|
||||
|
||||
struct ImGuiColumns
|
||||
@@ -1047,21 +1120,7 @@ struct ImGuiColumns
|
||||
ImVector<ImGuiColumnData> Columns;
|
||||
ImDrawListSplitter Splitter;
|
||||
|
||||
ImGuiColumns() { Clear(); }
|
||||
void Clear()
|
||||
{
|
||||
ID = 0;
|
||||
Flags = ImGuiColumnsFlags_None;
|
||||
IsFirstFrame = false;
|
||||
IsBeingResized = false;
|
||||
Current = 0;
|
||||
Count = 1;
|
||||
OffMinX = OffMaxX = 0.0f;
|
||||
LineMinY = LineMaxY = 0.0f;
|
||||
HostCursorPosY = 0.0f;
|
||||
HostCursorMaxPosX = 0.0f;
|
||||
Columns.clear();
|
||||
}
|
||||
ImGuiColumns() { memset(this, 0, sizeof(*this)); }
|
||||
};
|
||||
|
||||
//-----------------------------------------------------------------------------
|
||||
@@ -1124,6 +1183,7 @@ struct ImGuiDockNode
|
||||
ImGuiID ID;
|
||||
ImGuiDockNodeFlags SharedFlags; // Flags shared by all nodes of a same dockspace hierarchy (inherited from the root node)
|
||||
ImGuiDockNodeFlags LocalFlags; // Flags specific to this node
|
||||
ImGuiDockNodeState State;
|
||||
ImGuiDockNode* ParentNode;
|
||||
ImGuiDockNode* ChildNodes[2]; // [Split node only] Child nodes (left/right or top/bottom). Consider switching to an array.
|
||||
ImVector<ImGuiWindow*> Windows; // Note: unordered list! Iterate TabBar->Tabs for user-order.
|
||||
@@ -1134,7 +1194,6 @@ struct ImGuiDockNode
|
||||
ImGuiAxis SplitAxis; // [Split node only] Split axis (X or Y)
|
||||
ImGuiWindowClass WindowClass; // [Root node only]
|
||||
|
||||
ImGuiDockNodeState State;
|
||||
ImGuiWindow* HostWindow;
|
||||
ImGuiWindow* VisibleWindow; // Generally point to window which is ID is == SelectedTabID, but when CTRL+Tabbing this can be a different window.
|
||||
ImGuiDockNode* CentralNode; // [Root node only] Pointer to central node.
|
||||
@@ -1245,7 +1304,7 @@ struct ImGuiWindowSettings
|
||||
bool Collapsed;
|
||||
bool WantApply; // Set when loaded from .ini data (to enable merging/loading .ini data into an already running context)
|
||||
|
||||
ImGuiWindowSettings() { ID = 0; Pos = Size = ViewportPos = ImVec2ih(0, 0); ViewportId = DockId = ClassId = 0; DockOrder = -1; Collapsed = WantApply = false; }
|
||||
ImGuiWindowSettings() { memset(this, 0, sizeof(*this)); DockOrder = -1; }
|
||||
char* GetName() { return (char*)(this + 1); }
|
||||
};
|
||||
|
||||
@@ -1264,6 +1323,10 @@ struct ImGuiSettingsHandler
|
||||
ImGuiSettingsHandler() { memset(this, 0, sizeof(*this)); }
|
||||
};
|
||||
|
||||
//-----------------------------------------------------------------------------
|
||||
// [SECTION] Metrics, Debug
|
||||
//-----------------------------------------------------------------------------
|
||||
|
||||
struct ImGuiMetricsConfig
|
||||
{
|
||||
bool ShowWindowsRects;
|
||||
@@ -1288,6 +1351,21 @@ struct ImGuiMetricsConfig
|
||||
}
|
||||
};
|
||||
|
||||
struct IMGUI_API ImGuiStackSizes
|
||||
{
|
||||
short SizeOfIDStack;
|
||||
short SizeOfColorStack;
|
||||
short SizeOfStyleVarStack;
|
||||
short SizeOfFontStack;
|
||||
short SizeOfFocusScopeStack;
|
||||
short SizeOfGroupStack;
|
||||
short SizeOfBeginPopupStack;
|
||||
|
||||
ImGuiStackSizes() { memset(this, 0, sizeof(*this)); }
|
||||
IMGUI_API void SetToCurrentState();
|
||||
IMGUI_API void CompareWithCurrentState();
|
||||
};
|
||||
|
||||
//-----------------------------------------------------------------------------
|
||||
// [SECTION] Generic context hooks
|
||||
//-----------------------------------------------------------------------------
|
||||
@@ -1330,6 +1408,7 @@ struct ImGuiContext
|
||||
bool WithinFrameScope; // Set by NewFrame(), cleared by EndFrame()
|
||||
bool WithinFrameScopeWithImplicitWindow; // Set by NewFrame(), cleared by EndFrame() when the implicit debug window has been pushed
|
||||
bool WithinEndChild; // Set within EndChild()
|
||||
bool GcCompactAll; // Request full GC
|
||||
bool TestEngineHookItems; // Will call test engine hooks: ImGuiTestEngineHook_ItemAdd(), ImGuiTestEngineHook_ItemInfo(), ImGuiTestEngineHook_Log()
|
||||
ImGuiID TestEngineHookIdInfo; // Will call test engine hooks: ImGuiTestEngineHook_IdInfo() from GetID()
|
||||
void* TestEngine; // Test engine user data
|
||||
@@ -1386,9 +1465,12 @@ struct ImGuiContext
|
||||
ImGuiNextItemData NextItemData; // Storage for SetNextItem** functions
|
||||
|
||||
// Shared stacks
|
||||
ImVector<ImGuiColorMod> ColorModifiers; // Stack for PushStyleColor()/PopStyleColor()
|
||||
ImVector<ImGuiStyleMod> StyleModifiers; // Stack for PushStyleVar()/PopStyleVar()
|
||||
ImVector<ImFont*> FontStack; // Stack for PushFont()/PopFont()
|
||||
ImVector<ImGuiColorMod> ColorStack; // Stack for PushStyleColor()/PopStyleColor() - inherited by Begin()
|
||||
ImVector<ImGuiStyleMod> StyleVarStack; // Stack for PushStyleVar()/PopStyleVar() - inherited by Begin()
|
||||
ImVector<ImFont*> FontStack; // Stack for PushFont()/PopFont() - inherited by Begin()
|
||||
ImVector<ImGuiID> FocusScopeStack; // Stack for PushFocusScope()/PopFocusScope() - not inherited by Begin(), unless child window
|
||||
ImVector<ImGuiItemFlags>ItemFlagsStack; // Stack for PushItemFlag()/PopItemFlag() - inherited by Begin()
|
||||
ImVector<ImGuiGroupData>GroupStack; // Stack for BeginGroup()/EndGroup() - not inherited by Begin()
|
||||
ImVector<ImGuiPopupData>OpenPopupStack; // Which popups are open (persistent)
|
||||
ImVector<ImGuiPopupData>BeginPopupStack; // Which level of BeginPopup() we are in (reset every frame)
|
||||
|
||||
@@ -1479,6 +1561,13 @@ struct ImGuiContext
|
||||
ImVector<unsigned char> DragDropPayloadBufHeap; // We don't expose the ImVector<> directly, ImGuiPayload only holds pointer+size
|
||||
unsigned char DragDropPayloadBufLocal[16]; // Local buffer for small payloads
|
||||
|
||||
// Table
|
||||
ImGuiTable* CurrentTable;
|
||||
ImPool<ImGuiTable> Tables;
|
||||
ImVector<ImGuiPtrOrIndex> CurrentTableStack;
|
||||
ImVector<float> TablesLastTimeActive; // Last used timestamp of each tables (SOA, for efficient GC)
|
||||
ImVector<ImDrawChannel> DrawChannelsTempMergeBuffer;
|
||||
|
||||
// Tab bars
|
||||
ImGuiTabBar* CurrentTabBar;
|
||||
ImPool<ImGuiTabBar> TabBars;
|
||||
@@ -1521,6 +1610,7 @@ struct ImGuiContext
|
||||
ImGuiTextBuffer SettingsIniData; // In memory .ini settings
|
||||
ImVector<ImGuiSettingsHandler> SettingsHandlers; // List of .ini settings handlers
|
||||
ImChunkStream<ImGuiWindowSettings> SettingsWindows; // ImGuiWindow .ini settings entries
|
||||
ImChunkStream<ImGuiTableSettings> SettingsTables; // ImGuiTable .ini settings entries
|
||||
ImVector<ImGuiContextHook> Hooks; // Hooks for extensions (e.g. test engine)
|
||||
|
||||
// Capture/Logging
|
||||
@@ -1560,6 +1650,7 @@ struct ImGuiContext
|
||||
FrameCount = 0;
|
||||
FrameCountEnded = FrameCountPlatformEnded = FrameCountRendered = -1;
|
||||
WithinFrameScope = WithinFrameScopeWithImplicitWindow = WithinEndChild = false;
|
||||
GcCompactAll = false;
|
||||
TestEngineHookItems = false;
|
||||
TestEngineHookIdInfo = 0;
|
||||
TestEngine = NULL;
|
||||
@@ -1655,6 +1746,7 @@ struct ImGuiContext
|
||||
DragDropHoldJustPressedId = 0;
|
||||
memset(DragDropPayloadBufLocal, 0, sizeof(DragDropPayloadBufLocal));
|
||||
|
||||
CurrentTable = NULL;
|
||||
CurrentTabBar = NULL;
|
||||
|
||||
LastValidMousePos = ImVec2(0.0f, 0.0f);
|
||||
@@ -1701,7 +1793,8 @@ struct ImGuiContext
|
||||
//-----------------------------------------------------------------------------
|
||||
|
||||
// Transient per-window data, reset at the beginning of the frame. This used to be called ImGuiDrawContext, hence the DC variable name in ImGuiWindow.
|
||||
// FIXME: That's theory, in practice the delimitation between ImGuiWindow and ImGuiWindowTempData is quite tenuous and could be reconsidered.
|
||||
// (That's theory, in practice the delimitation between ImGuiWindow and ImGuiWindowTempData is quite tenuous and could be reconsidered..)
|
||||
// (This doesn't need a constructor because we zero-clear it as part of ImGuiWindow and all frame-temporary data are setup on Begin)
|
||||
struct IMGUI_API ImGuiWindowTempData
|
||||
{
|
||||
// Layout
|
||||
@@ -1740,6 +1833,7 @@ struct IMGUI_API ImGuiWindowTempData
|
||||
ImVector<ImGuiWindow*> ChildWindows;
|
||||
ImGuiStorage* StateStorage; // Current persistent per-window storage (store e.g. tree node open/close state)
|
||||
ImGuiColumns* CurrentColumns; // Current columns set
|
||||
int CurrentTableIdx; // Current table index (into g.Tables)
|
||||
ImGuiLayoutType LayoutType;
|
||||
ImGuiLayoutType ParentLayoutType; // Layout type of parent window at the time of Begin()
|
||||
int FocusCounterRegular; // (Legacy Focus/Tabbing system) Sequential counter, start at -1 and increase as assigned via FocusableItemRegister() (FIXME-NAV: Needs redesign)
|
||||
@@ -1747,48 +1841,12 @@ struct IMGUI_API ImGuiWindowTempData
|
||||
|
||||
// Local parameters stacks
|
||||
// We store the current settings outside of the vectors to increase memory locality (reduce cache misses). The vectors are rarely modified. Also it allows us to not heap allocate for short-lived windows which are not using those settings.
|
||||
ImGuiItemFlags ItemFlags; // == ItemFlagsStack.back() [empty == ImGuiItemFlags_Default]
|
||||
ImGuiItemFlags ItemFlags; // == g.ItemFlagsStack.back()
|
||||
float ItemWidth; // == ItemWidthStack.back(). 0.0: default, >0.0: width in pixels, <0.0: align xx pixels to the right of window
|
||||
float TextWrapPos; // == TextWrapPosStack.back() [empty == -1.0f]
|
||||
ImVector<ImGuiItemFlags>ItemFlagsStack;
|
||||
ImVector<float> ItemWidthStack;
|
||||
ImVector<float> TextWrapPosStack;
|
||||
ImVector<ImGuiGroupData>GroupStack;
|
||||
short StackSizesBackup[6]; // Store size of various stacks for asserting
|
||||
|
||||
ImGuiWindowTempData()
|
||||
{
|
||||
CursorPos = CursorPosPrevLine = CursorStartPos = CursorMaxPos = ImVec2(0.0f, 0.0f);
|
||||
CurrLineSize = PrevLineSize = ImVec2(0.0f, 0.0f);
|
||||
CurrLineTextBaseOffset = PrevLineTextBaseOffset = 0.0f;
|
||||
Indent = ImVec1(0.0f);
|
||||
ColumnsOffset = ImVec1(0.0f);
|
||||
GroupOffset = ImVec1(0.0f);
|
||||
|
||||
LastItemId = 0;
|
||||
LastItemStatusFlags = ImGuiItemStatusFlags_None;
|
||||
LastItemRect = LastItemDisplayRect = ImRect();
|
||||
|
||||
NavLayerActiveMask = NavLayerActiveMaskNext = 0x00;
|
||||
NavLayerCurrent = ImGuiNavLayer_Main;
|
||||
NavFocusScopeIdCurrent = 0;
|
||||
NavHideHighlightOneFrame = false;
|
||||
NavHasScroll = false;
|
||||
|
||||
MenuBarAppending = false;
|
||||
MenuBarOffset = ImVec2(0.0f, 0.0f);
|
||||
TreeDepth = 0;
|
||||
TreeJumpToParentOnPopMask = 0x00;
|
||||
StateStorage = NULL;
|
||||
CurrentColumns = NULL;
|
||||
LayoutType = ParentLayoutType = ImGuiLayoutType_Vertical;
|
||||
FocusCounterRegular = FocusCounterTabStop = -1;
|
||||
|
||||
ItemFlags = ImGuiItemFlags_Default_;
|
||||
ItemWidth = 0.0f;
|
||||
TextWrapPos = -1.0f;
|
||||
memset(StackSizesBackup, 0, sizeof(StackSizesBackup));
|
||||
}
|
||||
ImGuiStackSizes StackSizesOnBegin; // Store size of various stacks for asserting
|
||||
};
|
||||
|
||||
// Storage for one window
|
||||
@@ -1852,7 +1910,7 @@ struct IMGUI_API ImGuiWindow
|
||||
ImVector<ImGuiID> IDStack; // ID stack. ID are hashes seeded with the value at the top of the stack. (In theory this should be in the TempData structure)
|
||||
ImGuiWindowTempData DC; // Temporary per-window data, reset at the beginning of the frame. This used to be called ImGuiDrawContext, hence the "DC" variable name.
|
||||
|
||||
// The best way to understand what those rectangles are is to use the 'Metrics -> Tools -> Show windows rectangles' viewer.
|
||||
// The best way to understand what those rectangles are is to use the 'Metrics->Tools->Show Windows Rectangles' viewer.
|
||||
// The main 'OuterRect', omitted as a field, is window->Rect().
|
||||
ImRect OuterRectClipped; // == Window->Rect() just after setup in Begin(). == window->Rect() for root window.
|
||||
ImRect InnerRect; // Inner rectangle (omit title bar, menu bar, scroll bar)
|
||||
@@ -1886,9 +1944,9 @@ struct IMGUI_API ImGuiWindow
|
||||
ImGuiID NavLastIds[ImGuiNavLayer_COUNT]; // Last known NavId for this window, per layer (0/1)
|
||||
ImRect NavRectRel[ImGuiNavLayer_COUNT]; // Reference rectangle, in window relative space
|
||||
|
||||
bool MemoryCompacted; // Set when window extraneous data have been garbage collected
|
||||
int MemoryDrawListIdxCapacity; // Backup of last idx/vtx count, so when waking up the window we can preallocate and avoid iterative alloc/copy
|
||||
int MemoryDrawListVtxCapacity;
|
||||
bool MemoryCompacted; // Set when window extraneous data have been garbage collected
|
||||
|
||||
// Docking
|
||||
ImGuiDockNode* DockNode; // Which node are we docked into. Important: Prefer testing DockIsActive in many cases as this will still be set when the dock node is hidden.
|
||||
@@ -2026,7 +2084,212 @@ struct ImGuiTabBar
|
||||
//-----------------------------------------------------------------------------
|
||||
|
||||
#ifdef IMGUI_HAS_TABLE
|
||||
// <this is filled in 'tables' branch>
|
||||
|
||||
#define IM_COL32_DISABLE IM_COL32(0,0,0,1) // Special sentinel code which cannot be used as a regular color.
|
||||
#define IMGUI_TABLE_MAX_COLUMNS 64 // sizeof(ImU64) * 8. This is solely because we frequently encode columns set in a ImU64.
|
||||
#define IMGUI_TABLE_MAX_DRAW_CHANNELS (4 + 64 * 2) // See TableUpdateDrawChannels()
|
||||
|
||||
// [Internal] sizeof() ~ 100
|
||||
// We use the terminology "Visible" to refer to a column that is not Hidden by user or settings. However it may still be out of view and clipped (see IsClipped).
|
||||
struct ImGuiTableColumn
|
||||
{
|
||||
ImRect ClipRect; // Clipping rectangle for the column
|
||||
ImGuiID UserID; // Optional, value passed to TableSetupColumn()
|
||||
ImGuiTableColumnFlags FlagsIn; // Flags as they were provided by user. See ImGuiTableColumnFlags_
|
||||
ImGuiTableColumnFlags Flags; // Effective flags. See ImGuiTableColumnFlags_
|
||||
float MinX; // Absolute positions
|
||||
float MaxX;
|
||||
float InitStretchWeightOrWidth; // Value passed to TableSetupColumn(). For Width it is a content width (_without padding_).
|
||||
float StretchWeight; // Master width weight when (Flags & _WidthStretch). Often around ~1.0f initially.
|
||||
float WidthAuto; // Automatic width
|
||||
float WidthRequest; // Master width absolute value when !(Flags & _WidthStretch). When Stretch this is derived every frame from StretchWeight in TableUpdateLayout()
|
||||
float WidthGiven; // Final/actual width visible == (MaxX - MinX), locked in TableUpdateLayout(). May be > WidthRequest to honor minimum width, may be < WidthRequest to honor shrinking columns down in tight space.
|
||||
float WorkMinX; // Start position for the frame, currently ~(MinX + CellPaddingX)
|
||||
float WorkMaxX;
|
||||
float ContentMaxXFrozen; // Contents maximum position for frozen rows (apart from headers), from which we can infer content width.
|
||||
float ContentMaxXUnfrozen;
|
||||
float ContentMaxXHeadersUsed; // Contents maximum position for headers rows (regardless of freezing). TableHeader() automatically softclip itself + report ideal desired size, to avoid creating extraneous draw calls
|
||||
float ContentMaxXHeadersIdeal;
|
||||
ImS16 NameOffset; // Offset into parent ColumnsNames[]
|
||||
bool IsVisible; // Is the column not marked Hidden by the user? (even if off view, e.g. clipped by scrolling).
|
||||
bool IsVisibleNextFrame;
|
||||
bool IsClipped; // Is not actually in view (e.g. not overlapping the host window clipping rectangle).
|
||||
bool IsSkipItems; // Do we want item submissions to this column to be ignored early on.
|
||||
ImS8 NavLayerCurrent; // ImGuiNavLayer in 1 byte
|
||||
ImS8 DisplayOrder; // Index within Table's IndexToDisplayOrder[] (column may be reordered by users)
|
||||
ImS8 IndexWithinVisibleSet; // Index within visible set (<= IndexToDisplayOrder)
|
||||
ImS8 PrevVisibleColumn; // Index of prev visible column within Columns[], -1 if first visible column
|
||||
ImS8 NextVisibleColumn; // Index of next visible column within Columns[], -1 if last visible column
|
||||
ImS8 SortOrder; // Index of this column within sort specs, -1 if not sorting on this column, 0 for single-sort, may be >0 on multi-sort
|
||||
ImS8 SortDirection; // ImGuiSortDirection_Ascending or ImGuiSortDirection_Descending
|
||||
ImU8 AutoFitQueue; // Queue of 8 values for the next 8 frames to request auto-fit
|
||||
ImU8 CannotSkipItemsQueue; // Queue of 8 values for the next 8 frames to disable Clipped/SkipItem
|
||||
ImU8 DrawChannelCurrent; // Index within DrawSplitter.Channels[]
|
||||
ImU8 DrawChannelFrozen;
|
||||
ImU8 DrawChannelUnfrozen;
|
||||
|
||||
ImGuiTableColumn()
|
||||
{
|
||||
memset(this, 0, sizeof(*this));
|
||||
StretchWeight = WidthRequest = -1.0f;
|
||||
NameOffset = -1;
|
||||
IsVisible = IsVisibleNextFrame = true;
|
||||
DisplayOrder = IndexWithinVisibleSet = -1;
|
||||
PrevVisibleColumn = NextVisibleColumn = -1;
|
||||
SortOrder = -1;
|
||||
SortDirection = ImGuiSortDirection_None;
|
||||
AutoFitQueue = CannotSkipItemsQueue = (1 << 3) - 1; // Skip for three frames
|
||||
DrawChannelCurrent = DrawChannelFrozen = DrawChannelUnfrozen = (ImU8)-1;
|
||||
}
|
||||
};
|
||||
|
||||
// Transient cell data stored per row.
|
||||
// sizeof() ~ 6
|
||||
struct ImGuiTableCellData
|
||||
{
|
||||
ImU32 BgColor; // Actual color
|
||||
ImS8 Column; // Column number
|
||||
};
|
||||
|
||||
struct ImGuiTable
|
||||
{
|
||||
ImGuiID ID;
|
||||
ImGuiTableFlags Flags;
|
||||
void* RawData; // Single allocation to hold Columns[], DisplayOrderToIndex[] and RowCellData[]
|
||||
ImSpan<ImGuiTableColumn> Columns; // Point within RawData[]
|
||||
ImSpan<ImS8> DisplayOrderToIndex; // Point within RawData[]. Store display order of columns (when not reordered, the values are 0...Count-1)
|
||||
ImSpan<ImGuiTableCellData> RowCellData; // Point within RawData[]. Store cells background requests for current row.
|
||||
ImU64 VisibleMaskByIndex; // Column Index -> IsVisible map (== not hidden by user/api) in a format adequate for iterating column without touching cold data
|
||||
ImU64 VisibleMaskByDisplayOrder; // Column DisplayOrder -> IsVisible map
|
||||
ImU64 VisibleUnclippedMaskByIndex;// Visible and not Clipped, aka "actually visible" "not hidden by some scrolling"
|
||||
ImGuiTableFlags SettingsLoadedFlags; // Which data were loaded from the .ini file (e.g. when order is not altered we won't save order)
|
||||
int SettingsOffset; // Offset in g.SettingsTables
|
||||
int LastFrameActive;
|
||||
int ColumnsCount; // Number of columns declared in BeginTable()
|
||||
int ColumnsVisibleCount; // Number of non-hidden columns (<= ColumnsCount)
|
||||
int CurrentRow;
|
||||
int CurrentColumn;
|
||||
ImS16 InstanceCurrent; // Count of BeginTable() calls with same ID in the same frame (generally 0). This is a little bit similar to BeginCount for a window, but multiple table with same ID look are multiple tables, they are just synched.
|
||||
ImS16 InstanceInteracted; // Mark which instance (generally 0) of the same ID is being interacted with
|
||||
float RowPosY1;
|
||||
float RowPosY2;
|
||||
float RowMinHeight; // Height submitted to TableNextRow()
|
||||
float RowTextBaseline;
|
||||
float RowIndentOffsetX;
|
||||
ImGuiTableRowFlags RowFlags : 16; // Current row flags, see ImGuiTableRowFlags_
|
||||
ImGuiTableRowFlags LastRowFlags : 16;
|
||||
int RowBgColorCounter; // Counter for alternating background colors (can be fast-forwarded by e.g clipper), not same as CurrentRow because header rows typically don't increase this.
|
||||
ImU32 RowBgColor[2]; // Background color override for current row.
|
||||
ImU32 BorderColorStrong;
|
||||
ImU32 BorderColorLight;
|
||||
float BorderX1;
|
||||
float BorderX2;
|
||||
float HostIndentX;
|
||||
float OuterPaddingX;
|
||||
float CellPaddingX; // Padding from each borders
|
||||
float CellPaddingY;
|
||||
float CellSpacingX1; // Spacing between non-bordered cells
|
||||
float CellSpacingX2;
|
||||
float LastOuterHeight; // Outer height from last frame
|
||||
float LastFirstRowHeight; // Height of first row from last frame
|
||||
float InnerWidth; // User value passed to BeginTable(), see comments at the top of BeginTable() for details.
|
||||
float ColumnsTotalWidth; // Sum of current column width
|
||||
float ColumnsAutoFitWidth; // Sum of ideal column width in order nothing to be clipped, used for auto-fitting and content width submission in outer window
|
||||
float ResizedColumnNextWidth;
|
||||
float RefScale; // Reference scale to be able to rescale columns on font/dpi changes.
|
||||
ImRect OuterRect; // Note: OuterRect.Max.y is often FLT_MAX until EndTable(), unless a height has been specified in BeginTable().
|
||||
ImRect WorkRect;
|
||||
ImRect InnerClipRect;
|
||||
ImRect BgClipRect; // We use this to cpu-clip cell background color fill
|
||||
ImRect BgClipRectForDrawCmd;
|
||||
ImRect HostClipRect; // This is used to check if we can eventually merge our columns draw calls into the current draw call of the current window.
|
||||
ImRect HostBackupWorkRect; // Backup of InnerWindow->WorkRect at the end of BeginTable()
|
||||
ImRect HostBackupParentWorkRect; // Backup of InnerWindow->ParentWorkRect at the end of BeginTable()
|
||||
ImRect HostBackupClipRect; // Backup of InnerWindow->ClipRect during PushTableBackground()/PopTableBackground()
|
||||
ImVec2 HostBackupCursorMaxPos; // Backup of InnerWindow->DC.CursorMaxPos at the end of BeginTable()
|
||||
ImVec1 HostBackupColumnsOffset; // Backup of OuterWindow->ColumnsOffset at the end of BeginTable()
|
||||
ImGuiWindow* OuterWindow; // Parent window for the table
|
||||
ImGuiWindow* InnerWindow; // Window holding the table data (== OuterWindow or a child window)
|
||||
ImGuiTextBuffer ColumnsNames; // Contiguous buffer holding columns names
|
||||
ImDrawListSplitter DrawSplitter; // We carry our own ImDrawList splitter to allow recursion (FIXME: could be stored outside, worst case we need 1 splitter per recursing table)
|
||||
ImVector<ImGuiTableSortSpecsColumn> SortSpecsData; // FIXME-OPT: Fixed-size array / small-vector pattern, optimize for single sort spec
|
||||
ImGuiTableSortSpecs SortSpecs; // Public facing sorts specs, this is what we return in TableGetSortSpecs()
|
||||
ImS8 SortSpecsCount;
|
||||
ImS8 DeclColumnsCount; // Count calls to TableSetupColumn()
|
||||
ImS8 HoveredColumnBody; // Index of column whose visible region is being hovered. Important: == ColumnsCount when hovering empty region after the right-most column!
|
||||
ImS8 HoveredColumnBorder; // Index of column whose right-border is being hovered (for resizing).
|
||||
ImS8 ResizedColumn; // Index of column being resized. Reset when InstanceCurrent==0.
|
||||
ImS8 LastResizedColumn; // Index of column being resized from previous frame.
|
||||
ImS8 HeldHeaderColumn; // Index of column header being held.
|
||||
ImS8 ReorderColumn; // Index of column being reordered. (not cleared)
|
||||
ImS8 ReorderColumnDir; // -1 or +1
|
||||
ImS8 RightMostVisibleColumn; // Index of right-most non-hidden column.
|
||||
ImS8 LeftMostStretchedColumnDisplayOrder; // Display order of left-most stretched column.
|
||||
ImS8 ContextPopupColumn; // Column right-clicked on, of -1 if opening context menu from a neutral/empty spot
|
||||
ImS8 FreezeRowsRequest; // Requested frozen rows count
|
||||
ImS8 FreezeRowsCount; // Actual frozen row count (== FreezeRowsRequest, or == 0 when no scrolling offset)
|
||||
ImS8 FreezeColumnsRequest; // Requested frozen columns count
|
||||
ImS8 FreezeColumnsCount; // Actual frozen columns count (== FreezeColumnsRequest, or == 0 when no scrolling offset)
|
||||
ImS8 RowCellDataCurrent; // Index of current RowCellData[] entry in current row
|
||||
ImU8 DummyDrawChannel; // Redirect non-visible columns here.
|
||||
ImU8 Bg1DrawChannelCurrent; // For Selectable() and other widgets drawing accross columns after the freezing line. Index within DrawSplitter.Channels[]
|
||||
ImU8 Bg1DrawChannelUnfrozen;
|
||||
bool IsLayoutLocked; // Set by TableUpdateLayout() which is called when beginning the first row.
|
||||
bool IsInsideRow; // Set when inside TableBeginRow()/TableEndRow().
|
||||
bool IsInitializing;
|
||||
bool IsSortSpecsDirty;
|
||||
bool IsUsingHeaders; // Set when the first row had the ImGuiTableRowFlags_Headers flag.
|
||||
bool IsContextPopupOpen; // Set when default context menu is open (also see: ContextPopupColumn, InstanceInteracted).
|
||||
bool IsSettingsRequestLoad;
|
||||
bool IsSettingsDirty; // Set when table settings have changed and needs to be reported into ImGuiTableSetttings data.
|
||||
bool IsDefaultDisplayOrder; // Set when display order is unchanged from default (DisplayOrder contains 0...Count-1)
|
||||
bool IsResetDisplayOrderRequest;
|
||||
bool IsUnfrozen; // Set when we got past the frozen row.
|
||||
bool MemoryCompacted;
|
||||
bool HostSkipItems; // Backup of InnerWindow->SkipItem at the end of BeginTable(), because we will overwrite InnerWindow->SkipItem on a per-column basis
|
||||
|
||||
IMGUI_API ImGuiTable();
|
||||
IMGUI_API ~ImGuiTable();
|
||||
};
|
||||
|
||||
// sizeof() ~ 12
|
||||
struct ImGuiTableColumnSettings
|
||||
{
|
||||
float WidthOrWeight;
|
||||
ImGuiID UserID;
|
||||
ImS8 Index;
|
||||
ImS8 DisplayOrder;
|
||||
ImS8 SortOrder;
|
||||
ImU8 SortDirection : 2;
|
||||
ImU8 IsVisible : 1;
|
||||
ImU8 IsStretch : 1;
|
||||
|
||||
ImGuiTableColumnSettings()
|
||||
{
|
||||
WidthOrWeight = 0.0f;
|
||||
UserID = 0;
|
||||
Index = -1;
|
||||
DisplayOrder = SortOrder = -1;
|
||||
SortDirection = ImGuiSortDirection_None;
|
||||
IsVisible = 1;
|
||||
IsStretch = 0;
|
||||
}
|
||||
};
|
||||
|
||||
// This is designed to be stored in a single ImChunkStream (1 header followed by N ImGuiTableColumnSettings, etc.)
|
||||
struct ImGuiTableSettings
|
||||
{
|
||||
ImGuiID ID; // Set to 0 to invalidate/delete the setting
|
||||
ImGuiTableFlags SaveFlags; // Indicate data we want to save using the Resizable/Reorderable/Sortable/Hideable flags (could be using its own flags..)
|
||||
float RefScale; // Reference scale to be able to rescale columns on font/dpi changes.
|
||||
ImS8 ColumnsCount;
|
||||
ImS8 ColumnsCountMax; // Maximum number of columns this settings instance can store, we can recycle a settings instance with lower number of columns but not higher
|
||||
bool WantApply; // Set when loaded from .ini data (to enable merging/loading .ini data into an already running context)
|
||||
|
||||
ImGuiTableSettings() { memset(this, 0, sizeof(*this)); }
|
||||
ImGuiTableColumnSettings* GetColumnSettings() { return (ImGuiTableColumnSettings*)(this + 1); }
|
||||
};
|
||||
|
||||
#endif // #ifdef IMGUI_HAS_TABLE
|
||||
|
||||
//-----------------------------------------------------------------------------
|
||||
@@ -2109,6 +2372,7 @@ namespace ImGui
|
||||
inline ImGuiItemStatusFlags GetItemStatusFlags() { ImGuiContext& g = *GImGui; return g.CurrentWindow->DC.LastItemStatusFlags; }
|
||||
inline ImGuiID GetActiveID() { ImGuiContext& g = *GImGui; return g.ActiveId; }
|
||||
inline ImGuiID GetFocusID() { ImGuiContext& g = *GImGui; return g.NavId; }
|
||||
inline ImGuiItemFlags GetItemsFlags() { ImGuiContext& g = *GImGui; return g.CurrentWindow->DC.ItemFlags; }
|
||||
IMGUI_API void SetActiveID(ImGuiID id, ImGuiWindow* window);
|
||||
IMGUI_API void SetFocusID(ImGuiID id, ImGuiWindow* window);
|
||||
IMGUI_API void ClearActiveID();
|
||||
@@ -2171,7 +2435,8 @@ namespace ImGui
|
||||
// patterns generally need to react (e.g. clear selection) when landing on an item of the set.
|
||||
IMGUI_API void PushFocusScope(ImGuiID id);
|
||||
IMGUI_API void PopFocusScope();
|
||||
inline ImGuiID GetFocusScopeID() { ImGuiContext& g = *GImGui; return g.NavFocusScopeId; }
|
||||
inline ImGuiID GetFocusedFocusScope() { ImGuiContext& g = *GImGui; return g.NavFocusScopeId; } // Focus scope which is actually active
|
||||
inline ImGuiID GetFocusScope() { ImGuiContext& g = *GImGui; return g.CurrentWindow->DC.NavFocusScopeIdCurrent; } // Focus scope we are outputting into, set by PushFocusScope()
|
||||
|
||||
// Inputs
|
||||
// FIXME: Eventually we should aim to move e.g. IsActiveIdUsingKey() into IsKeyXXX functions.
|
||||
@@ -2249,6 +2514,45 @@ namespace ImGui
|
||||
IMGUI_API float GetColumnOffsetFromNorm(const ImGuiColumns* columns, float offset_norm);
|
||||
IMGUI_API float GetColumnNormFromOffset(const ImGuiColumns* columns, float offset);
|
||||
|
||||
// Tables
|
||||
IMGUI_API ImGuiTable* TableFindByID(ImGuiID id);
|
||||
IMGUI_API bool BeginTableEx(const char* name, ImGuiID id, int columns_count, ImGuiTableFlags flags = 0, const ImVec2& outer_size = ImVec2(0, 0), float inner_width = 0.0f);
|
||||
IMGUI_API void TableBeginUpdateColumns(ImGuiTable* table);
|
||||
IMGUI_API void TableUpdateDrawChannels(ImGuiTable* table);
|
||||
IMGUI_API void TableUpdateLayout(ImGuiTable* table);
|
||||
IMGUI_API void TableUpdateBorders(ImGuiTable* table);
|
||||
IMGUI_API void TableSetColumnWidth(int column_n, float width);
|
||||
IMGUI_API void TableSetColumnVisible(int column_n, bool visible);
|
||||
IMGUI_API void TableDrawBorders(ImGuiTable* table);
|
||||
IMGUI_API void TableDrawContextMenu(ImGuiTable* table);
|
||||
IMGUI_API void TableOpenContextMenu(int column_n = -1);
|
||||
IMGUI_API void TableReorderDrawChannelsForMerge(ImGuiTable* table);
|
||||
IMGUI_API void TableSetColumnSortDirection(int column_n, ImGuiSortDirection sort_direction, bool append_to_sort_specs);
|
||||
IMGUI_API void TableSortSpecsSanitize(ImGuiTable* table);
|
||||
IMGUI_API void TableSortSpecsBuild(ImGuiTable* table);
|
||||
IMGUI_API void TableBeginRow(ImGuiTable* table);
|
||||
IMGUI_API void TableEndRow(ImGuiTable* table);
|
||||
IMGUI_API void TableBeginCell(ImGuiTable* table, int column_n);
|
||||
IMGUI_API void TableEndCell(ImGuiTable* table);
|
||||
IMGUI_API ImRect TableGetCellBgRect(const ImGuiTable* table, int column_n);
|
||||
IMGUI_API const char* TableGetColumnName(const ImGuiTable* table, int column_n);
|
||||
IMGUI_API ImGuiID TableGetColumnResizeID(const ImGuiTable* table, int column_n, int instance_no = 0);
|
||||
IMGUI_API void TableSetColumnAutofit(ImGuiTable* table, int column_n);
|
||||
IMGUI_API void PushTableBackground();
|
||||
IMGUI_API void PopTableBackground();
|
||||
IMGUI_API void TableRemove(ImGuiTable* table);
|
||||
IMGUI_API void TableGcCompactTransientBuffers(ImGuiTable* table);
|
||||
IMGUI_API void TableGcCompactSettings();
|
||||
|
||||
// Tables: Settings
|
||||
IMGUI_API void TableLoadSettings(ImGuiTable* table);
|
||||
IMGUI_API void TableSaveSettings(ImGuiTable* table);
|
||||
IMGUI_API ImGuiTableSettings* TableGetBoundSettings(ImGuiTable* table);
|
||||
IMGUI_API void TableSettingsInstallHandler(ImGuiContext* context);
|
||||
IMGUI_API ImGuiTableSettings* TableSettingsCreate(ImGuiID id, int columns_count);
|
||||
IMGUI_API ImGuiTableSettings* TableSettingsFindByID(ImGuiID id);
|
||||
IMGUI_API void TableSettingsClearByID(ImGuiID id);
|
||||
|
||||
// Tab Bars
|
||||
IMGUI_API bool BeginTabBarEx(ImGuiTabBar* tab_bar, const ImRect& bb, ImGuiTabBarFlags flags, ImGuiDockNode* dock_node);
|
||||
IMGUI_API ImGuiTabItem* TabBarFindTabByID(ImGuiTabBar* tab_bar, ImGuiID tab_id);
|
||||
@@ -2325,6 +2629,7 @@ namespace ImGui
|
||||
template<typename T, typename SIGNED_T, typename FLOAT_T> IMGUI_API bool DragBehaviorT(ImGuiDataType data_type, T* v, float v_speed, T v_min, T v_max, const char* format, ImGuiSliderFlags flags);
|
||||
template<typename T, typename SIGNED_T, typename FLOAT_T> IMGUI_API bool SliderBehaviorT(const ImRect& bb, ImGuiID id, ImGuiDataType data_type, T* v, T v_min, T v_max, const char* format, ImGuiSliderFlags flags, ImRect* out_grab_bb);
|
||||
template<typename T, typename SIGNED_T> IMGUI_API T RoundScalarWithFormatT(const char* format, ImGuiDataType data_type, T v);
|
||||
template<typename T> IMGUI_API bool CheckboxFlagsT(const char* label, T* flags, T flags_value);
|
||||
|
||||
// Data type helpers
|
||||
IMGUI_API const ImGuiDataTypeInfo* DataTypeGetInfo(ImGuiDataType data_type);
|
||||
@@ -2354,6 +2659,7 @@ namespace ImGui
|
||||
IMGUI_API void ShadeVertsLinearUV(ImDrawList* draw_list, int vert_start_idx, int vert_end_idx, const ImVec2& a, const ImVec2& b, const ImVec2& uv_a, const ImVec2& uv_b, bool clamp);
|
||||
|
||||
// Garbage collection
|
||||
IMGUI_API void GcCompactTransientMiscBuffers();
|
||||
IMGUI_API void GcCompactTransientWindowBuffers(ImGuiWindow* window);
|
||||
IMGUI_API void GcAwakeTransientWindowBuffers(ImGuiWindow* window);
|
||||
|
||||
@@ -2367,6 +2673,8 @@ namespace ImGui
|
||||
IMGUI_API void DebugNodeDrawCmdShowMeshAndBoundingBox(ImDrawList* out_draw_list, const ImDrawList* draw_list, const ImDrawCmd* draw_cmd, bool show_mesh, bool show_aabb);
|
||||
IMGUI_API void DebugNodeStorage(ImGuiStorage* storage, const char* label);
|
||||
IMGUI_API void DebugNodeTabBar(ImGuiTabBar* tab_bar, const char* label);
|
||||
IMGUI_API void DebugNodeTable(ImGuiTable* table);
|
||||
IMGUI_API void DebugNodeTableSettings(ImGuiTableSettings* settings);
|
||||
IMGUI_API void DebugNodeWindow(ImGuiWindow* window, const char* label);
|
||||
IMGUI_API void DebugNodeWindowSettings(ImGuiWindowSettings* settings);
|
||||
IMGUI_API void DebugNodeWindowsList(ImVector<ImGuiWindow*>* windows, const char* label);
|
||||
|
||||
@@ -46,6 +46,9 @@
|
||||
|
||||
#include <stdio.h> // sprintf, scanf
|
||||
#include <stdint.h> // uint8_t, etc.
|
||||
#include "helpers/utils.hpp"
|
||||
|
||||
#include "views/view.hpp"
|
||||
|
||||
#ifdef _MSC_VER
|
||||
#define _PRISizeT "I"
|
||||
@@ -60,6 +63,8 @@
|
||||
#pragma warning (disable: 4996) // warning C4996: 'sprintf': This function or variable may be unsafe.
|
||||
#endif
|
||||
|
||||
ImU32 ImAlphaBlendColors(ImU32 col_a, ImU32 col_b);
|
||||
|
||||
struct MemoryEditor
|
||||
{
|
||||
enum DataFormat
|
||||
@@ -71,11 +76,9 @@ struct MemoryEditor
|
||||
};
|
||||
|
||||
// Settings
|
||||
bool Open; // = true // set to false when DrawWindow() was closed. ignore if not using DrawWindow().
|
||||
bool ReadOnly; // = false // disable any editing.
|
||||
int Cols; // = 16 // number of columns to display.
|
||||
bool OptShowOptions; // = true // display options button/context menu. when disabled, options will be locked unless you provide your own UI for them.
|
||||
bool OptShowDataPreview; // = false // display a footer previewing the decimal/binary/hex/float representation of the currently selected bytes.
|
||||
bool OptShowHexII; // = false // display values in HexII representation instead of regular hexadecimal: hide null/zero bytes, ascii values as ".X".
|
||||
bool OptShowAscii; // = true // display ASCII representation on the right side.
|
||||
bool OptGreyOutZeroes; // = true // display null/zero bytes using the TextDisabled color.
|
||||
@@ -91,6 +94,7 @@ struct MemoryEditor
|
||||
bool ContentsWidthChanged;
|
||||
size_t DataPreviewAddr;
|
||||
size_t DataEditingAddr;
|
||||
size_t DataPreviewAddrEnd;
|
||||
bool DataEditingTakeFocus;
|
||||
char DataInputBuf[32];
|
||||
char AddrInputBuf[32];
|
||||
@@ -102,11 +106,9 @@ struct MemoryEditor
|
||||
MemoryEditor()
|
||||
{
|
||||
// Settings
|
||||
Open = true;
|
||||
ReadOnly = false;
|
||||
Cols = 16;
|
||||
OptShowOptions = true;
|
||||
OptShowDataPreview = false;
|
||||
OptShowHexII = false;
|
||||
OptShowAscii = true;
|
||||
OptGreyOutZeroes = true;
|
||||
@@ -120,7 +122,7 @@ struct MemoryEditor
|
||||
|
||||
// State/Internals
|
||||
ContentsWidthChanged = false;
|
||||
DataPreviewAddr = DataEditingAddr = (size_t)-1;
|
||||
DataPreviewAddr = DataEditingAddr = DataPreviewAddrEnd = (size_t)-1;
|
||||
DataEditingTakeFocus = false;
|
||||
memset(DataInputBuf, 0, sizeof(DataInputBuf));
|
||||
memset(AddrInputBuf, 0, sizeof(AddrInputBuf));
|
||||
@@ -178,16 +180,13 @@ struct MemoryEditor
|
||||
}
|
||||
|
||||
// Standalone Memory Editor window
|
||||
void DrawWindow(const char* title, void* mem_data, size_t mem_size, size_t base_display_addr = 0x0000)
|
||||
void DrawWindow(const char* title, bool *p_open, void* mem_data, size_t mem_size, size_t base_display_addr = 0x0000)
|
||||
{
|
||||
Sizes s;
|
||||
CalcSizes(s, mem_size, base_display_addr);
|
||||
ImGui::SetNextWindowSizeConstraints(ImVec2(0.0f, 0.0f), ImVec2(s.WindowWidth, FLT_MAX));
|
||||
|
||||
if (ImGui::Begin(title, &Open, ImGuiWindowFlags_NoScrollbar))
|
||||
if (ImGui::Begin(title, p_open, ImGuiWindowFlags_NoScrollbar | ImGuiWindowFlags_NoCollapse))
|
||||
{
|
||||
if (ImGui::IsWindowHovered(ImGuiHoveredFlags_RootAndChildWindows) && ImGui::IsMouseReleased(ImGuiMouseButton_Right))
|
||||
ImGui::OpenPopup("context");
|
||||
DrawContents(mem_data, mem_size, base_display_addr);
|
||||
if (ContentsWidthChanged)
|
||||
{
|
||||
@@ -226,8 +225,18 @@ struct MemoryEditor
|
||||
float footer_height = 0;
|
||||
if (OptShowOptions)
|
||||
footer_height += height_separator + ImGui::GetFrameHeightWithSpacing() * 1;
|
||||
if (OptShowDataPreview)
|
||||
footer_height += height_separator + ImGui::GetFrameHeightWithSpacing() * 1 + ImGui::GetTextLineHeightWithSpacing() * 3;
|
||||
|
||||
ImGui::BeginChild("offset", ImVec2(0, s.LineHeight), false, ImGuiWindowFlags_NoMove | ImGuiWindowFlags_NoNav);
|
||||
ImGui::Text("%*c ", s.AddrDigitsCount, ' ');
|
||||
for (int i = 0; i < Cols; i++) {
|
||||
float byte_pos_x = s.PosHexStart + s.HexCellWidth * i;
|
||||
if (OptMidColsCount > 0)
|
||||
byte_pos_x += (float)(i / OptMidColsCount) * s.SpacingBetweenMidCols;
|
||||
ImGui::SameLine(byte_pos_x);
|
||||
ImGui::Text("%02X", i);
|
||||
}
|
||||
ImGui::EndChild();
|
||||
|
||||
ImGui::BeginChild("##scrolling", ImVec2(0, -footer_height), false, ImGuiWindowFlags_NoMove | ImGuiWindowFlags_NoNav);
|
||||
|
||||
ImGui::PushStyleVar(ImGuiStyleVar_FramePadding, ImVec2(0, 0));
|
||||
@@ -249,11 +258,13 @@ struct MemoryEditor
|
||||
DataEditingAddr = (size_t)-1;
|
||||
if (DataPreviewAddr >= mem_size)
|
||||
DataPreviewAddr = (size_t)-1;
|
||||
|
||||
size_t preview_data_type_size = OptShowDataPreview ? DataTypeGetSize(PreviewDataType) : 0;
|
||||
if (DataPreviewAddrEnd >= mem_size)
|
||||
DataPreviewAddrEnd = (size_t)-1;
|
||||
|
||||
size_t data_editing_addr_backup = DataEditingAddr;
|
||||
size_t data_preview_addr_backup = DataPreviewAddr;
|
||||
size_t data_editing_addr_next = (size_t)-1;
|
||||
size_t data_preview_addr_next = (size_t)-1;
|
||||
if (DataEditingAddr != (size_t)-1)
|
||||
{
|
||||
// Move cursor but only apply on next frame so scrolling with be synchronized (because currently we can't change the scrolling while the window is being rendered)
|
||||
@@ -261,6 +272,20 @@ struct MemoryEditor
|
||||
else if (ImGui::IsKeyPressed(ImGui::GetKeyIndex(ImGuiKey_DownArrow)) && DataEditingAddr < mem_size - Cols) { data_editing_addr_next = DataEditingAddr + Cols; DataEditingTakeFocus = true; }
|
||||
else if (ImGui::IsKeyPressed(ImGui::GetKeyIndex(ImGuiKey_LeftArrow)) && DataEditingAddr > 0) { data_editing_addr_next = DataEditingAddr - 1; DataEditingTakeFocus = true; }
|
||||
else if (ImGui::IsKeyPressed(ImGui::GetKeyIndex(ImGuiKey_RightArrow)) && DataEditingAddr < mem_size - 1) { data_editing_addr_next = DataEditingAddr + 1; DataEditingTakeFocus = true; }
|
||||
} else if (DataPreviewAddr != -1) {
|
||||
// Move cursor but only apply on next frame so scrolling with be synchronized (because currently we can't change the scrolling while the window is being rendered)
|
||||
if (ImGui::IsKeyPressed(ImGui::GetKeyIndex(ImGuiKey_UpArrow)) && DataPreviewAddr >= (size_t)Cols) { data_preview_addr_next = DataPreviewAddr - Cols; DataPreviewAddr = data_preview_addr_next; if (!ImGui::GetIO().KeyShift) DataPreviewAddrEnd = DataPreviewAddr; }
|
||||
else if (ImGui::IsKeyPressed(ImGui::GetKeyIndex(ImGuiKey_DownArrow)) && DataPreviewAddr < mem_size - Cols) { data_preview_addr_next = DataPreviewAddr + Cols; DataPreviewAddr = data_preview_addr_next; if (!ImGui::GetIO().KeyShift) DataPreviewAddrEnd = DataPreviewAddr; }
|
||||
else if (ImGui::IsKeyPressed(ImGui::GetKeyIndex(ImGuiKey_LeftArrow)) && DataPreviewAddr > 0) { data_preview_addr_next = DataPreviewAddr - 1; DataPreviewAddr = data_preview_addr_next; if (!ImGui::GetIO().KeyShift) DataPreviewAddrEnd = DataPreviewAddr; }
|
||||
else if (ImGui::IsKeyPressed(ImGui::GetKeyIndex(ImGuiKey_RightArrow)) && DataPreviewAddr < mem_size - 1) { data_preview_addr_next = DataPreviewAddr + 1; DataPreviewAddr = data_preview_addr_next; if (!ImGui::GetIO().KeyShift) DataPreviewAddrEnd = DataPreviewAddr; }
|
||||
}
|
||||
if (data_preview_addr_next != (size_t)-1 && (data_preview_addr_next / Cols) != (data_preview_addr_backup / Cols))
|
||||
{
|
||||
// Track cursor movements
|
||||
const int scroll_offset = ((int)(data_preview_addr_next / Cols) - (int)(data_preview_addr_backup / Cols));
|
||||
const bool scroll_desired = (scroll_offset < 0 && data_preview_addr_next < visible_start_addr + Cols * 2) || (scroll_offset > 0 && data_preview_addr_next > visible_end_addr - Cols * 2);
|
||||
if (scroll_desired)
|
||||
ImGui::SetScrollY(ImGui::GetScrollY() + scroll_offset * s.LineHeight);
|
||||
}
|
||||
if (data_editing_addr_next != (size_t)-1 && (data_editing_addr_next / Cols) != (data_editing_addr_backup / Cols))
|
||||
{
|
||||
@@ -300,19 +325,24 @@ struct MemoryEditor
|
||||
// Draw highlight
|
||||
bool is_highlight_from_user_range = (addr >= HighlightMin && addr < HighlightMax);
|
||||
bool is_highlight_from_user_func = (HighlightFn && HighlightFn(mem_data, addr, false));
|
||||
bool is_highlight_from_preview = (addr >= DataPreviewAddr && addr < DataPreviewAddr + preview_data_type_size);
|
||||
bool is_highlight_from_preview = (addr >= DataPreviewAddr && addr <= DataPreviewAddrEnd) || (addr >= DataPreviewAddrEnd && addr <= DataPreviewAddr);
|
||||
if (is_highlight_from_user_range || is_highlight_from_user_func || is_highlight_from_preview)
|
||||
{
|
||||
ImVec2 pos = ImGui::GetCursorScreenPos();
|
||||
float highlight_width = s.GlyphWidth * 2;
|
||||
bool is_next_byte_highlighted = (addr + 1 < mem_size) && ((HighlightMax != (size_t)-1 && addr + 1 < HighlightMax) || (HighlightFn && HighlightFn(mem_data, addr + 1, true)));
|
||||
if (is_next_byte_highlighted || (n + 1 == Cols))
|
||||
if (is_next_byte_highlighted)
|
||||
{
|
||||
highlight_width = s.HexCellWidth;
|
||||
if (OptMidColsCount > 0 && n > 0 && (n + 1) < Cols && ((n + 1) % OptMidColsCount) == 0)
|
||||
highlight_width += s.SpacingBetweenMidCols;
|
||||
}
|
||||
draw_list->AddRectFilled(pos, ImVec2(pos.x + highlight_width, pos.y + s.LineHeight), HighlightColor);
|
||||
|
||||
ImU32 color = HighlightColor;
|
||||
if ((is_highlight_from_user_range + is_highlight_from_user_func + is_highlight_from_preview) > 1)
|
||||
color = (ImAlphaBlendColors(HighlightColor, 0x60C08080) & 0x00FFFFFF) | 0x90000000;
|
||||
|
||||
draw_list->AddRectFilled(pos, ImVec2(pos.x + highlight_width, pos.y + s.LineHeight), color);
|
||||
}
|
||||
|
||||
if (DataEditingAddr == addr)
|
||||
@@ -397,10 +427,26 @@ struct MemoryEditor
|
||||
else
|
||||
ImGui::Text(format_byte_space, b);
|
||||
}
|
||||
if (!ReadOnly && ImGui::IsItemHovered() && ImGui::IsMouseClicked(0))
|
||||
if (!ReadOnly && ImGui::IsItemHovered() && ImGui::IsMouseClicked(0) && !ImGui::GetIO().KeyShift)
|
||||
{
|
||||
DataEditingTakeFocus = true;
|
||||
data_editing_addr_next = addr;
|
||||
if (ImGui::IsMouseDoubleClicked(0)) {
|
||||
DataEditingTakeFocus = true;
|
||||
data_editing_addr_next = addr;
|
||||
}
|
||||
|
||||
DataPreviewAddr = addr;
|
||||
DataPreviewAddrEnd = addr;
|
||||
|
||||
hex::Region selectionRegion { addr, 1 };
|
||||
hex::View::postEvent(hex::Events::RegionSelected, &selectionRegion);
|
||||
}
|
||||
if (!ReadOnly && ImGui::IsItemHovered() && ((ImGui::IsMouseClicked(0) && ImGui::GetIO().KeyShift) || ImGui::IsMouseDragging(0))) {
|
||||
DataPreviewAddrEnd = addr;
|
||||
|
||||
size_t dataPreviewStart = std::min(DataPreviewAddr, DataPreviewAddrEnd);
|
||||
|
||||
hex::Region selectionRegion { std::min(DataPreviewAddr, DataPreviewAddrEnd), std::max(DataPreviewAddr, DataPreviewAddrEnd) - std::min(DataPreviewAddr, DataPreviewAddrEnd) + 1 };
|
||||
hex::View::postEvent(hex::Events::RegionSelected, &selectionRegion);
|
||||
}
|
||||
}
|
||||
}
|
||||
@@ -411,13 +457,13 @@ struct MemoryEditor
|
||||
ImGui::SameLine(s.PosAsciiStart);
|
||||
ImVec2 pos = ImGui::GetCursorScreenPos();
|
||||
addr = line_i * Cols;
|
||||
ImGui::PushID(line_i);
|
||||
if (ImGui::InvisibleButton("ascii", ImVec2(s.PosAsciiEnd - s.PosAsciiStart, s.LineHeight)))
|
||||
{
|
||||
DataEditingAddr = DataPreviewAddr = addr + (size_t)((ImGui::GetIO().MousePos.x - pos.x) / s.GlyphWidth);
|
||||
DataEditingTakeFocus = true;
|
||||
}
|
||||
|
||||
ImGui::PushID(-1);
|
||||
ImGui::SameLine();
|
||||
ImGui::Dummy(ImVec2(s.GlyphWidth, s.LineHeight));
|
||||
|
||||
ImGui::PopID();
|
||||
|
||||
for (int n = 0; n < Cols && addr < mem_size; n++, addr++)
|
||||
{
|
||||
if (addr == DataEditingAddr)
|
||||
@@ -428,6 +474,42 @@ struct MemoryEditor
|
||||
unsigned char c = ReadFn ? ReadFn(mem_data, addr) : mem_data[addr];
|
||||
char display_c = (c < 32 || c >= 128) ? '.' : c;
|
||||
draw_list->AddText(pos, (display_c == c) ? color_text : color_disabled, &display_c, &display_c + 1);
|
||||
|
||||
// Draw highlight
|
||||
bool is_highlight_from_user_range = (addr >= HighlightMin && addr < HighlightMax);
|
||||
bool is_highlight_from_user_func = (HighlightFn && HighlightFn(mem_data, addr, false));
|
||||
bool is_highlight_from_preview = (addr >= DataPreviewAddr && addr <= DataPreviewAddrEnd) || (addr >= DataPreviewAddrEnd && addr <= DataPreviewAddr);
|
||||
if (is_highlight_from_user_range || is_highlight_from_user_func || is_highlight_from_preview)
|
||||
{
|
||||
ImU32 color = HighlightColor;
|
||||
if ((is_highlight_from_user_range + is_highlight_from_user_func + is_highlight_from_preview) > 1)
|
||||
color = (ImAlphaBlendColors(HighlightColor, 0x60C08080) & 0x00FFFFFF) | 0x90000000;
|
||||
|
||||
draw_list->AddRectFilled(pos, ImVec2(pos.x + s.GlyphWidth, pos.y + s.LineHeight), color);
|
||||
}
|
||||
|
||||
|
||||
ImGui::PushID(line_i * Cols + n);
|
||||
ImGui::SameLine();
|
||||
ImGui::Dummy(ImVec2(s.GlyphWidth, s.LineHeight));
|
||||
|
||||
ImGui::PopID();
|
||||
|
||||
if (!ReadOnly && ImGui::IsItemHovered() && ImGui::IsMouseClicked(0) && !ImGui::GetIO().KeyShift)
|
||||
{
|
||||
if (ImGui::IsMouseDoubleClicked(0)) {
|
||||
DataEditingTakeFocus = true;
|
||||
data_editing_addr_next = addr;
|
||||
}
|
||||
|
||||
DataPreviewAddr = addr;
|
||||
DataPreviewAddrEnd = addr;
|
||||
|
||||
}
|
||||
if (!ReadOnly && ImGui::IsItemHovered() && ((ImGui::IsMouseClicked(0) && ImGui::GetIO().KeyShift) || ImGui::IsMouseDragging(0))) {
|
||||
DataPreviewAddrEnd = addr;
|
||||
}
|
||||
|
||||
pos.x += s.GlyphWidth;
|
||||
}
|
||||
}
|
||||
@@ -444,22 +526,15 @@ struct MemoryEditor
|
||||
}
|
||||
else if (data_editing_addr_next != (size_t)-1)
|
||||
{
|
||||
DataEditingAddr = DataPreviewAddr = data_editing_addr_next;
|
||||
DataEditingAddr = DataPreviewAddr = DataPreviewAddrEnd = data_editing_addr_next;
|
||||
}
|
||||
|
||||
const bool lock_show_data_preview = OptShowDataPreview;
|
||||
if (OptShowOptions)
|
||||
{
|
||||
ImGui::Separator();
|
||||
DrawOptionsLine(s, mem_data, mem_size, base_display_addr);
|
||||
}
|
||||
|
||||
if (lock_show_data_preview)
|
||||
{
|
||||
ImGui::Separator();
|
||||
DrawPreviewLine(s, mem_data, mem_size, base_display_addr);
|
||||
}
|
||||
|
||||
// Notify the main window of our ideal child content size (FIXME: we are missing an API to get the contents size from the child)
|
||||
ImGui::SetCursorPosX(s.WindowWidth);
|
||||
}
|
||||
@@ -472,13 +547,12 @@ struct MemoryEditor
|
||||
|
||||
// Options menu
|
||||
if (ImGui::Button("Options"))
|
||||
ImGui::OpenPopup("context");
|
||||
if (ImGui::BeginPopup("context"))
|
||||
{
|
||||
ImGui::OpenPopup("options");
|
||||
|
||||
if (ImGui::BeginPopup("options")) {
|
||||
ImGui::PushItemWidth(56);
|
||||
if (ImGui::DragInt("##cols", &Cols, 0.2f, 4, 32, "%d cols")) { ContentsWidthChanged = true; if (Cols < 1) Cols = 1; }
|
||||
ImGui::PopItemWidth();
|
||||
ImGui::Checkbox("Show Data Preview", &OptShowDataPreview);
|
||||
ImGui::Checkbox("Show HexII", &OptShowHexII);
|
||||
if (ImGui::Checkbox("Show Ascii", &OptShowAscii)) { ContentsWidthChanged = true; }
|
||||
ImGui::Checkbox("Grey out zeroes", &OptGreyOutZeroes);
|
||||
@@ -489,18 +563,6 @@ struct MemoryEditor
|
||||
|
||||
ImGui::SameLine();
|
||||
ImGui::Text(format_range, s.AddrDigitsCount, base_display_addr, s.AddrDigitsCount, base_display_addr + mem_size - 1);
|
||||
ImGui::SameLine();
|
||||
ImGui::PushItemWidth((s.AddrDigitsCount + 1) * s.GlyphWidth + style.FramePadding.x * 2.0f);
|
||||
if (ImGui::InputText("##addr", AddrInputBuf, 32, ImGuiInputTextFlags_CharsHexadecimal | ImGuiInputTextFlags_EnterReturnsTrue))
|
||||
{
|
||||
size_t goto_addr;
|
||||
if (sscanf(AddrInputBuf, "%" _PRISizeT "X", &goto_addr) == 1)
|
||||
{
|
||||
GotoAddr = goto_addr - base_display_addr;
|
||||
HighlightMin = HighlightMax = (size_t)-1;
|
||||
}
|
||||
}
|
||||
ImGui::PopItemWidth();
|
||||
|
||||
if (GotoAddr != (size_t)-1)
|
||||
{
|
||||
@@ -516,66 +578,7 @@ struct MemoryEditor
|
||||
}
|
||||
}
|
||||
|
||||
void DrawPreviewLine(const Sizes& s, void* mem_data_void, size_t mem_size, size_t base_display_addr)
|
||||
{
|
||||
IM_UNUSED(base_display_addr);
|
||||
ImU8* mem_data = (ImU8*)mem_data_void;
|
||||
ImGuiStyle& style = ImGui::GetStyle();
|
||||
ImGui::AlignTextToFramePadding();
|
||||
ImGui::Text("Preview as:");
|
||||
ImGui::SameLine();
|
||||
ImGui::PushItemWidth((s.GlyphWidth * 10.0f) + style.FramePadding.x * 2.0f + style.ItemInnerSpacing.x);
|
||||
if (ImGui::BeginCombo("##combo_type", DataTypeGetDesc(PreviewDataType), ImGuiComboFlags_HeightLargest))
|
||||
{
|
||||
for (int n = 0; n < ImGuiDataType_COUNT; n++)
|
||||
if (ImGui::Selectable(DataTypeGetDesc((ImGuiDataType)n), PreviewDataType == n))
|
||||
PreviewDataType = (ImGuiDataType)n;
|
||||
ImGui::EndCombo();
|
||||
}
|
||||
ImGui::PopItemWidth();
|
||||
ImGui::SameLine();
|
||||
ImGui::PushItemWidth((s.GlyphWidth * 6.0f) + style.FramePadding.x * 2.0f + style.ItemInnerSpacing.x);
|
||||
ImGui::Combo("##combo_endianess", &PreviewEndianess, "LE\0BE\0\0");
|
||||
ImGui::PopItemWidth();
|
||||
|
||||
char buf[128] = "";
|
||||
float x = s.GlyphWidth * 6.0f;
|
||||
bool has_value = DataPreviewAddr != (size_t)-1;
|
||||
if (has_value)
|
||||
DrawPreviewData(DataPreviewAddr, mem_data, mem_size, PreviewDataType, DataFormat_Dec, buf, (size_t)IM_ARRAYSIZE(buf));
|
||||
ImGui::Text("Dec"); ImGui::SameLine(x); ImGui::TextUnformatted(has_value ? buf : "N/A");
|
||||
if (has_value)
|
||||
DrawPreviewData(DataPreviewAddr, mem_data, mem_size, PreviewDataType, DataFormat_Hex, buf, (size_t)IM_ARRAYSIZE(buf));
|
||||
ImGui::Text("Hex"); ImGui::SameLine(x); ImGui::TextUnformatted(has_value ? buf : "N/A");
|
||||
if (has_value)
|
||||
DrawPreviewData(DataPreviewAddr, mem_data, mem_size, PreviewDataType, DataFormat_Bin, buf, (size_t)IM_ARRAYSIZE(buf));
|
||||
buf[IM_ARRAYSIZE(buf) - 1] = 0;
|
||||
ImGui::Text("Bin"); ImGui::SameLine(x); ImGui::TextUnformatted(has_value ? buf : "N/A");
|
||||
}
|
||||
|
||||
// Utilities for Data Preview
|
||||
const char* DataTypeGetDesc(ImGuiDataType data_type) const
|
||||
{
|
||||
const char* descs[] = { "Int8", "Uint8", "Int16", "Uint16", "Int32", "Uint32", "Int64", "Uint64", "Float", "Double" };
|
||||
IM_ASSERT(data_type >= 0 && data_type < ImGuiDataType_COUNT);
|
||||
return descs[data_type];
|
||||
}
|
||||
|
||||
size_t DataTypeGetSize(ImGuiDataType data_type) const
|
||||
{
|
||||
const size_t sizes[] = { 1, 1, 2, 2, 4, 4, 8, 8, sizeof(float), sizeof(double) };
|
||||
IM_ASSERT(data_type >= 0 && data_type < ImGuiDataType_COUNT);
|
||||
return sizes[data_type];
|
||||
}
|
||||
|
||||
const char* DataFormatGetDesc(DataFormat data_format) const
|
||||
{
|
||||
const char* descs[] = { "Bin", "Dec", "Hex" };
|
||||
IM_ASSERT(data_format >= 0 && data_format < DataFormat_COUNT);
|
||||
return descs[data_format];
|
||||
}
|
||||
|
||||
bool IsBigEndian() const
|
||||
static bool IsBigEndian()
|
||||
{
|
||||
uint16_t x = 1;
|
||||
char c[2];
|
||||
@@ -622,132 +625,6 @@ struct MemoryEditor
|
||||
fp = IsBigEndian() ? EndianessCopyBigEndian : EndianessCopyLittleEndian;
|
||||
return fp(dst, src, size, PreviewEndianess);
|
||||
}
|
||||
|
||||
const char* FormatBinary(const uint8_t* buf, int width) const
|
||||
{
|
||||
IM_ASSERT(width <= 64);
|
||||
size_t out_n = 0;
|
||||
static char out_buf[64 + 8 + 1];
|
||||
int n = width / 8;
|
||||
for (int j = n - 1; j >= 0; --j)
|
||||
{
|
||||
for (int i = 0; i < 8; ++i)
|
||||
out_buf[out_n++] = (buf[j] & (1 << (7 - i))) ? '1' : '0';
|
||||
out_buf[out_n++] = ' ';
|
||||
}
|
||||
IM_ASSERT(out_n < IM_ARRAYSIZE(out_buf));
|
||||
out_buf[out_n] = 0;
|
||||
return out_buf;
|
||||
}
|
||||
|
||||
// [Internal]
|
||||
void DrawPreviewData(size_t addr, const ImU8* mem_data, size_t mem_size, ImGuiDataType data_type, DataFormat data_format, char* out_buf, size_t out_buf_size) const
|
||||
{
|
||||
uint8_t buf[8];
|
||||
size_t elem_size = DataTypeGetSize(data_type);
|
||||
size_t size = addr + elem_size > mem_size ? mem_size - addr : elem_size;
|
||||
if (ReadFn)
|
||||
for (int i = 0, n = (int)size; i < n; ++i)
|
||||
buf[i] = ReadFn(mem_data, addr + i);
|
||||
else
|
||||
memcpy(buf, mem_data + addr, size);
|
||||
|
||||
if (data_format == DataFormat_Bin)
|
||||
{
|
||||
uint8_t binbuf[8];
|
||||
EndianessCopy(binbuf, buf, size);
|
||||
ImSnprintf(out_buf, out_buf_size, "%s", FormatBinary(binbuf, (int)size * 8));
|
||||
return;
|
||||
}
|
||||
|
||||
out_buf[0] = 0;
|
||||
switch (data_type)
|
||||
{
|
||||
case ImGuiDataType_S8:
|
||||
{
|
||||
int8_t int8 = 0;
|
||||
EndianessCopy(&int8, buf, size);
|
||||
if (data_format == DataFormat_Dec) { ImSnprintf(out_buf, out_buf_size, "%hhd", int8); return; }
|
||||
if (data_format == DataFormat_Hex) { ImSnprintf(out_buf, out_buf_size, "0x%02x", int8 & 0xFF); return; }
|
||||
break;
|
||||
}
|
||||
case ImGuiDataType_U8:
|
||||
{
|
||||
uint8_t uint8 = 0;
|
||||
EndianessCopy(&uint8, buf, size);
|
||||
if (data_format == DataFormat_Dec) { ImSnprintf(out_buf, out_buf_size, "%hhu", uint8); return; }
|
||||
if (data_format == DataFormat_Hex) { ImSnprintf(out_buf, out_buf_size, "0x%02x", uint8 & 0XFF); return; }
|
||||
break;
|
||||
}
|
||||
case ImGuiDataType_S16:
|
||||
{
|
||||
int16_t int16 = 0;
|
||||
EndianessCopy(&int16, buf, size);
|
||||
if (data_format == DataFormat_Dec) { ImSnprintf(out_buf, out_buf_size, "%hd", int16); return; }
|
||||
if (data_format == DataFormat_Hex) { ImSnprintf(out_buf, out_buf_size, "0x%04x", int16 & 0xFFFF); return; }
|
||||
break;
|
||||
}
|
||||
case ImGuiDataType_U16:
|
||||
{
|
||||
uint16_t uint16 = 0;
|
||||
EndianessCopy(&uint16, buf, size);
|
||||
if (data_format == DataFormat_Dec) { ImSnprintf(out_buf, out_buf_size, "%hu", uint16); return; }
|
||||
if (data_format == DataFormat_Hex) { ImSnprintf(out_buf, out_buf_size, "0x%04x", uint16 & 0xFFFF); return; }
|
||||
break;
|
||||
}
|
||||
case ImGuiDataType_S32:
|
||||
{
|
||||
int32_t int32 = 0;
|
||||
EndianessCopy(&int32, buf, size);
|
||||
if (data_format == DataFormat_Dec) { ImSnprintf(out_buf, out_buf_size, "%d", int32); return; }
|
||||
if (data_format == DataFormat_Hex) { ImSnprintf(out_buf, out_buf_size, "0x%08x", int32); return; }
|
||||
break;
|
||||
}
|
||||
case ImGuiDataType_U32:
|
||||
{
|
||||
uint32_t uint32 = 0;
|
||||
EndianessCopy(&uint32, buf, size);
|
||||
if (data_format == DataFormat_Dec) { ImSnprintf(out_buf, out_buf_size, "%u", uint32); return; }
|
||||
if (data_format == DataFormat_Hex) { ImSnprintf(out_buf, out_buf_size, "0x%08x", uint32); return; }
|
||||
break;
|
||||
}
|
||||
case ImGuiDataType_S64:
|
||||
{
|
||||
int64_t int64 = 0;
|
||||
EndianessCopy(&int64, buf, size);
|
||||
if (data_format == DataFormat_Dec) { ImSnprintf(out_buf, out_buf_size, "%lld", (long long)int64); return; }
|
||||
if (data_format == DataFormat_Hex) { ImSnprintf(out_buf, out_buf_size, "0x%016llx", (long long)int64); return; }
|
||||
break;
|
||||
}
|
||||
case ImGuiDataType_U64:
|
||||
{
|
||||
uint64_t uint64 = 0;
|
||||
EndianessCopy(&uint64, buf, size);
|
||||
if (data_format == DataFormat_Dec) { ImSnprintf(out_buf, out_buf_size, "%llu", (long long)uint64); return; }
|
||||
if (data_format == DataFormat_Hex) { ImSnprintf(out_buf, out_buf_size, "0x%016llx", (long long)uint64); return; }
|
||||
break;
|
||||
}
|
||||
case ImGuiDataType_Float:
|
||||
{
|
||||
float float32 = 0.0f;
|
||||
EndianessCopy(&float32, buf, size);
|
||||
if (data_format == DataFormat_Dec) { ImSnprintf(out_buf, out_buf_size, "%f", float32); return; }
|
||||
if (data_format == DataFormat_Hex) { ImSnprintf(out_buf, out_buf_size, "%a", float32); return; }
|
||||
break;
|
||||
}
|
||||
case ImGuiDataType_Double:
|
||||
{
|
||||
double float64 = 0.0;
|
||||
EndianessCopy(&float64, buf, size);
|
||||
if (data_format == DataFormat_Dec) { ImSnprintf(out_buf, out_buf_size, "%f", float64); return; }
|
||||
if (data_format == DataFormat_Hex) { ImSnprintf(out_buf, out_buf_size, "%a", float64); return; }
|
||||
break;
|
||||
}
|
||||
case ImGuiDataType_COUNT:
|
||||
break;
|
||||
} // Switch
|
||||
IM_ASSERT(0); // Shouldn't reach
|
||||
}
|
||||
};
|
||||
|
||||
#undef _PRISizeT
|
||||
|
||||
1214
libs/ImGui/source/ImGuiFileBrowser.cpp
Normal file
1214
libs/ImGui/source/ImGuiFileBrowser.cpp
Normal file
File diff suppressed because it is too large
Load Diff
3160
libs/ImGui/source/TextEditor.cpp
Normal file
3160
libs/ImGui/source/TextEditor.cpp
Normal file
File diff suppressed because it is too large
Load Diff
File diff suppressed because it is too large
Load Diff
File diff suppressed because it is too large
Load Diff
@@ -60,12 +60,16 @@ Index of this file:
|
||||
#if __has_warning("-Wunknown-warning-option")
|
||||
#pragma clang diagnostic ignored "-Wunknown-warning-option" // warning: unknown warning group 'xxx' // not all warnings are known by all Clang versions and they tend to be rename-happy.. so ignoring warnings triggers new warnings on some configuration. Great!
|
||||
#endif
|
||||
#if __has_warning("-Walloca")
|
||||
#pragma clang diagnostic ignored "-Walloca" // warning: use of function '__builtin_alloca' is discouraged
|
||||
#endif
|
||||
#pragma clang diagnostic ignored "-Wunknown-pragmas" // warning: unknown warning group 'xxx'
|
||||
#pragma clang diagnostic ignored "-Wold-style-cast" // warning: use of old-style cast // yes, they are more terse.
|
||||
#pragma clang diagnostic ignored "-Wfloat-equal" // warning: comparing floating point with == or != is unsafe // storing and comparing against same constants ok.
|
||||
#pragma clang diagnostic ignored "-Wglobal-constructors" // warning: declaration requires a global destructor // similar to above, not sure what the exact difference is.
|
||||
#pragma clang diagnostic ignored "-Wsign-conversion" // warning: implicit conversion changes signedness
|
||||
#pragma clang diagnostic ignored "-Wzero-as-null-pointer-constant" // warning: zero as null pointer constant // some standard header variations use #define NULL 0
|
||||
#pragma clang diagnostic ignored "-Walloca" // warning: use of function '__builtin_alloca' is discouraged
|
||||
#pragma clang diagnostic ignored "-Wcomma" // warning: possible misuse of comma operator here
|
||||
#pragma clang diagnostic ignored "-Wreserved-id-macro" // warning: macro name is a reserved identifier
|
||||
#pragma clang diagnostic ignored "-Wdouble-promotion" // warning: implicit conversion from 'float' to 'double' when passing argument to function // using printf() is a misery with this as C++ va_arg ellipsis changes float to double.
|
||||
@@ -221,6 +225,11 @@ void ImGui::StyleColorsDark(ImGuiStyle* dst)
|
||||
colors[ImGuiCol_PlotLinesHovered] = ImVec4(1.00f, 0.43f, 0.35f, 1.00f);
|
||||
colors[ImGuiCol_PlotHistogram] = ImVec4(0.90f, 0.70f, 0.00f, 1.00f);
|
||||
colors[ImGuiCol_PlotHistogramHovered] = ImVec4(1.00f, 0.60f, 0.00f, 1.00f);
|
||||
colors[ImGuiCol_TableHeaderBg] = ImVec4(0.19f, 0.19f, 0.20f, 1.00f);
|
||||
colors[ImGuiCol_TableBorderStrong] = ImVec4(0.31f, 0.31f, 0.35f, 1.00f); // Prefer using Alpha=1.0 here
|
||||
colors[ImGuiCol_TableBorderLight] = ImVec4(0.23f, 0.23f, 0.25f, 1.00f); // Prefer using Alpha=1.0 here
|
||||
colors[ImGuiCol_TableRowBg] = ImVec4(0.00f, 0.00f, 0.00f, 0.00f);
|
||||
colors[ImGuiCol_TableRowBgAlt] = ImVec4(1.00f, 1.00f, 1.00f, 0.07f);
|
||||
colors[ImGuiCol_TextSelectedBg] = ImVec4(0.26f, 0.59f, 0.98f, 0.35f);
|
||||
colors[ImGuiCol_DragDropTarget] = ImVec4(1.00f, 1.00f, 0.00f, 0.90f);
|
||||
colors[ImGuiCol_NavHighlight] = ImVec4(0.26f, 0.59f, 0.98f, 1.00f);
|
||||
@@ -278,6 +287,11 @@ void ImGui::StyleColorsClassic(ImGuiStyle* dst)
|
||||
colors[ImGuiCol_PlotLinesHovered] = ImVec4(0.90f, 0.70f, 0.00f, 1.00f);
|
||||
colors[ImGuiCol_PlotHistogram] = ImVec4(0.90f, 0.70f, 0.00f, 1.00f);
|
||||
colors[ImGuiCol_PlotHistogramHovered] = ImVec4(1.00f, 0.60f, 0.00f, 1.00f);
|
||||
colors[ImGuiCol_TableHeaderBg] = ImVec4(0.27f, 0.27f, 0.38f, 1.00f);
|
||||
colors[ImGuiCol_TableBorderStrong] = ImVec4(0.31f, 0.31f, 0.45f, 1.00f); // Prefer using Alpha=1.0 here
|
||||
colors[ImGuiCol_TableBorderLight] = ImVec4(0.26f, 0.26f, 0.28f, 1.00f); // Prefer using Alpha=1.0 here
|
||||
colors[ImGuiCol_TableRowBg] = ImVec4(0.00f, 0.00f, 0.00f, 0.00f);
|
||||
colors[ImGuiCol_TableRowBgAlt] = ImVec4(1.00f, 1.00f, 1.00f, 0.07f);
|
||||
colors[ImGuiCol_TextSelectedBg] = ImVec4(0.00f, 0.00f, 1.00f, 0.35f);
|
||||
colors[ImGuiCol_DragDropTarget] = ImVec4(1.00f, 1.00f, 0.00f, 0.90f);
|
||||
colors[ImGuiCol_NavHighlight] = colors[ImGuiCol_HeaderHovered];
|
||||
@@ -336,6 +350,11 @@ void ImGui::StyleColorsLight(ImGuiStyle* dst)
|
||||
colors[ImGuiCol_PlotLinesHovered] = ImVec4(1.00f, 0.43f, 0.35f, 1.00f);
|
||||
colors[ImGuiCol_PlotHistogram] = ImVec4(0.90f, 0.70f, 0.00f, 1.00f);
|
||||
colors[ImGuiCol_PlotHistogramHovered] = ImVec4(1.00f, 0.45f, 0.00f, 1.00f);
|
||||
colors[ImGuiCol_TableHeaderBg] = ImVec4(0.78f, 0.87f, 0.98f, 1.00f);
|
||||
colors[ImGuiCol_TableBorderStrong] = ImVec4(0.57f, 0.57f, 0.64f, 1.00f); // Prefer using Alpha=1.0 here
|
||||
colors[ImGuiCol_TableBorderLight] = ImVec4(0.68f, 0.68f, 0.74f, 1.00f); // Prefer using Alpha=1.0 here
|
||||
colors[ImGuiCol_TableRowBg] = ImVec4(0.00f, 0.00f, 0.00f, 0.00f);
|
||||
colors[ImGuiCol_TableRowBgAlt] = ImVec4(0.30f, 0.30f, 0.30f, 0.07f);
|
||||
colors[ImGuiCol_TextSelectedBg] = ImVec4(0.26f, 0.59f, 0.98f, 0.35f);
|
||||
colors[ImGuiCol_DragDropTarget] = ImVec4(0.26f, 0.59f, 0.98f, 0.95f);
|
||||
colors[ImGuiCol_NavHighlight] = colors[ImGuiCol_HeaderHovered];
|
||||
@@ -350,21 +369,12 @@ void ImGui::StyleColorsLight(ImGuiStyle* dst)
|
||||
|
||||
ImDrawListSharedData::ImDrawListSharedData()
|
||||
{
|
||||
Font = NULL;
|
||||
FontSize = 0.0f;
|
||||
CurveTessellationTol = 0.0f;
|
||||
CircleSegmentMaxError = 0.0f;
|
||||
ClipRectFullscreen = ImVec4(-8192.0f, -8192.0f, +8192.0f, +8192.0f);
|
||||
InitialFlags = ImDrawListFlags_None;
|
||||
|
||||
// Lookup tables
|
||||
memset(this, 0, sizeof(*this));
|
||||
for (int i = 0; i < IM_ARRAYSIZE(ArcFastVtx); i++)
|
||||
{
|
||||
const float a = ((float)i * 2 * IM_PI) / (float)IM_ARRAYSIZE(ArcFastVtx);
|
||||
ArcFastVtx[i] = ImVec2(ImCos(a), ImSin(a));
|
||||
}
|
||||
memset(CircleSegmentCounts, 0, sizeof(CircleSegmentCounts)); // This will be set by SetCircleSegmentMaxError()
|
||||
TexUvLines = NULL;
|
||||
}
|
||||
|
||||
void ImDrawListSharedData::SetCircleSegmentMaxError(float max_error)
|
||||
@@ -1432,10 +1442,14 @@ void ImDrawListSplitter::ClearFreeMemory()
|
||||
|
||||
void ImDrawListSplitter::Split(ImDrawList* draw_list, int channels_count)
|
||||
{
|
||||
IM_UNUSED(draw_list);
|
||||
IM_ASSERT(_Current == 0 && _Count <= 1 && "Nested channel splitting is not supported. Please use separate instances of ImDrawListSplitter.");
|
||||
int old_channels_count = _Channels.Size;
|
||||
if (old_channels_count < channels_count)
|
||||
{
|
||||
_Channels.reserve(channels_count); // Avoid over reserving since this is likely to stay stable
|
||||
_Channels.resize(channels_count);
|
||||
}
|
||||
_Count = channels_count;
|
||||
|
||||
// Channels[] (24/32 bytes each) hold storage that we'll swap with draw_list->_CmdBuffer/_IdxBuffer
|
||||
@@ -1453,12 +1467,6 @@ void ImDrawListSplitter::Split(ImDrawList* draw_list, int channels_count)
|
||||
_Channels[i]._CmdBuffer.resize(0);
|
||||
_Channels[i]._IdxBuffer.resize(0);
|
||||
}
|
||||
if (_Channels[i]._CmdBuffer.Size == 0)
|
||||
{
|
||||
ImDrawCmd draw_cmd;
|
||||
ImDrawCmd_HeaderCopy(&draw_cmd, &draw_list->_CmdHeader); // Copy ClipRect, TextureId, VtxOffset
|
||||
_Channels[i]._CmdBuffer.push_back(draw_cmd);
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
@@ -1548,8 +1556,10 @@ void ImDrawListSplitter::SetCurrentChannel(ImDrawList* draw_list, int idx)
|
||||
draw_list->_IdxWritePtr = draw_list->IdxBuffer.Data + draw_list->IdxBuffer.Size;
|
||||
|
||||
// If current command is used with different settings we need to add a new command
|
||||
ImDrawCmd* curr_cmd = &draw_list->CmdBuffer.Data[draw_list->CmdBuffer.Size - 1];
|
||||
if (curr_cmd->ElemCount == 0)
|
||||
ImDrawCmd* curr_cmd = (draw_list->CmdBuffer.Size == 0) ? NULL : &draw_list->CmdBuffer.Data[draw_list->CmdBuffer.Size - 1];
|
||||
if (curr_cmd == NULL)
|
||||
draw_list->AddDrawCmd();
|
||||
else if (curr_cmd->ElemCount == 0)
|
||||
ImDrawCmd_HeaderCopy(curr_cmd, &draw_list->_CmdHeader); // Copy ClipRect, TextureId, VtxOffset
|
||||
else if (ImDrawCmd_HeaderCompare(curr_cmd, &draw_list->_CmdHeader) != 0)
|
||||
draw_list->AddDrawCmd();
|
||||
|
||||
File diff suppressed because it is too large
Load Diff
18
magic_dbs/nintendo_magic
Normal file
18
magic_dbs/nintendo_magic
Normal file
@@ -0,0 +1,18 @@
|
||||
# A libmagic database containing definitions for files used by Nintendo consoles
|
||||
|
||||
# Nintendo Switch NRO file
|
||||
0x10 string NRO0 Nintendo Switch NRO file
|
||||
>0x08 string HOMEBREW (Homebrew)
|
||||
>0x18 long x (Size %d)
|
||||
|
||||
# Nintendo Switch NSO file
|
||||
0x00 string NSO0 Nintendo Switch NSO file
|
||||
>0x04 long x Version %d
|
||||
>0x0C long x Flags %08x
|
||||
|
||||
# Nintendo Switch NCA file
|
||||
0x200 string NCA Nintendo Switch NCA file
|
||||
>0x203 byte x Version %c
|
||||
>0x204 byte 0 System NCA
|
||||
>0x204 byte 1 Gamecard NCA
|
||||
>0x210 quad x ProgramId %016llx
|
||||
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
resource.rc
Normal file
1
resource.rc
Normal file
@@ -0,0 +1 @@
|
||||
GLFW_ICON ICON icon.ico
|
||||
@@ -1,4 +1,4 @@
|
||||
#include "crypto.hpp"
|
||||
#include "helpers/crypto.hpp"
|
||||
|
||||
#include "providers/provider.hpp"
|
||||
|
||||
@@ -6,6 +6,8 @@
|
||||
#include <openssl/md5.h>
|
||||
#include <openssl/sha.h>
|
||||
|
||||
#include <openssl/evp.h>
|
||||
|
||||
#include <array>
|
||||
#include <span>
|
||||
|
||||
@@ -39,7 +41,7 @@ namespace hex {
|
||||
std::array<u8, 512> buffer = { 0 };
|
||||
|
||||
for (u64 bufferOffset = 0; offset < size; offset += buffer.size()) {
|
||||
const u64 readSize = std::min(buffer.size(), size - bufferOffset);
|
||||
const u64 readSize = std::min(u64(buffer.size()), size - bufferOffset);
|
||||
data->read(offset + bufferOffset, buffer.data(), readSize);
|
||||
|
||||
for (size_t i = 0; i < readSize; i++) {
|
||||
@@ -72,7 +74,7 @@ namespace hex {
|
||||
std::array<u8, 512> buffer = { 0 };
|
||||
|
||||
for (u64 bufferOffset = 0; offset < size; offset += buffer.size()) {
|
||||
const u64 readSize = std::min(buffer.size(), size - bufferOffset);
|
||||
const u64 readSize = std::min(u64(buffer.size()), size - bufferOffset);
|
||||
data->read(offset + bufferOffset, buffer.data(), readSize);
|
||||
|
||||
for (size_t i = 0; i < readSize; i++) {
|
||||
@@ -92,7 +94,7 @@ namespace hex {
|
||||
|
||||
std::array<u8, 512> buffer = { 0 };
|
||||
for (u64 bufferOffset = 0; bufferOffset < size; bufferOffset += buffer.size()) {
|
||||
const u64 readSize = std::min(buffer.size(), size - bufferOffset);
|
||||
const u64 readSize = std::min(u64(buffer.size()), size - bufferOffset);
|
||||
data->read(offset + bufferOffset, buffer.data(), readSize);
|
||||
MD4_Update(&ctx, buffer.data(), readSize);
|
||||
}
|
||||
@@ -111,7 +113,7 @@ namespace hex {
|
||||
|
||||
std::array<u8, 512> buffer = { 0 };
|
||||
for (u64 bufferOffset = 0; bufferOffset < size; bufferOffset += buffer.size()) {
|
||||
const u64 readSize = std::min(buffer.size(), size - bufferOffset);
|
||||
const u64 readSize = std::min(u64(buffer.size()), size - bufferOffset);
|
||||
data->read(offset + bufferOffset, buffer.data(), readSize);
|
||||
MD5_Update(&ctx, buffer.data(), readSize);
|
||||
}
|
||||
@@ -129,7 +131,7 @@ namespace hex {
|
||||
SHA1_Init(&ctx);
|
||||
std::array<u8, 512> buffer = { 0 };
|
||||
for (u64 bufferOffset = 0; bufferOffset < size; bufferOffset += buffer.size()) {
|
||||
const u64 readSize = std::min(buffer.size(), size - bufferOffset);
|
||||
const u64 readSize = std::min(u64(buffer.size()), size - bufferOffset);
|
||||
data->read(offset + bufferOffset, buffer.data(), readSize);
|
||||
SHA1_Update(&ctx, buffer.data(), readSize);
|
||||
}
|
||||
@@ -148,7 +150,7 @@ namespace hex {
|
||||
|
||||
std::array<u8, 512> buffer = { 0 };
|
||||
for (u64 bufferOffset = 0; bufferOffset < size; bufferOffset += buffer.size()) {
|
||||
const u64 readSize = std::min(buffer.size(), size - bufferOffset);
|
||||
const u64 readSize = std::min(u64(buffer.size()), size - bufferOffset);
|
||||
data->read(offset + bufferOffset, buffer.data(), readSize);
|
||||
SHA224_Update(&ctx, buffer.data(), readSize);
|
||||
}
|
||||
@@ -167,7 +169,7 @@ namespace hex {
|
||||
|
||||
std::array<u8, 512> buffer = { 0 };
|
||||
for (u64 bufferOffset = 0; bufferOffset < size; bufferOffset += buffer.size()) {
|
||||
const u64 readSize = std::min(buffer.size(), size - bufferOffset);
|
||||
const u64 readSize = std::min(u64(buffer.size()), size - bufferOffset);
|
||||
data->read(offset + bufferOffset, buffer.data(), readSize);
|
||||
SHA256_Update(&ctx, buffer.data(), readSize);
|
||||
}
|
||||
@@ -186,7 +188,7 @@ namespace hex {
|
||||
|
||||
std::array<u8, 512> buffer = { 0 };
|
||||
for (u64 bufferOffset = 0; bufferOffset < size; bufferOffset += buffer.size()) {
|
||||
const u64 readSize = std::min(buffer.size(), size - bufferOffset);
|
||||
const u64 readSize = std::min(u64(buffer.size()), size - bufferOffset);
|
||||
data->read(offset + bufferOffset, buffer.data(), readSize);
|
||||
SHA384_Update(&ctx, buffer.data(), readSize);
|
||||
}
|
||||
@@ -205,7 +207,7 @@ namespace hex {
|
||||
|
||||
std::array<u8, 512> buffer = { 0 };
|
||||
for (u64 bufferOffset = 0; bufferOffset < size; bufferOffset += buffer.size()) {
|
||||
const u64 readSize = std::min(buffer.size(), size - bufferOffset);
|
||||
const u64 readSize = std::min(u64(buffer.size()), size - bufferOffset);
|
||||
data->read(offset + bufferOffset, buffer.data(), readSize);
|
||||
SHA512_Update(&ctx, buffer.data(), readSize);
|
||||
}
|
||||
@@ -215,4 +217,24 @@ namespace hex {
|
||||
return result;
|
||||
}
|
||||
|
||||
std::vector<u8> decode64(const std::vector<u8> &input) {
|
||||
size_t outputSize = (3 * input.size()) / 4;
|
||||
std::vector<u8> output(outputSize + 1, 0x00);
|
||||
|
||||
if (EVP_DecodeBlock(output.data(), reinterpret_cast<const unsigned char *>(input.data()), input.size()) != outputSize)
|
||||
return { };
|
||||
|
||||
return output;
|
||||
}
|
||||
|
||||
std::vector<u8> encode64(const std::vector<u8> &input) {
|
||||
size_t outputSize = 4 * ((input.size() + 2) / 3);
|
||||
std::vector<u8> output(outputSize + 1, 0x00);
|
||||
|
||||
if (EVP_EncodeBlock(output.data(), reinterpret_cast<const unsigned char *>(input.data()), input.size()) != outputSize)
|
||||
return { };
|
||||
|
||||
return output;
|
||||
}
|
||||
|
||||
}
|
||||
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;
|
||||
}
|
||||
|
||||
}
|
||||
427
source/lang/evaluator.cpp
Normal file
427
source/lang/evaluator.cpp
Normal file
@@ -0,0 +1,427 @@
|
||||
#include "lang/evaluator.hpp"
|
||||
|
||||
#include <bit>
|
||||
#include <unordered_map>
|
||||
|
||||
namespace hex::lang {
|
||||
|
||||
Evaluator::Evaluator(prv::Provider* &provider, std::endian defaultDataEndianess) : m_provider(provider), m_defaultDataEndianess(defaultDataEndianess) {
|
||||
|
||||
}
|
||||
|
||||
std::pair<PatternData*, size_t> Evaluator::createStructPattern(ASTNodeVariableDecl *varDeclNode, u64 offset) {
|
||||
std::vector<PatternData*> members;
|
||||
|
||||
auto structNode = static_cast<ASTNodeStruct*>(this->m_types[varDeclNode->getCustomVariableTypeName()]);
|
||||
|
||||
if (structNode == nullptr) {
|
||||
this->m_error = { varDeclNode->getLineNumber(), hex::format("'%s' does not name a type", varDeclNode->getCustomVariableTypeName().c_str()) };
|
||||
return { nullptr, 0 };
|
||||
}
|
||||
|
||||
size_t structSize = 0;
|
||||
for (const auto &node : structNode->getNodes()) {
|
||||
const auto &member = static_cast<ASTNodeVariableDecl*>(node);
|
||||
|
||||
u64 memberOffset = 0;
|
||||
|
||||
if (member->getPointerSize().has_value()) {
|
||||
this->m_provider->read(offset + structSize, &memberOffset, member->getPointerSize().value());
|
||||
|
||||
memberOffset = hex::changeEndianess(memberOffset, member->getPointerSize().value(), member->getEndianess().value_or(varDeclNode->getEndianess().value_or(this->m_defaultDataEndianess)));
|
||||
}
|
||||
else
|
||||
memberOffset = offset + structSize;
|
||||
|
||||
const auto typeDeclNode = static_cast<ASTNodeTypeDecl*>(this->m_types[member->getCustomVariableTypeName()]);
|
||||
|
||||
PatternData *pattern = nullptr;
|
||||
u64 memberSize = 0;
|
||||
|
||||
if (member->getVariableType() == Token::TypeToken::Type::Signed8Bit && member->getArraySize() > 1) {
|
||||
std::tie(pattern, memberSize) = this->createStringPattern(member, memberOffset);
|
||||
} else if (member->getVariableType() == Token::TypeToken::Type::CustomType
|
||||
&& typeDeclNode != nullptr && typeDeclNode->getAssignedType() == Token::TypeToken::Type::Signed8Bit
|
||||
&& member->getArraySize() > 1) {
|
||||
|
||||
std::tie(pattern, memberSize) = this->createStringPattern(member, memberOffset);
|
||||
}
|
||||
else if (member->getArraySize() > 1) {
|
||||
std::tie(pattern, memberSize) = this->createArrayPattern(member, memberOffset);
|
||||
}
|
||||
else if (member->getArraySizeVariable().has_value()) {
|
||||
std::optional<size_t> arraySize;
|
||||
|
||||
|
||||
for (auto &prevMember : members) {
|
||||
if (prevMember->getPatternType() == PatternData::Type::Unsigned && prevMember->getName() == member->getArraySizeVariable()) {
|
||||
u64 value = 0;
|
||||
this->m_provider->read(prevMember->getOffset(), &value, prevMember->getSize());
|
||||
|
||||
value = hex::changeEndianess(value, prevMember->getSize(), prevMember->getEndianess());
|
||||
|
||||
arraySize = value;
|
||||
}
|
||||
}
|
||||
|
||||
if (!arraySize.has_value()) {
|
||||
this->m_error = { varDeclNode->getLineNumber(), hex::format("'%s' does not name a previous member of '%s'", member->getArraySizeVariable().value().c_str(), varDeclNode->getCustomVariableTypeName().c_str()) };
|
||||
return { nullptr, 0 };
|
||||
}
|
||||
|
||||
ASTNodeVariableDecl *processedMember = new ASTNodeVariableDecl(member->getLineNumber(), member->getVariableType(), member->getVariableName(), member->getCustomVariableTypeName(), member->getOffset(), arraySize.value());
|
||||
|
||||
std::tie(pattern, memberSize) = this->createArrayPattern(processedMember, memberOffset);
|
||||
}
|
||||
else if (member->getVariableType() != Token::TypeToken::Type::CustomType) {
|
||||
std::tie(pattern, memberSize) = this->createBuiltInTypePattern(member, memberOffset);
|
||||
}
|
||||
else {
|
||||
std::tie(pattern, memberSize) = this->createCustomTypePattern(member, memberOffset);
|
||||
}
|
||||
|
||||
if (pattern == nullptr)
|
||||
return { nullptr, 0 };
|
||||
|
||||
pattern->setEndianess(member->getEndianess().value_or(varDeclNode->getEndianess().value_or(this->m_defaultDataEndianess)));
|
||||
|
||||
if (member->getPointerSize().has_value()) {
|
||||
members.push_back(new PatternDataPointer(offset + structSize, member->getPointerSize().value(), member->getVariableName(), pattern, member->getEndianess().value_or(varDeclNode->getEndianess().value_or(this->m_defaultDataEndianess))));
|
||||
structSize += member->getPointerSize().value();
|
||||
}
|
||||
else {
|
||||
members.push_back(pattern);
|
||||
structSize += memberSize;
|
||||
}
|
||||
}
|
||||
|
||||
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::vector<PatternData*> members;
|
||||
|
||||
auto unionNode = static_cast<ASTNodeUnion*>(this->m_types[varDeclNode->getCustomVariableTypeName()]);
|
||||
|
||||
if (unionNode == nullptr) {
|
||||
this->m_error = { varDeclNode->getLineNumber(), hex::format("'%s' does not name a type", varDeclNode->getCustomVariableTypeName().c_str()) };
|
||||
return { nullptr, 0 };
|
||||
}
|
||||
|
||||
size_t unionSize = 0;
|
||||
for (const auto &node : unionNode->getNodes()) {
|
||||
const auto &member = static_cast<ASTNodeVariableDecl*>(node);
|
||||
|
||||
u64 memberOffset = 0;
|
||||
|
||||
if (member->getPointerSize().has_value()) {
|
||||
this->m_provider->read(offset + unionSize, &memberOffset, member->getPointerSize().value());
|
||||
|
||||
memberOffset = hex::changeEndianess(memberOffset, member->getPointerSize().value(), member->getEndianess().value_or(this->m_defaultDataEndianess));
|
||||
}
|
||||
else
|
||||
memberOffset = offset;
|
||||
|
||||
const auto typeDeclNode = static_cast<ASTNodeTypeDecl*>(this->m_types[member->getCustomVariableTypeName()]);
|
||||
|
||||
PatternData *pattern = nullptr;
|
||||
u64 memberSize = 0;
|
||||
|
||||
if (member->getVariableType() == Token::TypeToken::Type::Signed8Bit && member->getArraySize() > 1) {
|
||||
std::tie(pattern, memberSize) = this->createStringPattern(member, memberOffset);
|
||||
|
||||
} else if (member->getVariableType() == Token::TypeToken::Type::CustomType
|
||||
&& typeDeclNode != nullptr && typeDeclNode->getAssignedType() == Token::TypeToken::Type::Signed8Bit
|
||||
&& member->getArraySize() > 1) {
|
||||
|
||||
std::tie(pattern, memberSize) = this->createStringPattern(member, memberOffset);
|
||||
|
||||
}
|
||||
else if (member->getArraySize() > 1) {
|
||||
std::tie(pattern, memberSize) = this->createArrayPattern(member, memberOffset);
|
||||
|
||||
}
|
||||
else if (member->getArraySizeVariable().has_value()) {
|
||||
std::optional<size_t> arraySize;
|
||||
|
||||
|
||||
for (auto &prevMember : members) {
|
||||
if (prevMember->getPatternType() == PatternData::Type::Unsigned && prevMember->getName() == member->getArraySizeVariable()) {
|
||||
u64 value = 0;
|
||||
this->m_provider->read(prevMember->getOffset(), &value, prevMember->getSize());
|
||||
|
||||
value = hex::changeEndianess(value, prevMember->getSize(), prevMember->getEndianess());
|
||||
|
||||
arraySize = value;
|
||||
}
|
||||
}
|
||||
|
||||
if (!arraySize.has_value()) {
|
||||
this->m_error = { varDeclNode->getLineNumber(), hex::format("'%s' does not name a previous member of '%s'", member->getArraySizeVariable().value().c_str(), varDeclNode->getCustomVariableTypeName().c_str()) };
|
||||
return { nullptr, 0 };
|
||||
}
|
||||
|
||||
if (arraySize.value() == 0) {
|
||||
this->m_error = { varDeclNode->getLineNumber(), hex::format("Value of '%s' is zero", member->getArraySizeVariable().value().c_str()) };
|
||||
return { nullptr, 0 };
|
||||
}
|
||||
|
||||
ASTNodeVariableDecl *processedMember = new ASTNodeVariableDecl(member->getLineNumber(), member->getVariableType(), member->getVariableName(), member->getCustomVariableTypeName(), member->getOffset(), arraySize.value());
|
||||
|
||||
std::tie(pattern, memberSize) = this->createArrayPattern(processedMember, memberOffset);
|
||||
}
|
||||
else if (member->getVariableType() != Token::TypeToken::Type::CustomType) {
|
||||
std::tie(pattern, memberSize) = this->createBuiltInTypePattern(member, memberOffset);
|
||||
}
|
||||
else {
|
||||
std::tie(pattern, memberSize) = this->createCustomTypePattern(member, memberOffset);
|
||||
}
|
||||
|
||||
if (pattern == nullptr)
|
||||
return { nullptr, 0 };
|
||||
|
||||
pattern->setEndianess(member->getEndianess().value_or(varDeclNode->getEndianess().value_or(this->m_defaultDataEndianess)));
|
||||
|
||||
if (member->getPointerSize().has_value()) {
|
||||
members.push_back(new PatternDataPointer(offset, member->getPointerSize().value(), member->getVariableName(), pattern, member->getEndianess().value_or(this->m_defaultDataEndianess)));
|
||||
unionSize = std::max(size_t(member->getPointerSize().value()), unionSize);
|
||||
}
|
||||
else {
|
||||
members.push_back(pattern);
|
||||
unionSize = std::max(memberSize, 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) {
|
||||
auto *enumType = static_cast<ASTNodeEnum*>(this->m_types[varDeclNode->getCustomVariableTypeName()]);
|
||||
|
||||
if (enumType == nullptr) {
|
||||
this->m_error = { varDeclNode->getLineNumber(), hex::format("'%s' does not name a type", varDeclNode->getCustomVariableTypeName().c_str()) };
|
||||
return { nullptr, 0 };
|
||||
}
|
||||
|
||||
size_t size = getTypeSize(enumType->getUnderlyingType());
|
||||
|
||||
return { new PatternDataEnum(offset, size, varDeclNode->getVariableName(), enumType->getName(), enumType->getValues(), varDeclNode->getEndianess().value_or(this->m_defaultDataEndianess)), size };
|
||||
}
|
||||
|
||||
std::pair<PatternData*, size_t> Evaluator::createBitfieldPattern(ASTNodeVariableDecl *varDeclNode, u64 offset) {
|
||||
|
||||
auto *bitfieldType = static_cast<ASTNodeBitField*>(this->m_types[varDeclNode->getCustomVariableTypeName()]);
|
||||
|
||||
if (bitfieldType == nullptr) {
|
||||
this->m_error = { varDeclNode->getLineNumber(), hex::format("'%s' does not name a type", varDeclNode->getCustomVariableTypeName().c_str()) };
|
||||
return { nullptr, 0 };
|
||||
}
|
||||
|
||||
size_t size = 0;
|
||||
for (auto &[fieldName, fieldSize] : bitfieldType->getFields())
|
||||
size += fieldSize;
|
||||
|
||||
size = std::bit_ceil(size) / 8;
|
||||
|
||||
return { new PatternDataBitfield(offset, size, varDeclNode->getVariableName(), bitfieldType->getName(), bitfieldType->getFields(), varDeclNode->getEndianess().value_or(this->m_defaultDataEndianess)), size };
|
||||
}
|
||||
|
||||
std::pair<PatternData*, size_t> Evaluator::createArrayPattern(ASTNodeVariableDecl *varDeclNode, u64 offset) {
|
||||
std::vector<PatternData*> entries;
|
||||
|
||||
size_t arrayOffset = 0;
|
||||
std::optional<u32> arrayColor;
|
||||
for (u32 i = 0; i < varDeclNode->getArraySize(); i++) {
|
||||
ASTNodeVariableDecl *nonArrayVarDeclNode = new ASTNodeVariableDecl(varDeclNode->getLineNumber(), varDeclNode->getVariableType(), "[" + std::to_string(i) + "]", varDeclNode->getCustomVariableTypeName(), varDeclNode->getOffset(), 1);
|
||||
|
||||
|
||||
if (varDeclNode->getVariableType() == Token::TypeToken::Type::Padding) {
|
||||
return { new PatternDataPadding(offset, varDeclNode->getArraySize()), varDeclNode->getArraySize() };
|
||||
} else if (varDeclNode->getVariableType() != Token::TypeToken::Type::CustomType) {
|
||||
const auto& [pattern, size] = this->createBuiltInTypePattern(nonArrayVarDeclNode, offset + arrayOffset);
|
||||
|
||||
if (pattern == nullptr) {
|
||||
delete nonArrayVarDeclNode;
|
||||
return { nullptr, 0 };
|
||||
}
|
||||
|
||||
pattern->setEndianess(varDeclNode->getEndianess().value_or(varDeclNode->getEndianess().value_or(this->m_defaultDataEndianess)));
|
||||
|
||||
if (!arrayColor.has_value())
|
||||
arrayColor = pattern->getColor();
|
||||
|
||||
pattern->setColor(arrayColor.value());
|
||||
|
||||
entries.push_back(pattern);
|
||||
arrayOffset += size;
|
||||
} else {
|
||||
const auto &[pattern, size] = this->createCustomTypePattern(nonArrayVarDeclNode, offset + arrayOffset);
|
||||
|
||||
if (pattern == nullptr) {
|
||||
delete nonArrayVarDeclNode;
|
||||
return { nullptr, 0 };
|
||||
}
|
||||
|
||||
pattern->setEndianess(varDeclNode->getEndianess().value_or(varDeclNode->getEndianess().value_or(this->m_defaultDataEndianess)));
|
||||
|
||||
if (!arrayColor.has_value())
|
||||
arrayColor = pattern->getColor();
|
||||
|
||||
pattern->setColor(arrayColor.value());
|
||||
|
||||
entries.push_back(pattern);
|
||||
arrayOffset += size;
|
||||
}
|
||||
|
||||
delete nonArrayVarDeclNode;
|
||||
}
|
||||
|
||||
return { new PatternDataArray(offset, arrayOffset, varDeclNode->getVariableName(), varDeclNode->getEndianess().value_or(this->m_defaultDataEndianess), entries, arrayColor.value()), arrayOffset };
|
||||
}
|
||||
|
||||
std::pair<PatternData*, size_t> Evaluator::createStringPattern(ASTNodeVariableDecl *varDeclNode, u64 offset) {
|
||||
size_t arraySize = varDeclNode->getArraySize();
|
||||
|
||||
return { new PatternDataString(offset, arraySize, varDeclNode->getVariableName(), varDeclNode->getEndianess().value_or(this->m_defaultDataEndianess)), arraySize };
|
||||
}
|
||||
|
||||
std::pair<PatternData*, size_t> Evaluator::createCustomTypePattern(ASTNodeVariableDecl *varDeclNode, u64 offset) {
|
||||
auto &currType = this->m_types[varDeclNode->getCustomVariableTypeName()];
|
||||
|
||||
if (currType == nullptr) {
|
||||
this->m_error = { varDeclNode->getLineNumber(), hex::format("'%s' does not name a type", varDeclNode->getCustomVariableTypeName().c_str()) };
|
||||
return { nullptr, 0 };
|
||||
}
|
||||
|
||||
switch (currType->getType()) {
|
||||
case ASTNode::Type::Struct:
|
||||
return this->createStructPattern(varDeclNode, offset);
|
||||
case ASTNode::Type::Union:
|
||||
return this->createUnionPattern(varDeclNode, offset);
|
||||
case ASTNode::Type::Enum:
|
||||
return this->createEnumPattern(varDeclNode, offset);
|
||||
case ASTNode::Type::Bitfield:
|
||||
return this->createBitfieldPattern(varDeclNode, offset);
|
||||
case ASTNode::Type::TypeDecl:
|
||||
return this->createBuiltInTypePattern(varDeclNode, offset);
|
||||
}
|
||||
|
||||
return { nullptr, 0 };
|
||||
}
|
||||
|
||||
std::pair<PatternData*, size_t> Evaluator::createBuiltInTypePattern(ASTNodeVariableDecl *varDeclNode, u64 offset) {
|
||||
auto type = varDeclNode->getVariableType();
|
||||
if (type == Token::TypeToken::Type::CustomType) {
|
||||
const auto &currType = static_cast<ASTNodeTypeDecl*>(this->m_types[varDeclNode->getCustomVariableTypeName()]);
|
||||
if (currType == nullptr) {
|
||||
this->m_error = { varDeclNode->getLineNumber(), hex::format("'%s' does not name a type", varDeclNode->getCustomVariableTypeName().c_str()) };
|
||||
return { nullptr, 0 };
|
||||
}
|
||||
|
||||
type = currType->getAssignedType();
|
||||
}
|
||||
|
||||
size_t typeSize = getTypeSize(type);
|
||||
size_t arraySize = varDeclNode->getArraySize();
|
||||
|
||||
if (isSigned(type)) {
|
||||
if (typeSize == 1 && arraySize == 1)
|
||||
return { new PatternDataCharacter(offset, typeSize, varDeclNode->getVariableName(), varDeclNode->getEndianess().value_or(this->m_defaultDataEndianess)), 1 };
|
||||
else if (arraySize > 1)
|
||||
return createArrayPattern(varDeclNode, offset);
|
||||
else
|
||||
return { new PatternDataSigned(offset, typeSize, varDeclNode->getVariableName(), varDeclNode->getEndianess().value_or(this->m_defaultDataEndianess)), typeSize * arraySize };
|
||||
} else if (isUnsigned(varDeclNode->getVariableType())) {
|
||||
if (arraySize > 1)
|
||||
return createArrayPattern(varDeclNode, offset);
|
||||
else
|
||||
return { new PatternDataUnsigned(offset, typeSize, varDeclNode->getVariableName(), varDeclNode->getEndianess().value_or(this->m_defaultDataEndianess)), typeSize * arraySize };
|
||||
} else if (isFloatingPoint(varDeclNode->getVariableType())) {
|
||||
if (arraySize > 1)
|
||||
return createArrayPattern(varDeclNode, offset);
|
||||
else
|
||||
return { new PatternDataFloat(offset, typeSize, varDeclNode->getVariableName(), varDeclNode->getEndianess().value_or(this->m_defaultDataEndianess)), typeSize * arraySize };
|
||||
}
|
||||
|
||||
return { nullptr, 0 };
|
||||
}
|
||||
|
||||
std::pair<Result, std::vector<PatternData*>> Evaluator::evaluate(const std::vector<ASTNode *> &ast) {
|
||||
|
||||
// Evaluate types
|
||||
for (const auto &node : ast) {
|
||||
|
||||
switch(node->getType()) {
|
||||
case ASTNode::Type::Struct:
|
||||
{
|
||||
auto *structNode = static_cast<ASTNodeStruct*>(node);
|
||||
this->m_types.emplace(structNode->getName(), structNode);
|
||||
}
|
||||
break;
|
||||
case ASTNode::Type::Union:
|
||||
{
|
||||
auto *unionNode = static_cast<ASTNodeUnion*>(node);
|
||||
this->m_types.emplace(unionNode->getName(), unionNode);
|
||||
}
|
||||
break;
|
||||
case ASTNode::Type::Enum:
|
||||
{
|
||||
auto *enumNode = static_cast<ASTNodeEnum*>(node);
|
||||
this->m_types.emplace(enumNode->getName(), enumNode);
|
||||
}
|
||||
break;
|
||||
case ASTNode::Type::Bitfield:
|
||||
{
|
||||
auto *bitfieldNode = static_cast<ASTNodeBitField*>(node);
|
||||
this->m_types.emplace(bitfieldNode->getName(), bitfieldNode);
|
||||
}
|
||||
break;
|
||||
case ASTNode::Type::TypeDecl:
|
||||
{
|
||||
auto *typeDeclNode = static_cast<ASTNodeTypeDecl*>(node);
|
||||
|
||||
if (typeDeclNode->getAssignedType() == Token::TypeToken::Type::CustomType)
|
||||
this->m_types.emplace(typeDeclNode->getTypeName(), this->m_types[typeDeclNode->getAssignedCustomTypeName()]);
|
||||
else
|
||||
this->m_types.emplace(typeDeclNode->getTypeName(), typeDeclNode);
|
||||
}
|
||||
break;
|
||||
case ASTNode::Type::VariableDecl: break;
|
||||
case ASTNode::Type::Scope: break;
|
||||
}
|
||||
}
|
||||
|
||||
// Evaluate variable declarations
|
||||
|
||||
std::vector<PatternData*> variables;
|
||||
for (const auto &node : ast) {
|
||||
if (node->getType() != ASTNode::Type::VariableDecl)
|
||||
continue;
|
||||
|
||||
auto *varDeclNode = static_cast<ASTNodeVariableDecl*>(node);
|
||||
|
||||
if (varDeclNode->getVariableType() == Token::TypeToken::Type::Signed8Bit && varDeclNode->getArraySize() > 1) {
|
||||
const auto &[pattern, _] = createStringPattern(varDeclNode, varDeclNode->getOffset().value());
|
||||
variables.push_back(pattern);
|
||||
}
|
||||
else if (varDeclNode->getArraySize() > 1) {
|
||||
const auto &[pattern, _] = this->createArrayPattern(varDeclNode, varDeclNode->getOffset().value());
|
||||
variables.push_back(pattern);
|
||||
|
||||
} else if (varDeclNode->getVariableType() != Token::TypeToken::Type::CustomType) {
|
||||
const auto &[pattern, _] = this->createBuiltInTypePattern(varDeclNode, varDeclNode->getOffset().value());
|
||||
variables.push_back(pattern);
|
||||
} else {
|
||||
const auto &[pattern, _] = this->createCustomTypePattern(varDeclNode, varDeclNode->getOffset().value());
|
||||
variables.push_back(pattern);
|
||||
}
|
||||
}
|
||||
|
||||
for (const auto &var : variables)
|
||||
if (var == nullptr)
|
||||
return { ResultEvaluatorError, { } };
|
||||
|
||||
return { ResultSuccess, variables };
|
||||
}
|
||||
|
||||
}
|
||||
257
source/lang/lexer.cpp
Normal file
257
source/lang/lexer.cpp
Normal file
@@ -0,0 +1,257 @@
|
||||
#include "lang/lexer.hpp"
|
||||
|
||||
#include <vector>
|
||||
#include <functional>
|
||||
|
||||
namespace hex::lang {
|
||||
|
||||
Lexer::Lexer() { }
|
||||
|
||||
std::string matchTillInvalid(const char* characters, std::function<bool(char)> predicate) {
|
||||
std::string ret;
|
||||
|
||||
while (*characters != 0x00) {
|
||||
ret += *characters;
|
||||
characters++;
|
||||
|
||||
if (!predicate(*characters))
|
||||
break;
|
||||
}
|
||||
|
||||
return ret;
|
||||
}
|
||||
|
||||
std::optional<u64> parseInt(std::string_view string) {
|
||||
u64 integer = 0;
|
||||
u8 base;
|
||||
|
||||
std::string_view numberData;
|
||||
|
||||
if (string.starts_with("0x")) {
|
||||
numberData = string.substr(2);
|
||||
base = 16;
|
||||
|
||||
if (numberData.find_first_not_of("0123456789ABCDEFabcdef") != std::string_view::npos)
|
||||
return { };
|
||||
} else if (string.starts_with("0b")) {
|
||||
numberData = string.substr(2);
|
||||
base = 2;
|
||||
|
||||
if (numberData.find_first_not_of("01") != std::string_view::npos)
|
||||
return { };
|
||||
} else if (isdigit(string[0])) {
|
||||
numberData = string;
|
||||
base = 10;
|
||||
|
||||
if (numberData.find_first_not_of("0123456789") != std::string_view::npos)
|
||||
return { };
|
||||
} else return { };
|
||||
|
||||
if (numberData.length() == 0)
|
||||
return { };
|
||||
|
||||
for (const char& c : numberData) {
|
||||
integer *= base;
|
||||
|
||||
if (isdigit(c))
|
||||
integer += (c - '0');
|
||||
else if (c >= 'A' && c <= 'F')
|
||||
integer += 10 + (c - 'A');
|
||||
else if (c >= 'a' && c <= 'f')
|
||||
integer += 10 + (c - 'a');
|
||||
else return { };
|
||||
}
|
||||
|
||||
return integer;
|
||||
}
|
||||
|
||||
std::pair<Result, std::vector<Token>> Lexer::lex(const std::string& code) {
|
||||
std::vector<Token> tokens;
|
||||
u32 offset = 0;
|
||||
|
||||
u32 lineNumber = 1;
|
||||
|
||||
while (offset < code.length()) {
|
||||
|
||||
// Handle comments
|
||||
if (code[offset] == '/') {
|
||||
offset++;
|
||||
|
||||
if (offset < code.length() && code[offset] == '/') {
|
||||
offset++;
|
||||
while (offset < code.length()) {
|
||||
if (code[offset] == '\n' || code[offset] == '\r')
|
||||
break;
|
||||
offset++;
|
||||
}
|
||||
} else if (offset < code.length() && code[offset] == '*') {
|
||||
offset++;
|
||||
while (offset < (code.length() - 1)) {
|
||||
if (code[offset] == '\n') lineNumber++;
|
||||
|
||||
if (code[offset] == '*' && code[offset + 1] == '/')
|
||||
break;
|
||||
offset++;
|
||||
}
|
||||
|
||||
offset += 2;
|
||||
} else offset--;
|
||||
}
|
||||
|
||||
const char& c = code[offset];
|
||||
|
||||
if (c == 0x00)
|
||||
break;
|
||||
|
||||
if (std::isblank(c) || std::isspace(c)) {
|
||||
if (code[offset] == '\n') lineNumber++;
|
||||
offset += 1;
|
||||
} else if (c == ';') {
|
||||
tokens.push_back({ .type = Token::Type::EndOfExpression, .lineNumber = lineNumber });
|
||||
offset += 1;
|
||||
} else if (c == '{') {
|
||||
tokens.push_back({ .type = Token::Type::ScopeOpen, .lineNumber = lineNumber });
|
||||
offset += 1;
|
||||
} else if (c == '}') {
|
||||
tokens.push_back({ .type = Token::Type::ScopeClose, .lineNumber = lineNumber });
|
||||
offset += 1;
|
||||
} else if (c == '[') {
|
||||
tokens.push_back({ .type = Token::Type::ArrayOpen, .lineNumber = lineNumber });
|
||||
offset += 1;
|
||||
} else if (c == ']') {
|
||||
tokens.push_back({.type = Token::Type::ArrayClose, .lineNumber = lineNumber });
|
||||
offset += 1;
|
||||
} else if (c == ',') {
|
||||
tokens.push_back({ .type = Token::Type::Separator, .lineNumber = lineNumber });
|
||||
offset += 1;
|
||||
} else if (c == '@') {
|
||||
tokens.push_back({ .type = Token::Type::Operator, .operatorToken = { .op = Token::OperatorToken::Operator::AtDeclaration }, .lineNumber = lineNumber });
|
||||
offset += 1;
|
||||
} else if (c == '=') {
|
||||
tokens.push_back({ .type = Token::Type::Operator, .operatorToken = { .op = Token::OperatorToken::Operator::Assignment }, .lineNumber = lineNumber });
|
||||
offset += 1;
|
||||
} else if (c == ':') {
|
||||
tokens.push_back({ .type = Token::Type::Operator, .operatorToken = { .op = Token::OperatorToken::Operator::Inherit }, .lineNumber = lineNumber });
|
||||
offset += 1;
|
||||
} else if (c == '*') {
|
||||
tokens.push_back({ .type = Token::Type::Operator, .operatorToken = { .op = Token::OperatorToken::Operator::Star }, .lineNumber = lineNumber });
|
||||
offset += 1;
|
||||
} else if (c == '\'') {
|
||||
offset += 1;
|
||||
|
||||
if (offset >= code.length()) {
|
||||
this->m_error = { lineNumber, "Invalid character literal" };
|
||||
return { ResultLexicalError, { } };
|
||||
}
|
||||
|
||||
char character = code[offset];
|
||||
|
||||
if (character == '\\') {
|
||||
offset += 1;
|
||||
|
||||
if (offset >= code.length()) {
|
||||
this->m_error = { lineNumber, "Invalid character literal" };
|
||||
return { ResultLexicalError, { } };
|
||||
}
|
||||
|
||||
if (code[offset] != '\\' && code[offset] != '\'') {
|
||||
this->m_error = { lineNumber, "Invalid escape sequence" };
|
||||
return { ResultLexicalError, { } };
|
||||
}
|
||||
|
||||
character = code[offset];
|
||||
} else {
|
||||
if (code[offset] == '\\' || code[offset] == '\'' || character == '\n' || character == '\r') {
|
||||
this->m_error = { lineNumber, "Invalid character literal" };
|
||||
return { ResultLexicalError, { } };
|
||||
}
|
||||
}
|
||||
|
||||
offset += 1;
|
||||
|
||||
if (offset >= code.length() || code[offset] != '\'') {
|
||||
this->m_error = { lineNumber, "Missing terminating ' after character literal" };
|
||||
return { ResultLexicalError, { } };
|
||||
}
|
||||
|
||||
tokens.push_back({ .type = Token::Type::Integer, .integerToken = { .integer = character }, .lineNumber = lineNumber });
|
||||
offset += 1;
|
||||
|
||||
} else if (std::isalpha(c)) {
|
||||
std::string identifier = matchTillInvalid(&code[offset], [](char c) -> bool { return std::isalnum(c) || c == '_'; });
|
||||
|
||||
// Check for reserved keywords
|
||||
|
||||
if (identifier == "struct")
|
||||
tokens.push_back({ .type = Token::Type::Keyword, .keywordToken = { .keyword = Token::KeywordToken::Keyword::Struct }, .lineNumber = lineNumber });
|
||||
else if (identifier == "union")
|
||||
tokens.push_back({ .type = Token::Type::Keyword, .keywordToken = { .keyword = Token::KeywordToken::Keyword::Union }, .lineNumber = lineNumber });
|
||||
else if (identifier == "using")
|
||||
tokens.push_back({ .type = Token::Type::Keyword, .keywordToken = { .keyword = Token::KeywordToken::Keyword::Using }, .lineNumber = lineNumber });
|
||||
else if (identifier == "enum")
|
||||
tokens.push_back({ .type = Token::Type::Keyword, .keywordToken = { .keyword = Token::KeywordToken::Keyword::Enum }, .lineNumber = lineNumber });
|
||||
else if (identifier == "bitfield")
|
||||
tokens.push_back({ .type = Token::Type::Keyword, .keywordToken = { .keyword = Token::KeywordToken::Keyword::Bitfield }, .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
|
||||
else if (identifier == "u8")
|
||||
tokens.push_back({ .type = Token::Type::Type, .typeToken = { .type = Token::TypeToken::Type::Unsigned8Bit }, .lineNumber = lineNumber });
|
||||
else if (identifier == "s8")
|
||||
tokens.push_back({ .type = Token::Type::Type, .typeToken = { .type = Token::TypeToken::Type::Signed8Bit }, .lineNumber = lineNumber });
|
||||
else if (identifier == "u16")
|
||||
tokens.push_back({ .type = Token::Type::Type, .typeToken = { .type = Token::TypeToken::Type::Unsigned16Bit }, .lineNumber = lineNumber });
|
||||
else if (identifier == "s16")
|
||||
tokens.push_back({ .type = Token::Type::Type, .typeToken = { .type = Token::TypeToken::Type::Signed16Bit }, .lineNumber = lineNumber });
|
||||
else if (identifier == "u32")
|
||||
tokens.push_back({ .type = Token::Type::Type, .typeToken = { .type = Token::TypeToken::Type::Unsigned32Bit }, .lineNumber = lineNumber });
|
||||
else if (identifier == "s32")
|
||||
tokens.push_back({ .type = Token::Type::Type, .typeToken = { .type = Token::TypeToken::Type::Signed32Bit }, .lineNumber = lineNumber });
|
||||
else if (identifier == "u64")
|
||||
tokens.push_back({ .type = Token::Type::Type, .typeToken = { .type = Token::TypeToken::Type::Unsigned64Bit }, .lineNumber = lineNumber });
|
||||
else if (identifier == "s64")
|
||||
tokens.push_back({ .type = Token::Type::Type, .typeToken = { .type = Token::TypeToken::Type::Signed64Bit }, .lineNumber = lineNumber });
|
||||
else if (identifier == "u128")
|
||||
tokens.push_back({ .type = Token::Type::Type, .typeToken = { .type = Token::TypeToken::Type::Unsigned128Bit }, .lineNumber = lineNumber });
|
||||
else if (identifier == "s128")
|
||||
tokens.push_back({ .type = Token::Type::Type, .typeToken = { .type = Token::TypeToken::Type::Signed128Bit }, .lineNumber = lineNumber });
|
||||
else if (identifier == "float")
|
||||
tokens.push_back({ .type = Token::Type::Type, .typeToken = { .type = Token::TypeToken::Type::Float }, .lineNumber = lineNumber });
|
||||
else if (identifier == "double")
|
||||
tokens.push_back({ .type = Token::Type::Type, .typeToken = { .type = Token::TypeToken::Type::Double }, .lineNumber = lineNumber });
|
||||
else if (identifier == "padding")
|
||||
tokens.push_back({ .type = Token::Type::Type, .typeToken = { .type = Token::TypeToken::Type::Padding }, .lineNumber = lineNumber });
|
||||
|
||||
// If it's not a keyword and a builtin type, it has to be an identifier
|
||||
|
||||
else
|
||||
tokens.push_back({.type = Token::Type::Identifier, .identifierToken = { .identifier = identifier }, .lineNumber = lineNumber });
|
||||
|
||||
offset += identifier.length();
|
||||
} else if (std::isdigit(c)) {
|
||||
char *end = nullptr;
|
||||
std::strtoull(&code[offset], &end, 0);
|
||||
|
||||
auto integer = parseInt(std::string_view(&code[offset], end - &code[offset]));
|
||||
|
||||
if (!integer.has_value()) {
|
||||
this->m_error = { lineNumber, "Invalid integer literal" };
|
||||
return { ResultLexicalError, {}};
|
||||
}
|
||||
|
||||
tokens.push_back({ .type = Token::Type::Integer, .integerToken = { .integer = integer.value() }, .lineNumber = lineNumber });
|
||||
offset += (end - &code[offset]);
|
||||
} else {
|
||||
this->m_error = { lineNumber, "Unknown token" };
|
||||
return { ResultLexicalError, {} };
|
||||
}
|
||||
}
|
||||
|
||||
tokens.push_back({ .type = Token::Type::EndOfProgram, .lineNumber = lineNumber });
|
||||
|
||||
return { ResultSuccess, tokens };
|
||||
}
|
||||
}
|
||||
649
source/lang/parser.cpp
Normal file
649
source/lang/parser.cpp
Normal file
@@ -0,0 +1,649 @@
|
||||
#include "lang/parser.hpp"
|
||||
|
||||
#include "helpers/utils.hpp"
|
||||
|
||||
#include <optional>
|
||||
|
||||
namespace hex::lang {
|
||||
|
||||
Parser::Parser() {
|
||||
|
||||
}
|
||||
|
||||
bool Parser::tryConsume(TokenIter &curr, std::initializer_list<Token::Type> tokenTypes) {
|
||||
std::vector<Token>::const_iterator originalPosition = curr;
|
||||
|
||||
for (const auto& type : tokenTypes) {
|
||||
if (curr->type != type) {
|
||||
curr = originalPosition;
|
||||
return false;
|
||||
}
|
||||
curr++;
|
||||
}
|
||||
|
||||
return true;
|
||||
}
|
||||
|
||||
|
||||
ASTNode* Parser::parseBuiltinVariableDecl(TokenIter &curr, bool hasEndianDef) {
|
||||
if (hasEndianDef) {
|
||||
std::endian endianess;
|
||||
|
||||
if (curr[-4].keywordToken.keyword == Token::KeywordToken::Keyword::LittleEndian)
|
||||
endianess = std::endian::little;
|
||||
else if (curr[-4].keywordToken.keyword == Token::KeywordToken::Keyword::BigEndian)
|
||||
endianess = std::endian::big;
|
||||
else {
|
||||
this->m_error = { curr->lineNumber, "Expected be or le identifier" };
|
||||
return nullptr;
|
||||
}
|
||||
|
||||
return new ASTNodeVariableDecl(curr[-4].lineNumber, curr[-3].typeToken.type, curr[-2].identifierToken.identifier, "", {}, 1, {}, {}, endianess);
|
||||
}
|
||||
else
|
||||
return new ASTNodeVariableDecl(curr[-3].lineNumber, curr[-3].typeToken.type, curr[-2].identifierToken.identifier);
|
||||
}
|
||||
|
||||
ASTNode* Parser::parseCustomTypeVariableDecl(TokenIter &curr, bool hasEndianDef) {
|
||||
if (hasEndianDef) {
|
||||
std::endian endianess;
|
||||
|
||||
if (curr[-4].keywordToken.keyword == Token::KeywordToken::Keyword::LittleEndian)
|
||||
endianess = std::endian::little;
|
||||
else if (curr[-4].keywordToken.keyword == Token::KeywordToken::Keyword::BigEndian)
|
||||
endianess = std::endian::big;
|
||||
else return nullptr;
|
||||
|
||||
return new ASTNodeVariableDecl(curr[-4].lineNumber, Token::TypeToken::Type::CustomType, curr[-2].identifierToken.identifier, curr[-3].identifierToken.identifier, {}, 1, {}, {}, endianess);
|
||||
}
|
||||
else
|
||||
return new ASTNodeVariableDecl(curr[-3].lineNumber, Token::TypeToken::Type::CustomType, curr[-2].identifierToken.identifier, curr[-3].identifierToken.identifier);
|
||||
}
|
||||
|
||||
ASTNode* Parser::parseBuiltinPointerVariableDecl(TokenIter &curr, bool hasEndianDef) {
|
||||
auto pointerType = curr[-2].typeToken.type;
|
||||
|
||||
if (!isUnsigned(pointerType)) {
|
||||
this->m_error = { curr->lineNumber, "Pointer size needs to be a unsigned type" };
|
||||
return nullptr;
|
||||
}
|
||||
|
||||
if (curr[-5].operatorToken.op != Token::OperatorToken::Operator::Star) {
|
||||
this->m_error = { curr->lineNumber, "Expected '*' for pointer definition" };
|
||||
return nullptr;
|
||||
}
|
||||
|
||||
if (curr[-3].operatorToken.op != Token::OperatorToken::Operator::Inherit) {
|
||||
this->m_error = { curr->lineNumber, "Expected ':' after member name" };
|
||||
return nullptr;
|
||||
}
|
||||
|
||||
|
||||
if (hasEndianDef) {
|
||||
std::endian endianess;
|
||||
|
||||
if (curr[-7].keywordToken.keyword == Token::KeywordToken::Keyword::LittleEndian)
|
||||
endianess = std::endian::little;
|
||||
else if (curr[-7].keywordToken.keyword == Token::KeywordToken::Keyword::BigEndian)
|
||||
endianess = std::endian::big;
|
||||
else return nullptr;
|
||||
|
||||
return new ASTNodeVariableDecl(curr[-7].lineNumber, curr[-6].typeToken.type, curr[-4].identifierToken.identifier, "", { }, 1, { }, getTypeSize(pointerType), endianess);
|
||||
}
|
||||
else
|
||||
return new ASTNodeVariableDecl(curr[-6].lineNumber, curr[-6].typeToken.type, curr[-4].identifierToken.identifier, "", { }, 1, { }, getTypeSize(pointerType));
|
||||
}
|
||||
|
||||
ASTNode* Parser::parseCustomTypePointerVariableDecl(TokenIter &curr, bool hasEndianDef) {
|
||||
auto pointerType = curr[-2].typeToken.type;
|
||||
|
||||
if (!isUnsigned(pointerType)) {
|
||||
this->m_error = { curr->lineNumber, "Pointer size needs to be a unsigned type" };
|
||||
return nullptr;
|
||||
}
|
||||
|
||||
if (curr[-5].operatorToken.op != Token::OperatorToken::Operator::Star) {
|
||||
this->m_error = { curr->lineNumber, "Expected '*' for pointer definition" };
|
||||
return nullptr;
|
||||
}
|
||||
|
||||
if (curr[-3].operatorToken.op != Token::OperatorToken::Operator::Inherit) {
|
||||
this->m_error = { curr->lineNumber, "Expected ':' after member name" };
|
||||
return nullptr;
|
||||
}
|
||||
|
||||
if (hasEndianDef) {
|
||||
std::endian endianess;
|
||||
|
||||
if (curr[-7].keywordToken.keyword == Token::KeywordToken::Keyword::LittleEndian)
|
||||
endianess = std::endian::little;
|
||||
else if (curr[-7].keywordToken.keyword == Token::KeywordToken::Keyword::BigEndian)
|
||||
endianess = std::endian::big;
|
||||
else {
|
||||
this->m_error = { curr->lineNumber, "Expected be or le identifier" };
|
||||
return nullptr;
|
||||
}
|
||||
|
||||
return new ASTNodeVariableDecl(curr[-7].lineNumber,Token::TypeToken::Type::CustomType, curr[-4].identifierToken.identifier, curr[-6].identifierToken.identifier, { }, 1, { }, getTypeSize(pointerType), endianess);
|
||||
}
|
||||
else
|
||||
return new ASTNodeVariableDecl(curr[-6].lineNumber, Token::TypeToken::Type::CustomType, curr[-4].identifierToken.identifier, curr[-6].identifierToken.identifier, { }, 1, { }, getTypeSize(pointerType));
|
||||
}
|
||||
|
||||
ASTNode* Parser::parseBuiltinArrayDecl(TokenIter &curr, bool hasEndianDef) {
|
||||
if (hasEndianDef) {
|
||||
std::endian endianess;
|
||||
|
||||
if (curr[-7].keywordToken.keyword == Token::KeywordToken::Keyword::LittleEndian)
|
||||
endianess = std::endian::little;
|
||||
else if (curr[-7].keywordToken.keyword == Token::KeywordToken::Keyword::BigEndian)
|
||||
endianess = std::endian::big;
|
||||
else {
|
||||
this->m_error = { curr->lineNumber, "Expected be or le identifier" };
|
||||
return nullptr;
|
||||
}
|
||||
|
||||
return new ASTNodeVariableDecl(curr[-7].lineNumber, curr[-6].typeToken.type, curr[-5].identifierToken.identifier, "", { }, curr[-3].integerToken.integer, { }, { }, endianess);
|
||||
}
|
||||
else
|
||||
return new ASTNodeVariableDecl(curr[-6].lineNumber, curr[-6].typeToken.type, curr[-5].identifierToken.identifier, "", { }, curr[-3].integerToken.integer);
|
||||
}
|
||||
|
||||
ASTNode* Parser::parseCustomTypeArrayDecl(TokenIter &curr, bool hasEndianDef) {
|
||||
if (hasEndianDef) {
|
||||
std::endian endianess;
|
||||
|
||||
if (curr[-7].keywordToken.keyword == Token::KeywordToken::Keyword::LittleEndian)
|
||||
endianess = std::endian::little;
|
||||
else if (curr[-7].keywordToken.keyword == Token::KeywordToken::Keyword::BigEndian)
|
||||
endianess = std::endian::big;
|
||||
else {
|
||||
this->m_error = { curr->lineNumber, "Expected be or le identifier" };
|
||||
return nullptr;
|
||||
}
|
||||
|
||||
return new ASTNodeVariableDecl(curr[-7].lineNumber, Token::TypeToken::Type::CustomType, curr[-5].identifierToken.identifier, curr[-6].identifierToken.identifier, { }, curr[-3].integerToken.integer, { }, { }, endianess);
|
||||
}
|
||||
else
|
||||
return new ASTNodeVariableDecl(curr[-6].lineNumber, Token::TypeToken::Type::CustomType, curr[-5].identifierToken.identifier, curr[-6].identifierToken.identifier, { }, curr[-3].integerToken.integer);
|
||||
}
|
||||
|
||||
ASTNode* Parser::parseBuiltinVariableArrayDecl(TokenIter &curr, bool hasEndianDef) {
|
||||
if (hasEndianDef) {
|
||||
std::endian endianess;
|
||||
|
||||
if (curr[-7].keywordToken.keyword == Token::KeywordToken::Keyword::LittleEndian)
|
||||
endianess = std::endian::little;
|
||||
else if (curr[-7].keywordToken.keyword == Token::KeywordToken::Keyword::BigEndian)
|
||||
endianess = std::endian::big;
|
||||
else {
|
||||
this->m_error = { curr->lineNumber, "Expected be or le identifier" };
|
||||
return nullptr;
|
||||
}
|
||||
|
||||
return new ASTNodeVariableDecl(curr[-7].lineNumber, curr[-6].typeToken.type, curr[-5].identifierToken.identifier, "", { }, 0, curr[-3].identifierToken.identifier, { }, endianess);
|
||||
}
|
||||
else
|
||||
return new ASTNodeVariableDecl(curr[-6].lineNumber, curr[-6].typeToken.type, curr[-5].identifierToken.identifier, "", { }, 0, curr[-3].identifierToken.identifier);
|
||||
}
|
||||
|
||||
ASTNode* Parser::parseCustomTypeVariableArrayDecl(TokenIter &curr, bool hasEndianDef) {
|
||||
if (hasEndianDef) {
|
||||
std::endian endianess;
|
||||
|
||||
if (curr[-7].keywordToken.keyword == Token::KeywordToken::Keyword::LittleEndian)
|
||||
endianess = std::endian::little;
|
||||
else if (curr[-7].keywordToken.keyword == Token::KeywordToken::Keyword::BigEndian)
|
||||
endianess = std::endian::big;
|
||||
else {
|
||||
this->m_error = { curr->lineNumber, "Expected be or le identifier" };
|
||||
return nullptr;
|
||||
}
|
||||
|
||||
return new ASTNodeVariableDecl(curr[-7].lineNumber, Token::TypeToken::Type::CustomType, curr[-5].identifierToken.identifier, curr[-6].identifierToken.identifier, { }, 0, curr[-3].identifierToken.identifier, { }, endianess);
|
||||
}
|
||||
else
|
||||
return new ASTNodeVariableDecl(curr[-6].lineNumber, Token::TypeToken::Type::CustomType, curr[-5].identifierToken.identifier, curr[-6].identifierToken.identifier, { }, 0, curr[-3].identifierToken.identifier);
|
||||
}
|
||||
|
||||
ASTNode* Parser::parsePaddingDecl(TokenIter &curr) {
|
||||
return new ASTNodeVariableDecl(curr[-5].lineNumber, curr[-5].typeToken.type, "", "", { }, curr[-3].integerToken.integer);
|
||||
}
|
||||
|
||||
ASTNode* Parser::parseFreeBuiltinVariableDecl(TokenIter &curr, bool hasEndianDef) {
|
||||
if (hasEndianDef) {
|
||||
std::endian endianess;
|
||||
|
||||
if (curr[-6].keywordToken.keyword == Token::KeywordToken::Keyword::LittleEndian)
|
||||
endianess = std::endian::little;
|
||||
else if (curr[-6].keywordToken.keyword == Token::KeywordToken::Keyword::BigEndian)
|
||||
endianess = std::endian::big;
|
||||
else {
|
||||
this->m_error = { curr->lineNumber, "Expected be or le identifier" };
|
||||
return nullptr;
|
||||
}
|
||||
|
||||
return new ASTNodeVariableDecl(curr[-6].lineNumber, curr[-5].typeToken.type, curr[-4].identifierToken.identifier, "", curr[-2].integerToken.integer, 1, { }, { }, endianess);
|
||||
}
|
||||
else
|
||||
return new ASTNodeVariableDecl(curr[-5].lineNumber, curr[-5].typeToken.type, curr[-4].identifierToken.identifier, "", curr[-2].integerToken.integer);
|
||||
}
|
||||
|
||||
ASTNode* Parser::parseFreeCustomTypeVariableDecl(TokenIter &curr, bool hasEndianDef) {
|
||||
if (hasEndianDef) {
|
||||
std::endian endianess;
|
||||
|
||||
if (curr[-6].keywordToken.keyword == Token::KeywordToken::Keyword::LittleEndian)
|
||||
endianess = std::endian::little;
|
||||
else if (curr[-6].keywordToken.keyword == Token::KeywordToken::Keyword::BigEndian)
|
||||
endianess = std::endian::big;
|
||||
else {
|
||||
this->m_error = { curr->lineNumber, "Expected be or le identifier" };
|
||||
return nullptr;
|
||||
}
|
||||
|
||||
return new ASTNodeVariableDecl(curr[-6].lineNumber, Token::TypeToken::Type::CustomType, curr[-4].identifierToken.identifier, curr[-5].identifierToken.identifier, curr[-2].integerToken.integer, 1, { }, { }, endianess);
|
||||
}
|
||||
else
|
||||
return new ASTNodeVariableDecl(curr[-5].lineNumber, Token::TypeToken::Type::CustomType, curr[-4].identifierToken.identifier, curr[-5].identifierToken.identifier, curr[-2].integerToken.integer);
|
||||
}
|
||||
|
||||
ASTNode* Parser::parseStruct(TokenIter &curr) {
|
||||
const std::string &structName = curr[-2].identifierToken.identifier;
|
||||
std::vector<ASTNode*> nodes;
|
||||
|
||||
u32 startLineNumber = curr[-3].lineNumber;
|
||||
|
||||
while (!tryConsume(curr, {Token::Type::ScopeClose})) {
|
||||
if (tryConsume(curr, {Token::Type::Type, Token::Type::Identifier, Token::Type::EndOfExpression}))
|
||||
nodes.push_back(parseBuiltinVariableDecl(curr, false));
|
||||
else if (tryConsume(curr, {Token::Type::Identifier, Token::Type::Identifier, Token::Type::EndOfExpression}))
|
||||
nodes.push_back(parseCustomTypeVariableDecl(curr, false));
|
||||
else if (tryConsume(curr, {Token::Type::Type, Token::Type::Identifier, Token::Type::ArrayOpen, Token::Type::Integer, Token::Type::ArrayClose, Token::Type::EndOfExpression}))
|
||||
nodes.push_back(parseBuiltinArrayDecl(curr, false));
|
||||
else if (tryConsume(curr, {Token::Type::Identifier, Token::Type::Identifier, Token::Type::ArrayOpen, Token::Type::Integer, Token::Type::ArrayClose, Token::Type::EndOfExpression}))
|
||||
nodes.push_back(parseCustomTypeArrayDecl(curr, false));
|
||||
else if (tryConsume(curr, {Token::Type::Type, Token::Type::Identifier, Token::Type::ArrayOpen, Token::Type::Identifier, Token::Type::ArrayClose, Token::Type::EndOfExpression}))
|
||||
nodes.push_back(parseBuiltinVariableArrayDecl(curr, false));
|
||||
else if (tryConsume(curr, {Token::Type::Identifier, Token::Type::Identifier, Token::Type::ArrayOpen, Token::Type::Identifier, Token::Type::ArrayClose, Token::Type::EndOfExpression}))
|
||||
nodes.push_back(parseCustomTypeVariableArrayDecl(curr, false));
|
||||
else if (tryConsume(curr, {Token::Type::Type, Token::Type::ArrayOpen, Token::Type::Integer, Token::Type::ArrayClose, Token::Type::EndOfExpression})) {
|
||||
if (curr[-5].typeToken.type != Token::TypeToken::Type::Padding) {
|
||||
for(auto &node : nodes) delete node;
|
||||
|
||||
this->m_error = { curr[-5].lineNumber, "No member name provided" };
|
||||
return nullptr;
|
||||
}
|
||||
nodes.push_back(parsePaddingDecl(curr));
|
||||
} else if (tryConsume(curr, {Token::Type::Type, Token::Type::Operator, Token::Type::Identifier, Token::Type::Operator, Token::Type::Type, Token::Type::EndOfExpression}))
|
||||
nodes.push_back(parseBuiltinPointerVariableDecl(curr, false));
|
||||
else if (tryConsume(curr, {Token::Type::Identifier, Token::Type::Operator, Token::Type::Identifier, Token::Type::Operator, Token::Type::Type, Token::Type::EndOfExpression}))
|
||||
nodes.push_back(parseCustomTypePointerVariableDecl(curr, false));
|
||||
else if (tryConsume(curr, {Token::Type::Keyword, Token::Type::Type, Token::Type::Identifier, Token::Type::EndOfExpression}))
|
||||
nodes.push_back(parseBuiltinVariableDecl(curr, true));
|
||||
else if (tryConsume(curr, {Token::Type::Keyword, Token::Type::Identifier, Token::Type::Identifier, Token::Type::EndOfExpression}))
|
||||
nodes.push_back(parseCustomTypeVariableDecl(curr, true));
|
||||
else if (tryConsume(curr, {Token::Type::Keyword, Token::Type::Type, Token::Type::Identifier, Token::Type::ArrayOpen, Token::Type::Integer, Token::Type::ArrayClose, Token::Type::EndOfExpression}))
|
||||
nodes.push_back(parseBuiltinArrayDecl(curr, true));
|
||||
else if (tryConsume(curr, {Token::Type::Keyword, Token::Type::Identifier, Token::Type::Identifier, Token::Type::ArrayOpen, Token::Type::Integer, Token::Type::ArrayClose, Token::Type::EndOfExpression}))
|
||||
nodes.push_back(parseCustomTypeArrayDecl(curr, true));
|
||||
else if (tryConsume(curr, {Token::Type::Keyword, Token::Type::Type, Token::Type::Identifier, Token::Type::ArrayOpen, Token::Type::Identifier, Token::Type::ArrayClose, Token::Type::EndOfExpression}))
|
||||
nodes.push_back(parseBuiltinVariableArrayDecl(curr, true));
|
||||
else if (tryConsume(curr, {Token::Type::Keyword, Token::Type::Identifier, Token::Type::Identifier, Token::Type::ArrayOpen, Token::Type::Identifier, Token::Type::ArrayClose, Token::Type::EndOfExpression}))
|
||||
nodes.push_back(parseCustomTypeVariableArrayDecl(curr, true));
|
||||
else if (tryConsume(curr, {Token::Type::Keyword, Token::Type::Type, Token::Type::Operator, Token::Type::Identifier, Token::Type::Operator, Token::Type::Type, Token::Type::EndOfExpression}))
|
||||
nodes.push_back(parseBuiltinPointerVariableDecl(curr, true));
|
||||
else if (tryConsume(curr, {Token::Type::Keyword, Token::Type::Identifier, Token::Type::Operator, Token::Type::Identifier, Token::Type::Operator, Token::Type::Type, Token::Type::EndOfExpression}))
|
||||
nodes.push_back(parseCustomTypePointerVariableDecl(curr, true));
|
||||
else {
|
||||
for(auto &node : nodes) delete node;
|
||||
this->m_error = { curr->lineNumber, "Invalid sequence, expected member declaration" };
|
||||
return nullptr;
|
||||
}
|
||||
}
|
||||
|
||||
if (!tryConsume(curr, {Token::Type::EndOfExpression})) {
|
||||
this->m_error = { curr->lineNumber, "Expected ';' after struct definition" };
|
||||
for(auto &node : nodes) delete node;
|
||||
return nullptr;
|
||||
}
|
||||
|
||||
return new ASTNodeStruct(startLineNumber, structName, nodes);
|
||||
}
|
||||
|
||||
ASTNode* Parser::parseUnion(TokenIter &curr) {
|
||||
const std::string &unionName = curr[-2].identifierToken.identifier;
|
||||
std::vector<ASTNode*> nodes;
|
||||
|
||||
u32 startLineNumber = curr[-3].lineNumber;
|
||||
|
||||
while (!tryConsume(curr, {Token::Type::ScopeClose})) {
|
||||
if (tryConsume(curr, {Token::Type::Type, Token::Type::Identifier, Token::Type::EndOfExpression}))
|
||||
nodes.push_back(parseBuiltinVariableDecl(curr, false));
|
||||
else if (tryConsume(curr, {Token::Type::Identifier, Token::Type::Identifier, Token::Type::EndOfExpression}))
|
||||
nodes.push_back(parseCustomTypeVariableDecl(curr, false));
|
||||
else if (tryConsume(curr, {Token::Type::Type, Token::Type::Identifier, Token::Type::ArrayOpen, Token::Type::Integer, Token::Type::ArrayClose, Token::Type::EndOfExpression}))
|
||||
nodes.push_back(parseBuiltinArrayDecl(curr, false));
|
||||
else if (tryConsume(curr, {Token::Type::Identifier, Token::Type::Identifier, Token::Type::ArrayOpen, Token::Type::Integer, Token::Type::ArrayClose, Token::Type::EndOfExpression}))
|
||||
nodes.push_back(parseCustomTypeArrayDecl(curr, false));
|
||||
else if (tryConsume(curr, {Token::Type::Type, Token::Type::Operator, Token::Type::Identifier, Token::Type::Operator, Token::Type::Type, Token::Type::EndOfExpression}))
|
||||
nodes.push_back(parseBuiltinPointerVariableDecl(curr, false));
|
||||
else if (tryConsume(curr, {Token::Type::Identifier, Token::Type::Operator, Token::Type::Identifier, Token::Type::Operator, Token::Type::Type, Token::Type::EndOfExpression}))
|
||||
nodes.push_back(parseCustomTypePointerVariableDecl(curr, false));
|
||||
else if (tryConsume(curr, {Token::Type::Keyword, Token::Type::Type, Token::Type::Identifier, Token::Type::EndOfExpression}))
|
||||
nodes.push_back(parseBuiltinVariableDecl(curr, true));
|
||||
else if (tryConsume(curr, {Token::Type::Keyword, Token::Type::Identifier, Token::Type::Identifier, Token::Type::EndOfExpression}))
|
||||
nodes.push_back(parseCustomTypeVariableDecl(curr, true));
|
||||
else if (tryConsume(curr, {Token::Type::Keyword, Token::Type::Type, Token::Type::Identifier, Token::Type::ArrayOpen, Token::Type::Integer, Token::Type::ArrayClose, Token::Type::EndOfExpression}))
|
||||
nodes.push_back(parseBuiltinArrayDecl(curr, true));
|
||||
else if (tryConsume(curr, {Token::Type::Keyword, Token::Type::Identifier, Token::Type::Identifier, Token::Type::ArrayOpen, Token::Type::Integer, Token::Type::ArrayClose, Token::Type::EndOfExpression}))
|
||||
nodes.push_back(parseCustomTypeArrayDecl(curr, true));
|
||||
else if (tryConsume(curr, {Token::Type::Keyword, Token::Type::Type, Token::Type::Operator, Token::Type::Identifier, Token::Type::Operator, Token::Type::Type, Token::Type::EndOfExpression}))
|
||||
nodes.push_back(parseBuiltinPointerVariableDecl(curr, true));
|
||||
else if (tryConsume(curr, {Token::Type::Keyword, Token::Type::Identifier, Token::Type::Operator, Token::Type::Identifier, Token::Type::Operator, Token::Type::Type, Token::Type::EndOfExpression}))
|
||||
nodes.push_back(parseCustomTypePointerVariableDecl(curr, true));
|
||||
else {
|
||||
for(auto &node : nodes) delete node;
|
||||
this->m_error = { curr->lineNumber, "Invalid sequence, expected member declaration" };
|
||||
return nullptr;
|
||||
}
|
||||
}
|
||||
|
||||
if (!tryConsume(curr, {Token::Type::EndOfExpression})) {
|
||||
for(auto &node : nodes) delete node;
|
||||
this->m_error = { curr[-1].lineNumber, "Expected ';' after union definition" };
|
||||
return nullptr;
|
||||
}
|
||||
|
||||
return new ASTNodeUnion(startLineNumber, unionName, nodes);
|
||||
}
|
||||
|
||||
ASTNode* Parser::parseEnum(TokenIter &curr) {
|
||||
const std::string &enumName = curr[-4].identifierToken.identifier;
|
||||
const Token::TypeToken::Type underlyingType = curr[-2].typeToken.type;
|
||||
|
||||
u32 startLineNumber = curr[-5].lineNumber;
|
||||
|
||||
if (curr[-3].operatorToken.op != Token::OperatorToken::Operator::Inherit) {
|
||||
this->m_error = { curr[-3].lineNumber, "Expected ':' after enum name" };
|
||||
return nullptr;
|
||||
}
|
||||
|
||||
if (!isUnsigned(underlyingType)) {
|
||||
this->m_error = { curr[-3].lineNumber, "Underlying type needs to be an unsigned type" };
|
||||
return nullptr;
|
||||
}
|
||||
|
||||
auto enumNode = new ASTNodeEnum(startLineNumber, underlyingType, enumName);
|
||||
|
||||
while (!tryConsume(curr, {Token::Type::ScopeClose})) {
|
||||
if (tryConsume(curr, { Token::Type::Identifier, Token::Type::Separator }) || tryConsume(curr, { Token::Type::Identifier, Token::Type::ScopeClose })) {
|
||||
u64 value;
|
||||
if (enumNode->getValues().empty())
|
||||
value = 0;
|
||||
else
|
||||
value = enumNode->getValues().back().first + 1;
|
||||
|
||||
enumNode->getValues().emplace_back(value, curr[-2].identifierToken.identifier);
|
||||
|
||||
if (curr[-1].type == Token::Type::ScopeClose)
|
||||
break;
|
||||
}
|
||||
else if (tryConsume(curr, { Token::Type::Identifier, Token::Type::Operator, Token::Type::Integer, Token::Type::Separator})) {
|
||||
enumNode->getValues().emplace_back(curr[-2].integerToken.integer, curr[-4].identifierToken.identifier);
|
||||
}
|
||||
else if (tryConsume(curr, { Token::Type::Identifier, Token::Type::Operator, Token::Type::Integer, Token::Type::ScopeClose})) {
|
||||
enumNode->getValues().emplace_back(curr[-2].integerToken.integer, curr[-4].identifierToken.identifier);
|
||||
break;
|
||||
}
|
||||
else {
|
||||
delete enumNode;
|
||||
this->m_error = { curr->lineNumber, "Expected constant identifier" };
|
||||
return nullptr;
|
||||
}
|
||||
}
|
||||
|
||||
if (!tryConsume(curr, {Token::Type::EndOfExpression})) {
|
||||
delete enumNode;
|
||||
this->m_error = { curr[-1].lineNumber, "Expected ';' after enum definition" };
|
||||
return nullptr;
|
||||
}
|
||||
|
||||
return enumNode;
|
||||
}
|
||||
|
||||
ASTNode* Parser::parseBitField(TokenIter &curr) {
|
||||
const std::string &bitfieldName = curr[-2].identifierToken.identifier;
|
||||
std::vector<std::pair<std::string, size_t>> fields;
|
||||
|
||||
u32 startLineNumber = curr[-3].lineNumber;
|
||||
|
||||
while (!tryConsume(curr, {Token::Type::ScopeClose})) {
|
||||
if (tryConsume(curr, {Token::Type::Identifier, Token::Type::Operator, Token::Type::Integer, Token::Type::EndOfExpression})) {
|
||||
if (curr[-3].operatorToken.op != Token::OperatorToken::Operator::Inherit) {
|
||||
this->m_error = { curr[-3].lineNumber, "Expected ':' after member name" };
|
||||
return nullptr;
|
||||
}
|
||||
|
||||
fields.emplace_back(curr[-4].identifierToken.identifier, curr[-2].integerToken.integer);
|
||||
}
|
||||
else {
|
||||
this->m_error = { curr->lineNumber, "Invalid sequence, expected member declaration" };
|
||||
return nullptr;
|
||||
}
|
||||
}
|
||||
|
||||
if (!tryConsume(curr, {Token::Type::EndOfExpression})) {
|
||||
this->m_error = { curr[-1].lineNumber, "Expected ';' after bitfield definition" };
|
||||
return nullptr;
|
||||
}
|
||||
|
||||
return new ASTNodeBitField(startLineNumber, bitfieldName, fields);
|
||||
}
|
||||
|
||||
ASTNode* Parser::parseScope(TokenIter &curr) {
|
||||
return new ASTNodeScope(curr[-1].lineNumber, parseTillToken(curr, Token::Type::ScopeClose));
|
||||
}
|
||||
|
||||
std::optional<ASTNode*> Parser::parseUsingDeclaration(TokenIter &curr) {
|
||||
auto keyword = curr[-5].keywordToken;
|
||||
auto name = curr[-4].identifierToken;
|
||||
auto op = curr[-3].operatorToken;
|
||||
|
||||
if (keyword.keyword != Token::KeywordToken::Keyword::Using) {
|
||||
this->m_error = { curr[-5].lineNumber, "Invalid keyword. Expected 'using'" };
|
||||
return { };
|
||||
}
|
||||
|
||||
if (op.op != Token::OperatorToken::Operator::Assignment) {
|
||||
this->m_error = { curr[-3].lineNumber, "Invalid operator. Expected '='" };
|
||||
return { };
|
||||
}
|
||||
|
||||
if (curr[-2].type == Token::Type::Type) {
|
||||
auto type = curr[-2].typeToken;
|
||||
|
||||
return new ASTNodeTypeDecl(curr[-2].lineNumber, type.type, name.identifier);
|
||||
} else if (curr[-2].type == Token::Type::Identifier) {
|
||||
auto customType = curr[-2].identifierToken;
|
||||
|
||||
return new ASTNodeTypeDecl(curr[-2].lineNumber, Token::TypeToken::Type::CustomType, name.identifier, customType.identifier);
|
||||
}
|
||||
|
||||
this->m_error = { curr[-2].lineNumber, hex::format("'%s' does not name a type") };
|
||||
return { };
|
||||
}
|
||||
|
||||
std::optional<std::vector<ASTNode*>> Parser::parseStatement(TokenIter &curr) {
|
||||
std::vector<ASTNode*> program;
|
||||
|
||||
// Struct
|
||||
if (tryConsume(curr, { Token::Type::Keyword, Token::Type::Identifier, Token::Type::ScopeOpen })) {
|
||||
if (curr[-3].keywordToken.keyword == Token::KeywordToken::Keyword::Struct) {
|
||||
auto structAst = parseStruct(curr);
|
||||
|
||||
if (structAst == nullptr) {
|
||||
for(auto &node : program) delete node;
|
||||
return { };
|
||||
}
|
||||
|
||||
program.push_back(structAst);
|
||||
} else if (curr[-3].keywordToken.keyword == Token::KeywordToken::Keyword::Union) {
|
||||
auto unionAst = parseUnion(curr);
|
||||
|
||||
if (unionAst == nullptr) {
|
||||
for(auto &node : program) delete node;
|
||||
return { };
|
||||
}
|
||||
|
||||
program.push_back(unionAst);
|
||||
} else if (curr[-3].keywordToken.keyword == Token::KeywordToken::Keyword::Bitfield) {
|
||||
auto bitfieldAst = parseBitField(curr);
|
||||
|
||||
if (bitfieldAst == nullptr) {
|
||||
for(auto &node : program) delete node;
|
||||
return { };
|
||||
}
|
||||
|
||||
program.push_back(bitfieldAst);
|
||||
}
|
||||
|
||||
return program;
|
||||
|
||||
} // Enum
|
||||
else if (tryConsume(curr, { Token::Type::Keyword, Token::Type::Identifier, Token::Type::Operator, Token::Type::Type, Token::Type::ScopeOpen })) {
|
||||
if (curr[-5].keywordToken.keyword == Token::KeywordToken::Keyword::Enum) {
|
||||
auto enumAst = parseEnum(curr);
|
||||
|
||||
if (enumAst == nullptr) {
|
||||
for(auto &node : program) delete node;
|
||||
return { };
|
||||
}
|
||||
|
||||
program.push_back(enumAst);
|
||||
}
|
||||
|
||||
return program;
|
||||
// Scope
|
||||
} else if (tryConsume(curr, { Token::Type::ScopeOpen })) {
|
||||
program.push_back(parseScope(curr));
|
||||
|
||||
return program;
|
||||
|
||||
// Using declaration with built-in type
|
||||
} else if (tryConsume(curr, { Token::Type::Keyword, Token::Type::Identifier, Token::Type::Operator, Token::Type::Type, Token::Type::EndOfExpression})) {
|
||||
auto usingDecl = parseUsingDeclaration(curr);
|
||||
|
||||
if (!usingDecl.has_value()) {
|
||||
for(auto &node : program) delete node;
|
||||
return { };
|
||||
}
|
||||
|
||||
program.push_back(usingDecl.value());
|
||||
|
||||
return program;
|
||||
|
||||
// Using declaration with custom type
|
||||
} else if (tryConsume(curr, { Token::Type::Keyword, Token::Type::Identifier, Token::Type::Operator, Token::Type::Identifier, Token::Type::EndOfExpression})) {
|
||||
auto usingDecl = parseUsingDeclaration(curr);
|
||||
|
||||
if (!usingDecl.has_value()) {
|
||||
for(auto &node : program) delete node;
|
||||
return { };
|
||||
}
|
||||
|
||||
program.push_back(usingDecl.value());
|
||||
|
||||
return program;
|
||||
// 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})) {
|
||||
if (curr[-3].operatorToken.op != Token::OperatorToken::Operator::AtDeclaration) {
|
||||
this->m_error = { curr[-3].lineNumber, "Expected '@' after variable placement declaration" };
|
||||
for(auto &node : program) delete node;
|
||||
return { };
|
||||
}
|
||||
|
||||
auto variableDecl = parseFreeBuiltinVariableDecl(curr, false);
|
||||
|
||||
program.push_back(variableDecl);
|
||||
|
||||
return program;
|
||||
|
||||
// Variable placement declaration with custom type
|
||||
} else if (tryConsume(curr, { 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, 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);
|
||||
|
||||
return program;
|
||||
}
|
||||
else {
|
||||
for(auto &node : program) delete node;
|
||||
this->m_error = { curr->lineNumber, "Invalid sequence" };
|
||||
return { };
|
||||
}
|
||||
}
|
||||
|
||||
std::vector<ASTNode*> Parser::parseTillToken(TokenIter &curr, Token::Type endTokenType) {
|
||||
std::vector<ASTNode*> program;
|
||||
|
||||
while (curr->type != endTokenType) {
|
||||
auto newTokens = parseStatement(curr);
|
||||
|
||||
if (!newTokens.has_value())
|
||||
break;
|
||||
|
||||
program.insert(program.end(), newTokens->begin(), newTokens->end());
|
||||
}
|
||||
|
||||
curr++;
|
||||
|
||||
return program;
|
||||
}
|
||||
|
||||
std::pair<Result, std::vector<ASTNode*>> Parser::parse(const std::vector<Token> &tokens) {
|
||||
auto currentToken = tokens.begin();
|
||||
|
||||
auto program = parseTillToken(currentToken, Token::Type::EndOfProgram);
|
||||
|
||||
if (program.empty() || currentToken != tokens.end())
|
||||
return { ResultParseError, { } };
|
||||
|
||||
return { ResultSuccess, program };
|
||||
}
|
||||
|
||||
}
|
||||
184
source/lang/preprocessor.cpp
Normal file
184
source/lang/preprocessor.cpp
Normal file
@@ -0,0 +1,184 @@
|
||||
#include "lang/preprocessor.hpp"
|
||||
|
||||
namespace hex::lang {
|
||||
|
||||
Preprocessor::Preprocessor() {
|
||||
|
||||
}
|
||||
|
||||
std::pair<Result, std::string> Preprocessor::preprocess(const std::string& code, bool initialRun) {
|
||||
u32 offset = 0;
|
||||
u32 lineNumber = 1;
|
||||
|
||||
if (initialRun) {
|
||||
this->m_defines.clear();
|
||||
this->m_pragmas.clear();
|
||||
}
|
||||
|
||||
std::string output;
|
||||
output.reserve(code.length());
|
||||
|
||||
while (offset < code.length()) {
|
||||
if (code[offset] == '#') {
|
||||
offset += 1;
|
||||
|
||||
if (code.substr(offset, 7) == "include") {
|
||||
offset += 7;
|
||||
|
||||
while (std::isblank(code[offset]) || std::isspace(code[offset]))
|
||||
offset += 1;
|
||||
|
||||
if (code[offset] != '<' && code[offset] != '"')
|
||||
return { ResultPreprocessingError, "" };
|
||||
|
||||
char endChar = code[offset];
|
||||
if (endChar == '<') endChar = '>';
|
||||
|
||||
offset += 1;
|
||||
|
||||
std::string includeFile;
|
||||
while (code[offset] != endChar) {
|
||||
includeFile += code[offset];
|
||||
offset += 1;
|
||||
|
||||
if (offset >= code.length())
|
||||
return { ResultPreprocessingError, "" };
|
||||
}
|
||||
offset += 1;
|
||||
|
||||
if (includeFile[0] != '/')
|
||||
includeFile = "include/" + includeFile;
|
||||
|
||||
FILE *file = fopen(includeFile.c_str(), "r");
|
||||
if (file == nullptr)
|
||||
return { ResultPreprocessingError, "" };
|
||||
|
||||
fseek(file, 0, SEEK_END);
|
||||
size_t size = ftell(file);
|
||||
char *buffer = new char[size + 1];
|
||||
rewind(file);
|
||||
|
||||
fread(buffer, size, 1, file);
|
||||
buffer[size] = 0x00;
|
||||
|
||||
auto [result, preprocessedInclude] = this->preprocess(buffer, false);
|
||||
if (result.failed())
|
||||
return { ResultPreprocessingError, "" };
|
||||
|
||||
output += preprocessedInclude;
|
||||
|
||||
delete[] buffer;
|
||||
|
||||
fclose(file);
|
||||
} else if (code.substr(offset, 6) == "define") {
|
||||
offset += 6;
|
||||
|
||||
while (std::isblank(code[offset]))
|
||||
offset += 1;
|
||||
|
||||
std::string defineName;
|
||||
while (!std::isblank(code[offset])) {
|
||||
defineName += code[offset];
|
||||
|
||||
if (offset >= code.length() || code[offset] == '\n' || code[offset] == '\r')
|
||||
return { ResultPreprocessingError, "" };
|
||||
offset += 1;
|
||||
}
|
||||
|
||||
while (std::isblank(code[offset]))
|
||||
offset += 1;
|
||||
|
||||
std::string replaceValue;
|
||||
while (code[offset] != '\n' && code[offset] != '\r') {
|
||||
if (offset >= code.length())
|
||||
return { ResultPreprocessingError, "" };
|
||||
|
||||
replaceValue += code[offset];
|
||||
offset += 1;
|
||||
}
|
||||
|
||||
if (replaceValue.empty())
|
||||
return { ResultPreprocessingError, "" };
|
||||
|
||||
this->m_defines.emplace(defineName, replaceValue);
|
||||
} else if (code.substr(offset, 6) == "pragma") {
|
||||
offset += 6;
|
||||
|
||||
while (std::isblank(code[offset]))
|
||||
offset += 1;
|
||||
|
||||
std::string pragmaKey;
|
||||
while (!std::isblank(code[offset])) {
|
||||
pragmaKey += code[offset];
|
||||
|
||||
if (offset >= code.length() || code[offset] == '\n' || code[offset] == '\r')
|
||||
return { ResultPreprocessingError, "" };
|
||||
|
||||
offset += 1;
|
||||
}
|
||||
|
||||
while (std::isblank(code[offset]))
|
||||
offset += 1;
|
||||
|
||||
std::string pragmaValue;
|
||||
while (code[offset] != '\n' && code[offset] != '\r') {
|
||||
if (offset >= code.length())
|
||||
return { ResultPreprocessingError, "" };
|
||||
|
||||
pragmaValue += code[offset];
|
||||
offset += 1;
|
||||
}
|
||||
|
||||
if (pragmaValue.empty())
|
||||
return { ResultPreprocessingError, "" };
|
||||
|
||||
this->m_pragmas.emplace(pragmaKey, pragmaValue);
|
||||
} else
|
||||
return { ResultPreprocessingError, "" };
|
||||
}
|
||||
|
||||
if (code[offset] == '\n')
|
||||
lineNumber++;
|
||||
|
||||
output += code[offset];
|
||||
offset += 1;
|
||||
}
|
||||
|
||||
if (initialRun) {
|
||||
// Apply defines
|
||||
for (const auto &[define, value] : this->m_defines) {
|
||||
s32 index = 0;
|
||||
while((index = output.find(define, index)) != std::string::npos) {
|
||||
output.replace(index, define.length(), value);
|
||||
index += value.length();
|
||||
}
|
||||
}
|
||||
|
||||
// Handle pragmas
|
||||
for (const auto &[type, value] : this->m_pragmas) {
|
||||
if (this->m_pragmaHandlers.contains(type)) {
|
||||
if (!this->m_pragmaHandlers[type](value))
|
||||
return { ResultPreprocessingError, { } };
|
||||
} else
|
||||
return { ResultPreprocessingError, { } };
|
||||
}
|
||||
}
|
||||
|
||||
return { ResultSuccess, output };
|
||||
}
|
||||
|
||||
void Preprocessor::addPragmaHandler(std::string pragmaType, std::function<bool(std::string)> function) {
|
||||
if (!this->m_pragmaHandlers.contains(pragmaType))
|
||||
this->m_pragmaHandlers.emplace(pragmaType, function);
|
||||
}
|
||||
|
||||
void Preprocessor::addDefaultPragmaHandlers() {
|
||||
this->addPragmaHandler("MIME", [](std::string value) {
|
||||
return !std::all_of(value.begin(), value.end(), isspace) && !value.ends_with('\n') && !value.ends_with('\r');
|
||||
});
|
||||
this->addPragmaHandler("endian", [](std::string value) {
|
||||
return value == "big" || value == "little" || value == "native";
|
||||
});
|
||||
}
|
||||
|
||||
}
|
||||
122
source/lang/validator.cpp
Normal file
122
source/lang/validator.cpp
Normal file
@@ -0,0 +1,122 @@
|
||||
#include "lang/validator.hpp"
|
||||
|
||||
#include <unordered_set>
|
||||
#include <string>
|
||||
|
||||
#include "helpers/utils.hpp"
|
||||
|
||||
namespace hex::lang {
|
||||
|
||||
Validator::Validator() {
|
||||
|
||||
}
|
||||
|
||||
bool Validator::validate(const std::vector<ASTNode*>& ast) {
|
||||
|
||||
std::unordered_set<std::string> typeNames;
|
||||
|
||||
for (const auto &node : ast) {
|
||||
switch (node->getType()) {
|
||||
case ASTNode::Type::VariableDecl:
|
||||
{
|
||||
// Check for duplicate variable names
|
||||
auto varDeclNode = static_cast<ASTNodeVariableDecl*>(node);
|
||||
if (!typeNames.insert(varDeclNode->getVariableName()).second) {
|
||||
this->m_error = { varDeclNode->getLineNumber(), hex::format("Redefinition of variable '%s'", varDeclNode->getVariableName().c_str()) };
|
||||
return false;
|
||||
}
|
||||
|
||||
if (varDeclNode->getArraySize() == 0 && !varDeclNode->getArraySizeVariable().has_value() ||
|
||||
varDeclNode->getArraySize() != 0 && varDeclNode->getArraySizeVariable().has_value()) {
|
||||
|
||||
this->m_error = { varDeclNode->getLineNumber(), "Invalid array size" };
|
||||
return false;
|
||||
}
|
||||
}
|
||||
break;
|
||||
case ASTNode::Type::TypeDecl:
|
||||
{
|
||||
// Check for duplicate type names
|
||||
auto typeDeclNode = static_cast<ASTNodeTypeDecl*>(node);
|
||||
if (!typeNames.insert(typeDeclNode->getTypeName()).second) {
|
||||
this->m_error = { typeDeclNode->getLineNumber(), hex::format("Redefinition of type '%s'", typeDeclNode->getTypeName().c_str()) };
|
||||
return false;
|
||||
}
|
||||
|
||||
if (typeDeclNode->getAssignedType() == Token::TypeToken::Type::CustomType && !typeNames.contains(typeDeclNode->getAssignedCustomTypeName())) {
|
||||
this->m_error = { typeDeclNode->getLineNumber(), "Type declaration without a name" };
|
||||
return false;
|
||||
}
|
||||
}
|
||||
break;
|
||||
case ASTNode::Type::Struct:
|
||||
{
|
||||
// Check for duplicate type name
|
||||
auto structNode = static_cast<ASTNodeStruct*>(node);
|
||||
if (!typeNames.insert(structNode->getName()).second) {
|
||||
this->m_error = { structNode->getLineNumber(), hex::format("Redeclaration of type '%s'", structNode->getName().c_str()) };
|
||||
return false;
|
||||
}
|
||||
|
||||
// Check for duplicate member names
|
||||
std::unordered_set<std::string> memberNames;
|
||||
for (const auto &member : structNode->getNodes())
|
||||
if (!memberNames.insert(static_cast<ASTNodeVariableDecl*>(member)->getVariableName()).second) {
|
||||
this->m_error = { member->getLineNumber(), hex::format("Redeclaration of member '%s'", static_cast<ASTNodeVariableDecl*>(member)->getVariableName().c_str()) };
|
||||
return false;
|
||||
}
|
||||
}
|
||||
break;
|
||||
case ASTNode::Type::Enum:
|
||||
{
|
||||
// Check for duplicate type name
|
||||
auto enumNode = static_cast<ASTNodeEnum*>(node);
|
||||
if (!typeNames.insert(enumNode->getName()).second) {
|
||||
this->m_error = { enumNode->getLineNumber(), hex::format("Redeclaration of type '%s'", enumNode->getName().c_str()) };
|
||||
return false;
|
||||
}
|
||||
|
||||
// Check for duplicate constant names
|
||||
std::unordered_set<std::string> constantNames;
|
||||
for (const auto &[value, name] : enumNode->getValues())
|
||||
if (!constantNames.insert(name).second) {
|
||||
this->m_error = { enumNode->getLineNumber(), hex::format("Redeclaration of enum constant '%s'", name.c_str()) };
|
||||
return false;
|
||||
}
|
||||
}
|
||||
break;
|
||||
case ASTNode::Type::Bitfield:
|
||||
{
|
||||
// Check for duplicate type name
|
||||
auto bitfieldNode = static_cast<ASTNodeBitField*>(node);
|
||||
if (!typeNames.insert(bitfieldNode->getName()).second) {
|
||||
this->m_error = { bitfieldNode->getLineNumber(), hex::format("Redeclaration of type '%s'", bitfieldNode->getName().c_str()) };
|
||||
return false;
|
||||
}
|
||||
|
||||
size_t bitfieldSize = 0;
|
||||
|
||||
// Check for duplicate constant names
|
||||
std::unordered_set<std::string> flagNames;
|
||||
for (const auto &[name, size] : bitfieldNode->getFields()) {
|
||||
if (!flagNames.insert(name).second) {
|
||||
this->m_error = { bitfieldNode->getLineNumber(), hex::format("Redeclaration of member '%s'", name.c_str()) };
|
||||
return false;
|
||||
}
|
||||
|
||||
bitfieldSize += size;
|
||||
}
|
||||
|
||||
if (bitfieldSize > 64) {
|
||||
this->m_error = { bitfieldNode->getLineNumber(), "Bitfield exceeds maximum size of 64 bits" };
|
||||
return false;
|
||||
}
|
||||
}
|
||||
break;
|
||||
}
|
||||
}
|
||||
|
||||
return true;
|
||||
}
|
||||
|
||||
}
|
||||
@@ -1,29 +1,52 @@
|
||||
#include "window.hpp"
|
||||
|
||||
#include "views/highlight.hpp"
|
||||
#include "lang/pattern_data.hpp"
|
||||
#include "views/view_hexeditor.hpp"
|
||||
#include "views/view_pattern.hpp"
|
||||
#include "views/view_pattern_data.hpp"
|
||||
#include "views/view_hashes.hpp"
|
||||
#include "views/view_information.hpp"
|
||||
#include "views/view_help.hpp"
|
||||
#include "views/view_tools.hpp"
|
||||
#include "views/view_strings.hpp"
|
||||
#include "views/view_data_inspector.hpp"
|
||||
#include "views/view_disassembler.hpp"
|
||||
#include "views/view_bookmarks.hpp"
|
||||
#include "views/view_patches.hpp"
|
||||
|
||||
#include "providers/provider.hpp"
|
||||
|
||||
#include <vector>
|
||||
|
||||
int main() {
|
||||
int mainArgc;
|
||||
char **mainArgv;
|
||||
|
||||
int main(int argc, char **argv) {
|
||||
mainArgc = argc;
|
||||
mainArgv = argv;
|
||||
|
||||
hex::Window window;
|
||||
|
||||
// Shared Data
|
||||
std::vector<hex::Highlight> highlights;
|
||||
std::vector<hex::lang::PatternData*> patternData;
|
||||
hex::prv::Provider *dataProvider = nullptr;
|
||||
|
||||
// Create views
|
||||
window.addView<hex::ViewHexEditor>(dataProvider, highlights);
|
||||
window.addView<hex::ViewPattern>(highlights);
|
||||
window.addView<hex::ViewPatternData>(dataProvider, highlights);
|
||||
window.addView<hex::ViewHexEditor>(dataProvider, patternData);
|
||||
window.addView<hex::ViewPattern>(dataProvider, patternData);
|
||||
window.addView<hex::ViewPatternData>(dataProvider, patternData);
|
||||
window.addView<hex::ViewDataInspector>(dataProvider);
|
||||
window.addView<hex::ViewHashes>(dataProvider);
|
||||
window.addView<hex::ViewInformation>(dataProvider);
|
||||
window.addView<hex::ViewStrings>(dataProvider);
|
||||
window.addView<hex::ViewDisassembler>(dataProvider);
|
||||
window.addView<hex::ViewBookmarks>(dataProvider);
|
||||
window.addView<hex::ViewPatches>(dataProvider);
|
||||
window.addView<hex::ViewTools>(dataProvider);
|
||||
window.addView<hex::ViewHelp>();
|
||||
|
||||
if (argc > 1)
|
||||
hex::View::postEvent(hex::Events::FileDropped, argv[1]);
|
||||
|
||||
window.loop();
|
||||
|
||||
|
||||
@@ -1,161 +0,0 @@
|
||||
#include "parser/lexer.hpp"
|
||||
|
||||
#include <vector>
|
||||
#include <functional>
|
||||
|
||||
namespace hex::lang {
|
||||
|
||||
Lexer::Lexer() { }
|
||||
|
||||
std::string matchTillInvalid(const char* characters, std::function<bool(char)> predicate) {
|
||||
std::string ret;
|
||||
|
||||
while (*characters != 0x00) {
|
||||
ret += *characters;
|
||||
characters++;
|
||||
|
||||
if (!predicate(*characters))
|
||||
break;
|
||||
}
|
||||
|
||||
return ret;
|
||||
}
|
||||
|
||||
std::optional<u64> parseInt(std::string_view string) {
|
||||
u64 integer = 0;
|
||||
u8 base;
|
||||
|
||||
std::string_view numberData;
|
||||
|
||||
if (string.starts_with("0x")) {
|
||||
numberData = string.substr(2);
|
||||
base = 16;
|
||||
|
||||
if (numberData.find_first_not_of("0123456789ABCDEFabcdef") != std::string_view::npos)
|
||||
return { };
|
||||
} else if (string.starts_with("0b")) {
|
||||
numberData = string.substr(2);
|
||||
base = 2;
|
||||
|
||||
if (numberData.find_first_not_of("01") != std::string_view::npos)
|
||||
return { };
|
||||
} else if (isdigit(string[0])) {
|
||||
numberData = string;
|
||||
base = 10;
|
||||
|
||||
if (numberData.find_first_not_of("0123456789") != std::string_view::npos)
|
||||
return { };
|
||||
} else return { };
|
||||
|
||||
if (numberData.length() == 0)
|
||||
return { };
|
||||
|
||||
for (const char& c : numberData) {
|
||||
integer *= base;
|
||||
|
||||
if (isdigit(c))
|
||||
integer += (c - '0');
|
||||
else if (c >= 'A' && c <= 'F')
|
||||
integer += 10 + (c - 'A');
|
||||
else if (c >= 'a' && c <= 'f')
|
||||
integer += 10 + (c - 'a');
|
||||
else return { };
|
||||
}
|
||||
|
||||
return integer;
|
||||
}
|
||||
|
||||
std::pair<Result, std::vector<Token>> Lexer::lex(const std::string& code) {
|
||||
std::vector<Token> tokens;
|
||||
u32 offset = 0;
|
||||
|
||||
while (offset < code.length()) {
|
||||
const char& c = code[offset];
|
||||
|
||||
if (std::isblank(c) || std::isspace(c)) {
|
||||
offset += 1;
|
||||
} else if (c == ';') {
|
||||
tokens.push_back({.type = Token::Type::EndOfExpression});
|
||||
offset += 1;
|
||||
} else if (c == '{') {
|
||||
tokens.push_back({.type = Token::Type::ScopeOpen});
|
||||
offset += 1;
|
||||
} else if (c == '}') {
|
||||
tokens.push_back({.type = Token::Type::ScopeClose});
|
||||
offset += 1;
|
||||
} else if (c == '[') {
|
||||
tokens.push_back({.type = Token::Type::ArrayOpen});
|
||||
offset += 1;
|
||||
} else if (c == ']') {
|
||||
tokens.push_back({.type = Token::Type::ArrayClose});
|
||||
offset += 1;
|
||||
} else if (c == ',') {
|
||||
tokens.push_back({.type = Token::Type::Separator});
|
||||
offset += 1;
|
||||
} else if (c == '@') {
|
||||
tokens.push_back({.type = Token::Type::Operator, .operatorToken = { .op = Token::OperatorToken::Operator::AtDeclaration}});
|
||||
offset += 1;
|
||||
} else if (c == '=') {
|
||||
tokens.push_back({.type = Token::Type::Operator, .operatorToken = { .op = Token::OperatorToken::Operator::Assignment}});
|
||||
offset += 1;
|
||||
} else if (std::isalpha(c)) {
|
||||
std::string identifier = matchTillInvalid(&code[offset], [](char c) -> bool { return std::isalnum(c) || c == '_'; });
|
||||
|
||||
// Check for reserved keywords
|
||||
|
||||
if (identifier == "struct")
|
||||
tokens.push_back({.type = Token::Type::Keyword, .keywordToken = { .keyword = Token::KeywordToken::Keyword::Struct}});
|
||||
else if (identifier == "using")
|
||||
tokens.push_back({.type = Token::Type::Keyword, .keywordToken = { .keyword = Token::KeywordToken::Keyword::Using}});
|
||||
|
||||
// Check for built-in types
|
||||
else if (identifier == "u8")
|
||||
tokens.push_back({.type = Token::Type::Type, .typeToken = { .type = Token::TypeToken::Type::Unsigned8Bit }});
|
||||
else if (identifier == "s8")
|
||||
tokens.push_back({.type = Token::Type::Type, .typeToken = { .type = Token::TypeToken::Type::Signed8Bit }});
|
||||
else if (identifier == "u16")
|
||||
tokens.push_back({.type = Token::Type::Type, .typeToken = { .type = Token::TypeToken::Type::Unsigned16Bit }});
|
||||
else if (identifier == "s16")
|
||||
tokens.push_back({.type = Token::Type::Type, .typeToken = { .type = Token::TypeToken::Type::Signed16Bit }});
|
||||
else if (identifier == "u32")
|
||||
tokens.push_back({.type = Token::Type::Type, .typeToken = { .type = Token::TypeToken::Type::Unsigned32Bit }});
|
||||
else if (identifier == "s32")
|
||||
tokens.push_back({.type = Token::Type::Type, .typeToken = { .type = Token::TypeToken::Type::Signed32Bit }});
|
||||
else if (identifier == "u64")
|
||||
tokens.push_back({.type = Token::Type::Type, .typeToken = { .type = Token::TypeToken::Type::Unsigned64Bit }});
|
||||
else if (identifier == "s64")
|
||||
tokens.push_back({.type = Token::Type::Type, .typeToken = { .type = Token::TypeToken::Type::Signed64Bit }});
|
||||
else if (identifier == "u128")
|
||||
tokens.push_back({.type = Token::Type::Type, .typeToken = { .type = Token::TypeToken::Type::Unsigned128Bit }});
|
||||
else if (identifier == "s128")
|
||||
tokens.push_back({.type = Token::Type::Type, .typeToken = { .type = Token::TypeToken::Type::Signed128Bit }});
|
||||
else if (identifier == "float")
|
||||
tokens.push_back({.type = Token::Type::Type, .typeToken = { .type = Token::TypeToken::Type::Float }});
|
||||
else if (identifier == "double")
|
||||
tokens.push_back({.type = Token::Type::Type, .typeToken = { .type = Token::TypeToken::Type::Double }});
|
||||
|
||||
// If it's not a keyword and a builtin type, it has to be an identifier
|
||||
|
||||
else
|
||||
tokens.push_back({.type = Token::Type::Identifier, .identifierToken = { .identifier = identifier}});
|
||||
|
||||
offset += identifier.length();
|
||||
} else if (std::isdigit(c)) {
|
||||
char *end = nullptr;
|
||||
std::strtoull(&code[offset], &end, 0);
|
||||
|
||||
auto integer = parseInt(std::string_view(&code[offset], end));
|
||||
|
||||
if (!integer.has_value())
|
||||
return { ResultLexicalError, {}};
|
||||
|
||||
tokens.push_back({.type = Token::Type::Integer, .integerToken = { .integer = integer.value() }});
|
||||
offset += (end - &code[offset]);
|
||||
} else return { ResultLexicalError, {}};
|
||||
}
|
||||
|
||||
tokens.push_back({.type = Token::Type::EndOfProgram});
|
||||
|
||||
return { ResultSuccess, tokens };
|
||||
}
|
||||
}
|
||||
@@ -1,205 +0,0 @@
|
||||
#include "parser/parser.hpp"
|
||||
|
||||
#include <optional>
|
||||
|
||||
namespace hex::lang {
|
||||
|
||||
Parser::Parser() {
|
||||
|
||||
}
|
||||
|
||||
using TokenIter = std::vector<Token>::const_iterator;
|
||||
|
||||
std::vector<ASTNode*> parseTillToken(TokenIter &curr, Token::Type endTokenType);
|
||||
|
||||
bool tryConsume(TokenIter &curr, std::initializer_list<Token::Type> tokenTypes) {
|
||||
std::vector<Token>::const_iterator originalPosition = curr;
|
||||
|
||||
for (const auto& type : tokenTypes) {
|
||||
if (curr->type != type) {
|
||||
curr = originalPosition;
|
||||
return false;
|
||||
}
|
||||
curr++;
|
||||
}
|
||||
|
||||
return true;
|
||||
}
|
||||
|
||||
|
||||
ASTNode* parseBuiltinVariableDecl(TokenIter &curr) {
|
||||
return new ASTNodeVariableDecl(curr[-3].typeToken.type, curr[-2].identifierToken.identifier);
|
||||
}
|
||||
|
||||
ASTNode* parseCustomTypeVariableDecl(TokenIter &curr) {
|
||||
return new ASTNodeVariableDecl(Token::TypeToken::Type::CustomType, curr[-2].identifierToken.identifier, curr[-3].identifierToken.identifier);
|
||||
}
|
||||
|
||||
ASTNode* parseBuiltinArrayDecl(TokenIter &curr) {
|
||||
return new ASTNodeVariableDecl(curr[-6].typeToken.type, curr[-5].identifierToken.identifier, "", { }, curr[-3].integerToken.integer);
|
||||
}
|
||||
|
||||
ASTNode* parseCustomTypeArrayDecl(TokenIter &curr) {
|
||||
return new ASTNodeVariableDecl(Token::TypeToken::Type::CustomType, curr[-5].identifierToken.identifier, curr[-6].identifierToken.identifier, { }, curr[-3].integerToken.integer);
|
||||
}
|
||||
|
||||
ASTNode* parseFreeBuiltinVariableDecl(TokenIter &curr) {
|
||||
return new ASTNodeVariableDecl(curr[-5].typeToken.type, curr[-4].identifierToken.identifier, "", curr[-2].integerToken.integer);
|
||||
}
|
||||
|
||||
ASTNode* parseFreeCustomTypeVariableDecl(TokenIter &curr) {
|
||||
return new ASTNodeVariableDecl(Token::TypeToken::Type::CustomType, curr[-4].identifierToken.identifier, curr[-5].identifierToken.identifier, curr[-2].integerToken.integer);
|
||||
}
|
||||
|
||||
std::optional<ASTNode*> parseStruct(TokenIter &curr) {
|
||||
const std::string &structName = curr[-2].identifierToken.identifier;
|
||||
std::vector<ASTNode*> nodes;
|
||||
|
||||
while (!tryConsume(curr, {Token::Type::ScopeClose})) {
|
||||
if (tryConsume(curr, {Token::Type::Type, Token::Type::Identifier, Token::Type::EndOfExpression}))
|
||||
nodes.push_back(parseBuiltinVariableDecl(curr));
|
||||
else if (tryConsume(curr, {Token::Type::Identifier, Token::Type::Identifier, Token::Type::EndOfExpression}))
|
||||
nodes.push_back(parseCustomTypeVariableDecl(curr));
|
||||
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));
|
||||
else if (tryConsume(curr, {Token::Type::Identifier, Token::Type::Identifier, Token::Type::ArrayOpen, Token::Type::Integer, Token::Type::ArrayClose, Token::Type::EndOfExpression}))
|
||||
nodes.push_back(parseCustomTypeArrayDecl(curr));
|
||||
else break;
|
||||
}
|
||||
|
||||
if (!tryConsume(curr, {Token::Type::EndOfExpression})) {
|
||||
for(auto &node : nodes) delete node;
|
||||
return { };
|
||||
}
|
||||
|
||||
return new ASTNodeStruct(structName, nodes);
|
||||
}
|
||||
|
||||
ASTNode *parseScope(TokenIter &curr) {
|
||||
return new ASTNodeScope(parseTillToken(curr, Token::Type::ScopeClose));
|
||||
}
|
||||
|
||||
std::optional<ASTNode*> parseUsingDeclaration(TokenIter &curr) {
|
||||
auto keyword = curr[-5].keywordToken;
|
||||
auto name = curr[-4].identifierToken;
|
||||
auto op = curr[-3].operatorToken;
|
||||
|
||||
if (keyword.keyword != Token::KeywordToken::Keyword::Using)
|
||||
return { };
|
||||
|
||||
if (op.op != Token::OperatorToken::Operator::Assignment)
|
||||
return { };
|
||||
|
||||
if (curr[-2].type == Token::Type::Type) {
|
||||
auto type = curr[-2].typeToken;
|
||||
|
||||
return new ASTNodeTypeDecl(type.type, name.identifier);
|
||||
} else if (curr[-2].type == Token::Type::Identifier) {
|
||||
auto customType = curr[-2].identifierToken;
|
||||
|
||||
return new ASTNodeTypeDecl(Token::TypeToken::Type::CustomType, name.identifier, customType.identifier);
|
||||
}
|
||||
|
||||
return { };
|
||||
}
|
||||
|
||||
std::optional<std::vector<ASTNode*>> parseStatement(TokenIter &curr) {
|
||||
std::vector<ASTNode*> program;
|
||||
|
||||
// Struct
|
||||
if (tryConsume(curr, { Token::Type::Keyword, Token::Type::Identifier, Token::Type::ScopeOpen })) {
|
||||
if (curr[-3].keywordToken.keyword == Token::KeywordToken::Keyword::Struct) {
|
||||
auto structAst = parseStruct(curr);
|
||||
|
||||
if (!structAst.has_value()) {
|
||||
for(auto &node : program) delete node;
|
||||
return { };
|
||||
}
|
||||
|
||||
program.push_back(structAst.value());
|
||||
}
|
||||
|
||||
return program;
|
||||
|
||||
// Scope
|
||||
} else if (tryConsume(curr, { Token::Type::ScopeOpen })) {
|
||||
program.push_back(parseScope(curr));
|
||||
|
||||
return program;
|
||||
|
||||
// Using declaration with built-in type
|
||||
} else if (tryConsume(curr, { Token::Type::Keyword, Token::Type::Identifier, Token::Type::Operator, Token::Type::Type, Token::Type::EndOfExpression})) {
|
||||
auto usingDecl = parseUsingDeclaration(curr);
|
||||
|
||||
if (!usingDecl.has_value()) {
|
||||
for(auto &node : program) delete node;
|
||||
return { };
|
||||
}
|
||||
|
||||
program.push_back(usingDecl.value());
|
||||
|
||||
return program;
|
||||
|
||||
// Using declaration with custom type
|
||||
} else if (tryConsume(curr, { Token::Type::Keyword, Token::Type::Identifier, Token::Type::Operator, Token::Type::Identifier, Token::Type::EndOfExpression})) {
|
||||
auto usingDecl = parseUsingDeclaration(curr);
|
||||
|
||||
if (!usingDecl.has_value()) {
|
||||
for(auto &node : program) delete node;
|
||||
return { };
|
||||
}
|
||||
|
||||
program.push_back(usingDecl.value());
|
||||
|
||||
return program;
|
||||
// Variable declaration with built-in type
|
||||
} else if (tryConsume(curr, { Token::Type::Type, Token::Type::Identifier, Token::Type::Operator, Token::Type::Integer, Token::Type::EndOfExpression})) {
|
||||
auto variableDecl = parseFreeBuiltinVariableDecl(curr);
|
||||
|
||||
program.push_back(variableDecl);
|
||||
|
||||
return program;
|
||||
|
||||
// Variable declaration with custom type
|
||||
} else if (tryConsume(curr, { Token::Type::Identifier, Token::Type::Identifier, Token::Type::Operator, Token::Type::Integer, Token::Type::EndOfExpression})) {
|
||||
auto variableDecl = parseFreeCustomTypeVariableDecl(curr);
|
||||
|
||||
program.push_back(variableDecl);
|
||||
|
||||
return program;
|
||||
}
|
||||
else {
|
||||
for(auto &node : program) delete node;
|
||||
return { };
|
||||
}
|
||||
}
|
||||
|
||||
std::vector<ASTNode*> parseTillToken(TokenIter &curr, Token::Type endTokenType) {
|
||||
std::vector<ASTNode*> program;
|
||||
|
||||
while (curr->type != endTokenType) {
|
||||
auto newTokens = parseStatement(curr);
|
||||
|
||||
if (!newTokens.has_value())
|
||||
break;
|
||||
|
||||
program.insert(program.end(), newTokens->begin(), newTokens->end());
|
||||
}
|
||||
|
||||
curr++;
|
||||
|
||||
return program;
|
||||
}
|
||||
|
||||
std::pair<Result, std::vector<ASTNode*>> Parser::parse(const std::vector<Token> &tokens) {
|
||||
auto currentToken = tokens.begin();
|
||||
|
||||
auto program = parseTillToken(currentToken, Token::Type::EndOfProgram);
|
||||
|
||||
if (program.empty())
|
||||
return { ResultParseError, { } };
|
||||
|
||||
return { ResultSuccess, program };
|
||||
}
|
||||
|
||||
}
|
||||
@@ -1,59 +0,0 @@
|
||||
#include "providers/file_provider.hpp"
|
||||
|
||||
#include <cstdio>
|
||||
|
||||
namespace hex::prv {
|
||||
|
||||
FileProvider::FileProvider(std::string_view path) {
|
||||
this->m_file = fopen(path.data(), "r+b");
|
||||
|
||||
this->m_readable = true;
|
||||
this->m_writable = true;
|
||||
|
||||
if (this->m_file == nullptr) {
|
||||
this->m_file = fopen(path.data(), "rb");
|
||||
this->m_writable = false;
|
||||
}
|
||||
}
|
||||
|
||||
FileProvider::~FileProvider() {
|
||||
if (this->m_file != nullptr)
|
||||
fclose(this->m_file);
|
||||
}
|
||||
|
||||
|
||||
bool FileProvider::isAvailable() {
|
||||
return this->m_file != nullptr;
|
||||
}
|
||||
|
||||
bool FileProvider::isReadable() {
|
||||
return isAvailable() && this->m_readable;
|
||||
}
|
||||
|
||||
bool FileProvider::isWritable() {
|
||||
return isAvailable() && this->m_writable;
|
||||
}
|
||||
|
||||
|
||||
void FileProvider::read(u64 offset, void *buffer, size_t size) {
|
||||
if ((offset + size) > this->getSize() || buffer == nullptr || size == 0)
|
||||
return;
|
||||
|
||||
fseek(this->m_file, offset, SEEK_SET);
|
||||
fread(buffer, 1, size, this->m_file);
|
||||
}
|
||||
|
||||
void FileProvider::write(u64 offset, void *buffer, size_t size) {
|
||||
if (buffer == nullptr || size == 0)
|
||||
return;
|
||||
|
||||
fseek(this->m_file, offset, SEEK_SET);
|
||||
fwrite(buffer, 1, size, this->m_file);
|
||||
}
|
||||
|
||||
size_t FileProvider::getSize() {
|
||||
fseek(this->m_file, 0, SEEK_END);
|
||||
return ftell(this->m_file);
|
||||
}
|
||||
|
||||
}
|
||||
117
source/providers/file_provider.cpp
Normal file
117
source/providers/file_provider.cpp
Normal file
@@ -0,0 +1,117 @@
|
||||
#include "providers/file_provider.hpp"
|
||||
|
||||
#undef __STRICT_ANSI__
|
||||
#include <cstdio>
|
||||
|
||||
#include <sys/stat.h>
|
||||
#include <time.h>
|
||||
|
||||
#include "helpers/utils.hpp"
|
||||
#include "helpers/project_file_handler.hpp"
|
||||
|
||||
|
||||
#ifdef __APPLE__
|
||||
#define off64_t off_t
|
||||
#define fopen64 fopen
|
||||
#define fseeko64 fseek
|
||||
#define ftello64 ftell
|
||||
#endif
|
||||
|
||||
namespace hex::prv {
|
||||
|
||||
FileProvider::FileProvider(std::string_view path) : Provider(), m_path(path) {
|
||||
this->m_fileStatsValid = stat(path.data(), &this->m_fileStats) == 0;
|
||||
|
||||
this->m_file = fopen64(path.data(), "r+b");
|
||||
|
||||
this->m_readable = true;
|
||||
this->m_writable = true;
|
||||
|
||||
if (this->m_file == nullptr) {
|
||||
this->m_file = fopen64(path.data(), "rb");
|
||||
this->m_writable = false;
|
||||
}
|
||||
|
||||
if (this->m_file != nullptr)
|
||||
ProjectFile::setFilePath(path);
|
||||
}
|
||||
|
||||
FileProvider::~FileProvider() {
|
||||
if (this->m_file != nullptr)
|
||||
fclose(this->m_file);
|
||||
}
|
||||
|
||||
|
||||
bool FileProvider::isAvailable() {
|
||||
return this->m_file != nullptr;
|
||||
}
|
||||
|
||||
bool FileProvider::isReadable() {
|
||||
return isAvailable() && this->m_readable;
|
||||
}
|
||||
|
||||
bool FileProvider::isWritable() {
|
||||
return isAvailable() && this->m_writable;
|
||||
}
|
||||
|
||||
|
||||
void FileProvider::read(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);
|
||||
size_t readSize = fread(buffer, 1, size, this->m_file);
|
||||
|
||||
|
||||
|
||||
for (u64 i = 0; i < readSize; i++)
|
||||
if (this->m_patches.back().contains(offset + i))
|
||||
reinterpret_cast<u8*>(buffer)[i] = this->m_patches.back()[offset + i];
|
||||
}
|
||||
|
||||
void FileProvider::write(u64 offset, const void *buffer, size_t size) {
|
||||
if (buffer == nullptr || size == 0)
|
||||
return;
|
||||
|
||||
this->m_patches.push_back(this->m_patches.back());
|
||||
|
||||
for (u64 i = 0; i < size; i++)
|
||||
this->m_patches.back()[offset + i] = reinterpret_cast<const u8*>(buffer)[i];
|
||||
}
|
||||
|
||||
void FileProvider::readRaw(u64 offset, void *buffer, size_t size) {
|
||||
if ((offset + size) > this->getSize() || buffer == nullptr || size == 0)
|
||||
return;
|
||||
|
||||
fseeko64(this->m_file, this->getCurrentPage() * PageSize + offset, SEEK_SET);
|
||||
fread(buffer, 1, size, this->m_file);
|
||||
}
|
||||
|
||||
void FileProvider::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() {
|
||||
fseeko64(this->m_file, 0, SEEK_END);
|
||||
return ftello64(this->m_file);
|
||||
}
|
||||
|
||||
std::vector<std::pair<std::string, std::string>> FileProvider::getDataInformation() {
|
||||
std::vector<std::pair<std::string, std::string>> result;
|
||||
|
||||
result.emplace_back("File path", this->m_path);
|
||||
result.emplace_back("Size", hex::toByteString(this->getActualSize()));
|
||||
|
||||
if (this->m_fileStatsValid) {
|
||||
result.emplace_back("Creation time", ctime(&this->m_fileStats.st_ctime));
|
||||
result.emplace_back("Last access time", ctime(&this->m_fileStats.st_atime));
|
||||
result.emplace_back("Last modification time", ctime(&this->m_fileStats.st_mtime));
|
||||
}
|
||||
|
||||
return result;
|
||||
}
|
||||
|
||||
}
|
||||
@@ -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() {
|
||||
|
||||
}
|
||||
|
||||
}
|
||||
159
source/views/view_data_inspector.cpp
Normal file
159
source/views/view_data_inspector.cpp
Normal file
@@ -0,0 +1,159 @@
|
||||
#include "views/view_data_inspector.hpp"
|
||||
|
||||
#include "providers/provider.hpp"
|
||||
#include "helpers/utils.hpp"
|
||||
|
||||
#include <cstring>
|
||||
|
||||
extern int ImTextCharFromUtf8(unsigned int* out_char, const char* in_text, const char* in_text_end);
|
||||
|
||||
namespace hex {
|
||||
|
||||
ViewDataInspector::ViewDataInspector(prv::Provider* &dataProvider) : View("Data Inspector"), m_dataProvider(dataProvider) {
|
||||
View::subscribeEvent(Events::RegionSelected, [this](const void* userData){
|
||||
Region region = *static_cast<const Region*>(userData);
|
||||
|
||||
this->m_validBytes = std::min(u64(this->m_dataProvider->getSize() - region.address), u64(sizeof(PreviewData)));
|
||||
std::memset(&this->m_previewData, 0x00, sizeof(PreviewData));
|
||||
this->m_dataProvider->read(region.address, &this->m_previewData, this->m_validBytes);
|
||||
|
||||
this->m_shouldInvalidate = true;
|
||||
});
|
||||
}
|
||||
|
||||
ViewDataInspector::~ViewDataInspector() {
|
||||
View::unsubscribeEvent(Events::RegionSelected);
|
||||
}
|
||||
|
||||
void ViewDataInspector::createView() {
|
||||
if (this->m_shouldInvalidate) {
|
||||
this->m_shouldInvalidate = false;
|
||||
|
||||
this->m_cachedData.clear();
|
||||
|
||||
{
|
||||
std::string binary;
|
||||
for (u8 i = 0; i < 8; i++)
|
||||
binary += ((this->m_previewData.unsigned8 << i) & 0x80) == 0 ? '0' : '1';
|
||||
this->m_cachedData.emplace_back("Binary (8 bit)", binary);
|
||||
}
|
||||
|
||||
this->m_cachedData.emplace_back("uint8_t", hex::format("%u", hex::changeEndianess(this->m_previewData.unsigned8, this->m_endianess)));
|
||||
this->m_cachedData.emplace_back("int8_t", hex::format("%d", hex::changeEndianess(this->m_previewData.signed8, this->m_endianess)));
|
||||
this->m_cachedData.emplace_back("uint16_t", hex::format("%u", hex::changeEndianess(this->m_previewData.unsigned16, this->m_endianess)));
|
||||
this->m_cachedData.emplace_back("int16_t", hex::format("%d", hex::changeEndianess(this->m_previewData.signed16, this->m_endianess)));
|
||||
this->m_cachedData.emplace_back("uint32_t", hex::format("%lu", hex::changeEndianess(this->m_previewData.unsigned32, this->m_endianess)));
|
||||
this->m_cachedData.emplace_back("int32_t", hex::format("%ld", hex::changeEndianess(this->m_previewData.signed32, this->m_endianess)));
|
||||
this->m_cachedData.emplace_back("uint64_t", hex::format("%llu", hex::changeEndianess(this->m_previewData.unsigned64, this->m_endianess)));
|
||||
this->m_cachedData.emplace_back("int64_t", hex::format("%lld", hex::changeEndianess(this->m_previewData.signed64, this->m_endianess)));
|
||||
|
||||
this->m_cachedData.emplace_back("ASCII Character", hex::format("'%s'", makePrintable(this->m_previewData.ansiChar).c_str()));
|
||||
this->m_cachedData.emplace_back("Wide Character", hex::format("'%lc'", this->m_previewData.wideChar == 0 ? '\x01' : hex::changeEndianess(this->m_previewData.wideChar, this->m_endianess)));
|
||||
{
|
||||
char buffer[5] = { 0 };
|
||||
char codepointString[5] = { 0 };
|
||||
u32 codepoint = 0;
|
||||
|
||||
std::memcpy(buffer, &this->m_previewData.utf8Char, 4);
|
||||
u8 codepointSize = ImTextCharFromUtf8(&codepoint, buffer, buffer + 4);
|
||||
|
||||
std::memcpy(codepointString, &codepoint, std::min(codepointSize, u8(4)));
|
||||
this->m_cachedData.emplace_back("UTF-8 code point", hex::format("'%s' (U+%04lx)", codepoint == 0xFFFD ? "Invalid" : codepointString, codepoint));
|
||||
}
|
||||
|
||||
this->m_cachedData.emplace_back("float (32 bit)", hex::format("%e", hex::changeEndianess(this->m_previewData.float32, this->m_endianess)));
|
||||
this->m_cachedData.emplace_back("double (64 bit)", hex::format("%e", hex::changeEndianess(this->m_previewData.float64, this->m_endianess)));
|
||||
|
||||
#if defined(_WIN64)
|
||||
{
|
||||
auto endianAdjustedTime = hex::changeEndianess(this->m_previewData.time32, this->m_endianess);
|
||||
std::tm * ptm = _localtime32(&endianAdjustedTime);
|
||||
char buffer[32];
|
||||
if (ptm != nullptr && std::strftime(buffer, 32, "%a, %d.%m.%Y %H:%M:%S", ptm))
|
||||
this->m_cachedData.emplace_back("__time32_t", buffer);
|
||||
else
|
||||
this->m_cachedData.emplace_back("__time32_t", "Invalid");
|
||||
}
|
||||
|
||||
{
|
||||
auto endianAdjustedTime = hex::changeEndianess(this->m_previewData.time64, this->m_endianess);
|
||||
std::tm * ptm = _localtime64(&endianAdjustedTime);
|
||||
char buffer[64];
|
||||
if (ptm != nullptr && std::strftime(buffer, 64, "%a, %d.%m.%Y %H:%M:%S", ptm) != 0)
|
||||
this->m_cachedData.emplace_back("__time64_t", buffer);
|
||||
else
|
||||
this->m_cachedData.emplace_back("__time64_t", "Invalid");
|
||||
}
|
||||
#else
|
||||
{
|
||||
auto endianAdjustedTime = hex::changeEndianess(this->m_previewData.time, this->m_endianess);
|
||||
std::tm * ptm = localtime(&endianAdjustedTime);
|
||||
char buffer[64];
|
||||
if (ptm != nullptr && std::strftime(buffer, 64, "%a, %d.%m.%Y %H:%M:%S", ptm) != 0)
|
||||
this->m_cachedData.emplace_back("time_t", buffer);
|
||||
else
|
||||
this->m_cachedData.emplace_back("time_t", "Invalid");
|
||||
}
|
||||
#endif
|
||||
|
||||
this->m_cachedData.emplace_back("GUID", hex::format("%s{%08lX-%04hX-%04hX-%02hhX%02hhX-%02hhX%02hhX%02hhX%02hhX%02hhX%02hhX}",
|
||||
(this->m_previewData.guid.data3 >> 12) <= 5 && ((this->m_previewData.guid.data4[0] >> 4) >= 8 || (this->m_previewData.guid.data4[0] >> 4) == 0) ? "" : "[INVALID] ",
|
||||
hex::changeEndianess(this->m_previewData.guid.data1, this->m_endianess),
|
||||
hex::changeEndianess(this->m_previewData.guid.data2, this->m_endianess),
|
||||
hex::changeEndianess(this->m_previewData.guid.data3, this->m_endianess),
|
||||
this->m_previewData.guid.data4[0], this->m_previewData.guid.data4[1], this->m_previewData.guid.data4[2], this->m_previewData.guid.data4[3],
|
||||
this->m_previewData.guid.data4[4], this->m_previewData.guid.data4[5], this->m_previewData.guid.data4[6], this->m_previewData.guid.data4[7]));
|
||||
}
|
||||
|
||||
|
||||
if (ImGui::Begin("Data Inspector", &this->getWindowOpenState(), ImGuiWindowFlags_NoCollapse)) {
|
||||
if (this->m_dataProvider != nullptr && this->m_dataProvider->isReadable()) {
|
||||
if (ImGui::BeginChild("##scrolling", ImVec2(0, ImGui::GetWindowHeight() - 60))) {
|
||||
if (ImGui::BeginTable("##datainspector", 2, ImGuiTableFlags_Borders | ImGuiTableFlags_Resizable | ImGuiTableFlags_RowBg | ImGuiTableFlags_NoBordersInBody)) {
|
||||
ImGui::TableSetupColumn("Name");
|
||||
ImGui::TableSetupColumn("Value");
|
||||
|
||||
ImGui::TableHeadersRow();
|
||||
|
||||
for (const auto &[name, value] : this->m_cachedData) {
|
||||
ImGui::TableNextRow();
|
||||
ImGui::TableNextColumn();
|
||||
ImGui::TextUnformatted(name.c_str());
|
||||
ImGui::TableNextColumn();
|
||||
ImGui::TextUnformatted(value.c_str());
|
||||
}
|
||||
|
||||
{
|
||||
ImGui::TableNextRow();
|
||||
ImGui::TableNextColumn();
|
||||
ImGui::TextUnformatted("RGBA Color");
|
||||
ImGui::TableNextColumn();
|
||||
ImGui::ColorButton("##nolabel", ImColor(hex::changeEndianess(this->m_previewData.unsigned32, this->m_endianess)),
|
||||
ImGuiColorEditFlags_None, ImVec2(ImGui::GetColumnWidth(), 15));
|
||||
}
|
||||
|
||||
|
||||
ImGui::EndTable();
|
||||
}
|
||||
}
|
||||
ImGui::EndChild();
|
||||
|
||||
if (ImGui::RadioButton("Little Endian", this->m_endianess == std::endian::little)) {
|
||||
this->m_endianess = std::endian::little;
|
||||
this->m_shouldInvalidate = true;
|
||||
}
|
||||
ImGui::SameLine();
|
||||
if (ImGui::RadioButton("Big Endian", this->m_endianess == std::endian::big)) {
|
||||
this->m_endianess = std::endian::big;
|
||||
this->m_shouldInvalidate = true;
|
||||
}
|
||||
}
|
||||
}
|
||||
ImGui::End();
|
||||
}
|
||||
|
||||
void ViewDataInspector::createMenu() {
|
||||
|
||||
}
|
||||
|
||||
}
|
||||
289
source/views/view_disassembler.cpp
Normal file
289
source/views/view_disassembler.cpp
Normal file
@@ -0,0 +1,289 @@
|
||||
#include "views/view_disassembler.hpp"
|
||||
|
||||
#include "providers/provider.hpp"
|
||||
#include "helpers/utils.hpp"
|
||||
|
||||
#include <cstring>
|
||||
|
||||
using namespace std::literals::string_literals;
|
||||
|
||||
namespace hex {
|
||||
|
||||
ViewDisassembler::ViewDisassembler(prv::Provider* &dataProvider) : View("Disassembler"), m_dataProvider(dataProvider) {
|
||||
View::subscribeEvent(Events::DataChanged, [this](const void*){
|
||||
this->m_shouldInvalidate = true;
|
||||
});
|
||||
|
||||
View::subscribeEvent(Events::RegionSelected, [this](const void *userData) {
|
||||
Region region = *static_cast<const Region*>(userData);
|
||||
|
||||
if (this->m_shouldMatchSelection) {
|
||||
this->m_codeRegion[0] = region.address;
|
||||
this->m_codeRegion[1] = region.address + region.size - 1;
|
||||
}
|
||||
});
|
||||
}
|
||||
|
||||
ViewDisassembler::~ViewDisassembler() {
|
||||
View::unsubscribeEvent(Events::DataChanged);
|
||||
View::unsubscribeEvent(Events::RegionSelected);
|
||||
}
|
||||
|
||||
void ViewDisassembler::createView() {
|
||||
if (this->m_shouldInvalidate) {
|
||||
this->m_disassembly.clear();
|
||||
|
||||
csh capstoneHandle;
|
||||
cs_insn *instructions = nullptr;
|
||||
|
||||
cs_mode mode = cs_mode(this->m_modeBasicARM | this->m_modeExtraARM | this->m_modeBasicMIPS | this->m_modeBasicX86 | this->m_modeBasicPPC);
|
||||
|
||||
if (this->m_littleEndianMode)
|
||||
mode = cs_mode(mode | CS_MODE_LITTLE_ENDIAN);
|
||||
else
|
||||
mode = cs_mode(mode | CS_MODE_BIG_ENDIAN);
|
||||
|
||||
if (this->m_micoMode)
|
||||
mode = cs_mode(mode | CS_MODE_MICRO);
|
||||
|
||||
if (this->m_sparcV9Mode)
|
||||
mode = cs_mode(mode | CS_MODE_V9);
|
||||
|
||||
if (cs_open(Disassembler::toCapstoneArchictecture(this->m_architecture), mode, &capstoneHandle) == CS_ERR_OK) {
|
||||
|
||||
std::vector<u8> buffer(2048, 0x00);
|
||||
for (u64 address = 0; address < (this->m_codeRegion[1] - this->m_codeRegion[0] + 1); address += 2048) {
|
||||
size_t bufferSize = std::min(u64(2048), (this->m_codeRegion[1] - this->m_codeRegion[0] + 1) - address);
|
||||
this->m_dataProvider->read(this->m_codeRegion[0] + address, buffer.data(), bufferSize);
|
||||
|
||||
size_t instructionCount = cs_disasm(capstoneHandle, buffer.data(), bufferSize, this->m_baseAddress + address, 0, &instructions);
|
||||
|
||||
if (instructionCount == 0)
|
||||
break;
|
||||
|
||||
u64 usedBytes = 0;
|
||||
for (u32 instr = 0; instr < instructionCount; instr++) {
|
||||
Disassembly disassembly = { 0 };
|
||||
disassembly.address = instructions[instr].address;
|
||||
disassembly.offset = this->m_codeRegion[0] + address + usedBytes;
|
||||
disassembly.size = instructions[instr].size;
|
||||
disassembly.mnemonic = instructions[instr].mnemonic;
|
||||
disassembly.operators = instructions[instr].op_str;
|
||||
|
||||
for (u8 i = 0; i < instructions[instr].size; i++)
|
||||
disassembly.bytes += hex::format("%02X ", instructions[instr].bytes[i]);
|
||||
disassembly.bytes.pop_back();
|
||||
|
||||
this->m_disassembly.push_back(disassembly);
|
||||
|
||||
usedBytes += instructions[instr].size;
|
||||
}
|
||||
|
||||
if (instructionCount < bufferSize)
|
||||
address -= (bufferSize - usedBytes);
|
||||
|
||||
cs_free(instructions, instructionCount);
|
||||
}
|
||||
|
||||
cs_close(&capstoneHandle);
|
||||
}
|
||||
|
||||
this->m_shouldInvalidate = false;
|
||||
}
|
||||
|
||||
|
||||
if (ImGui::Begin("Disassembler", &this->getWindowOpenState(), ImGuiWindowFlags_NoCollapse)) {
|
||||
|
||||
if (this->m_dataProvider != nullptr && this->m_dataProvider->isReadable()) {
|
||||
ImGui::TextUnformatted("Position");
|
||||
ImGui::Separator();
|
||||
|
||||
ImGui::InputScalar("Base address", ImGuiDataType_U64, &this->m_baseAddress, nullptr, nullptr, "%08llX", ImGuiInputTextFlags_CharsHexadecimal);
|
||||
ImGui::InputScalarN("Code region", ImGuiDataType_U64, this->m_codeRegion, 2, nullptr, nullptr, "%08llX", ImGuiInputTextFlags_CharsHexadecimal);
|
||||
ImGui::Checkbox("Match selection", &this->m_shouldMatchSelection);
|
||||
|
||||
ImGui::NewLine();
|
||||
ImGui::TextUnformatted("Settings");
|
||||
ImGui::Separator();
|
||||
|
||||
ImGui::Combo("Architecture", reinterpret_cast<int*>(&this->m_architecture), Disassembler::ArchitectureNames, Disassembler::getArchitectureSupportedCount());
|
||||
|
||||
|
||||
if (ImGui::BeginChild("modes", ImVec2(0, 100), true)) {
|
||||
|
||||
if (ImGui::RadioButton("Little Endian", this->m_littleEndianMode))
|
||||
this->m_littleEndianMode = true;
|
||||
ImGui::SameLine();
|
||||
if (ImGui::RadioButton("Big Endian", !this->m_littleEndianMode))
|
||||
this->m_littleEndianMode = false;
|
||||
|
||||
ImGui::NewLine();
|
||||
|
||||
switch (this->m_architecture) {
|
||||
case Architecture::ARM:
|
||||
this->m_modeBasicMIPS = cs_mode(0);
|
||||
this->m_modeBasicX86 = cs_mode(0);
|
||||
this->m_modeBasicPPC = cs_mode(0);
|
||||
this->m_micoMode = false;
|
||||
this->m_sparcV9Mode = false;
|
||||
|
||||
if (this->m_modeBasicARM == cs_mode(0))
|
||||
this->m_modeBasicARM = CS_MODE_ARM;
|
||||
|
||||
if (ImGui::RadioButton("ARM mode", this->m_modeBasicARM == CS_MODE_ARM))
|
||||
this->m_modeBasicARM = CS_MODE_ARM;
|
||||
ImGui::SameLine();
|
||||
if (ImGui::RadioButton("Thumb mode", this->m_modeBasicARM == CS_MODE_THUMB))
|
||||
this->m_modeBasicARM = CS_MODE_THUMB;
|
||||
|
||||
if (ImGui::RadioButton("Default mode", (this->m_modeExtraARM & (CS_MODE_MCLASS | CS_MODE_V8)) == 0))
|
||||
this->m_modeExtraARM = cs_mode(0);
|
||||
ImGui::SameLine();
|
||||
if (ImGui::RadioButton("Cortex-M mode", (this->m_modeExtraARM & (CS_MODE_MCLASS | CS_MODE_V8)) == CS_MODE_MCLASS))
|
||||
this->m_modeExtraARM = CS_MODE_MCLASS;
|
||||
ImGui::SameLine();
|
||||
if (ImGui::RadioButton("ARMv8 mode", (this->m_modeExtraARM & (CS_MODE_MCLASS | CS_MODE_V8)) == CS_MODE_V8))
|
||||
this->m_modeExtraARM = CS_MODE_V8;
|
||||
break;
|
||||
case Architecture::MIPS:
|
||||
this->m_modeBasicARM = cs_mode(0);
|
||||
this->m_modeExtraARM = cs_mode(0);
|
||||
this->m_modeBasicX86 = cs_mode(0);
|
||||
this->m_modeBasicPPC = cs_mode(0);
|
||||
this->m_sparcV9Mode = false;
|
||||
|
||||
if (this->m_modeBasicMIPS == cs_mode(0))
|
||||
this->m_modeBasicMIPS = CS_MODE_MIPS32;
|
||||
|
||||
if (ImGui::RadioButton("MIPS32 mode", this->m_modeBasicMIPS == CS_MODE_MIPS32))
|
||||
this->m_modeBasicMIPS = CS_MODE_MIPS32;
|
||||
ImGui::SameLine();
|
||||
if (ImGui::RadioButton("MIPS64 mode", this->m_modeBasicMIPS == CS_MODE_MIPS64))
|
||||
this->m_modeBasicMIPS = CS_MODE_MIPS64;
|
||||
ImGui::SameLine();
|
||||
if (ImGui::RadioButton("MIPS32R6 mode", this->m_modeBasicMIPS == CS_MODE_MIPS32R6))
|
||||
this->m_modeBasicMIPS = CS_MODE_MIPS32R6;
|
||||
|
||||
ImGui::Checkbox("Micro Mode", &this->m_micoMode);
|
||||
break;
|
||||
case Architecture::X86:
|
||||
this->m_modeBasicARM = cs_mode(0);
|
||||
this->m_modeExtraARM = cs_mode(0);
|
||||
this->m_modeBasicMIPS = cs_mode(0);
|
||||
this->m_modeBasicPPC = cs_mode(0);
|
||||
this->m_micoMode = false;
|
||||
this->m_sparcV9Mode = false;
|
||||
|
||||
if (this->m_modeBasicX86 == cs_mode(0))
|
||||
this->m_modeBasicX86 = CS_MODE_16;
|
||||
|
||||
if (ImGui::RadioButton("16-bit mode", this->m_modeBasicX86 == CS_MODE_16))
|
||||
this->m_modeBasicX86 = CS_MODE_16;
|
||||
ImGui::SameLine();
|
||||
if (ImGui::RadioButton("32-bit mode", this->m_modeBasicX86 == CS_MODE_32))
|
||||
this->m_modeBasicX86 = CS_MODE_32;
|
||||
ImGui::SameLine();
|
||||
if (ImGui::RadioButton("64-bit mode", this->m_modeBasicX86 == CS_MODE_64))
|
||||
this->m_modeBasicX86 = CS_MODE_64;
|
||||
break;
|
||||
case Architecture::PPC:
|
||||
this->m_modeBasicARM = cs_mode(0);
|
||||
this->m_modeExtraARM = cs_mode(0);
|
||||
this->m_modeBasicMIPS = cs_mode(0);
|
||||
this->m_modeBasicX86 = cs_mode(0);
|
||||
this->m_micoMode = false;
|
||||
this->m_sparcV9Mode = false;
|
||||
|
||||
if (m_modeBasicPPC == cs_mode(0))
|
||||
this->m_modeBasicPPC = CS_MODE_32;
|
||||
|
||||
if (ImGui::RadioButton("32-bit mode", this->m_modeBasicPPC == CS_MODE_32))
|
||||
this->m_modeBasicPPC = CS_MODE_32;
|
||||
ImGui::SameLine();
|
||||
if (ImGui::RadioButton("64-bit mode", this->m_modeBasicPPC == CS_MODE_64))
|
||||
this->m_modeBasicPPC = CS_MODE_64;
|
||||
break;
|
||||
case Architecture::SPARC:
|
||||
this->m_modeBasicARM = cs_mode(0);
|
||||
this->m_modeExtraARM = cs_mode(0);
|
||||
this->m_modeBasicMIPS = cs_mode(0);
|
||||
this->m_modeBasicX86 = cs_mode(0);
|
||||
this->m_modeBasicPPC = cs_mode(0);
|
||||
this->m_micoMode = false;
|
||||
|
||||
ImGui::Checkbox("Sparc V9 mode", &this->m_sparcV9Mode);
|
||||
break;
|
||||
case Architecture::ARM64:
|
||||
case Architecture::SYSZ:
|
||||
case Architecture::XCORE:
|
||||
case Architecture::M68K:
|
||||
case Architecture::TMS320C64X:
|
||||
case Architecture::M680X:
|
||||
case Architecture::EVM:
|
||||
this->m_modeBasicARM = cs_mode(0);
|
||||
this->m_modeExtraARM = cs_mode(0);
|
||||
this->m_modeBasicMIPS = cs_mode(0);
|
||||
this->m_modeBasicX86 = cs_mode(0);
|
||||
this->m_modeBasicPPC = cs_mode(0);
|
||||
this->m_micoMode = false;
|
||||
this->m_sparcV9Mode = false;
|
||||
break;
|
||||
}
|
||||
|
||||
}
|
||||
ImGui::EndChild();
|
||||
|
||||
ImGui::SetCursorPosX((ImGui::GetContentRegionAvailWidth() - 300) / 2);
|
||||
if (ImGui::Button("Disassemble", ImVec2(300, 20)))
|
||||
this->m_shouldInvalidate = true;
|
||||
ImGui::NewLine();
|
||||
|
||||
ImGui::TextUnformatted("Disassembly");
|
||||
ImGui::Separator();
|
||||
|
||||
if (ImGui::BeginTable("##disassembly", 4, ImGuiTableFlags_ScrollY | ImGuiTableFlags_Borders | ImGuiTableFlags_Resizable | ImGuiTableFlags_RowBg | ImGuiTableFlags_Reorderable)) {
|
||||
ImGui::TableSetupScrollFreeze(0, 1);
|
||||
ImGui::TableSetupColumn("Address");
|
||||
ImGui::TableSetupColumn("Offset");
|
||||
ImGui::TableSetupColumn("Bytes");
|
||||
ImGui::TableSetupColumn("Disassembly");
|
||||
|
||||
ImGuiListClipper clipper;
|
||||
clipper.Begin(this->m_disassembly.size());
|
||||
|
||||
ImGui::TableHeadersRow();
|
||||
while (clipper.Step()) {
|
||||
for (u64 i = clipper.DisplayStart; i < clipper.DisplayEnd; i++) {
|
||||
ImGui::TableNextRow();
|
||||
ImGui::TableNextColumn();
|
||||
if (ImGui::Selectable(("##DisassemblyLine"s + std::to_string(i)).c_str(), false, ImGuiSelectableFlags_SpanAllColumns)) {
|
||||
Region selectRegion = { this->m_disassembly[i].offset, this->m_disassembly[i].size };
|
||||
View::postEvent(Events::SelectionChangeRequest, &selectRegion);
|
||||
}
|
||||
ImGui::SameLine();
|
||||
ImGui::Text("0x%llx", this->m_disassembly[i].address);
|
||||
ImGui::TableNextColumn();
|
||||
ImGui::Text("0x%llx", this->m_disassembly[i].offset);
|
||||
ImGui::TableNextColumn();
|
||||
ImGui::TextUnformatted(this->m_disassembly[i].bytes.c_str());
|
||||
ImGui::TableNextColumn();
|
||||
ImGui::TextColored(ImColor(0xFFD69C56), "%s", this->m_disassembly[i].mnemonic.c_str());
|
||||
ImGui::SameLine();
|
||||
ImGui::TextUnformatted(this->m_disassembly[i].operators.c_str());
|
||||
}
|
||||
}
|
||||
|
||||
clipper.End();
|
||||
|
||||
ImGui::EndTable();
|
||||
}
|
||||
}
|
||||
}
|
||||
ImGui::End();
|
||||
}
|
||||
|
||||
void ViewDisassembler::createMenu() {
|
||||
|
||||
}
|
||||
|
||||
}
|
||||
@@ -2,65 +2,74 @@
|
||||
|
||||
#include "providers/provider.hpp"
|
||||
|
||||
#include "crypto.hpp"
|
||||
#include "helpers/crypto.hpp"
|
||||
|
||||
#include <vector>
|
||||
|
||||
#include "helpers/utils.hpp"
|
||||
|
||||
namespace hex {
|
||||
|
||||
ViewHashes::ViewHashes(prv::Provider* &dataProvider) : View(), m_dataProvider(dataProvider) {
|
||||
View::subscribeEvent(Events::DataChanged, [this](void*){
|
||||
ViewHashes::ViewHashes(prv::Provider* &dataProvider) : View("Hashes"), m_dataProvider(dataProvider) {
|
||||
View::subscribeEvent(Events::DataChanged, [this](const void*){
|
||||
this->m_shouldInvalidate = true;
|
||||
});
|
||||
|
||||
View::subscribeEvent(Events::RegionSelected, [this](const void *userData) {
|
||||
Region region = *static_cast<const Region*>(userData);
|
||||
|
||||
if (this->m_shouldMatchSelection) {
|
||||
this->m_hashRegion[0] = region.address;
|
||||
this->m_hashRegion[1] = region.address + region.size - 1;
|
||||
this->m_shouldInvalidate = true;
|
||||
}
|
||||
});
|
||||
}
|
||||
|
||||
ViewHashes::~ViewHashes() {
|
||||
View::unsubscribeEvent(Events::DataChanged);
|
||||
View::unsubscribeEvent(Events::RegionSelected);
|
||||
}
|
||||
|
||||
|
||||
static void formatBigHexInt(auto dataArray, char *buffer, size_t bufferSize) {
|
||||
for (int i = 0; i < dataArray.size(); i++)
|
||||
snprintf(buffer + 8 * i, bufferSize - 8 * i, "%08X", __builtin_bswap32(dataArray[i]));
|
||||
snprintf(buffer + 8 * i, bufferSize - 8 * i, "%08X", hex::changeEndianess(dataArray[i], std::endian::big));
|
||||
}
|
||||
|
||||
void ViewHashes::createView() {
|
||||
if (!this->m_windowOpen)
|
||||
return;
|
||||
|
||||
if (ImGui::Begin("Hashing", &this->m_windowOpen)) {
|
||||
if (ImGui::Begin("Hashing", &this->getWindowOpenState(), ImGuiWindowFlags_NoCollapse)) {
|
||||
ImGui::BeginChild("##scrolling", ImVec2(0, 0), false, ImGuiWindowFlags_NoMove | ImGuiWindowFlags_NoNav);
|
||||
ImGui::NewLine();
|
||||
|
||||
if (this->m_dataProvider != nullptr && this->m_dataProvider->isAvailable()) {
|
||||
|
||||
ImGui::TextUnformatted("Region");
|
||||
ImGui::Separator();
|
||||
|
||||
ImGui::InputScalarN("##nolabel", ImGuiDataType_U64, this->m_hashRegion, 2, nullptr, nullptr, "%08X", ImGuiInputTextFlags_CharsHexadecimal);
|
||||
if (ImGui::IsItemEdited()) this->m_shouldInvalidate = true;
|
||||
|
||||
ImGui::Checkbox("Match selection", &this->m_shouldMatchSelection);
|
||||
if (ImGui::IsItemEdited()) this->m_shouldInvalidate = true;
|
||||
|
||||
ImGui::NewLine();
|
||||
ImGui::TextUnformatted("Settings");
|
||||
ImGui::Separator();
|
||||
|
||||
if (ImGui::Combo("Hash Function", &this->m_currHashFunction, HashFunctionNames,sizeof(HashFunctionNames) / sizeof(const char *)))
|
||||
this->m_shouldInvalidate = true;
|
||||
|
||||
ImGui::NewLine();
|
||||
ImGui::Separator();
|
||||
ImGui::NewLine();
|
||||
|
||||
ImGui::InputInt("Begin", &this->m_hashStart, 0, 0, ImGuiInputTextFlags_CharsHexadecimal);
|
||||
if (ImGui::IsItemEdited()) this->m_shouldInvalidate = true;
|
||||
|
||||
ImGui::InputInt("End", &this->m_hashEnd, 0, 0, ImGuiInputTextFlags_CharsHexadecimal);
|
||||
if (ImGui::IsItemEdited()) this->m_shouldInvalidate = true;
|
||||
|
||||
size_t dataSize = this->m_dataProvider->getSize();
|
||||
if (this->m_hashEnd >= dataSize)
|
||||
this->m_hashEnd = dataSize - 1;
|
||||
if (this->m_hashRegion[1] >= dataSize)
|
||||
this->m_hashRegion[1] = dataSize - 1;
|
||||
|
||||
ImGui::NewLine();
|
||||
ImGui::Separator();
|
||||
ImGui::NewLine();
|
||||
|
||||
if (this->m_hashEnd >= this->m_hashStart) {
|
||||
if (this->m_hashRegion[1] >= this->m_hashRegion[0]) {
|
||||
|
||||
switch (this->m_currHashFunction) {
|
||||
case 0: // CRC16
|
||||
{
|
||||
int polynomial = 0, init = 0;
|
||||
static int polynomial = 0, init = 0;
|
||||
|
||||
ImGui::InputInt("Initial Value", &init, 0, 0, ImGuiInputTextFlags_CharsHexadecimal);
|
||||
if (ImGui::IsItemEdited()) this->m_shouldInvalidate = true;
|
||||
@@ -68,23 +77,24 @@ namespace hex {
|
||||
ImGui::InputInt("Polynomial", &polynomial, 0, 0, ImGuiInputTextFlags_CharsHexadecimal);
|
||||
if (ImGui::IsItemEdited()) this->m_shouldInvalidate = true;
|
||||
|
||||
ImGui::NewLine();
|
||||
ImGui::Separator();
|
||||
ImGui::NewLine();
|
||||
|
||||
static u16 result = 0;
|
||||
|
||||
if (this->m_shouldInvalidate)
|
||||
result = crc16(this->m_dataProvider, this->m_hashStart, this->m_hashEnd - this->m_hashStart + 1, polynomial, init);
|
||||
result = crc16(this->m_dataProvider, this->m_hashRegion[0], this->m_hashRegion[1] - this->m_hashRegion[0] + 1, polynomial, init);
|
||||
|
||||
char buffer[sizeof(result) * 2 + 1];
|
||||
snprintf(buffer, sizeof(buffer), "%04X", result);
|
||||
ImGui::InputText("Hash value", buffer, ImGuiInputTextFlags_ReadOnly);
|
||||
|
||||
ImGui::TextUnformatted("Result");
|
||||
ImGui::Separator();
|
||||
ImGui::InputText("##nolabel", buffer, ImGuiInputTextFlags_ReadOnly);
|
||||
}
|
||||
break;
|
||||
case 1: // CRC32
|
||||
{
|
||||
int polynomial = 0, init = 0;
|
||||
static int polynomial = 0, init = 0;
|
||||
|
||||
ImGui::InputInt("Initial Value", &init, 0, 0, ImGuiInputTextFlags_CharsHexadecimal);
|
||||
if (ImGui::IsItemEdited()) this->m_shouldInvalidate = true;
|
||||
@@ -92,18 +102,19 @@ namespace hex {
|
||||
ImGui::InputInt("Polynomial", &polynomial, 0, 0, ImGuiInputTextFlags_CharsHexadecimal);
|
||||
if (ImGui::IsItemEdited()) this->m_shouldInvalidate = true;
|
||||
|
||||
ImGui::NewLine();
|
||||
ImGui::Separator();
|
||||
ImGui::NewLine();
|
||||
|
||||
static u32 result = 0;
|
||||
|
||||
if (this->m_shouldInvalidate)
|
||||
result = crc32(this->m_dataProvider, this->m_hashStart, this->m_hashEnd - this->m_hashStart + 1, polynomial, init);
|
||||
result = crc32(this->m_dataProvider, this->m_hashRegion[0], this->m_hashRegion[1] - this->m_hashRegion[0] + 1, polynomial, init);
|
||||
|
||||
char buffer[sizeof(result) * 2 + 1];
|
||||
snprintf(buffer, sizeof(buffer), "%08X", result);
|
||||
ImGui::InputText("Hash value", buffer, ImGuiInputTextFlags_ReadOnly);
|
||||
|
||||
ImGui::TextUnformatted("Result");
|
||||
ImGui::Separator();
|
||||
ImGui::InputText("##nolabel", buffer, ImGuiInputTextFlags_ReadOnly);
|
||||
}
|
||||
break;
|
||||
case 2: // MD4
|
||||
@@ -111,11 +122,15 @@ namespace hex {
|
||||
static std::array<u32, 4> result;
|
||||
|
||||
if (this->m_shouldInvalidate)
|
||||
result = md4(this->m_dataProvider, this->m_hashStart, this->m_hashEnd - this->m_hashStart + 1);
|
||||
result = md4(this->m_dataProvider, this->m_hashRegion[0], this->m_hashRegion[1] - this->m_hashRegion[0] + 1);
|
||||
|
||||
char buffer[sizeof(result) * 2 + 1];
|
||||
formatBigHexInt(result, buffer, sizeof(buffer));
|
||||
ImGui::InputText("Hash value", buffer, ImGuiInputTextFlags_ReadOnly);
|
||||
|
||||
ImGui::NewLine();
|
||||
ImGui::TextUnformatted("Result");
|
||||
ImGui::Separator();
|
||||
ImGui::InputText("##nolabel", buffer, ImGuiInputTextFlags_ReadOnly);
|
||||
}
|
||||
break;
|
||||
case 3: // MD5
|
||||
@@ -123,11 +138,15 @@ namespace hex {
|
||||
static std::array<u32, 4> result = { 0 };
|
||||
|
||||
if (this->m_shouldInvalidate)
|
||||
result = md5(this->m_dataProvider, this->m_hashStart, this->m_hashEnd - this->m_hashStart + 1);
|
||||
result = md5(this->m_dataProvider, this->m_hashRegion[0], this->m_hashRegion[1] - this->m_hashRegion[0] + 1);
|
||||
|
||||
char buffer[sizeof(result) * 2 + 1];
|
||||
formatBigHexInt(result, buffer, sizeof(buffer));
|
||||
ImGui::InputText("Hash value", buffer, ImGuiInputTextFlags_ReadOnly);
|
||||
|
||||
ImGui::NewLine();
|
||||
ImGui::TextUnformatted("Result");
|
||||
ImGui::Separator();
|
||||
ImGui::InputText("##nolabel", buffer, ImGuiInputTextFlags_ReadOnly);
|
||||
}
|
||||
break;
|
||||
case 4: // SHA-1
|
||||
@@ -135,11 +154,15 @@ namespace hex {
|
||||
static std::array<u32, 5> result = { 0 };
|
||||
|
||||
if (this->m_shouldInvalidate)
|
||||
result = sha1(this->m_dataProvider, this->m_hashStart, this->m_hashEnd - this->m_hashStart + 1);
|
||||
result = sha1(this->m_dataProvider, this->m_hashRegion[0], this->m_hashRegion[1] - this->m_hashRegion[0] + 1);
|
||||
|
||||
char buffer[sizeof(result) * 2 + 1];
|
||||
formatBigHexInt(result, buffer, sizeof(buffer));
|
||||
ImGui::InputText("Hash value", buffer, ImGuiInputTextFlags_ReadOnly);
|
||||
|
||||
ImGui::NewLine();
|
||||
ImGui::TextUnformatted("Result");
|
||||
ImGui::Separator();
|
||||
ImGui::InputText("##nolabel", buffer, ImGuiInputTextFlags_ReadOnly);
|
||||
}
|
||||
break;
|
||||
case 5: // SHA-224
|
||||
@@ -147,11 +170,15 @@ namespace hex {
|
||||
static std::array<u32, 7> result = { 0 };
|
||||
|
||||
if (this->m_shouldInvalidate)
|
||||
result = sha224(this->m_dataProvider, this->m_hashStart, this->m_hashEnd - this->m_hashStart + 1);
|
||||
result = sha224(this->m_dataProvider, this->m_hashRegion[0], this->m_hashRegion[1] - this->m_hashRegion[0] + 1);
|
||||
|
||||
char buffer[sizeof(result) * 2 + 1];
|
||||
formatBigHexInt(result, buffer, sizeof(buffer));
|
||||
ImGui::InputText("Hash value", buffer, ImGuiInputTextFlags_ReadOnly);
|
||||
|
||||
ImGui::NewLine();
|
||||
ImGui::TextUnformatted("Result");
|
||||
ImGui::Separator();
|
||||
ImGui::InputText("##nolabel", buffer, ImGuiInputTextFlags_ReadOnly);
|
||||
}
|
||||
break;
|
||||
case 6: // SHA-256
|
||||
@@ -159,11 +186,15 @@ namespace hex {
|
||||
static std::array<u32, 8> result;
|
||||
|
||||
if (this->m_shouldInvalidate)
|
||||
result = sha256(this->m_dataProvider, this->m_hashStart, this->m_hashEnd - this->m_hashStart + 1);
|
||||
result = sha256(this->m_dataProvider, this->m_hashRegion[0], this->m_hashRegion[1] - this->m_hashRegion[0] + 1);
|
||||
|
||||
char buffer[sizeof(result) * 2 + 1];
|
||||
formatBigHexInt(result, buffer, sizeof(buffer));
|
||||
ImGui::InputText("Hash value", buffer, ImGuiInputTextFlags_ReadOnly);
|
||||
|
||||
ImGui::NewLine();
|
||||
ImGui::TextUnformatted("Result");
|
||||
ImGui::Separator();
|
||||
ImGui::InputText("##nolabel", buffer, ImGuiInputTextFlags_ReadOnly);
|
||||
}
|
||||
break;
|
||||
case 7: // SHA-384
|
||||
@@ -171,11 +202,15 @@ namespace hex {
|
||||
static std::array<u32, 12> result;
|
||||
|
||||
if (this->m_shouldInvalidate)
|
||||
result = sha384(this->m_dataProvider, this->m_hashStart, this->m_hashEnd - this->m_hashStart + 1);
|
||||
result = sha384(this->m_dataProvider, this->m_hashRegion[0], this->m_hashRegion[1] - this->m_hashRegion[0] + 1);
|
||||
|
||||
char buffer[sizeof(result) * 2 + 1];
|
||||
formatBigHexInt(result, buffer, sizeof(buffer));
|
||||
ImGui::InputText("Hash value", buffer, ImGuiInputTextFlags_ReadOnly);
|
||||
|
||||
ImGui::NewLine();
|
||||
ImGui::TextUnformatted("Result");
|
||||
ImGui::Separator();
|
||||
ImGui::InputText("##nolabel", buffer, ImGuiInputTextFlags_ReadOnly);
|
||||
}
|
||||
break;
|
||||
case 8: // SHA-512
|
||||
@@ -183,11 +218,15 @@ namespace hex {
|
||||
static std::array<u32, 16> result;
|
||||
|
||||
if (this->m_shouldInvalidate)
|
||||
result = sha512(this->m_dataProvider, this->m_hashStart, this->m_hashEnd - this->m_hashStart + 1);
|
||||
result = sha512(this->m_dataProvider, this->m_hashRegion[0], this->m_hashRegion[1] - this->m_hashRegion[0] + 1);
|
||||
|
||||
char buffer[sizeof(result) * 2 + 1];
|
||||
formatBigHexInt(result, buffer, sizeof(buffer));
|
||||
ImGui::InputText("Hash value", buffer, ImGuiInputTextFlags_ReadOnly);
|
||||
|
||||
ImGui::NewLine();
|
||||
ImGui::TextUnformatted("Result");
|
||||
ImGui::Separator();
|
||||
ImGui::InputText("##nolabel", buffer, ImGuiInputTextFlags_ReadOnly);
|
||||
}
|
||||
break;
|
||||
}
|
||||
@@ -202,10 +241,7 @@ namespace hex {
|
||||
}
|
||||
|
||||
void ViewHashes::createMenu() {
|
||||
if (ImGui::BeginMenu("View")) {
|
||||
ImGui::MenuItem("Hash View", "", &this->m_windowOpen);
|
||||
ImGui::EndMenu();
|
||||
}
|
||||
|
||||
}
|
||||
|
||||
}
|
||||
308
source/views/view_help.cpp
Normal file
308
source/views/view_help.cpp
Normal file
@@ -0,0 +1,308 @@
|
||||
#include "views/view_help.hpp"
|
||||
|
||||
namespace hex {
|
||||
|
||||
ViewHelp::ViewHelp() : View("Help") {
|
||||
this->getWindowOpenState() = true;
|
||||
}
|
||||
|
||||
ViewHelp::~ViewHelp() {
|
||||
|
||||
}
|
||||
|
||||
|
||||
static void drawTitle(const std::string &title) {
|
||||
ImGui::TextColored(ImVec4(0.6F, 0.6F, 1.0F, 1.0F), title.c_str());
|
||||
}
|
||||
|
||||
static void drawCodeSegment(const std::string &id, const std::string &code) {
|
||||
ImGui::PushStyleColor(ImGuiCol_ChildBg, ImVec4(0.2F, 0.2F, 0.2F, 0.3F));
|
||||
ImGui::BeginChild(id.c_str(), ImVec2(-1, ImGui::CalcTextSize(code.c_str()).y));
|
||||
|
||||
ImGui::Text("%s", code.c_str());
|
||||
|
||||
ImGui::EndChild();
|
||||
ImGui::NewLine();
|
||||
ImGui::PopStyleColor();
|
||||
};
|
||||
|
||||
|
||||
|
||||
void ViewHelp::drawAboutPopup() {
|
||||
ImGui::SetNextWindowSizeConstraints(ImVec2(450, 300), ImVec2(450, 300));
|
||||
if (ImGui::BeginPopupModal("About", &this->m_aboutWindowOpen, ImGuiWindowFlags_NoResize)) {
|
||||
|
||||
ImGui::Text("ImHex Hex Editor");
|
||||
ImGui::Text("by WerWolv");
|
||||
ImGui::Separator();
|
||||
ImGui::NewLine();
|
||||
|
||||
ImGui::Text("Source code found at"); ImGui::SameLine();
|
||||
ImGui::TextColored(ImVec4(0.4F, 0.4F, 0.8F, 1.0F), "https://github.com/WerWolv/ImHex");
|
||||
ImGui::NewLine();
|
||||
|
||||
ImGui::Separator();
|
||||
ImGui::Text("Libraries used");
|
||||
ImGui::PushStyleColor(ImGuiCol_ChildBg, ImVec4(0.2F, 0.2F, 0.2F, 0.3F));
|
||||
ImGui::BeginChild("##scroll");
|
||||
ImGui::NewLine();
|
||||
ImGui::BulletText("ImGui by ocornut");
|
||||
ImGui::BulletText("imgui_club by ocornut");
|
||||
ImGui::BulletText("ImGui-Addons by gallickgunner");
|
||||
ImGui::BulletText("ImGuiColorTextEdit by BalazsJako");
|
||||
ImGui::BulletText("capstone by aquynh");
|
||||
ImGui::BulletText("JSON for Modern C++ by nlohmann");
|
||||
ImGui::NewLine();
|
||||
ImGui::BulletText("GNU libmagic");
|
||||
ImGui::BulletText("OpenSSL libcrypto");
|
||||
ImGui::BulletText("GLFW3");
|
||||
ImGui::BulletText("LLVM");
|
||||
ImGui::BulletText("Python 3");
|
||||
|
||||
ImGui::EndChild();
|
||||
ImGui::PopStyleColor();
|
||||
ImGui::EndPopup();
|
||||
}
|
||||
}
|
||||
|
||||
void ViewHelp::drawPatternHelpPopup() {
|
||||
if (!this->m_patternHelpWindowOpen) return;
|
||||
|
||||
ImGui::SetNextWindowSizeConstraints(ImVec2(450, 300), ImVec2(2000, 1000));
|
||||
if (ImGui::Begin("Pattern Language Cheat Sheet", &this->m_patternHelpWindowOpen)) {
|
||||
ImGui::Text("ImHex Pattern Language Cheat Sheet");
|
||||
ImGui::Separator();
|
||||
ImGui::NewLine();
|
||||
|
||||
drawTitle("Preprocessor directives");
|
||||
ImGui::TextWrapped(
|
||||
"Preprocessor directives can be used to alter the code before it's being parsed. Supported are "
|
||||
"#define to replace one string with another and #include to include a separate file");
|
||||
drawCodeSegment("preprocessor",
|
||||
"#define HEADER_OFFSET 0x100\n"
|
||||
"#include <cstdint.hexpat>\n"
|
||||
"#include \"mypattern.hexpat\""
|
||||
);
|
||||
|
||||
drawTitle("Pragma directives");
|
||||
ImGui::TextWrapped(
|
||||
"Pragma directives are used to give ImHex additional information about the code being read. Currently "
|
||||
"the following directives are supported.");
|
||||
drawCodeSegment("pragma",
|
||||
"// Allow this file to be loaded as pattern when a file\n"
|
||||
"// identified as application/x-executable gets loaded\n"
|
||||
"#pragma MIME application/x-executable\n"
|
||||
);
|
||||
|
||||
drawTitle("Built-in types");
|
||||
ImGui::TextWrapped(
|
||||
"The following built-in types are available for use");
|
||||
drawCodeSegment("built-in",
|
||||
"u8, s8\n"
|
||||
"u16, s16\n"
|
||||
"u32, s32\n"
|
||||
"u64, s64\n"
|
||||
"u128, s128\n"
|
||||
"float, double"
|
||||
);
|
||||
|
||||
drawTitle("Variables and Arrays");
|
||||
ImGui::TextWrapped(
|
||||
"Normal variables as well as arrays are used to highlight and display values. "
|
||||
"It is possible to create arrays within structs and unions that use the value of a previously "
|
||||
"declared variable as size. In order to override the native / globally set endianess setting, every "
|
||||
"type in a variable declaration may be prefixed with be or le to turn only it into a big or little endian type.");
|
||||
drawCodeSegment("vars arrays",
|
||||
"u32 variable;\n"
|
||||
"s8 string[16];\n"
|
||||
"u8 customSizedArray[variable];\n"
|
||||
"be u32 bigEndianVariable;"
|
||||
);
|
||||
|
||||
drawTitle("Structs");
|
||||
ImGui::TextWrapped(
|
||||
"To bundle multiple variables together, a struct can be used. To insert padding bytes which won't show "
|
||||
"up in the pattern data view or be highlighted, use the padding[size] syntax.");
|
||||
drawCodeSegment("struct",
|
||||
"struct Header {\n"
|
||||
" u32 magic;\n"
|
||||
" u8 version;\n"
|
||||
" padding[4];\n"
|
||||
" Flags flags;\n"
|
||||
"};"
|
||||
);
|
||||
|
||||
drawTitle("Unions");
|
||||
ImGui::TextWrapped(
|
||||
"A union is used to make two or more variables occupy the same region of memory. "
|
||||
"The union will have the size of the biggest contained variable.");
|
||||
drawCodeSegment("union",
|
||||
"union Color {\n"
|
||||
" u32 rgba;\n"
|
||||
" Components components;\n"
|
||||
"};"
|
||||
);
|
||||
|
||||
drawTitle("Pointers");
|
||||
ImGui::TextWrapped(
|
||||
"\"Another possible type of member in structs and unions are pointers. They are variables"
|
||||
"whose value is used as an offset from the start of the file to locate the actual offset. "
|
||||
"The leading type is treated as the data being pointed to and the trailing type as the size of the pointer.");
|
||||
drawCodeSegment("pointer",
|
||||
"Data *data : u16;"
|
||||
);
|
||||
|
||||
drawTitle("Bitfields");
|
||||
ImGui::TextWrapped(
|
||||
"To decode values that are stored in fields that don't follow the typical 8 bit alignment, bitfields can be used. "
|
||||
"The size of these fields get specified in numbers of bits.");
|
||||
drawCodeSegment("bitfield",
|
||||
"bitfield Permission {\n"
|
||||
" r : 1;\n"
|
||||
" w : 1;\n"
|
||||
" x : 1;\n"
|
||||
"};"
|
||||
);
|
||||
|
||||
drawTitle("Enum");
|
||||
ImGui::TextWrapped(
|
||||
"If a value can only be a few specific values with special meaning, an enum can be used. "
|
||||
"The underlying type has to be specified using a unsigned, built-in type after the name. "
|
||||
"Entries can be listed with or without a value. The values start counting at zero and increase by one "
|
||||
"for every next entry");
|
||||
drawCodeSegment("enum",
|
||||
"enum OperatingSystem : u8 {\n"
|
||||
" Windows = 0x10,\n"
|
||||
" MacOSX,\n"
|
||||
" Linux = 'L'\n"
|
||||
"};"
|
||||
);
|
||||
|
||||
drawTitle("Using declarations");
|
||||
ImGui::TextWrapped(
|
||||
"A using declaration can be used to create type aliases for already existing types. This can be "
|
||||
"a built-in type, a struct, enum or another alias type.");
|
||||
drawCodeSegment("using",
|
||||
"using magic_t = u32;"
|
||||
);
|
||||
|
||||
drawTitle("Comments");
|
||||
ImGui::TextWrapped(
|
||||
"To create a comment the C // or /* */ syntax can be used. //-style comments end at the next new line "
|
||||
"and /*-style comments only end when at the next */.");
|
||||
drawCodeSegment("comment",
|
||||
"// This is a single line comment\n\n"
|
||||
"/* This is a\n"
|
||||
"multiline comment */"
|
||||
);
|
||||
|
||||
drawTitle("Variable placement");
|
||||
ImGui::TextWrapped(
|
||||
"In order to highlight bytes and displaying their value in the pattern data window, "
|
||||
"a variable needs to be created and placed in memory. The following line of code creates"
|
||||
"a unsigned 32 bit variable named data and places it at offset 0x100."
|
||||
);
|
||||
drawCodeSegment("var placement", "u32 data @ 0x100;");
|
||||
}
|
||||
ImGui::End();
|
||||
}
|
||||
|
||||
void ViewHelp::drawMathEvaluatorHelp() {
|
||||
if (!this->m_mathHelpWindowOpen) return;
|
||||
|
||||
ImGui::SetNextWindowSizeConstraints(ImVec2(450, 300), ImVec2(2000, 1000));
|
||||
if (ImGui::Begin("Calculator Cheat Sheet", &this->m_mathHelpWindowOpen)) {
|
||||
ImGui::Text("ImHex Math Evaluator Cheat Sheet");
|
||||
ImGui::Separator();
|
||||
ImGui::NewLine();
|
||||
|
||||
ImGui::TextWrapped("ImGui has a simple math evaluator / calculator built-in. It works by parsing the "
|
||||
"expressions passed to it and displaying the result in the History table.");
|
||||
ImGui::NewLine();
|
||||
|
||||
drawTitle("Basic Operators");
|
||||
ImGui::TextWrapped(
|
||||
"The following basic mathematical operators are supported");
|
||||
drawCodeSegment("basicOperators",
|
||||
"+ : Addition\n"
|
||||
"- : Subtraction\n"
|
||||
"* : Multiplication\n"
|
||||
"/ : Division\n"
|
||||
"% : Modulus\n"
|
||||
"** : Exponentiation\n"
|
||||
"() : Brackets\n"
|
||||
"x = : Variable assignment");
|
||||
|
||||
drawTitle("Bitwise Operators");
|
||||
ImGui::TextWrapped(
|
||||
"The following bitwise operators are supported");
|
||||
drawCodeSegment("bitwiseOperators",
|
||||
"& : Bitwise AND\n"
|
||||
"| : Bitwise OR\n"
|
||||
"^ : Bitwise XOR\n"
|
||||
"~ : Bitwise NOT\n"
|
||||
"<< : Logical shift left\n"
|
||||
">> : Logical shift right\n"
|
||||
"## : Bitwise concatenation");
|
||||
|
||||
drawTitle("Boolean Operators");
|
||||
ImGui::TextWrapped(
|
||||
"The following bitwise operators are supported");
|
||||
drawCodeSegment("booleanOperators",
|
||||
"&& : Boolean AND\n"
|
||||
"|| : Boolean OR\n"
|
||||
"^^ : Boolean XOR\n"
|
||||
"! : Boolean NOT\n"
|
||||
"== : Equality comparison\n"
|
||||
"!= : Inequality comparison\n"
|
||||
"> : Greater than comparison\n"
|
||||
"< : Less than comparison\n"
|
||||
">= : Greater than or equals comparison\n"
|
||||
"<= : Less than or equals comparison");
|
||||
|
||||
drawTitle("Built-in Functions");
|
||||
ImGui::TextWrapped(
|
||||
"The following functions are built into the evaluator");
|
||||
drawCodeSegment("functions",
|
||||
"sin(x) : Sine function\n"
|
||||
"cos(x) : Cosine function\n"
|
||||
"tan(x) : Tangent function\n"
|
||||
"ceil(x) : Rounds value to next bigger integer\n"
|
||||
"floor(x) : Rounds value to next smaller integer\n"
|
||||
"sign(x) : Returns the value's sign\n"
|
||||
"abs(x) : Absolute value\n"
|
||||
"ln(x) : Natural Logarithm\n"
|
||||
"lb(x) : Logarithm with base 2\n"
|
||||
"log(x, b) : Logarithm with custom base. Defaults to base 10\n"
|
||||
"clear() : Clear History and variables\n"
|
||||
"read(a) : Read 1 byte from loaded data\n"
|
||||
"write(a, x) : Write 1 byte to loaded data");
|
||||
|
||||
}
|
||||
ImGui::End();
|
||||
}
|
||||
|
||||
void ViewHelp::createView() {
|
||||
this->drawAboutPopup();
|
||||
this->drawPatternHelpPopup();
|
||||
this->drawMathEvaluatorHelp();
|
||||
}
|
||||
|
||||
void ViewHelp::createMenu() {
|
||||
if (ImGui::BeginMenu("Help")) {
|
||||
if (ImGui::MenuItem("About", "")) {
|
||||
View::doLater([] { ImGui::OpenPopup("About"); });
|
||||
this->m_aboutWindowOpen = true;
|
||||
}
|
||||
ImGui::Separator();
|
||||
if (ImGui::MenuItem("Pattern Language Cheat Sheet", "")) {
|
||||
this->m_patternHelpWindowOpen = true;
|
||||
}
|
||||
if (ImGui::MenuItem("Calculator Cheat Sheet", "")) {
|
||||
this->m_mathHelpWindowOpen = true;
|
||||
}
|
||||
ImGui::EndMenu();
|
||||
}
|
||||
}
|
||||
|
||||
}
|
||||
File diff suppressed because it is too large
Load Diff
@@ -2,7 +2,7 @@
|
||||
|
||||
#include "providers/provider.hpp"
|
||||
|
||||
#include "utils.hpp"
|
||||
#include "helpers/utils.hpp"
|
||||
|
||||
#include <cstring>
|
||||
#include <cmath>
|
||||
@@ -12,18 +12,20 @@
|
||||
|
||||
#include <magic.h>
|
||||
|
||||
|
||||
#if defined(__EMX__) || defined (WIN32)
|
||||
#define MAGIC_PATH_SEPARATOR ";"
|
||||
#else
|
||||
#define MAGIC_PATH_SEPARATOR ":"
|
||||
#endif
|
||||
|
||||
namespace hex {
|
||||
|
||||
ViewInformation::ViewInformation(prv::Provider* &dataProvider) : View(), m_dataProvider(dataProvider) {
|
||||
View::subscribeEvent(Events::DataChanged, [this](void*) {
|
||||
this->m_shouldInvalidate = true;
|
||||
ViewInformation::ViewInformation(prv::Provider* &dataProvider)
|
||||
: View("Information"), m_dataProvider(dataProvider) {
|
||||
View::subscribeEvent(Events::DataChanged, [this](const void*) {
|
||||
this->m_dataValid = false;
|
||||
this->m_highestBlockEntropy = 0;
|
||||
this->m_blockEntropy.clear();
|
||||
this->m_averageEntropy = 0;
|
||||
this->m_blockSize = 0;
|
||||
this->m_valueCounts.fill(0x00);
|
||||
this->m_mimeType = "";
|
||||
this->m_fileDescription = "";
|
||||
this->m_analyzedRegion = { 0, 0 };
|
||||
});
|
||||
}
|
||||
|
||||
@@ -45,29 +47,29 @@ namespace hex {
|
||||
}
|
||||
|
||||
void ViewInformation::createView() {
|
||||
if (!this->m_windowOpen)
|
||||
return;
|
||||
|
||||
if (ImGui::Begin("File Information", &this->m_windowOpen)) {
|
||||
if (ImGui::Begin("Data Information", &this->getWindowOpenState(), ImGuiWindowFlags_NoCollapse)) {
|
||||
ImGui::BeginChild("##scrolling", ImVec2(0, 0), false, ImGuiWindowFlags_NoMove | ImGuiWindowFlags_NoNav);
|
||||
|
||||
if (this->m_dataProvider != nullptr && this->m_dataProvider->isReadable()) {
|
||||
if (this->m_shouldInvalidate) {
|
||||
|
||||
this->m_analyzedRegion = { this->m_dataProvider->getBaseAddress(), this->m_dataProvider->getBaseAddress() + this->m_dataProvider->getSize() };
|
||||
|
||||
{
|
||||
std::vector<u8> buffer(512, 0x00);
|
||||
this->m_blockSize = std::ceil(this->m_dataProvider->getSize() / 2048.0F);
|
||||
std::vector<u8> buffer(this->m_blockSize, 0x00);
|
||||
std::memset(this->m_valueCounts.data(), 0x00, this->m_valueCounts.size() * sizeof(u32));
|
||||
this->m_blockEntropy.clear();
|
||||
|
||||
for (u64 i = 0; i < this->m_dataProvider->getSize(); i += 512) {
|
||||
for (u64 i = 0; i < this->m_dataProvider->getSize(); i += this->m_blockSize) {
|
||||
std::array<float, 256> blockValueCounts = { 0 };
|
||||
this->m_dataProvider->read(i, buffer.data(), std::min(size_t(512), this->m_dataProvider->getSize() - i));
|
||||
this->m_dataProvider->read(i, buffer.data(), std::min(u64(this->m_blockSize), this->m_dataProvider->getSize() - i));
|
||||
|
||||
for (u16 j = 0; j < 512; j++) {
|
||||
for (size_t j = 0; j < this->m_blockSize; j++) {
|
||||
blockValueCounts[buffer[j]]++;
|
||||
this->m_valueCounts[buffer[j]]++;
|
||||
}
|
||||
this->m_blockEntropy.push_back(calculateEntropy(blockValueCounts, 512));
|
||||
this->m_blockEntropy.push_back(calculateEntropy(blockValueCounts, this->m_blockSize));
|
||||
}
|
||||
|
||||
this->m_averageEntropy = calculateEntropy(this->m_valueCounts, this->m_dataProvider->getSize());
|
||||
@@ -94,12 +96,10 @@ namespace hex {
|
||||
|
||||
{
|
||||
magic_t cookie = magic_open(MAGIC_NONE);
|
||||
if (magic_load(cookie, magicFiles.c_str()) == -1)
|
||||
goto skip_description;
|
||||
|
||||
this->m_fileDescription = magic_buffer(cookie, buffer.data(), buffer.size());
|
||||
|
||||
skip_description:
|
||||
if (magic_load(cookie, magicFiles.c_str()) != -1)
|
||||
this->m_fileDescription = magic_buffer(cookie, buffer.data(), buffer.size());
|
||||
else
|
||||
this->m_fileDescription = "";
|
||||
|
||||
magic_close(cookie);
|
||||
}
|
||||
@@ -107,12 +107,10 @@ namespace hex {
|
||||
|
||||
{
|
||||
magic_t cookie = magic_open(MAGIC_MIME);
|
||||
if (magic_load(cookie, magicFiles.c_str()) == -1)
|
||||
goto skip_mime;
|
||||
|
||||
this->m_mimeType = magic_buffer(cookie, buffer.data(), buffer.size());
|
||||
|
||||
skip_mime:
|
||||
if (magic_load(cookie, magicFiles.c_str()) != -1)
|
||||
this->m_mimeType = magic_buffer(cookie, buffer.data(), buffer.size());
|
||||
else
|
||||
this->m_mimeType = "";
|
||||
|
||||
magic_close(cookie);
|
||||
}
|
||||
@@ -121,45 +119,67 @@ namespace hex {
|
||||
|
||||
|
||||
this->m_shouldInvalidate = false;
|
||||
this->m_dataValid = true;
|
||||
}
|
||||
}
|
||||
|
||||
ImGui::NewLine();
|
||||
|
||||
ImGui::Text("Byte Distribution");
|
||||
ImGui::PlotHistogram("##nolabel", this->m_valueCounts.data(), 256, 0, nullptr, FLT_MAX, FLT_MAX, ImVec2(0, 100));
|
||||
if (ImGui::Button("Analyze current page"))
|
||||
this->m_shouldInvalidate = true;
|
||||
|
||||
ImGui::NewLine();
|
||||
ImGui::Separator();
|
||||
ImGui::NewLine();
|
||||
|
||||
ImGui::Text("Entropy");
|
||||
ImGui::PlotLines("##nolabel", this->m_blockEntropy.data(), this->m_blockEntropy.size(), 0, nullptr, FLT_MAX, FLT_MAX, ImVec2(0, 100));
|
||||
if (this->m_dataValid) {
|
||||
|
||||
ImGui::NewLine();
|
||||
for (auto &[name, value] : this->m_dataProvider->getDataInformation()) {
|
||||
ImGui::LabelText(name.c_str(), "%s", value.c_str());
|
||||
}
|
||||
|
||||
ImGui::LabelText("Average entropy", "%.8f", this->m_averageEntropy);
|
||||
ImGui::LabelText("Highest entropy block", "%.8f", this->m_highestBlockEntropy);
|
||||
ImGui::LabelText("Analyzed region", "0x%llx - 0x%llx", this->m_analyzedRegion.first, this->m_analyzedRegion.second);
|
||||
|
||||
if (this->m_averageEntropy > 0.83 && this->m_highestBlockEntropy > 0.9) {
|
||||
ImGui::NewLine();
|
||||
ImGui::TextColored(ImVec4(0.92F, 0.25F, 0.2F, 1.0F), "This data is most likely encrypted or compressed!");
|
||||
}
|
||||
|
||||
ImGui::NewLine();
|
||||
ImGui::Separator();
|
||||
ImGui::NewLine();
|
||||
|
||||
if (!this->m_fileDescription.empty()) {
|
||||
ImGui::TextUnformatted("Description:");
|
||||
ImGui::TextWrapped("%s", this->m_fileDescription.c_str());
|
||||
ImGui::Separator();
|
||||
ImGui::NewLine();
|
||||
}
|
||||
|
||||
if (!this->m_mimeType.empty()) {
|
||||
ImGui::TextUnformatted("MIME Type:");
|
||||
ImGui::TextWrapped("%s", this->m_mimeType.c_str());
|
||||
if (!this->m_fileDescription.empty()) {
|
||||
ImGui::TextUnformatted("Description:");
|
||||
ImGui::TextWrapped("%s", this->m_fileDescription.c_str());
|
||||
ImGui::NewLine();
|
||||
}
|
||||
|
||||
if (!this->m_mimeType.empty()) {
|
||||
ImGui::TextUnformatted("MIME Type:");
|
||||
ImGui::TextWrapped("%s", this->m_mimeType.c_str());
|
||||
ImGui::NewLine();
|
||||
}
|
||||
|
||||
ImGui::Separator();
|
||||
ImGui::NewLine();
|
||||
|
||||
ImGui::Text("Byte Distribution");
|
||||
ImGui::PlotHistogram("##nolabel", this->m_valueCounts.data(), 256, 0, nullptr, FLT_MAX, FLT_MAX,ImVec2(0, 100));
|
||||
|
||||
ImGui::NewLine();
|
||||
ImGui::Separator();
|
||||
ImGui::NewLine();
|
||||
|
||||
ImGui::Text("Entropy");
|
||||
ImGui::PlotLines("##nolabel", this->m_blockEntropy.data(), this->m_blockEntropy.size(), 0, nullptr, FLT_MAX, FLT_MAX, ImVec2(0, 100));
|
||||
|
||||
ImGui::NewLine();
|
||||
|
||||
ImGui::LabelText("Block size", "2048 blocks of %lu bytes", this->m_blockSize);
|
||||
ImGui::LabelText("Average entropy", "%.8f", this->m_averageEntropy);
|
||||
ImGui::LabelText("Highest entropy block", "%.8f", this->m_highestBlockEntropy);
|
||||
|
||||
if (this->m_averageEntropy > 0.83 && this->m_highestBlockEntropy > 0.9) {
|
||||
ImGui::NewLine();
|
||||
ImGui::TextColored(ImVec4(0.92F, 0.25F, 0.2F, 1.0F),"This data is most likely encrypted or compressed!");
|
||||
}
|
||||
|
||||
}
|
||||
}
|
||||
|
||||
@@ -169,10 +189,7 @@ namespace hex {
|
||||
}
|
||||
|
||||
void ViewInformation::createMenu() {
|
||||
if (ImGui::BeginMenu("View")) {
|
||||
ImGui::MenuItem("Entropy 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() {
|
||||
|
||||
}
|
||||
|
||||
}
|
||||
@@ -1,87 +1,248 @@
|
||||
#include <random>
|
||||
#include "views/view_pattern.hpp"
|
||||
|
||||
#include "utils.hpp"
|
||||
#include "lang/preprocessor.hpp"
|
||||
#include "lang/parser.hpp"
|
||||
#include "lang/lexer.hpp"
|
||||
#include "lang/validator.hpp"
|
||||
#include "lang/evaluator.hpp"
|
||||
|
||||
#include "helpers/project_file_handler.hpp"
|
||||
#include "helpers/utils.hpp"
|
||||
|
||||
#include <magic.h>
|
||||
|
||||
namespace hex {
|
||||
|
||||
ViewPattern::ViewPattern(std::vector<Highlight> &highlights) : View(), m_highlights(highlights) {
|
||||
this->m_buffer = new char[0xFFFFFF];
|
||||
std::memset(this->m_buffer, 0x00, 0xFFFFFF);
|
||||
static const TextEditor::LanguageDefinition& PatternLanguage() {
|
||||
static bool initialized = false;
|
||||
static TextEditor::LanguageDefinition langDef;
|
||||
if (!initialized) {
|
||||
static const char* const keywords[] = {
|
||||
"using", "struct", "union", "enum", "bitfield", "be", "le"
|
||||
};
|
||||
for (auto& k : keywords)
|
||||
langDef.mKeywords.insert(k);
|
||||
|
||||
static std::pair<const char* const, size_t> builtInTypes[] = {
|
||||
{ "u8", 1 }, { "u16", 2 }, { "u32", 4 }, { "u64", 8 }, { "u128", 16 },
|
||||
{ "s8", 1 }, { "s16", 2 }, { "s32", 4 }, { "s64", 8 }, { "s128", 16 },
|
||||
{ "float", 4 }, { "double", 8 }, { "padding", 1 }
|
||||
};
|
||||
for (const auto &[name, size] : builtInTypes) {
|
||||
TextEditor::Identifier id;
|
||||
id.mDeclaration = std::to_string(size);
|
||||
id.mDeclaration += size == 1 ? " byte" : " bytes";
|
||||
langDef.mIdentifiers.insert(std::make_pair(std::string(name), id));
|
||||
}
|
||||
|
||||
langDef.mTokenize = [](const char * inBegin, const char * inEnd, const char *& outBegin, const char *& outEnd, TextEditor::PaletteIndex & paletteIndex) -> bool {
|
||||
paletteIndex = TextEditor::PaletteIndex::Max;
|
||||
|
||||
while (inBegin < inEnd && isascii(*inBegin) && isblank(*inBegin))
|
||||
inBegin++;
|
||||
|
||||
if (inBegin == inEnd) {
|
||||
outBegin = inEnd;
|
||||
outEnd = inEnd;
|
||||
paletteIndex = TextEditor::PaletteIndex::Default;
|
||||
}
|
||||
else if (TokenizeCStyleIdentifier(inBegin, inEnd, outBegin, outEnd))
|
||||
paletteIndex = TextEditor::PaletteIndex::Identifier;
|
||||
else if (TokenizeCStyleNumber(inBegin, inEnd, outBegin, outEnd))
|
||||
paletteIndex = TextEditor::PaletteIndex::Number;
|
||||
else if (TokenizeCStyleCharacterLiteral(inBegin, inEnd, outBegin, outEnd))
|
||||
paletteIndex = TextEditor::PaletteIndex::CharLiteral;
|
||||
|
||||
return paletteIndex != TextEditor::PaletteIndex::Max;
|
||||
};
|
||||
|
||||
langDef.mCommentStart = "/*";
|
||||
langDef.mCommentEnd = "*/";
|
||||
langDef.mSingleLineComment = "//";
|
||||
|
||||
langDef.mCaseSensitive = true;
|
||||
langDef.mAutoIndentation = true;
|
||||
langDef.mPreprocChar = '#';
|
||||
|
||||
langDef.mName = "Pattern Language";
|
||||
|
||||
initialized = true;
|
||||
}
|
||||
return langDef;
|
||||
}
|
||||
|
||||
|
||||
ViewPattern::ViewPattern(prv::Provider* &dataProvider, std::vector<lang::PatternData*> &patternData)
|
||||
: View("Pattern"), m_dataProvider(dataProvider), m_patternData(patternData) {
|
||||
|
||||
this->m_textEditor.SetLanguageDefinition(PatternLanguage());
|
||||
this->m_textEditor.SetShowWhitespaces(false);
|
||||
|
||||
View::subscribeEvent(Events::ProjectFileStore, [this](const void*) {
|
||||
ProjectFile::setPattern(this->m_textEditor.GetText());
|
||||
});
|
||||
|
||||
View::subscribeEvent(Events::ProjectFileLoad, [this](const void*) {
|
||||
this->m_textEditor.SetText(ProjectFile::getPattern());
|
||||
this->parsePattern(this->m_textEditor.GetText().data());
|
||||
});
|
||||
|
||||
View::subscribeEvent(Events::AppendPatternLanguageCode, [this](const void *userData) {
|
||||
const char *code = static_cast<const char*>(userData);
|
||||
|
||||
this->m_textEditor.InsertText("\n");
|
||||
this->m_textEditor.InsertText(code);
|
||||
});
|
||||
|
||||
View::subscribeEvent(Events::FileLoaded, [this](const void* userData) {
|
||||
if (this->m_textEditor.GetText().find_first_not_of(" \f\n\r\t\v") != std::string::npos)
|
||||
return;
|
||||
|
||||
lang::Preprocessor preprocessor;
|
||||
std::string magicFiles;
|
||||
|
||||
std::error_code error;
|
||||
for (const auto &entry : std::filesystem::directory_iterator("magic", error)) {
|
||||
if (entry.is_regular_file() && entry.path().extension() == ".mgc")
|
||||
magicFiles += entry.path().string() + MAGIC_PATH_SEPARATOR;
|
||||
}
|
||||
|
||||
if (error)
|
||||
return;
|
||||
|
||||
std::vector<u8> buffer(std::min(this->m_dataProvider->getSize(), size_t(0xFFFF)), 0x00);
|
||||
this->m_dataProvider->read(0, buffer.data(), buffer.size());
|
||||
|
||||
std::string mimeType;
|
||||
|
||||
magic_t cookie = magic_open(MAGIC_MIME_TYPE);
|
||||
if (magic_load(cookie, magicFiles.c_str()) != -1)
|
||||
mimeType = magic_buffer(cookie, buffer.data(), buffer.size());
|
||||
|
||||
magic_close(cookie);
|
||||
|
||||
bool foundCorrectType = false;
|
||||
preprocessor.addPragmaHandler("MIME", [&mimeType, &foundCorrectType](std::string value) {
|
||||
if (value == mimeType) {
|
||||
foundCorrectType = true;
|
||||
return true;
|
||||
}
|
||||
return !std::all_of(value.begin(), value.end(), isspace) && !value.ends_with('\n') && !value.ends_with('\r');
|
||||
});
|
||||
preprocessor.addDefaultPragmaHandlers();
|
||||
|
||||
|
||||
std::error_code errorCode;
|
||||
for (auto &entry : std::filesystem::directory_iterator("patterns", errorCode)) {
|
||||
if (!entry.is_regular_file())
|
||||
continue;
|
||||
|
||||
FILE *file = fopen(entry.path().string().c_str(), "r");
|
||||
|
||||
if (file == nullptr)
|
||||
continue;
|
||||
|
||||
fseek(file, 0, SEEK_END);
|
||||
size_t size = ftell(file);
|
||||
rewind(file);
|
||||
|
||||
std::vector<char> buffer( size + 1, 0x00);
|
||||
fread(buffer.data(), 1, size, file);
|
||||
fclose(file);
|
||||
|
||||
preprocessor.preprocess(buffer.data());
|
||||
|
||||
if (foundCorrectType) {
|
||||
this->m_possiblePatternFile = entry.path();
|
||||
View::doLater([] { ImGui::OpenPopup("Accept Pattern"); });
|
||||
ImGui::SetNextWindowSize(ImVec2(200, 100));
|
||||
break;
|
||||
}
|
||||
}
|
||||
});
|
||||
}
|
||||
|
||||
ViewPattern::~ViewPattern() {
|
||||
delete[] this->m_buffer;
|
||||
View::unsubscribeEvent(Events::ProjectFileStore);
|
||||
View::unsubscribeEvent(Events::ProjectFileLoad);
|
||||
}
|
||||
|
||||
void ViewPattern::createMenu() {
|
||||
if (ImGui::BeginMenu("File")) {
|
||||
if (ImGui::MenuItem("Load pattern...")) {
|
||||
this->m_fileBrowser.SetTitle("Open Hex Pattern");
|
||||
this->m_fileBrowser.SetTypeFilters({ ".hexpat" });
|
||||
this->m_fileBrowser.Open();
|
||||
View::doLater([]{ ImGui::OpenPopup("Open Hex Pattern"); });
|
||||
}
|
||||
ImGui::EndMenu();
|
||||
}
|
||||
|
||||
if (ImGui::BeginMenu("View")) {
|
||||
ImGui::MenuItem("Pattern View", "", &this->m_windowOpen);
|
||||
ImGui::EndMenu();
|
||||
}
|
||||
}
|
||||
|
||||
void ViewPattern::createView() {
|
||||
if (!this->m_windowOpen)
|
||||
return;
|
||||
if (ImGui::Begin("Pattern", &this->getWindowOpenState(), ImGuiWindowFlags_None | ImGuiWindowFlags_NoCollapse)) {
|
||||
if (this->m_dataProvider != nullptr && this->m_dataProvider->isAvailable()) {
|
||||
this->m_textEditor.Render("Pattern");
|
||||
|
||||
if (ImGui::Begin("Pattern", &this->m_windowOpen, ImGuiWindowFlags_None)) {
|
||||
ImGui::PushStyleVar(ImGuiStyleVar_FramePadding, ImVec2(0, 0));
|
||||
ImGui::PushStyleVar(ImGuiStyleVar_ItemSpacing, ImVec2(0, 0));
|
||||
|
||||
auto size = ImGui::GetWindowSize();
|
||||
size.y -= 50;
|
||||
ImGui::InputTextMultiline("Pattern", this->m_buffer, 0xFFFF, size, ImGuiInputTextFlags_AllowTabInput | ImGuiInputTextFlags_CallbackEdit,
|
||||
[](ImGuiInputTextCallbackData* data) -> int {
|
||||
auto _this = static_cast<ViewPattern*>(data->UserData);
|
||||
|
||||
_this->parsePattern(data->Buf);
|
||||
|
||||
return 0;
|
||||
}, this
|
||||
);
|
||||
|
||||
ImGui::PopStyleVar(2);
|
||||
if (this->m_textEditor.IsTextChanged()) {
|
||||
this->parsePattern(this->m_textEditor.GetText().data());
|
||||
}
|
||||
}
|
||||
}
|
||||
ImGui::End();
|
||||
|
||||
this->m_fileBrowser.Display();
|
||||
if (this->m_fileBrowser.showFileDialog("Open Hex Pattern", imgui_addons::ImGuiFileBrowser::DialogMode::OPEN, ImVec2(0, 0), ".hexpat")) {
|
||||
this->loadPatternFile(this->m_fileBrowser.selected_path);
|
||||
}
|
||||
|
||||
if (this->m_fileBrowser.HasSelected()) {
|
||||
FILE *file = fopen(this->m_fileBrowser.GetSelected().string().c_str(), "rb");
|
||||
if (ImGui::BeginPopupModal("Accept Pattern", nullptr, ImGuiWindowFlags_NoResize)) {
|
||||
ImGui::TextUnformatted("A pattern compatible with this data type has been found:");
|
||||
ImGui::Text("%ls", this->m_possiblePatternFile.filename().c_str());
|
||||
ImGui::NewLine();
|
||||
ImGui::Text("Do you want to load it?");
|
||||
ImGui::NewLine();
|
||||
|
||||
fseek(file, 0, SEEK_END);
|
||||
size_t size = ftell(file);
|
||||
rewind(file);
|
||||
if (ImGui::Button("Yes", ImVec2(40, 20))) {
|
||||
this->loadPatternFile(this->m_possiblePatternFile.string());
|
||||
ImGui::CloseCurrentPopup();
|
||||
}
|
||||
ImGui::SameLine();
|
||||
if (ImGui::Button("No", ImVec2(40, 20))) {
|
||||
ImGui::CloseCurrentPopup();
|
||||
}
|
||||
|
||||
if (size > 0xFF'FFFF)
|
||||
return;
|
||||
|
||||
fread(this->m_buffer, size, 1, file);
|
||||
|
||||
fclose(file);
|
||||
|
||||
this->parsePattern(this->m_buffer);
|
||||
ImGui::EndPopup();
|
||||
}
|
||||
}
|
||||
|
||||
|
||||
void ViewPattern::setHighlight(u64 offset, size_t size, std::string name, u32 color) {
|
||||
if (color == 0)
|
||||
color = std::mt19937(std::random_device()())();
|
||||
void ViewPattern::loadPatternFile(std::string path) {
|
||||
FILE *file = fopen(path.c_str(), "rb");
|
||||
|
||||
color &= ~0xFF00'0000;
|
||||
color |= 0x5000'0000;
|
||||
if (file != nullptr) {
|
||||
char *buffer;
|
||||
fseek(file, 0, SEEK_END);
|
||||
size_t size = ftell(file);
|
||||
rewind(file);
|
||||
|
||||
this->m_highlights.emplace_back(offset, size, color, name);
|
||||
buffer = new char[size + 1];
|
||||
|
||||
fread(buffer, size, 1, file);
|
||||
buffer[size] = 0x00;
|
||||
|
||||
|
||||
fclose(file);
|
||||
|
||||
this->parsePattern(buffer);
|
||||
this->m_textEditor.SetText(buffer);
|
||||
|
||||
delete[] buffer;
|
||||
}
|
||||
}
|
||||
|
||||
void ViewPattern::clearPatternData() {
|
||||
for (auto &data : this->m_patternData)
|
||||
delete data;
|
||||
|
||||
this->m_patternData.clear();
|
||||
lang::PatternData::resetPalette();
|
||||
}
|
||||
|
||||
template<std::derived_from<lang::ASTNode> T>
|
||||
@@ -96,125 +257,66 @@ namespace hex {
|
||||
}
|
||||
|
||||
void ViewPattern::parsePattern(char *buffer) {
|
||||
static hex::lang::Lexer lexer;
|
||||
static hex::lang::Parser parser;
|
||||
this->clearPatternData();
|
||||
this->m_textEditor.SetErrorMarkers({ });
|
||||
this->postEvent(Events::PatternChanged);
|
||||
|
||||
this->m_highlights.clear();
|
||||
hex::lang::Preprocessor preprocessor;
|
||||
std::endian defaultDataEndianess = std::endian::native;
|
||||
|
||||
auto [lexResult, tokens] = lexer.lex(buffer);
|
||||
preprocessor.addPragmaHandler("endian", [&defaultDataEndianess](std::string value) {
|
||||
if (value == "big") {
|
||||
defaultDataEndianess = std::endian::big;
|
||||
return true;
|
||||
} else if (value == "little") {
|
||||
defaultDataEndianess = std::endian::little;
|
||||
return true;
|
||||
} else if (value == "native") {
|
||||
defaultDataEndianess = std::endian::native;
|
||||
return true;
|
||||
} else
|
||||
return false;
|
||||
});
|
||||
preprocessor.addDefaultPragmaHandlers();
|
||||
|
||||
if (lexResult.failed()) {
|
||||
auto [preprocessingResult, preprocesedCode] = preprocessor.preprocess(buffer);
|
||||
if (preprocessingResult.failed()) {
|
||||
this->m_textEditor.SetErrorMarkers({ preprocessor.getError() });
|
||||
return;
|
||||
}
|
||||
|
||||
hex::lang::Lexer lexer;
|
||||
auto [lexResult, tokens] = lexer.lex(preprocesedCode);
|
||||
if (lexResult.failed()) {
|
||||
this->m_textEditor.SetErrorMarkers({ lexer.getError() });
|
||||
return;
|
||||
}
|
||||
|
||||
hex::lang::Parser parser;
|
||||
auto [parseResult, ast] = parser.parse(tokens);
|
||||
if (parseResult.failed()) {
|
||||
for(auto &node : ast) delete node;
|
||||
this->m_textEditor.SetErrorMarkers({ parser.getError() });
|
||||
return;
|
||||
}
|
||||
|
||||
for (auto &varNode : findNodes<lang::ASTNodeVariableDecl>(lang::ASTNode::Type::VariableDecl, ast)) {
|
||||
if (!varNode->getOffset().has_value())
|
||||
continue;
|
||||
|
||||
u64 offset = varNode->getOffset().value();
|
||||
if (varNode->getVariableType() != lang::Token::TypeToken::Type::CustomType) {
|
||||
this->setHighlight(offset, (static_cast<u32>(varNode->getVariableType()) >> 4) * varNode->getArraySize(), varNode->getVariableName());
|
||||
} else {
|
||||
for (auto &structNode : findNodes<lang::ASTNodeStruct>(lang::ASTNode::Type::Struct, ast))
|
||||
if (varNode->getCustomVariableTypeName() == structNode->getName())
|
||||
if (this->highlightStruct(ast, structNode, offset) == -1)
|
||||
this->m_highlights.clear();
|
||||
|
||||
for (auto &usingNode : findNodes<lang::ASTNodeTypeDecl>(lang::ASTNode::Type::TypeDecl, ast))
|
||||
if (varNode->getCustomVariableTypeName() == usingNode->getTypeName())
|
||||
if (this->highlightUsingDecls(ast, usingNode, varNode, offset) == -1)
|
||||
this->m_highlights.clear();
|
||||
}
|
||||
hex::ScopeExit deleteAst([&ast]{ for(auto &node : ast) delete node; });
|
||||
|
||||
hex::lang::Validator validator;
|
||||
auto validatorResult = validator.validate(ast);
|
||||
if (!validatorResult) {
|
||||
this->m_textEditor.SetErrorMarkers({ validator.getError() });
|
||||
return;
|
||||
}
|
||||
|
||||
for(auto &node : ast) delete node;
|
||||
}
|
||||
|
||||
s32 ViewPattern::highlightUsingDecls(std::vector<lang::ASTNode*> &ast, lang::ASTNodeTypeDecl* currTypeDeclNode, lang::ASTNodeVariableDecl* currVarDecl, u64 offset) {
|
||||
if (currTypeDeclNode->getAssignedType() != lang::Token::TypeToken::Type::CustomType) {
|
||||
size_t size = (static_cast<u32>(currTypeDeclNode->getAssignedType()) >> 4) * currVarDecl->getArraySize();
|
||||
|
||||
this->setHighlight(offset, size, currVarDecl->getVariableName());
|
||||
offset += size;
|
||||
} else {
|
||||
bool foundType = false;
|
||||
for (auto &structNode : findNodes<lang::ASTNodeStruct>(lang::ASTNode::Type::Struct, ast))
|
||||
if (structNode->getName() == currTypeDeclNode->getAssignedCustomTypeName()) {
|
||||
offset = this->highlightStruct(ast, structNode, offset);
|
||||
foundType = true;
|
||||
break;
|
||||
}
|
||||
|
||||
for (auto &typeDeclNode : findNodes<lang::ASTNodeTypeDecl>(lang::ASTNode::Type::TypeDecl, ast))
|
||||
if (typeDeclNode->getTypeName() == currTypeDeclNode->getAssignedCustomTypeName()) {
|
||||
offset = this->highlightUsingDecls(ast, typeDeclNode, currVarDecl, offset);
|
||||
foundType = true;
|
||||
break;
|
||||
}
|
||||
|
||||
if (!foundType)
|
||||
return -1;
|
||||
hex::lang::Evaluator evaluator(this->m_dataProvider, defaultDataEndianess);
|
||||
auto [evaluateResult, patternData] = evaluator.evaluate(ast);
|
||||
if (evaluateResult.failed()) {
|
||||
this->m_textEditor.SetErrorMarkers({ evaluator.getError() });
|
||||
return;
|
||||
}
|
||||
this->m_patternData = patternData;
|
||||
|
||||
return offset;
|
||||
}
|
||||
|
||||
s32 ViewPattern::highlightStruct(std::vector<lang::ASTNode*> &ast, lang::ASTNodeStruct* currStructNode, u64 offset) {
|
||||
u64 startOffset = offset;
|
||||
|
||||
for (auto &node : currStructNode->getNodes()) {
|
||||
auto var = static_cast<lang::ASTNodeVariableDecl*>(node);
|
||||
|
||||
if (var->getVariableType() != lang::Token::TypeToken::Type::CustomType) {
|
||||
size_t size = (static_cast<u32>(var->getVariableType()) >> 4) * var->getArraySize();
|
||||
|
||||
this->setHighlight(offset, size, var->getVariableName());
|
||||
offset += size;
|
||||
} else {
|
||||
bool foundType = false;
|
||||
for (auto &structNode : findNodes<lang::ASTNodeStruct>(lang::ASTNode::Type::Struct, ast))
|
||||
if (structNode->getName() == var->getCustomVariableTypeName()) {
|
||||
size_t size = 0;
|
||||
for (size_t i = 0; i < var->getArraySize(); i++) {
|
||||
size = this->highlightStruct(ast, structNode, offset);
|
||||
|
||||
if (size == -1)
|
||||
return -1;
|
||||
|
||||
offset += size;
|
||||
}
|
||||
|
||||
offset += size;
|
||||
foundType = true;
|
||||
break;
|
||||
}
|
||||
|
||||
for (auto &typeDeclNode : findNodes<lang::ASTNodeTypeDecl>(lang::ASTNode::Type::TypeDecl, ast))
|
||||
if (typeDeclNode->getTypeName() == var->getCustomVariableTypeName()) {
|
||||
auto size = this->highlightUsingDecls(ast, typeDeclNode, var, offset);
|
||||
|
||||
if (size == -1)
|
||||
return -1;
|
||||
|
||||
offset = size;
|
||||
foundType = true;
|
||||
break;
|
||||
}
|
||||
|
||||
if (!foundType)
|
||||
return -1;
|
||||
}
|
||||
|
||||
}
|
||||
|
||||
return offset - startOffset;
|
||||
this->postEvent(Events::PatternChanged);
|
||||
}
|
||||
|
||||
}
|
||||
@@ -1,69 +1,76 @@
|
||||
#include "views/view_pattern_data.hpp"
|
||||
|
||||
#include "providers/provider.hpp"
|
||||
|
||||
#include <cstring>
|
||||
#include "lang/pattern_data.hpp"
|
||||
|
||||
namespace hex {
|
||||
|
||||
ViewPatternData::ViewPatternData(prv::Provider* &dataProvider, std::vector<Highlight> &highlights)
|
||||
: View(), m_dataProvider(dataProvider), m_highlights(highlights) {
|
||||
ViewPatternData::ViewPatternData(prv::Provider* &dataProvider, std::vector<lang::PatternData*> &patternData)
|
||||
: View("Pattern Data"), m_dataProvider(dataProvider), m_patternData(patternData) {
|
||||
|
||||
this->subscribeEvent(Events::PatternChanged, [this](auto data) {
|
||||
this->m_sortedPatternData.clear();
|
||||
});
|
||||
}
|
||||
|
||||
ViewPatternData::~ViewPatternData() {
|
||||
|
||||
this->unsubscribeEvent(Events::PatternChanged);
|
||||
}
|
||||
|
||||
std::string makeDisplayable(u8 *data, size_t size) {
|
||||
std::string result;
|
||||
for (u8* c = data; c < (data + size - 1); c++) {
|
||||
if (iscntrl(*c) || *c > 0x7F)
|
||||
result += " ";
|
||||
else
|
||||
result += *c;
|
||||
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_ScrollY)) {
|
||||
ImGui::TableSetupScrollFreeze(0, 1);
|
||||
ImGui::TableSetupColumn("Name", 0, -1, ImGui::GetID("name"));
|
||||
ImGui::TableSetupColumn("Color", 0, -1, ImGui::GetID("color"));
|
||||
ImGui::TableSetupColumn("Offset", 0, -1, ImGui::GetID("offset"));
|
||||
ImGui::TableSetupColumn("Size", 0, -1, ImGui::GetID("size"));
|
||||
ImGui::TableSetupColumn("Type", 0, -1, ImGui::GetID("type"));
|
||||
ImGui::TableSetupColumn("Value", 0, -1, ImGui::GetID("value"));
|
||||
|
||||
auto sortSpecs = ImGui::TableGetSortSpecs();
|
||||
|
||||
if (sortSpecs->SpecsDirty || sortedPatterns.empty()) {
|
||||
sortedPatterns = patterns;
|
||||
|
||||
std::sort(sortedPatterns.begin(), sortedPatterns.end(), [&sortSpecs, &provider](lang::PatternData* left, lang::PatternData* right) -> bool {
|
||||
return lang::PatternData::sortPatternDataTable(sortSpecs, provider, left, right);
|
||||
});
|
||||
|
||||
for (auto &pattern : sortedPatterns)
|
||||
pattern->sort(sortSpecs, provider);
|
||||
|
||||
sortSpecs->SpecsDirty = false;
|
||||
}
|
||||
|
||||
return true;
|
||||
}
|
||||
|
||||
return result;
|
||||
return false;
|
||||
}
|
||||
|
||||
void ViewPatternData::createView() {
|
||||
if (!this->m_windowOpen)
|
||||
return;
|
||||
|
||||
if (ImGui::Begin("Pattern Data", &this->m_windowOpen)) {
|
||||
ImGui::BeginChild("##scrolling", ImVec2(0, 0), false, ImGuiWindowFlags_NoMove | ImGuiWindowFlags_NoNav);
|
||||
|
||||
if (ImGui::Begin("Pattern Data", &this->getWindowOpenState(), ImGuiWindowFlags_NoCollapse)) {
|
||||
if (this->m_dataProvider != nullptr && this->m_dataProvider->isReadable()) {
|
||||
|
||||
for (auto&[offset, size, color, name] : this->m_highlights) {
|
||||
std::vector<u8> buffer(size + 1, 0x00);
|
||||
if (beginPatternDataTable(this->m_dataProvider, this->m_patternData, this->m_sortedPatternData)) {
|
||||
if (this->m_sortedPatternData.size() > 0) {
|
||||
ImGui::TableHeadersRow();
|
||||
|
||||
this->m_dataProvider->read(offset, buffer.data(), size);
|
||||
for (auto &patternData : this->m_sortedPatternData)
|
||||
patternData->createEntry(this->m_dataProvider);
|
||||
|
||||
if (size <= 8) {
|
||||
u64 data = 0;
|
||||
std::memcpy(&data, buffer.data(), size);
|
||||
}
|
||||
|
||||
ImGui::LabelText(name.c_str(), "[0x%08lx:0x%08lx] %lu (0x%08lx) \"%s\"", offset,
|
||||
offset + size, data, data,
|
||||
makeDisplayable(buffer.data(), buffer.size()).c_str());
|
||||
} else
|
||||
ImGui::LabelText(name.c_str(), "[0x%08lx:0x%08lx] [ ARRAY ] \"%s\"", offset, offset + size,
|
||||
makeDisplayable(buffer.data(), buffer.size()).c_str());
|
||||
ImGui::EndTable();
|
||||
}
|
||||
}
|
||||
|
||||
ImGui::EndChild();
|
||||
}
|
||||
}
|
||||
ImGui::End();
|
||||
}
|
||||
|
||||
void ViewPatternData::createMenu() {
|
||||
if (ImGui::BeginMenu("View")) {
|
||||
ImGui::MenuItem("Data View", "", &this->m_windowOpen);
|
||||
ImGui::EndMenu();
|
||||
}
|
||||
|
||||
}
|
||||
|
||||
}
|
||||
188
source/views/view_strings.cpp
Normal file
188
source/views/view_strings.cpp
Normal file
@@ -0,0 +1,188 @@
|
||||
#include "views/view_strings.hpp"
|
||||
|
||||
#include "providers/provider.hpp"
|
||||
#include "helpers/utils.hpp"
|
||||
|
||||
#include <cstring>
|
||||
|
||||
#include <llvm/Demangle/Demangle.h>
|
||||
|
||||
using namespace std::literals::string_literals;
|
||||
|
||||
namespace hex {
|
||||
|
||||
ViewStrings::ViewStrings(prv::Provider* &dataProvider) : View("Strings"), m_dataProvider(dataProvider) {
|
||||
View::subscribeEvent(Events::DataChanged, [this](const void*){
|
||||
this->m_foundStrings.clear();
|
||||
});
|
||||
|
||||
this->m_filter = new char[0xFFFF];
|
||||
std::memset(this->m_filter, 0x00, 0xFFFF);
|
||||
}
|
||||
|
||||
ViewStrings::~ViewStrings() {
|
||||
View::unsubscribeEvent(Events::DataChanged);
|
||||
delete[] this->m_filter;
|
||||
}
|
||||
|
||||
|
||||
void ViewStrings::createStringContextMenu(const FoundString &foundString) {
|
||||
if (ImGui::TableGetHoveredColumn() == 2 && ImGui::IsMouseReleased(1) && ImGui::IsItemHovered()) {
|
||||
ImGui::OpenPopup("StringContextMenu");
|
||||
this->m_selectedString = foundString.string;
|
||||
}
|
||||
if (ImGui::BeginPopup("StringContextMenu")) {
|
||||
if (ImGui::MenuItem("Copy string")) {
|
||||
ImGui::SetClipboardText(this->m_selectedString.c_str());
|
||||
}
|
||||
ImGui::Separator();
|
||||
if (ImGui::MenuItem("Demangle")) {
|
||||
this->m_demangledName = llvm::demangle(this->m_selectedString);
|
||||
if (!this->m_demangledName.empty())
|
||||
View::doLater([]{ ImGui::OpenPopup("Demangled Name"); });
|
||||
}
|
||||
ImGui::EndPopup();
|
||||
}
|
||||
}
|
||||
|
||||
|
||||
void ViewStrings::createView() {
|
||||
if (this->m_shouldInvalidate) {
|
||||
this->m_shouldInvalidate = false;
|
||||
|
||||
this->m_foundStrings.clear();
|
||||
|
||||
std::vector<u8> buffer(1024, 0x00);
|
||||
u32 foundCharacters = 0;
|
||||
for (u64 offset = 0; offset < this->m_dataProvider->getSize(); offset += buffer.size()) {
|
||||
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 < readSize; i++) {
|
||||
if (buffer[i] >= 0x20 && buffer[i] <= 0x7E)
|
||||
foundCharacters++;
|
||||
else {
|
||||
if (foundCharacters >= this->m_minimumLength) {
|
||||
FoundString foundString;
|
||||
|
||||
foundString.offset = offset + i - foundCharacters;
|
||||
foundString.size = foundCharacters;
|
||||
foundString.string.reserve(foundCharacters);
|
||||
foundString.string.resize(foundCharacters);
|
||||
this->m_dataProvider->read(foundString.offset, foundString.string.data(), foundCharacters);
|
||||
|
||||
this->m_foundStrings.push_back(foundString);
|
||||
}
|
||||
|
||||
foundCharacters = 0;
|
||||
}
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
|
||||
if (ImGui::Begin("Strings", &this->getWindowOpenState(), ImGuiWindowFlags_NoCollapse)) {
|
||||
if (this->m_dataProvider != nullptr && this->m_dataProvider->isReadable()) {
|
||||
if (ImGui::InputInt("Minimum length", &this->m_minimumLength, 1, 0))
|
||||
this->m_shouldInvalidate = true;
|
||||
|
||||
ImGui::InputText("Filter", this->m_filter, 0xFFFF);
|
||||
if (ImGui::Button("Extract"))
|
||||
this->m_shouldInvalidate = true;
|
||||
|
||||
ImGui::Separator();
|
||||
ImGui::NewLine();
|
||||
|
||||
if (ImGui::BeginTable("##strings", 3,
|
||||
ImGuiTableFlags_Borders | ImGuiTableFlags_Resizable | ImGuiTableFlags_Sortable |
|
||||
ImGuiTableFlags_Reorderable | ImGuiTableFlags_RowBg | ImGuiTableFlags_ScrollY)) {
|
||||
ImGui::TableSetupScrollFreeze(0, 1);
|
||||
ImGui::TableSetupColumn("Offset", 0, -1, ImGui::GetID("offset"));
|
||||
ImGui::TableSetupColumn("Size", 0, -1, ImGui::GetID("size"));
|
||||
ImGui::TableSetupColumn("String", 0, -1, ImGui::GetID("string"));
|
||||
|
||||
auto sortSpecs = ImGui::TableGetSortSpecs();
|
||||
|
||||
if (sortSpecs->SpecsDirty) {
|
||||
std::sort(this->m_foundStrings.begin(), this->m_foundStrings.end(),
|
||||
[&sortSpecs](FoundString &left, FoundString &right) -> bool {
|
||||
if (sortSpecs->Specs->ColumnUserID == ImGui::GetID("offset")) {
|
||||
if (sortSpecs->Specs->SortDirection == ImGuiSortDirection_Ascending)
|
||||
return left.offset > right.offset;
|
||||
else
|
||||
return left.offset < right.offset;
|
||||
} else if (sortSpecs->Specs->ColumnUserID == ImGui::GetID("size")) {
|
||||
if (sortSpecs->Specs->SortDirection == ImGuiSortDirection_Ascending)
|
||||
return left.size > right.size;
|
||||
else
|
||||
return left.size < right.size;
|
||||
} else if (sortSpecs->Specs->ColumnUserID == ImGui::GetID("string")) {
|
||||
if (sortSpecs->Specs->SortDirection == ImGuiSortDirection_Ascending)
|
||||
return left.string > right.string;
|
||||
else
|
||||
return left.string < right.string;
|
||||
}
|
||||
|
||||
return false;
|
||||
});
|
||||
|
||||
sortSpecs->SpecsDirty = false;
|
||||
}
|
||||
|
||||
ImGui::TableHeadersRow();
|
||||
|
||||
ImGuiListClipper clipper;
|
||||
clipper.Begin(this->m_foundStrings.size());
|
||||
|
||||
while (clipper.Step()) {
|
||||
for (u64 i = clipper.DisplayStart; i < clipper.DisplayEnd; i++) {
|
||||
auto &foundString = this->m_foundStrings[i];
|
||||
|
||||
if (strlen(this->m_filter) != 0 &&
|
||||
foundString.string.find(this->m_filter) == std::string::npos)
|
||||
continue;
|
||||
|
||||
ImGui::TableNextRow();
|
||||
ImGui::TableNextColumn();
|
||||
if (ImGui::Selectable(("##StringLine"s + std::to_string(i)).c_str(), false, ImGuiSelectableFlags_SpanAllColumns)) {
|
||||
Region selectRegion = { foundString.offset, foundString.size };
|
||||
View::postEvent(Events::SelectionChangeRequest, &selectRegion);
|
||||
}
|
||||
ImGui::PushID(i + 1);
|
||||
createStringContextMenu(foundString);
|
||||
ImGui::PopID();
|
||||
ImGui::SameLine();
|
||||
ImGui::Text("0x%08lx : 0x%08lx", foundString.offset, foundString.offset + foundString.size);
|
||||
ImGui::TableNextColumn();
|
||||
ImGui::Text("0x%04lx", foundString.size);
|
||||
ImGui::TableNextColumn();
|
||||
ImGui::Text("%s", foundString.string.c_str());
|
||||
}
|
||||
}
|
||||
clipper.End();
|
||||
|
||||
ImGui::EndTable();
|
||||
}
|
||||
}
|
||||
}
|
||||
ImGui::End();
|
||||
|
||||
if (ImGui::BeginPopup("Demangled Name")) {
|
||||
if (ImGui::BeginChild("##scrolling", ImVec2(500, 150))) {
|
||||
ImGui::Text("Demangled Name");
|
||||
ImGui::Separator();
|
||||
ImGui::TextWrapped("%s", this->m_demangledName.c_str());
|
||||
ImGui::EndChild();
|
||||
ImGui::NewLine();
|
||||
if (ImGui::Button("Copy"))
|
||||
ImGui::SetClipboardText(this->m_demangledName.c_str());
|
||||
}
|
||||
ImGui::EndPopup();
|
||||
}
|
||||
}
|
||||
|
||||
void ViewStrings::createMenu() {
|
||||
|
||||
}
|
||||
|
||||
}
|
||||
311
source/views/view_tools.cpp
Normal file
311
source/views/view_tools.cpp
Normal file
@@ -0,0 +1,311 @@
|
||||
#include "views/view_tools.hpp"
|
||||
|
||||
#include <cstring>
|
||||
#include <regex>
|
||||
#include <optional>
|
||||
|
||||
#include "providers/provider.hpp"
|
||||
#include "helpers/utils.hpp"
|
||||
|
||||
#include <llvm/Demangle/Demangle.h>
|
||||
|
||||
namespace hex {
|
||||
|
||||
ViewTools::ViewTools(hex::prv::Provider* &provider) : View("Tools"), m_dataProvider(provider) {
|
||||
this->m_mangledBuffer = new char[0xF'FFFF];
|
||||
std::memset(this->m_mangledBuffer, 0x00, 0xF'FFFF);
|
||||
|
||||
this->m_regexInput = new char[0xF'FFFF];
|
||||
this->m_regexPattern = new char[0xF'FFFF];
|
||||
this->m_replacePattern = new char[0xF'FFFF];
|
||||
std::memset(this->m_regexInput, 0x00, 0xF'FFFF);
|
||||
std::memset(this->m_regexPattern, 0x00, 0xF'FFFF);
|
||||
std::memset(this->m_replacePattern, 0x00, 0xF'FFFF);
|
||||
|
||||
|
||||
this->m_mathInput = new char[0xFFFF];
|
||||
std::memset(this->m_mathInput, 0x00, 0xFFFF);
|
||||
this->m_mathEvaluator.registerStandardVariables();
|
||||
this->m_mathEvaluator.registerStandardFunctions();
|
||||
|
||||
this->m_mathEvaluator.setFunction("clear", [this](auto args) -> std::optional<long double> {
|
||||
this->m_mathHistory.clear();
|
||||
this->m_lastMathError.clear();
|
||||
this->m_mathEvaluator.getVariables().clear();
|
||||
this->m_mathEvaluator.registerStandardVariables();
|
||||
std::memset(this->m_mathInput, 0x00, 0xFFFF);
|
||||
|
||||
return { };
|
||||
}, 0, 0);
|
||||
|
||||
this->m_mathEvaluator.setFunction("read", [this](auto args) -> std::optional<long double> {
|
||||
u8 value = 0;
|
||||
|
||||
if (this->m_dataProvider == nullptr || !this->m_dataProvider->isReadable() || args[0] >= this->m_dataProvider->getActualSize())
|
||||
return { };
|
||||
|
||||
this->m_dataProvider->read(args[0], &value, sizeof(u8));
|
||||
|
||||
return value;
|
||||
}, 1, 1);
|
||||
|
||||
this->m_mathEvaluator.setFunction("write", [this](auto args) -> std::optional<long double> {
|
||||
if (this->m_dataProvider == nullptr || !this->m_dataProvider->isWritable() || args[0] >= this->m_dataProvider->getActualSize())
|
||||
return { };
|
||||
|
||||
if (args[1] > 0xFF)
|
||||
return { };
|
||||
|
||||
u8 value = args[1];
|
||||
this->m_dataProvider->write(args[0], &value, sizeof(u8));
|
||||
|
||||
return { };
|
||||
}, 2, 2);
|
||||
}
|
||||
|
||||
ViewTools::~ViewTools() {
|
||||
delete[] this->m_mangledBuffer;
|
||||
|
||||
delete[] this->m_regexInput;
|
||||
delete[] this->m_regexPattern;
|
||||
delete[] this->m_replacePattern;
|
||||
|
||||
delete[] this->m_mathInput;
|
||||
}
|
||||
|
||||
void ViewTools::drawDemangler() {
|
||||
if (ImGui::CollapsingHeader("Itanium/MSVC demangler")) {
|
||||
if (ImGui::InputText("Mangled name", this->m_mangledBuffer, 0xF'FFFF)) {
|
||||
this->m_demangledName = llvm::demangle(this->m_mangledBuffer);
|
||||
}
|
||||
|
||||
ImGui::InputText("Demangled name", this->m_demangledName.data(), this->m_demangledName.size(), ImGuiInputTextFlags_ReadOnly);
|
||||
ImGui::NewLine();
|
||||
}
|
||||
}
|
||||
|
||||
void ViewTools::drawASCIITable() {
|
||||
if (ImGui::CollapsingHeader("ASCII table")) {
|
||||
ImGui::BeginTable("##asciitable", 4);
|
||||
ImGui::TableSetupColumn("");
|
||||
ImGui::TableSetupColumn("");
|
||||
ImGui::TableSetupColumn("");
|
||||
ImGui::TableSetupColumn("");
|
||||
|
||||
ImGui::TableNextColumn();
|
||||
|
||||
for (u8 tablePart = 0; tablePart < 4; tablePart++) {
|
||||
ImGui::BeginTable("##asciitablepart", this->m_asciiTableShowOctal ? 4 : 3, ImGuiTableFlags_BordersInnerV | ImGuiTableFlags_BordersOuter | ImGuiTableFlags_RowBg);
|
||||
ImGui::TableSetupColumn("dec");
|
||||
if (this->m_asciiTableShowOctal)
|
||||
ImGui::TableSetupColumn("oct");
|
||||
ImGui::TableSetupColumn("hex");
|
||||
ImGui::TableSetupColumn("char");
|
||||
|
||||
ImGui::TableHeadersRow();
|
||||
|
||||
u32 rowCount = 0;
|
||||
for (u8 i = 0; i < 0x80 / 4; i++) {
|
||||
ImGui::TableNextRow(ImGuiTableRowFlags_Headers);
|
||||
|
||||
ImGui::TableNextColumn();
|
||||
ImGui::Text("%02d", i + 32 * tablePart);
|
||||
|
||||
if (this->m_asciiTableShowOctal) {
|
||||
ImGui::TableNextColumn();
|
||||
ImGui::Text("0o%02o", i + 32 * tablePart);
|
||||
}
|
||||
|
||||
ImGui::TableNextColumn();
|
||||
ImGui::Text("0x%02x", i + 32 * tablePart);
|
||||
|
||||
ImGui::TableNextColumn();
|
||||
ImGui::Text("%s", makePrintable(i + 32 * tablePart).c_str());
|
||||
|
||||
ImGui::TableSetBgColor(ImGuiTableBgTarget_RowBg0, ((rowCount % 2) == 0) ? 0xFF101010 : 0xFF303030);
|
||||
|
||||
rowCount++;
|
||||
}
|
||||
|
||||
ImGui::EndTable();
|
||||
ImGui::TableNextColumn();
|
||||
}
|
||||
ImGui::EndTable();
|
||||
|
||||
ImGui::Checkbox("Show octal", &this->m_asciiTableShowOctal);
|
||||
ImGui::NewLine();
|
||||
}
|
||||
}
|
||||
|
||||
void ViewTools::drawRegexReplacer() {
|
||||
if (ImGui::CollapsingHeader("Regex replacer")) {
|
||||
bool shouldInvalidate;
|
||||
|
||||
shouldInvalidate = ImGui::InputText("Regex pattern", this->m_regexPattern, 0xF'FFFF);
|
||||
shouldInvalidate = ImGui::InputText("Replace pattern", this->m_replacePattern, 0xF'FFFF) || shouldInvalidate;
|
||||
shouldInvalidate = ImGui::InputTextMultiline("Input", this->m_regexInput, 0xF'FFFF) || shouldInvalidate;
|
||||
|
||||
if (shouldInvalidate) {
|
||||
try {
|
||||
this->m_regexOutput = std::regex_replace(this->m_regexInput, std::regex(this->m_regexPattern), this->m_replacePattern);
|
||||
} catch (std::regex_error&) {}
|
||||
}
|
||||
|
||||
ImGui::InputTextMultiline("Output", this->m_regexOutput.data(), this->m_regexOutput.size(), ImVec2(0, 0), ImGuiInputTextFlags_ReadOnly);
|
||||
ImGui::NewLine();
|
||||
|
||||
|
||||
}
|
||||
}
|
||||
|
||||
void ViewTools::drawColorPicker() {
|
||||
if (ImGui::CollapsingHeader("Color picker")) {
|
||||
ImGui::SetNextItemWidth(300.0F);
|
||||
ImGui::ColorPicker4("Color Picker", this->m_pickedColor.data(),
|
||||
ImGuiColorEditFlags_Uint8 | ImGuiColorEditFlags_AlphaBar | ImGuiColorEditFlags_DisplayRGB | ImGuiColorEditFlags_DisplayHex);
|
||||
ImGui::NewLine();
|
||||
}
|
||||
}
|
||||
|
||||
void ViewTools::drawMathEvaluator() {
|
||||
if (ImGui::CollapsingHeader("Calculator")) {
|
||||
if (ImGui::InputText("Input", this->m_mathInput, 0xFFFF, ImGuiInputTextFlags_EnterReturnsTrue | ImGuiInputTextFlags_AutoSelectAll)) {
|
||||
ImGui::SetKeyboardFocusHere();
|
||||
std::optional<long double> result;
|
||||
|
||||
try {
|
||||
result = this->m_mathEvaluator.evaluate(this->m_mathInput);
|
||||
} catch (std::invalid_argument &e) {
|
||||
this->m_lastMathError = e.what();
|
||||
}
|
||||
|
||||
if (result.has_value()) {
|
||||
this->m_mathHistory.push_back(result.value());
|
||||
std::memset(this->m_mathInput, 0x00, 0xFFFF);
|
||||
this->m_lastMathError.clear();
|
||||
}
|
||||
|
||||
}
|
||||
|
||||
if (!this->m_lastMathError.empty())
|
||||
ImGui::TextColored(ImColor(0xA00040FF), "Last Error: %s", this->m_lastMathError.c_str());
|
||||
else
|
||||
ImGui::NewLine();
|
||||
|
||||
enum class MathDisplayType { Standard, Scientific, Programmer } mathDisplayType;
|
||||
if (ImGui::BeginTabBar("##mathFormatTabBar")) {
|
||||
if (ImGui::BeginTabItem("Standard")) {
|
||||
mathDisplayType = MathDisplayType::Standard;
|
||||
ImGui::EndTabItem();
|
||||
}
|
||||
if (ImGui::BeginTabItem("Scientific")) {
|
||||
mathDisplayType = MathDisplayType::Scientific;
|
||||
ImGui::EndTabItem();
|
||||
}
|
||||
if (ImGui::BeginTabItem("Programmer")) {
|
||||
mathDisplayType = MathDisplayType::Programmer;
|
||||
ImGui::EndTabItem();
|
||||
}
|
||||
|
||||
ImGui::EndTabBar();
|
||||
}
|
||||
|
||||
if (ImGui::BeginTable("##mathWrapper", 2)) {
|
||||
ImGui::TableSetupColumn("##results");
|
||||
ImGui::TableSetupColumn("##variables", ImGuiTableColumnFlags_WidthStretch, 0.7);
|
||||
|
||||
ImGui::TableNextRow();
|
||||
ImGui::TableNextColumn();
|
||||
if (ImGui::BeginTable("##mathHistory", 1, ImGuiTableFlags_ScrollY | ImGuiTableFlags_Borders | ImGuiTableFlags_RowBg, ImVec2(0, 400))) {
|
||||
ImGui::TableSetupScrollFreeze(0, 1);
|
||||
ImGui::TableSetupColumn("History");
|
||||
|
||||
ImGuiListClipper clipper;
|
||||
clipper.Begin(this->m_mathHistory.size());
|
||||
|
||||
ImGui::TableHeadersRow();
|
||||
while (clipper.Step()) {
|
||||
for (u64 i = clipper.DisplayStart; i < clipper.DisplayEnd; i++) {
|
||||
if (i == 0)
|
||||
ImGui::PushStyleColor(ImGuiCol_Text, ImU32(ImColor(0xA5, 0x45, 0x45)));
|
||||
|
||||
ImGui::TableNextRow();
|
||||
ImGui::TableNextColumn();
|
||||
|
||||
switch (mathDisplayType) {
|
||||
case MathDisplayType::Standard:
|
||||
ImGui::Text("%.3Lf", this->m_mathHistory[(this->m_mathHistory.size() - 1) - i]);
|
||||
break;
|
||||
case MathDisplayType::Scientific:
|
||||
ImGui::Text("%.6Le", this->m_mathHistory[(this->m_mathHistory.size() - 1) - i]);
|
||||
break;
|
||||
case MathDisplayType::Programmer:
|
||||
ImGui::Text("0x%llX (%llu)",
|
||||
u64(this->m_mathHistory[(this->m_mathHistory.size() - 1) - i]),
|
||||
u64(this->m_mathHistory[(this->m_mathHistory.size() - 1) - i]));
|
||||
break;
|
||||
}
|
||||
|
||||
if (i == 0)
|
||||
ImGui::PopStyleColor();
|
||||
}
|
||||
}
|
||||
|
||||
clipper.End();
|
||||
|
||||
ImGui::EndTable();
|
||||
}
|
||||
|
||||
ImGui::TableNextColumn();
|
||||
if (ImGui::BeginTable("##mathVariables", 2, ImGuiTableFlags_ScrollY | ImGuiTableFlags_Borders | ImGuiTableFlags_RowBg, ImVec2(0, 400))) {
|
||||
ImGui::TableSetupScrollFreeze(0, 1);
|
||||
ImGui::TableSetupColumn("Name");
|
||||
ImGui::TableSetupColumn("Value");
|
||||
|
||||
ImGui::TableHeadersRow();
|
||||
for (const auto &[name, value] : this->m_mathEvaluator.getVariables()) {
|
||||
ImGui::TableNextRow();
|
||||
ImGui::TableNextColumn();
|
||||
ImGui::TextUnformatted(name.c_str());
|
||||
|
||||
ImGui::TableNextColumn();
|
||||
switch (mathDisplayType) {
|
||||
case MathDisplayType::Standard:
|
||||
ImGui::Text("%.3Lf", value);
|
||||
break;
|
||||
case MathDisplayType::Scientific:
|
||||
ImGui::Text("%.6Le", value);
|
||||
break;
|
||||
case MathDisplayType::Programmer:
|
||||
ImGui::Text("0x%llX (%llu)", u64(value), u64(value));
|
||||
break;
|
||||
}
|
||||
}
|
||||
|
||||
ImGui::EndTable();
|
||||
}
|
||||
|
||||
ImGui::EndTable();
|
||||
}
|
||||
|
||||
}
|
||||
}
|
||||
|
||||
void ViewTools::createView() {
|
||||
if (ImGui::Begin("Tools", &this->getWindowOpenState(), ImGuiWindowFlags_NoCollapse)) {
|
||||
|
||||
this->drawDemangler();
|
||||
this->drawASCIITable();
|
||||
this->drawRegexReplacer();
|
||||
this->drawMathEvaluator();
|
||||
this->drawColorPicker();
|
||||
|
||||
}
|
||||
ImGui::End();
|
||||
}
|
||||
|
||||
void ViewTools::createMenu() {
|
||||
|
||||
}
|
||||
|
||||
}
|
||||
@@ -3,6 +3,7 @@
|
||||
#include <iostream>
|
||||
|
||||
#include "imgui.h"
|
||||
#include "imgui_internal.h"
|
||||
#include "imgui_impl_glfw.h"
|
||||
#include "imgui_impl_opengl3.h"
|
||||
|
||||
@@ -11,6 +12,53 @@
|
||||
|
||||
namespace hex {
|
||||
|
||||
constexpr auto MenuBarItems = { "File", "Edit", "View", "Help" };
|
||||
|
||||
void *ImHexSettingsHandler_ReadOpenFn(ImGuiContext *ctx, ImGuiSettingsHandler *, const char *) {
|
||||
return ctx; // Unused, but the return value has to be non-null
|
||||
}
|
||||
|
||||
void ImHexSettingsHandler_ReadLine(ImGuiContext*, ImGuiSettingsHandler *handler, void *, const char* line) {
|
||||
auto *window = reinterpret_cast<Window *>(handler->UserData);
|
||||
|
||||
float scale;
|
||||
if (sscanf(line, "Scale=%f", &scale) == 1) { window->m_globalScale = scale; }
|
||||
else if (sscanf(line, "FontScale=%f", &scale) == 1) { window->m_fontScale = scale; }
|
||||
else {
|
||||
for (auto &view : window->m_views) {
|
||||
std::string format = view->getName() + "=%d";
|
||||
sscanf(line, format.c_str(), &view->getWindowOpenState());
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
void ImHexSettingsHandler_ApplyAll(ImGuiContext *ctx, ImGuiSettingsHandler *handler) {
|
||||
auto *window = reinterpret_cast<Window *>(handler->UserData);
|
||||
auto &style = ImGui::GetStyle();
|
||||
auto &io = ImGui::GetIO();
|
||||
|
||||
if (window->m_globalScale != 0.0f)
|
||||
style.ScaleAllSizes(window->m_globalScale);
|
||||
if (window->m_fontScale != 0.0f)
|
||||
io.FontGlobalScale = window->m_fontScale;
|
||||
}
|
||||
|
||||
void ImHexSettingsHandler_WriteAll(ImGuiContext* ctx, ImGuiSettingsHandler *handler, ImGuiTextBuffer *buf) {
|
||||
auto *window = reinterpret_cast<Window *>(handler->UserData);
|
||||
|
||||
buf->reserve(buf->size() + 0x20); // Ballpark reserve
|
||||
|
||||
buf->appendf("[%s][General]\n", handler->TypeName);
|
||||
buf->appendf("Scale=%.1f\n", window->m_globalScale);
|
||||
buf->appendf("FontScale=%.1f\n", window->m_fontScale);
|
||||
|
||||
for (auto &view : window->m_views) {
|
||||
buf->appendf("%s=%d\n", view->getName().c_str(), view->getWindowOpenState());
|
||||
}
|
||||
|
||||
buf->append("\n");
|
||||
}
|
||||
|
||||
Window::Window() {
|
||||
this->initGLFW();
|
||||
this->initImGui();
|
||||
@@ -28,10 +76,25 @@ namespace hex {
|
||||
while (!glfwWindowShouldClose(this->m_window)) {
|
||||
this->frameBegin();
|
||||
|
||||
for (const auto &call : View::getDeferedCalls())
|
||||
call();
|
||||
View::getDeferedCalls().clear();
|
||||
|
||||
for (auto &view : this->m_views) {
|
||||
if (!view->getWindowOpenState())
|
||||
continue;
|
||||
|
||||
ImGui::SetNextWindowSizeConstraints(ImVec2(480, 720), ImVec2(FLT_MAX, FLT_MAX));
|
||||
view->createView();
|
||||
}
|
||||
|
||||
View::drawCommonInterfaces();
|
||||
|
||||
#ifdef DEBUG
|
||||
if (this->m_demoWindowOpen)
|
||||
ImGui::ShowDemoWindow(&this->m_demoWindowOpen);
|
||||
#endif
|
||||
|
||||
this->frameEnd();
|
||||
}
|
||||
}
|
||||
@@ -44,63 +107,90 @@ namespace hex {
|
||||
ImGui_ImplGlfw_NewFrame();
|
||||
ImGui::NewFrame();
|
||||
|
||||
ImGuiWindowFlags windowFlags = ImGuiWindowFlags_MenuBar | ImGuiWindowFlags_NoDocking;
|
||||
ImGuiViewport* viewport = ImGui::GetMainViewport();
|
||||
ImGui::SetNextWindowPos(viewport->GetWorkPos());
|
||||
ImGui::SetNextWindowSize(viewport->GetWorkSize());
|
||||
ImGui::SetNextWindowViewport(viewport->ID);
|
||||
ImGui::PushStyleVar(ImGuiStyleVar_WindowRounding, 0.0f);
|
||||
ImGui::PushStyleVar(ImGuiStyleVar_WindowBorderSize, 0.0f);
|
||||
windowFlags |= ImGuiWindowFlags_NoTitleBar | ImGuiWindowFlags_NoCollapse | ImGuiWindowFlags_NoMove | ImGuiWindowFlags_NoResize;
|
||||
windowFlags |= ImGuiWindowFlags_NoBringToFrontOnFocus | ImGuiWindowFlags_NoNavFocus;
|
||||
|
||||
ImGui::Begin("DockSpace", nullptr, windowFlags);
|
||||
ImGui::PopStyleVar(2);
|
||||
ImGui::DockSpace(ImGui::GetID("MainDock"), ImVec2(0.0f, 0.0f), ImGuiDockNodeFlags_None);
|
||||
ImGuiWindowFlags windowFlags = ImGuiWindowFlags_MenuBar | ImGuiWindowFlags_NoDocking
|
||||
| ImGuiWindowFlags_NoTitleBar | ImGuiWindowFlags_NoCollapse
|
||||
| ImGuiWindowFlags_NoMove | ImGuiWindowFlags_NoResize
|
||||
| ImGuiWindowFlags_NoNavFocus | ImGuiWindowFlags_NoBringToFrontOnFocus;
|
||||
|
||||
ImGui::BeginMenuBar();
|
||||
if (ImGui::Begin("DockSpace", nullptr, windowFlags)) {
|
||||
ImGui::PopStyleVar(2);
|
||||
ImGui::DockSpace(ImGui::GetID("MainDock"), ImVec2(0.0f, 0.0f), ImGuiDockNodeFlags_None);
|
||||
|
||||
for (auto &view : this->m_views)
|
||||
view->createMenu();
|
||||
if (ImGui::BeginMenuBar()) {
|
||||
|
||||
if (ImGui::BeginMenu("View")) {
|
||||
ImGui::MenuItem("Display FPS", "", &this->m_fpsVisible);
|
||||
ImGui::EndMenu();
|
||||
}
|
||||
for (auto menu : MenuBarItems)
|
||||
if (ImGui::BeginMenu(menu)) ImGui::EndMenu();
|
||||
|
||||
if (this->m_fpsVisible) {
|
||||
char buffer[0x20];
|
||||
snprintf(buffer, 0x20, "%.1f FPS", ImGui::GetIO().Framerate);
|
||||
if (ImGui::BeginMenu("View")) {
|
||||
for (auto &view : this->m_views) {
|
||||
if (view->hasViewMenuItemEntry())
|
||||
ImGui::MenuItem((view->getName() + " View").c_str(), "", &view->getWindowOpenState());
|
||||
}
|
||||
ImGui::EndMenu();
|
||||
}
|
||||
|
||||
ImGui::SameLine(ImGui::GetWindowWidth() - ImGui::GetFontSize() * strlen(buffer) + 20);
|
||||
ImGui::TextUnformatted(buffer);
|
||||
}
|
||||
for (auto &view : this->m_views) {
|
||||
view->createMenu();
|
||||
}
|
||||
|
||||
if (ImGui::BeginMenu("View")) {
|
||||
ImGui::Separator();
|
||||
ImGui::MenuItem("Display FPS", "", &this->m_fpsVisible);
|
||||
#ifdef DEBUG
|
||||
ImGui::MenuItem("Demo View", "", &this->m_demoWindowOpen);
|
||||
#endif
|
||||
ImGui::EndMenu();
|
||||
}
|
||||
|
||||
ImGui::EndMenuBar();
|
||||
if (this->m_fpsVisible) {
|
||||
char buffer[0x20];
|
||||
snprintf(buffer, 0x20, "%.1f FPS", ImGui::GetIO().Framerate);
|
||||
|
||||
ImGui::End();
|
||||
ImGui::SameLine(ImGui::GetWindowWidth() - ImGui::GetFontSize() * strlen(buffer) + 20);
|
||||
ImGui::TextUnformatted(buffer);
|
||||
}
|
||||
|
||||
if (auto &[key, mods] = Window::s_currShortcut; key != -1) {
|
||||
for (auto &view : this->m_views) {
|
||||
if (view->handleShortcut(key, mods))
|
||||
break;
|
||||
ImGui::EndMenuBar();
|
||||
}
|
||||
|
||||
if (auto &[key, mods] = Window::s_currShortcut; key != -1) {
|
||||
for (auto &view : this->m_views) {
|
||||
if (view->getWindowOpenState()) {
|
||||
if (view->handleShortcut(key, mods))
|
||||
break;
|
||||
}
|
||||
}
|
||||
|
||||
Window::s_currShortcut = { -1, -1 };
|
||||
}
|
||||
|
||||
Window::s_currShortcut = { -1, -1 };
|
||||
}
|
||||
ImGui::End();
|
||||
|
||||
}
|
||||
|
||||
void Window::frameEnd() {
|
||||
ImGui::Render();
|
||||
int display_w, display_h;
|
||||
glfwGetFramebufferSize(this->m_window, &display_w, &display_h);
|
||||
glViewport(0, 0, display_w, display_h);
|
||||
|
||||
int displayWidth, displayHeight;
|
||||
glfwGetFramebufferSize(this->m_window, &displayWidth, &displayHeight);
|
||||
glViewport(0, 0, displayWidth, displayHeight);
|
||||
glClearColor(0.45f, 0.55f, 0.60f, 1.00f);
|
||||
glClear(GL_COLOR_BUFFER_BIT);
|
||||
ImGui_ImplOpenGL3_RenderDrawData(ImGui::GetDrawData());
|
||||
|
||||
GLFWwindow* backup_current_context = glfwGetCurrentContext();
|
||||
ImGui::UpdatePlatformWindows();
|
||||
ImGui::RenderPlatformWindowsDefault();
|
||||
glfwMakeContextCurrent(backup_current_context);
|
||||
|
||||
glfwSwapBuffers(this->m_window);
|
||||
}
|
||||
|
||||
@@ -112,16 +202,18 @@ namespace hex {
|
||||
if (!glfwInit())
|
||||
throw std::runtime_error("Failed to initialize GLFW!");
|
||||
|
||||
#ifdef __APPLE__
|
||||
glfwWindowHint(GLFW_OPENGL_FORWARD_COMPAT, GL_TRUE);
|
||||
#endif
|
||||
#ifdef __APPLE__
|
||||
glfwWindowHint(GLFW_OPENGL_FORWARD_COMPAT, GL_TRUE);
|
||||
#endif
|
||||
|
||||
glfwWindowHint(GLFW_CONTEXT_VERSION_MAJOR, 3);
|
||||
glfwWindowHint(GLFW_CONTEXT_VERSION_MINOR, 2);
|
||||
glfwWindowHint(GLFW_OPENGL_PROFILE, GLFW_OPENGL_CORE_PROFILE);
|
||||
|
||||
|
||||
this->m_window = glfwCreateWindow(1280, 720, "ImHex", nullptr, nullptr);
|
||||
|
||||
|
||||
if (this->m_window == nullptr)
|
||||
throw std::runtime_error("Failed to create window!");
|
||||
|
||||
@@ -133,7 +225,19 @@ namespace hex {
|
||||
Window::s_currShortcut = { key, mods };
|
||||
});
|
||||
|
||||
glfwSetWindowSizeLimits(this->m_window, 720, 480, GLFW_DONT_CARE, GLFW_DONT_CARE);
|
||||
glfwSetDropCallback(this->m_window, [](GLFWwindow *window, int count, const char **paths) {
|
||||
if (count != 1)
|
||||
return;
|
||||
|
||||
View::postEvent(Events::FileDropped, paths[0]);
|
||||
});
|
||||
|
||||
glfwSetWindowCloseCallback(this->m_window, [](GLFWwindow *window) {
|
||||
View::postEvent(Events::WindowClosing, window);
|
||||
});
|
||||
|
||||
|
||||
glfwSetWindowSizeLimits(this->m_window, 720, 480, GLFW_DONT_CARE, GLFW_DONT_CARE);
|
||||
|
||||
if (gladLoadGL() == 0)
|
||||
throw std::runtime_error("Failed to initialize OpenGL loader!");
|
||||
@@ -141,9 +245,26 @@ namespace hex {
|
||||
|
||||
void Window::initImGui() {
|
||||
IMGUI_CHECKVERSION();
|
||||
ImGui::CreateContext();
|
||||
ImGuiIO& io = ImGui::GetIO(); (void)io;
|
||||
io.ConfigFlags |= ImGuiConfigFlags_DockingEnable;
|
||||
auto *ctx = ImGui::CreateContext();
|
||||
ImGuiIO& io = ImGui::GetIO();
|
||||
ImGuiStyle& style = ImGui::GetStyle();
|
||||
|
||||
io.ConfigFlags |= ImGuiConfigFlags_DockingEnable | ImGuiConfigFlags_ViewportsEnable;
|
||||
io.ConfigViewportsNoTaskBarIcon = true;
|
||||
|
||||
style.WindowMenuButtonPosition = ImGuiDir_None;
|
||||
style.IndentSpacing = 10.0F;
|
||||
|
||||
// Install custom settings handler
|
||||
ImGuiSettingsHandler handler;
|
||||
handler.TypeName = "ImHex";
|
||||
handler.TypeHash = ImHashStr("ImHex");
|
||||
handler.ReadOpenFn = ImHexSettingsHandler_ReadOpenFn;
|
||||
handler.ReadLineFn = ImHexSettingsHandler_ReadLine;
|
||||
handler.ApplyAllFn = ImHexSettingsHandler_ApplyAll;
|
||||
handler.WriteAllFn = ImHexSettingsHandler_WriteAll;
|
||||
handler.UserData = this;
|
||||
ctx->SettingsHandlers.push_back(handler);
|
||||
|
||||
ImGui::StyleColorsDark();
|
||||
|
||||
|
||||
Reference in New Issue
Block a user